mirror of https://github.com/PCSX2/pcsx2.git
GS: Rewrite presentation interface and OSD
This commit is contained in:
parent
1348c8880e
commit
0c36647506
|
@ -15,7 +15,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pcsx2", "pcsx2\pcsx2.vcxpro
|
||||||
{12728250-16EC-4DC6-94D7-E21DD88947F8} = {12728250-16EC-4DC6-94D7-E21DD88947F8}
|
{12728250-16EC-4DC6-94D7-E21DD88947F8} = {12728250-16EC-4DC6-94D7-E21DD88947F8}
|
||||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E} = {D6973076-9317-4EF2-A0B8-B7A18AC0713E}
|
{D6973076-9317-4EF2-A0B8-B7A18AC0713E} = {D6973076-9317-4EF2-A0B8-B7A18AC0713E}
|
||||||
{27F17499-A372-4408-8AFA-4F9F4584FBD3} = {27F17499-A372-4408-8AFA-4F9F4584FBD3}
|
{27F17499-A372-4408-8AFA-4F9F4584FBD3} = {27F17499-A372-4408-8AFA-4F9F4584FBD3}
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B} = {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}
|
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SoundTouch", "3rdparty\soundtouch\SoundTouch.vcxproj", "{E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SoundTouch", "3rdparty\soundtouch\SoundTouch.vcxproj", "{E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}"
|
||||||
|
@ -40,8 +39,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pthreads4w", "3rdparty\pthr
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "baseclasses", "3rdparty\baseclasses\baseclasses.vcxproj", "{27F17499-A372-4408-8AFA-4F9F4584FBD3}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "baseclasses", "3rdparty\baseclasses\baseclasses.vcxproj", "{27F17499-A372-4408-8AFA-4F9F4584FBD3}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "freetype", "3rdparty\freetype\builds\windows\freetype.vcxproj", "{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}"
|
|
||||||
EndProject
|
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "3rdparty\xz\liblzma.vcxproj", "{12728250-16EC-4DC6-94D7-E21DD88947F8}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "3rdparty\xz\liblzma.vcxproj", "{12728250-16EC-4DC6-94D7-E21DD88947F8}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmt", "3rdparty\fmt\fmt.vcxproj", "{449AD25E-424A-4714-BABC-68706CDCC33B}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmt", "3rdparty\fmt\fmt.vcxproj", "{449AD25E-424A-4714-BABC-68706CDCC33B}"
|
||||||
|
@ -367,30 +364,6 @@ Global
|
||||||
{27F17499-A372-4408-8AFA-4F9F4584FBD3}.Release|Win32.Build.0 = Release|Win32
|
{27F17499-A372-4408-8AFA-4F9F4584FBD3}.Release|Win32.Build.0 = Release|Win32
|
||||||
{27F17499-A372-4408-8AFA-4F9F4584FBD3}.Release|x64.ActiveCfg = Release|x64
|
{27F17499-A372-4408-8AFA-4F9F4584FBD3}.Release|x64.ActiveCfg = Release|x64
|
||||||
{27F17499-A372-4408-8AFA-4F9F4584FBD3}.Release|x64.Build.0 = Release|x64
|
{27F17499-A372-4408-8AFA-4F9F4584FBD3}.Release|x64.Build.0 = Release|x64
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Debug AVX2|Win32.ActiveCfg = Debug|Win32
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Debug AVX2|Win32.Build.0 = Debug|Win32
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Debug AVX2|x64.ActiveCfg = Debug|x64
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Debug AVX2|x64.Build.0 = Debug|x64
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Debug|Win32.ActiveCfg = Debug|Win32
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Debug|Win32.Build.0 = Debug|Win32
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Devel AVX2|Win32.ActiveCfg = Devel|Win32
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Devel AVX2|Win32.Build.0 = Devel|Win32
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Devel AVX2|x64.ActiveCfg = Devel|x64
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Devel AVX2|x64.Build.0 = Devel|x64
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Devel|Win32.ActiveCfg = Devel|Win32
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Devel|Win32.Build.0 = Devel|Win32
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Devel|x64.ActiveCfg = Devel|x64
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Devel|x64.Build.0 = Devel|x64
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release AVX2|Win32.ActiveCfg = Release|Win32
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release AVX2|Win32.Build.0 = Release|Win32
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release AVX2|x64.ActiveCfg = Release|x64
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release AVX2|x64.Build.0 = Release|x64
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release|Win32.ActiveCfg = Release|Win32
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release|Win32.Build.0 = Release|Win32
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release|x64.Build.0 = Release|x64
|
|
||||||
{12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug AVX2|Win32.ActiveCfg = Debug|Win32
|
{12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug AVX2|Win32.ActiveCfg = Debug|Win32
|
||||||
{12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug AVX2|Win32.Build.0 = Debug|Win32
|
{12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug AVX2|Win32.Build.0 = Debug|Win32
|
||||||
{12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug AVX2|x64.ActiveCfg = Debug|x64
|
{12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug AVX2|x64.ActiveCfg = Debug|x64
|
||||||
|
@ -671,7 +644,6 @@ Global
|
||||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
{D6973076-9317-4EF2-A0B8-B7A18AC0713E} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
||||||
{0FAE817D-9A32-4830-857E-81DA57246E16} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
{0FAE817D-9A32-4830-857E-81DA57246E16} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
||||||
{27F17499-A372-4408-8AFA-4F9F4584FBD3} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
{27F17499-A372-4408-8AFA-4F9F4584FBD3} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
||||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
|
||||||
{12728250-16EC-4DC6-94D7-E21DD88947F8} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
{12728250-16EC-4DC6-94D7-E21DD88947F8} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
||||||
{449AD25E-424A-4714-BABC-68706CDCC33B} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
{449AD25E-424A-4714-BABC-68706CDCC33B} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
||||||
{47AFDBEF-F15F-4BC0-B436-5BE443C3F80F} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
{47AFDBEF-F15F-4BC0-B436-5BE443C3F80F} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
|
||||||
|
|
|
@ -367,13 +367,4 @@ PS_OUTPUT ps_yuv(PS_INPUT input)
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
PS_OUTPUT ps_osd(PS_INPUT input)
|
|
||||||
{
|
|
||||||
PS_OUTPUT output;
|
|
||||||
|
|
||||||
output.c = input.c * float4(1.0, 1.0, 1.0, sample_c(input.t).a);
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -272,13 +272,6 @@ void ps_convert_rgba_8i()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ps_osd
|
|
||||||
void ps_osd()
|
|
||||||
{
|
|
||||||
SV_Target0 = PSin_c * vec4(1.0, 1.0, 1.0, sample_c().r);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef ps_filter_transparency
|
#ifdef ps_filter_transparency
|
||||||
void ps_filter_transparency()
|
void ps_filter_transparency()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,181 +0,0 @@
|
||||||
|
|
||||||
# Copyright (c) 2012, Intel Corporation
|
|
||||||
# Copyright (c) 2019 Sony Interactive Entertainment Inc.
|
|
||||||
#
|
|
||||||
# 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 name of Intel Corporation nor the names of its 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 OWNER 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.
|
|
||||||
#
|
|
||||||
# Try to find Harfbuzz include and library directories.
|
|
||||||
#
|
|
||||||
# After successful discovery, this will set for inclusion where needed:
|
|
||||||
# HarfBuzz_INCLUDE_DIRS - containg the HarfBuzz headers
|
|
||||||
# HarfBuzz_LIBRARIES - containg the HarfBuzz library
|
|
||||||
|
|
||||||
#[=======================================================================[.rst:
|
|
||||||
FindHarfBuzz
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Find HarfBuzz headers and libraries.
|
|
||||||
|
|
||||||
Imported Targets
|
|
||||||
^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
``HarfBuzz::HarfBuzz``
|
|
||||||
The HarfBuzz library, if found.
|
|
||||||
|
|
||||||
``HarfBuzz::ICU``
|
|
||||||
The HarfBuzz ICU library, if found.
|
|
||||||
|
|
||||||
Result Variables
|
|
||||||
^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
This will define the following variables in your project:
|
|
||||||
|
|
||||||
``HarfBuzz_FOUND``
|
|
||||||
true if (the requested version of) HarfBuzz is available.
|
|
||||||
``HarfBuzz_VERSION``
|
|
||||||
the version of HarfBuzz.
|
|
||||||
``HarfBuzz_LIBRARIES``
|
|
||||||
the libraries to link against to use HarfBuzz.
|
|
||||||
``HarfBuzz_INCLUDE_DIRS``
|
|
||||||
where to find the HarfBuzz headers.
|
|
||||||
``HarfBuzz_COMPILE_OPTIONS``
|
|
||||||
this should be passed to target_compile_options(), if the
|
|
||||||
target is not used for linking
|
|
||||||
|
|
||||||
#]=======================================================================]
|
|
||||||
|
|
||||||
find_package(PkgConfig QUIET)
|
|
||||||
pkg_check_modules(PC_HARFBUZZ QUIET harfbuzz)
|
|
||||||
set(HarfBuzz_COMPILE_OPTIONS ${PC_HARFBUZZ_CFLAGS_OTHER})
|
|
||||||
set(HarfBuzz_VERSION ${PC_HARFBUZZ_CFLAGS_VERSION})
|
|
||||||
|
|
||||||
find_path(HarfBuzz_INCLUDE_DIR
|
|
||||||
NAMES hb.h
|
|
||||||
HINTS ${PC_HARFBUZZ_INCLUDEDIR} ${PC_HARFBUZZ_INCLUDE_DIRS}
|
|
||||||
PATH_SUFFIXES harfbuzz
|
|
||||||
)
|
|
||||||
|
|
||||||
find_library(HarfBuzz_LIBRARY
|
|
||||||
NAMES ${HarfBuzz_NAMES} harfbuzz
|
|
||||||
HINTS ${PC_HARFBUZZ_LIBDIR} ${PC_HARFBUZZ_LIBRARY_DIRS}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (HarfBuzz_INCLUDE_DIR AND NOT HarfBuzz_VERSION)
|
|
||||||
if (EXISTS "${HarfBuzz_INCLUDE_DIR}/hb-version.h")
|
|
||||||
file(READ "${HarfBuzz_INCLUDE_DIR}/hb-version.h" _harfbuzz_version_content)
|
|
||||||
|
|
||||||
string(REGEX MATCH "#define +HB_VERSION_STRING +\"([0-9]+\\.[0-9]+\\.[0-9]+)\"" _dummy "${_harfbuzz_version_content}")
|
|
||||||
set(HarfBuzz_VERSION "${CMAKE_MATCH_1}")
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if ("${HarfBuzz_FIND_VERSION}" VERSION_GREATER "${HarfBuzz_VERSION}")
|
|
||||||
message(FATAL_ERROR "Required version (" ${HarfBuzz_FIND_VERSION} ") is higher than found version (" ${HarfBuzz_VERSION} ")")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Find components
|
|
||||||
if (HarfBuzz_INCLUDE_DIR AND HarfBuzz_LIBRARY)
|
|
||||||
set(_HarfBuzz_REQUIRED_LIBS_FOUND ON)
|
|
||||||
set(HarfBuzz_LIBS_FOUND "HarfBuzz (required): ${HarfBuzz_LIBRARY}")
|
|
||||||
else ()
|
|
||||||
set(_HarfBuzz_REQUIRED_LIBS_FOUND OFF)
|
|
||||||
set(HarfBuzz_LIBS_NOT_FOUND "HarfBuzz (required)")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if ("ICU" IN_LIST HarfBuzz_FIND_COMPONENTS)
|
|
||||||
pkg_check_modules(PC_HARFBUZZ_ICU QUIET harfbuzz-icu)
|
|
||||||
set(HarfBuzz_ICU_COMPILE_OPTIONS ${PC_HARFBUZZ_ICU_CFLAGS_OTHER})
|
|
||||||
|
|
||||||
find_library(HarfBuzz_ICU_LIBRARY
|
|
||||||
NAMES ${HarfBuzz_ICU_NAMES} harfbuzz-icu
|
|
||||||
HINTS ${PC_HARFBUZZ_ICU_LIBDIR} ${PC_HARFBUZZ_ICU_LIBRARY_DIRS}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (HarfBuzz_ICU_LIBRARY)
|
|
||||||
if (HarfBuzz_FIND_REQUIRED_ICU)
|
|
||||||
list(APPEND HarfBuzz_LIBS_FOUND "ICU (required): ${HarfBuzz_ICU_LIBRARY}")
|
|
||||||
else ()
|
|
||||||
list(APPEND HarfBuzz_LIBS_FOUND "ICU (optional): ${HarfBuzz_ICU_LIBRARY}")
|
|
||||||
endif ()
|
|
||||||
else ()
|
|
||||||
if (HarfBuzz_FIND_REQUIRED_ICU)
|
|
||||||
set(_HarfBuzz_REQUIRED_LIBS_FOUND OFF)
|
|
||||||
list(APPEND HarfBuzz_LIBS_NOT_FOUND "ICU (required)")
|
|
||||||
else ()
|
|
||||||
list(APPEND HarfBuzz_LIBS_NOT_FOUND "ICU (optional)")
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (NOT HarfBuzz_FIND_QUIETLY)
|
|
||||||
if (HarfBuzz_LIBS_FOUND)
|
|
||||||
message(STATUS "Found the following HarfBuzz libraries:")
|
|
||||||
foreach (found ${HarfBuzz_LIBS_FOUND})
|
|
||||||
message(STATUS " ${found}")
|
|
||||||
endforeach ()
|
|
||||||
endif ()
|
|
||||||
if (HarfBuzz_LIBS_NOT_FOUND)
|
|
||||||
message(STATUS "The following HarfBuzz libraries were not found:")
|
|
||||||
foreach (found ${HarfBuzz_LIBS_NOT_FOUND})
|
|
||||||
message(STATUS " ${found}")
|
|
||||||
endforeach ()
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
find_package_handle_standard_args(HarfBuzz
|
|
||||||
FOUND_VAR HarfBuzz_FOUND
|
|
||||||
REQUIRED_VARS HarfBuzz_INCLUDE_DIR HarfBuzz_LIBRARY _HarfBuzz_REQUIRED_LIBS_FOUND
|
|
||||||
VERSION_VAR HarfBuzz_VERSION
|
|
||||||
)
|
|
||||||
|
|
||||||
if (HarfBuzz_LIBRARY AND NOT TARGET HarfBuzz::HarfBuzz)
|
|
||||||
add_library(HarfBuzz::HarfBuzz UNKNOWN IMPORTED GLOBAL)
|
|
||||||
set_target_properties(HarfBuzz::HarfBuzz PROPERTIES
|
|
||||||
IMPORTED_LOCATION "${HarfBuzz_LIBRARY}"
|
|
||||||
INTERFACE_COMPILE_OPTIONS "${HarfBuzz_COMPILE_OPTIONS}"
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${HarfBuzz_INCLUDE_DIR}"
|
|
||||||
)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (HarfBuzz_ICU_LIBRARY AND NOT TARGET HarfBuzz::ICU)
|
|
||||||
add_library(HarfBuzz::ICU UNKNOWN IMPORTED GLOBAL)
|
|
||||||
set_target_properties(HarfBuzz::ICU PROPERTIES
|
|
||||||
IMPORTED_LOCATION "${HarfBuzz_ICU_LIBRARY}"
|
|
||||||
INTERFACE_COMPILE_OPTIONS "${HarfBuzz_ICU_COMPILE_OPTIONS}"
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${HarfBuzz_INCLUDE_DIR}"
|
|
||||||
)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
mark_as_advanced(
|
|
||||||
HarfBuzz_INCLUDE_DIR
|
|
||||||
HarfBuzz_LIBRARY
|
|
||||||
HarfBuzz_ICU_LIBRARY
|
|
||||||
)
|
|
||||||
|
|
||||||
if (HarfBuzz_FOUND)
|
|
||||||
set(HarfBuzz_LIBRARIES ${HarfBuzz_LIBRARY} ${HarfBuzz_ICU_LIBRARY})
|
|
||||||
set(HarfBuzz_INCLUDE_DIRS ${HarfBuzz_INCLUDE_DIR})
|
|
||||||
endif ()
|
|
|
@ -11,7 +11,6 @@ if (WIN32)
|
||||||
add_subdirectory(3rdparty/libjpeg EXCLUDE_FROM_ALL)
|
add_subdirectory(3rdparty/libjpeg EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(3rdparty/libsamplerate EXCLUDE_FROM_ALL)
|
add_subdirectory(3rdparty/libsamplerate EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(3rdparty/baseclasses EXCLUDE_FROM_ALL)
|
add_subdirectory(3rdparty/baseclasses EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(3rdparty/freetype EXCLUDE_FROM_ALL)
|
|
||||||
add_subdirectory(3rdparty/pthreads4w EXCLUDE_FROM_ALL)
|
add_subdirectory(3rdparty/pthreads4w EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(3rdparty/soundtouch EXCLUDE_FROM_ALL)
|
add_subdirectory(3rdparty/soundtouch EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(3rdparty/wil EXCLUDE_FROM_ALL)
|
add_subdirectory(3rdparty/wil EXCLUDE_FROM_ALL)
|
||||||
|
@ -26,7 +25,6 @@ else()
|
||||||
find_package(PCAP REQUIRED)
|
find_package(PCAP REQUIRED)
|
||||||
find_package(LibXml2 REQUIRED)
|
find_package(LibXml2 REQUIRED)
|
||||||
make_imported_target_if_missing(LibXml2::LibXml2 LibXml2)
|
make_imported_target_if_missing(LibXml2::LibXml2 LibXml2)
|
||||||
find_package(Freetype REQUIRED) # GS OSD
|
|
||||||
find_package(Gettext) # translation tool
|
find_package(Gettext) # translation tool
|
||||||
find_package(LibLZMA REQUIRED)
|
find_package(LibLZMA REQUIRED)
|
||||||
make_imported_target_if_missing(LibLZMA::LibLZMA LIBLZMA)
|
make_imported_target_if_missing(LibLZMA::LibLZMA LIBLZMA)
|
||||||
|
@ -168,12 +166,6 @@ else()
|
||||||
if(WAYLAND_API)
|
if(WAYLAND_API)
|
||||||
find_package(Wayland REQUIRED)
|
find_package(Wayland REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
#----------------------------------------
|
|
||||||
# Use system include
|
|
||||||
#----------------------------------------
|
|
||||||
find_package(HarfBuzz)
|
|
||||||
|
|
||||||
endif(WIN32)
|
endif(WIN32)
|
||||||
|
|
||||||
# Require threads on all OSes.
|
# Require threads on all OSes.
|
||||||
|
|
|
@ -103,7 +103,11 @@ protected:
|
||||||
|
|
||||||
#define SettingsWrapSection(section) const char* CURRENT_SETTINGS_SECTION = section;
|
#define SettingsWrapSection(section) const char* CURRENT_SETTINGS_SECTION = section;
|
||||||
#define SettingsWrapEntry(var) wrap.Entry(CURRENT_SETTINGS_SECTION, #var, var, var)
|
#define SettingsWrapEntry(var) wrap.Entry(CURRENT_SETTINGS_SECTION, #var, var, var)
|
||||||
|
#define SettingsWrapEntryEx(var, name) wrap.Entry(CURRENT_SETTINGS_SECTION, name, var, var)
|
||||||
#define SettingsWrapBitfield(varname) varname = wrap.EntryBitfield(CURRENT_SETTINGS_SECTION, #varname, varname, varname)
|
#define SettingsWrapBitfield(varname) varname = wrap.EntryBitfield(CURRENT_SETTINGS_SECTION, #varname, varname, varname)
|
||||||
#define SettingsWrapBitBool(varname) varname = wrap.EntryBitBool(CURRENT_SETTINGS_SECTION, #varname, !!varname, varname)
|
#define SettingsWrapBitBool(varname) varname = wrap.EntryBitBool(CURRENT_SETTINGS_SECTION, #varname, !!varname, varname)
|
||||||
#define SettingsWrapBitfieldEx(varname, textname) varname = wrap.EntryBitfield(CURRENT_SETTINGS_SECTION, textname, varname, varname)
|
#define SettingsWrapBitfieldEx(varname, textname) varname = wrap.EntryBitfield(CURRENT_SETTINGS_SECTION, textname, varname, varname)
|
||||||
#define SettingsWrapBitBoolEx(varname, textname) varname = wrap.EntryBitBool(CURRENT_SETTINGS_SECTION, textname, !!varname, varname)
|
#define SettingsWrapBitBoolEx(varname, textname) varname = wrap.EntryBitBool(CURRENT_SETTINGS_SECTION, textname, !!varname, varname)
|
||||||
|
#define SettingsWrapEnumEx(varname, textname, names) wrap.EnumEntry(CURRENT_SETTINGS_SECTION, textname, varname, names, varname)
|
||||||
|
#define SettingsWrapIntEnumEx(varname, textname) varname = static_cast<decltype(varname)>(wrap.EntryBitfield(CURRENT_SETTINGS_SECTION, textname, static_cast<int>(varname), static_cast<int>(varname)))
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,8 @@ set(pcsx2Sources
|
||||||
Gif_Unit.cpp
|
Gif_Unit.cpp
|
||||||
GS.cpp
|
GS.cpp
|
||||||
GSState.cpp
|
GSState.cpp
|
||||||
|
Host.cpp
|
||||||
|
HostDisplay.cpp
|
||||||
Hw.cpp
|
Hw.cpp
|
||||||
HwRead.cpp
|
HwRead.cpp
|
||||||
HwWrite.cpp
|
HwWrite.cpp
|
||||||
|
@ -187,6 +189,7 @@ set(pcsx2Headers
|
||||||
GS.h
|
GS.h
|
||||||
Hardware.h
|
Hardware.h
|
||||||
Host.h
|
Host.h
|
||||||
|
HostDisplay.h
|
||||||
Hw.h
|
Hw.h
|
||||||
IopBios.h
|
IopBios.h
|
||||||
IopCommon.h
|
IopCommon.h
|
||||||
|
@ -607,7 +610,6 @@ set(pcsx2GSSources
|
||||||
GS/Renderers/Common/GSDevice.cpp
|
GS/Renderers/Common/GSDevice.cpp
|
||||||
GS/Renderers/Common/GSDirtyRect.cpp
|
GS/Renderers/Common/GSDirtyRect.cpp
|
||||||
GS/Renderers/Common/GSFunctionMap.cpp
|
GS/Renderers/Common/GSFunctionMap.cpp
|
||||||
GS/Renderers/Common/GSOsdManager.cpp
|
|
||||||
GS/Renderers/Common/GSRenderer.cpp
|
GS/Renderers/Common/GSRenderer.cpp
|
||||||
GS/Renderers/Common/GSTexture.cpp
|
GS/Renderers/Common/GSTexture.cpp
|
||||||
GS/Renderers/Common/GSVertexTrace.cpp
|
GS/Renderers/Common/GSVertexTrace.cpp
|
||||||
|
@ -672,7 +674,6 @@ set(pcsx2GSHeaders
|
||||||
GS/Renderers/Common/GSDirtyRect.h
|
GS/Renderers/Common/GSDirtyRect.h
|
||||||
GS/Renderers/Common/GSFastList.h
|
GS/Renderers/Common/GSFastList.h
|
||||||
GS/Renderers/Common/GSFunctionMap.h
|
GS/Renderers/Common/GSFunctionMap.h
|
||||||
GS/Renderers/Common/GSOsdManager.h
|
|
||||||
GS/Renderers/Common/GSRenderer.h
|
GS/Renderers/Common/GSRenderer.h
|
||||||
GS/Renderers/Common/GSTexture.h
|
GS/Renderers/Common/GSTexture.h
|
||||||
GS/Renderers/Common/GSVertex.h
|
GS/Renderers/Common/GSVertex.h
|
||||||
|
@ -872,6 +873,27 @@ set(pcsx2DebugToolsHeaders
|
||||||
DebugTools/DisVUops.h
|
DebugTools/DisVUops.h
|
||||||
DebugTools/BiosDebugData.h)
|
DebugTools/BiosDebugData.h)
|
||||||
|
|
||||||
|
# Frontend sources
|
||||||
|
set(pcsx2FrontendSources
|
||||||
|
Frontend/ImGuiManager.cpp
|
||||||
|
Frontend/OpenGLHostDisplay.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Frontend headers
|
||||||
|
set(pcsx2FrontendHeaders
|
||||||
|
Frontend/ImGuiManager.h
|
||||||
|
Frontend/OpenGLHostDisplay.h
|
||||||
|
)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
list(APPEND pcsx2FrontendSources
|
||||||
|
Frontend/D3D11HostDisplay.cpp
|
||||||
|
)
|
||||||
|
list(APPEND pcsx2FrontendHeaders
|
||||||
|
Frontend/D3D11HostDisplay.h
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
# gui sources
|
# gui sources
|
||||||
set(pcsx2GuiSources
|
set(pcsx2GuiSources
|
||||||
gui/AppAssert.cpp
|
gui/AppAssert.cpp
|
||||||
|
@ -952,6 +974,7 @@ set(pcsx2GuiHeaders
|
||||||
gui/AppConfig.h
|
gui/AppConfig.h
|
||||||
gui/AppCoreThread.h
|
gui/AppCoreThread.h
|
||||||
gui/AppEventListeners.h
|
gui/AppEventListeners.h
|
||||||
|
gui/AppHost.h
|
||||||
gui/AppForwardDefs.h
|
gui/AppForwardDefs.h
|
||||||
gui/App.h
|
gui/App.h
|
||||||
gui/ApplyState.h
|
gui/ApplyState.h
|
||||||
|
@ -1296,19 +1319,27 @@ target_sources(PCSX2 PRIVATE
|
||||||
${pcsx2GSSources}
|
${pcsx2GSSources}
|
||||||
${pcsx2DebugToolsSources}
|
${pcsx2DebugToolsSources}
|
||||||
${pcsx2DebugToolsHeaders}
|
${pcsx2DebugToolsHeaders}
|
||||||
${pcsx2GuiSources}
|
${pcsx2FrontendSources}
|
||||||
${pcsx2GuiResources}
|
${pcsx2FrontendHeaders}
|
||||||
${pcsx2GuiHeaders}
|
|
||||||
${pcsx2ps2Sources}
|
${pcsx2ps2Sources}
|
||||||
${pcsx2ps2Headers}
|
${pcsx2ps2Headers}
|
||||||
${pcsx2RecordingSources}
|
|
||||||
${pcsx2RecordingVirtualPadResources}
|
|
||||||
${pcsx2RecordingHeaders}
|
|
||||||
${pcsx2SystemSources}
|
${pcsx2SystemSources}
|
||||||
${pcsx2SystemHeaders}
|
${pcsx2SystemHeaders}
|
||||||
${pcsx2UtilitiesSources}
|
${pcsx2UtilitiesSources}
|
||||||
${pcsx2UtilitiesHeaders})
|
${pcsx2UtilitiesHeaders})
|
||||||
|
|
||||||
|
# gui sources when not doing a qt build
|
||||||
|
if (NOT PCSX2_CORE)
|
||||||
|
target_sources(PCSX2 PRIVATE
|
||||||
|
${pcsx2GuiSources}
|
||||||
|
${pcsx2GuiResources}
|
||||||
|
${pcsx2GuiHeaders}
|
||||||
|
${pcsx2RecordingSources}
|
||||||
|
${pcsx2RecordingVirtualPadResources}
|
||||||
|
${pcsx2RecordingHeaders}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
# platform sources
|
# platform sources
|
||||||
# Linux
|
# Linux
|
||||||
if(Linux)
|
if(Linux)
|
||||||
|
@ -1368,6 +1399,7 @@ endif()
|
||||||
target_link_libraries(PCSX2_FLAGS INTERFACE
|
target_link_libraries(PCSX2_FLAGS INTERFACE
|
||||||
common
|
common
|
||||||
glad
|
glad
|
||||||
|
imgui
|
||||||
fmt::fmt
|
fmt::fmt
|
||||||
ryml
|
ryml
|
||||||
chdr-static
|
chdr-static
|
||||||
|
@ -1376,7 +1408,6 @@ target_link_libraries(PCSX2_FLAGS INTERFACE
|
||||||
PkgConfig::SOUNDTOUCH
|
PkgConfig::SOUNDTOUCH
|
||||||
PkgConfig::SAMPLERATE
|
PkgConfig::SAMPLERATE
|
||||||
PNG::PNG
|
PNG::PNG
|
||||||
Freetype::Freetype
|
|
||||||
LibLZMA::LibLZMA
|
LibLZMA::LibLZMA
|
||||||
${LIBC_LIBRARIES}
|
${LIBC_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
@ -1410,7 +1441,6 @@ elseif(APPLE)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(PCSX2_FLAGS INTERFACE
|
target_link_libraries(PCSX2_FLAGS INTERFACE
|
||||||
GTK::gtk
|
GTK::gtk
|
||||||
HarfBuzz::HarfBuzz
|
|
||||||
OpenGL::GL
|
OpenGL::GL
|
||||||
PCAP::PCAP
|
PCAP::PCAP
|
||||||
LibXml2::LibXml2
|
LibXml2::LibXml2
|
||||||
|
|
206
pcsx2/Config.h
206
pcsx2/Config.h
|
@ -32,6 +32,7 @@ enum GamefixId
|
||||||
Fix_FpuMultiply = GamefixId_FIRST,
|
Fix_FpuMultiply = GamefixId_FIRST,
|
||||||
Fix_FpuNegDiv,
|
Fix_FpuNegDiv,
|
||||||
Fix_GoemonTlbMiss,
|
Fix_GoemonTlbMiss,
|
||||||
|
Fix_SoftwareRendererFMV,
|
||||||
Fix_SkipMpeg,
|
Fix_SkipMpeg,
|
||||||
Fix_OPHFlag,
|
Fix_OPHFlag,
|
||||||
Fix_EETiming,
|
Fix_EETiming,
|
||||||
|
@ -97,6 +98,73 @@ enum class LimiterModeType : u8
|
||||||
Nominal,
|
Nominal,
|
||||||
Turbo,
|
Turbo,
|
||||||
Slomo,
|
Slomo,
|
||||||
|
Unlimited,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GSRendererType : s8
|
||||||
|
{
|
||||||
|
Auto = -1,
|
||||||
|
DX11 = 3,
|
||||||
|
Null = 11,
|
||||||
|
OGL = 12,
|
||||||
|
SW = 13,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GSInterlaceMode : u8
|
||||||
|
{
|
||||||
|
Off,
|
||||||
|
WeaveTFF,
|
||||||
|
WeaveBFF,
|
||||||
|
BobTFF,
|
||||||
|
BobBFF,
|
||||||
|
BlendTFF,
|
||||||
|
BlendBFF,
|
||||||
|
Automatic,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ordering was done to keep compatibility with older ini file.
|
||||||
|
enum class BiFiltering : u8
|
||||||
|
{
|
||||||
|
Nearest,
|
||||||
|
Forced,
|
||||||
|
PS2,
|
||||||
|
Forced_But_Sprite,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TriFiltering : u8
|
||||||
|
{
|
||||||
|
Off,
|
||||||
|
PS2,
|
||||||
|
Forced,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class HWMipmapLevel : s8
|
||||||
|
{
|
||||||
|
Automatic = -1,
|
||||||
|
Off,
|
||||||
|
Basic,
|
||||||
|
Full
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class CRCHackLevel : s8
|
||||||
|
{
|
||||||
|
Automatic = -1,
|
||||||
|
Off,
|
||||||
|
Minimum,
|
||||||
|
Partial,
|
||||||
|
Full,
|
||||||
|
Aggressive
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class AccBlendLevel : u8
|
||||||
|
{
|
||||||
|
Minimum,
|
||||||
|
Basic,
|
||||||
|
Medium,
|
||||||
|
High,
|
||||||
|
Full,
|
||||||
|
Ultra,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Template function for casting enumerations to their underlying type
|
// Template function for casting enumerations to their underlying type
|
||||||
|
@ -318,6 +386,61 @@ struct Pcsx2Config
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
struct GSOptions
|
struct GSOptions
|
||||||
{
|
{
|
||||||
|
static const char* AspectRatioNames[];
|
||||||
|
static const char* FMVAspectRatioSwitchNames[];
|
||||||
|
|
||||||
|
static const char* GetRendererName(GSRendererType type);
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
u64 bitset;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
bool
|
||||||
|
IntegerScaling : 1,
|
||||||
|
LinearPresent : 1,
|
||||||
|
UseDebugDevice : 1,
|
||||||
|
UseBlitSwapChain : 1,
|
||||||
|
DisableShaderCache : 1,
|
||||||
|
OsdShowMessages : 1,
|
||||||
|
OsdShowSpeed : 1,
|
||||||
|
OsdShowFPS : 1,
|
||||||
|
OsdShowCPU : 1,
|
||||||
|
OsdShowResolution : 1,
|
||||||
|
OsdShowGSStats : 1;
|
||||||
|
|
||||||
|
bool
|
||||||
|
HWDisableReadbacks : 1,
|
||||||
|
AccurateDATE : 1,
|
||||||
|
GPUPaletteConversion : 1,
|
||||||
|
ConservativeFramebuffer : 1,
|
||||||
|
AutoFlushSW : 1,
|
||||||
|
PreloadFrameWithGSData : 1,
|
||||||
|
WrapGSMem : 1,
|
||||||
|
Mipmap : 1,
|
||||||
|
AA1 : 1,
|
||||||
|
UserHacks : 1,
|
||||||
|
UserHacks_AlignSpriteX : 1,
|
||||||
|
UserHacks_AutoFlush : 1,
|
||||||
|
UserHacks_CPUFBConversion : 1,
|
||||||
|
UserHacks_DisableDepthSupport : 1,
|
||||||
|
UserHacks_DisablePartialInvalidation : 1,
|
||||||
|
UserHacks_DisableSafeFeatures : 1,
|
||||||
|
UserHacks_MergePPSprite : 1,
|
||||||
|
UserHacks_WildHack : 1,
|
||||||
|
UserHacks_TextureInsideRt : 1,
|
||||||
|
FXAA : 1,
|
||||||
|
ShadeBoost : 1,
|
||||||
|
ShaderFX : 1,
|
||||||
|
DumpGSData : 1,
|
||||||
|
SaveRT : 1,
|
||||||
|
SaveFrame : 1,
|
||||||
|
SaveTexture : 1,
|
||||||
|
SaveDepth : 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
int VsyncQueueSize{2};
|
int VsyncQueueSize{2};
|
||||||
|
|
||||||
// forces the MTGS to execute tags/tasks in fully blocking/synchronous
|
// forces the MTGS to execute tags/tasks in fully blocking/synchronous
|
||||||
|
@ -337,45 +460,73 @@ struct Pcsx2Config
|
||||||
|
|
||||||
AspectRatioType AspectRatio{AspectRatioType::R4_3};
|
AspectRatioType AspectRatio{AspectRatioType::R4_3};
|
||||||
FMVAspectRatioSwitchType FMVAspectRatioSwitch{FMVAspectRatioSwitchType::Off};
|
FMVAspectRatioSwitchType FMVAspectRatioSwitch{FMVAspectRatioSwitchType::Off};
|
||||||
|
GSInterlaceMode InterlaceMode{GSInterlaceMode::Automatic};
|
||||||
|
|
||||||
double Zoom{100.0};
|
double Zoom{100.0};
|
||||||
double StretchY{100.0};
|
double StretchY{100.0};
|
||||||
double OffsetX{0.0};
|
double OffsetX{0.0};
|
||||||
double OffsetY{0.0};
|
double OffsetY{0.0};
|
||||||
|
|
||||||
|
double OsdScale{100.0};
|
||||||
|
|
||||||
|
GSRendererType Renderer{GSRendererType::Auto};
|
||||||
|
uint UpscaleMultiplier{1};
|
||||||
|
|
||||||
|
HWMipmapLevel HWMipmap{HWMipmapLevel::Automatic};
|
||||||
|
AccBlendLevel AccurateBlendingUnit{AccBlendLevel::Basic};
|
||||||
|
CRCHackLevel CRCHack{CRCHackLevel::Automatic};
|
||||||
|
BiFiltering TextureFiltering{BiFiltering::PS2};
|
||||||
|
int Dithering{2};
|
||||||
|
int MaxAnisotropy{0};
|
||||||
|
int SWExtraThreads{2};
|
||||||
|
int SWExtraThreadsHeight{4};
|
||||||
|
int TVShader{0};
|
||||||
|
int SkipDraw{0};
|
||||||
|
int SkipDrawOffset{0};
|
||||||
|
|
||||||
|
int UserHacks_HalfBottomOverride{-1};
|
||||||
|
int UserHacks_HalfPixelOffset{0};
|
||||||
|
int UserHacks_RoundSprite{0};
|
||||||
|
int UserHacks_TCOffsetX{0};
|
||||||
|
int UserHacks_TCOffsetY{0};
|
||||||
|
TriFiltering UserHacks_TriFilter{TriFiltering::Off};
|
||||||
|
|
||||||
|
int ShadeBoost_Brightness{50};
|
||||||
|
int ShadeBoost_Contrast{50};
|
||||||
|
int ShadeBoost_Saturation{50};
|
||||||
|
int SaveN{0};
|
||||||
|
int SaveL{5000};
|
||||||
|
std::string Adapter;
|
||||||
|
std::string ShaderFX_Conf;
|
||||||
|
std::string ShaderFX_GLSL;
|
||||||
|
|
||||||
|
GSOptions();
|
||||||
|
|
||||||
void LoadSave(SettingsWrapper& wrap);
|
void LoadSave(SettingsWrapper& wrap);
|
||||||
|
|
||||||
int GetVsync() const;
|
#ifndef PCSX2_CORE
|
||||||
|
/// Because some GS settings are stored in a separate INI in wx, we need a way to reload them.
|
||||||
|
/// This is because the SettingsWrapper is only created on full save/load.
|
||||||
|
void ReloadIniSettings();
|
||||||
|
#else
|
||||||
|
void LoadSaveIniSettings(SettingsWrapper& wrap);
|
||||||
|
#endif
|
||||||
|
|
||||||
bool operator==(const GSOptions& right) const
|
/// Sets user hack values to defaults when user hacks are not enabled.
|
||||||
{
|
void MaskUserHacks();
|
||||||
return OpEqu(SynchronousMTGS) &&
|
|
||||||
OpEqu(VsyncQueueSize) &&
|
|
||||||
|
|
||||||
OpEqu(FrameSkipEnable) &&
|
/// Returns true if any of the hardware renderers are selected.
|
||||||
OpEqu(FrameLimitEnable) &&
|
bool UseHardwareRenderer() const;
|
||||||
OpEqu(VsyncEnable) &&
|
|
||||||
|
|
||||||
OpEqu(LimitScalar) &&
|
/// Returns false if the compared to the old settings, we need to reopen GS.
|
||||||
OpEqu(FramerateNTSC) &&
|
/// (i.e. renderer change, swap chain mode change, etc.)
|
||||||
OpEqu(FrameratePAL) &&
|
bool RestartOptionsAreEqual(const GSOptions& right) const;
|
||||||
|
|
||||||
OpEqu(FramesToDraw) &&
|
/// Returns false if any options need to be applied to the MTGS.
|
||||||
OpEqu(FramesToSkip) &&
|
bool OptionsAreEqual(const GSOptions& right) const;
|
||||||
|
|
||||||
OpEqu(AspectRatio) &&
|
bool operator==(const GSOptions& right) const;
|
||||||
OpEqu(FMVAspectRatioSwitch) &&
|
bool operator!=(const GSOptions& right) const;
|
||||||
|
|
||||||
OpEqu(Zoom) &&
|
|
||||||
OpEqu(StretchY) &&
|
|
||||||
OpEqu(OffsetX) &&
|
|
||||||
OpEqu(OffsetY);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const GSOptions& right) const
|
|
||||||
{
|
|
||||||
return !this->operator==(right);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SPU2Options
|
struct SPU2Options
|
||||||
|
@ -463,6 +614,7 @@ struct Pcsx2Config
|
||||||
FpuMulHack : 1, // Tales of Destiny hangs.
|
FpuMulHack : 1, // Tales of Destiny hangs.
|
||||||
FpuNegDivHack : 1, // Gundam games messed up camera-view.
|
FpuNegDivHack : 1, // Gundam games messed up camera-view.
|
||||||
GoemonTlbHack : 1, // Gomeon tlb miss hack. The game need to access unmapped virtual address. Instead to handle it as exception, tlb are preloaded at startup
|
GoemonTlbHack : 1, // Gomeon tlb miss hack. The game need to access unmapped virtual address. Instead to handle it as exception, tlb are preloaded at startup
|
||||||
|
SoftwareRendererFMVHack : 1, // Switches to software renderer for FMVs
|
||||||
SkipMPEGHack : 1, // Skips MPEG videos (Katamari and other games need this)
|
SkipMPEGHack : 1, // Skips MPEG videos (Katamari and other games need this)
|
||||||
OPHFlagHack : 1, // Bleach Blade Battlers
|
OPHFlagHack : 1, // Bleach Blade Battlers
|
||||||
EETimingHack : 1, // General purpose timing hack.
|
EETimingHack : 1, // General purpose timing hack.
|
||||||
|
@ -680,6 +832,8 @@ struct Pcsx2Config
|
||||||
|
|
||||||
bool MultitapEnabled(uint port) const;
|
bool MultitapEnabled(uint port) const;
|
||||||
|
|
||||||
|
VsyncMode GetEffectiveVsyncMode() const;
|
||||||
|
|
||||||
bool operator==(const Pcsx2Config& right) const;
|
bool operator==(const Pcsx2Config& right) const;
|
||||||
bool operator!=(const Pcsx2Config& right) const
|
bool operator!=(const Pcsx2Config& right) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "gui/App.h"
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "R3000A.h"
|
#include "R3000A.h"
|
||||||
#include "Counters.h"
|
#include "Counters.h"
|
||||||
|
@ -28,10 +27,17 @@
|
||||||
#include "GS.h"
|
#include "GS.h"
|
||||||
#include "VUmicro.h"
|
#include "VUmicro.h"
|
||||||
#include "PerformanceMetrics.h"
|
#include "PerformanceMetrics.h"
|
||||||
|
#include "Patch.h"
|
||||||
|
|
||||||
#include "ps2/HwInternal.h"
|
#include "ps2/HwInternal.h"
|
||||||
#include "Sio.h"
|
#include "Sio.h"
|
||||||
|
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
|
#include "gui/App.h"
|
||||||
|
#else
|
||||||
|
#include "VMManager.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef DISABLE_RECORDING
|
#ifndef DISABLE_RECORDING
|
||||||
# include "Recording/InputRecordingControls.h"
|
# include "Recording/InputRecordingControls.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -283,6 +289,12 @@ const char* ReportVideoMode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* ReportInterlaceMode()
|
||||||
|
{
|
||||||
|
const u64& smode2 = *(u64*)PS2GS_BASE(GS_SMODE2);
|
||||||
|
return (smode2 & 1) ? ((smode2 & 2) ? "Interlaced (Frame)" : "Interlaced (Field)") : "Progressive";
|
||||||
|
}
|
||||||
|
|
||||||
double GetVerticalFrequency()
|
double GetVerticalFrequency()
|
||||||
{
|
{
|
||||||
// Note about NTSC/PAL "double strike" modes:
|
// Note about NTSC/PAL "double strike" modes:
|
||||||
|
@ -416,10 +428,68 @@ void frameLimitReset()
|
||||||
m_iStart = GetCPUTicks();
|
m_iStart = GetCPUTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FMV switch stuff
|
||||||
|
extern uint eecount_on_last_vdec;
|
||||||
|
extern bool FMVstarted;
|
||||||
|
extern bool EnableFMV;
|
||||||
|
static bool s_last_fmv_state = false;
|
||||||
|
|
||||||
|
static __fi void DoFMVSwitch()
|
||||||
|
{
|
||||||
|
bool new_fmv_state = s_last_fmv_state;
|
||||||
|
if (EnableFMV)
|
||||||
|
{
|
||||||
|
DevCon.WriteLn("FMV started");
|
||||||
|
new_fmv_state = true;
|
||||||
|
EnableFMV = false;
|
||||||
|
}
|
||||||
|
else if (FMVstarted)
|
||||||
|
{
|
||||||
|
const int diff = cpuRegs.cycle - eecount_on_last_vdec;
|
||||||
|
if (diff > 60000000)
|
||||||
|
{
|
||||||
|
DevCon.WriteLn("FMV ended");
|
||||||
|
new_fmv_state = false;
|
||||||
|
FMVstarted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_fmv_state == s_last_fmv_state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
s_last_fmv_state = new_fmv_state;
|
||||||
|
|
||||||
|
switch (EmuConfig.GS.FMVAspectRatioSwitch)
|
||||||
|
{
|
||||||
|
case FMVAspectRatioSwitchType::Off:
|
||||||
|
break;
|
||||||
|
case FMVAspectRatioSwitchType::R4_3:
|
||||||
|
EmuConfig.CurrentAspectRatio = new_fmv_state ? AspectRatioType::R4_3 : EmuConfig.GS.AspectRatio;
|
||||||
|
break;
|
||||||
|
case FMVAspectRatioSwitchType::R16_9:
|
||||||
|
EmuConfig.CurrentAspectRatio = new_fmv_state ? AspectRatioType::R16_9 : EmuConfig.GS.AspectRatio;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EmuConfig.Gamefixes.SoftwareRendererFMVHack && EmuConfig.GS.UseHardwareRenderer())
|
||||||
|
{
|
||||||
|
// we don't use the sw toggle here, because it'll change back to auto if set to sw
|
||||||
|
GetMTGS().SwitchRenderer(new_fmv_state ? GSRendererType::SW : EmuConfig.GS.Renderer, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Convenience function to update UI thread and set patches.
|
// Convenience function to update UI thread and set patches.
|
||||||
static __fi void frameLimitUpdateCore()
|
static __fi void frameLimitUpdateCore()
|
||||||
{
|
{
|
||||||
|
DoFMVSwitch();
|
||||||
|
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
GetCoreThread().VsyncInThread();
|
GetCoreThread().VsyncInThread();
|
||||||
|
#else
|
||||||
|
VMManager::Internal::VSyncOnCPUThread();
|
||||||
|
#endif
|
||||||
Cpu->CheckExecutionState();
|
Cpu->CheckExecutionState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,7 +499,7 @@ static __fi void frameLimitUpdateCore()
|
||||||
static __fi void frameLimit()
|
static __fi void frameLimit()
|
||||||
{
|
{
|
||||||
// Framelimiter off in settings? Framelimiter go brrr.
|
// Framelimiter off in settings? Framelimiter go brrr.
|
||||||
if (!EmuConfig.GS.FrameLimitEnable)
|
if (EmuConfig.GS.LimitScalar == 0.0)
|
||||||
{
|
{
|
||||||
frameLimitUpdateCore();
|
frameLimitUpdateCore();
|
||||||
return;
|
return;
|
||||||
|
@ -482,6 +552,8 @@ static __fi void VSyncStart(u32 sCycle)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
PerformanceMetrics::Update();
|
||||||
|
|
||||||
frameLimit(); // limit FPS
|
frameLimit(); // limit FPS
|
||||||
gsPostVsyncStart(); // MUST be after framelimit; doing so before causes funk with frame times!
|
gsPostVsyncStart(); // MUST be after framelimit; doing so before causes funk with frame times!
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,7 @@ struct SyncCounter
|
||||||
#define MODE_HBLANK 0x1 //Set for the remaining ~1/6 of 1 Scanline
|
#define MODE_HBLANK 0x1 //Set for the remaining ~1/6 of 1 Scanline
|
||||||
|
|
||||||
extern const char* ReportVideoMode();
|
extern const char* ReportVideoMode();
|
||||||
|
extern const char* ReportInterlaceMode();
|
||||||
extern Counter counters[4];
|
extern Counter counters[4];
|
||||||
extern SyncCounter hsyncCounter;
|
extern SyncCounter hsyncCounter;
|
||||||
extern SyncCounter vsyncCounter;
|
extern SyncCounter vsyncCounter;
|
||||||
|
|
|
@ -0,0 +1,754 @@
|
||||||
|
/* PCSX2 - PS2 Emulator for PCs
|
||||||
|
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||||
|
*
|
||||||
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||||
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||||
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PrecompiledHeader.h"
|
||||||
|
|
||||||
|
#include "Frontend/D3D11HostDisplay.h"
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_impl_dx11.h"
|
||||||
|
#include <array>
|
||||||
|
#include <dxgi1_5.h>
|
||||||
|
|
||||||
|
#include "common/Assertions.h"
|
||||||
|
#include "common/Console.h"
|
||||||
|
#include "common/StringUtil.h"
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#pragma comment(lib, "d3d11.lib")
|
||||||
|
#pragma comment(lib, "dxgi.lib")
|
||||||
|
|
||||||
|
class D3D11HostDisplayTexture : public HostDisplayTexture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
D3D11HostDisplayTexture(Microsoft::WRL::ComPtr<ID3D11Texture2D> texture,
|
||||||
|
Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> srv, u32 width, u32 height, u32 layers,
|
||||||
|
u32 levels, bool dynamic)
|
||||||
|
: m_texture(std::move(texture))
|
||||||
|
, m_srv(std::move(srv))
|
||||||
|
, m_width(width)
|
||||||
|
, m_height(height)
|
||||||
|
, m_layers(layers)
|
||||||
|
, m_levels(levels)
|
||||||
|
, m_dynamic(dynamic)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~D3D11HostDisplayTexture() override = default;
|
||||||
|
|
||||||
|
void* GetHandle() const override { return m_srv.Get(); }
|
||||||
|
u32 GetWidth() const override { return m_width; }
|
||||||
|
u32 GetHeight() const override { return m_height; }
|
||||||
|
u32 GetLayers() const override { return m_layers; }
|
||||||
|
u32 GetLevels() const override { return m_levels; }
|
||||||
|
|
||||||
|
__fi ID3D11Texture2D* GetD3DTexture() const { return m_texture.Get(); }
|
||||||
|
__fi ID3D11ShaderResourceView* GetD3DSRV() const { return m_srv.Get(); }
|
||||||
|
__fi ID3D11ShaderResourceView* const* GetD3DSRVArray() const { return m_srv.GetAddressOf(); }
|
||||||
|
__fi bool IsDynamic() const { return m_dynamic; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Microsoft::WRL::ComPtr<ID3D11Texture2D> m_texture;
|
||||||
|
Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> m_srv;
|
||||||
|
u32 m_width;
|
||||||
|
u32 m_height;
|
||||||
|
u32 m_layers;
|
||||||
|
u32 m_levels;
|
||||||
|
bool m_dynamic;
|
||||||
|
};
|
||||||
|
|
||||||
|
static Microsoft::WRL::ComPtr<ID3D11VertexShader> CreateVertexShader(ID3D11Device* device, const void* bytecode,
|
||||||
|
size_t bytecode_length)
|
||||||
|
{
|
||||||
|
Microsoft::WRL::ComPtr<ID3D11VertexShader> shader;
|
||||||
|
const HRESULT hr = device->CreateVertexShader(bytecode, bytecode_length, nullptr, shader.GetAddressOf());
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Console.Error("Failed to create vertex shader: 0x%08X", hr);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Microsoft::WRL::ComPtr<ID3D11PixelShader> CreatePixelShader(ID3D11Device* device, const void* bytecode,
|
||||||
|
size_t bytecode_length)
|
||||||
|
{
|
||||||
|
Microsoft::WRL::ComPtr<ID3D11PixelShader> shader;
|
||||||
|
const HRESULT hr = device->CreatePixelShader(bytecode, bytecode_length, nullptr, shader.GetAddressOf());
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Console.Error("Failed to create pixel shader: 0x%08X", hr);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D11HostDisplay::D3D11HostDisplay() = default;
|
||||||
|
|
||||||
|
D3D11HostDisplay::~D3D11HostDisplay()
|
||||||
|
{
|
||||||
|
pxAssertMsg(!m_context, "Context should have been destroyed by now");
|
||||||
|
pxAssertMsg(!m_swap_chain, "Swap chain should have been destroyed by now");
|
||||||
|
}
|
||||||
|
|
||||||
|
HostDisplay::RenderAPI D3D11HostDisplay::GetRenderAPI() const
|
||||||
|
{
|
||||||
|
return HostDisplay::RenderAPI::D3D11;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* D3D11HostDisplay::GetRenderDevice() const
|
||||||
|
{
|
||||||
|
return m_device.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void* D3D11HostDisplay::GetRenderContext() const
|
||||||
|
{
|
||||||
|
return m_context.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void* D3D11HostDisplay::GetRenderSurface() const
|
||||||
|
{
|
||||||
|
return m_swap_chain.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::HasRenderDevice() const
|
||||||
|
{
|
||||||
|
return static_cast<bool>(m_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::HasRenderSurface() const
|
||||||
|
{
|
||||||
|
return static_cast<bool>(m_swap_chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<HostDisplayTexture> D3D11HostDisplay::CreateTexture(u32 width, u32 height, u32 layers, u32 levels,
|
||||||
|
const void* data, u32 data_stride, bool dynamic /* = false */)
|
||||||
|
{
|
||||||
|
const CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, width, height, layers, levels,
|
||||||
|
D3D11_BIND_SHADER_RESOURCE, dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT,
|
||||||
|
dynamic ? D3D11_CPU_ACCESS_WRITE : 0, 1, 0, 0);
|
||||||
|
const D3D11_SUBRESOURCE_DATA srd{data, data_stride, data_stride * height};
|
||||||
|
ComPtr<ID3D11Texture2D> texture;
|
||||||
|
HRESULT hr = m_device->CreateTexture2D(&desc, data ? &srd : nullptr, texture.GetAddressOf());
|
||||||
|
if (FAILED(hr))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const CD3D11_SHADER_RESOURCE_VIEW_DESC srv_desc(D3D11_SRV_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 1, 0,
|
||||||
|
1);
|
||||||
|
ComPtr<ID3D11ShaderResourceView> srv;
|
||||||
|
hr = m_device->CreateShaderResourceView(texture.Get(), &srv_desc, srv.GetAddressOf());
|
||||||
|
if (FAILED(hr))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return std::make_unique<D3D11HostDisplayTexture>(std::move(texture), std::move(srv), width, height, layers, levels, dynamic);
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D11HostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
|
||||||
|
const void* texture_data, u32 texture_data_stride)
|
||||||
|
{
|
||||||
|
D3D11HostDisplayTexture* d3d11_texture = static_cast<D3D11HostDisplayTexture*>(texture);
|
||||||
|
if (!d3d11_texture->IsDynamic())
|
||||||
|
{
|
||||||
|
const CD3D11_BOX dst_box(x, y, 0, x + width, y + height, 1);
|
||||||
|
m_context->UpdateSubresource(d3d11_texture->GetD3DTexture(), 0, &dst_box, texture_data, texture_data_stride,
|
||||||
|
texture_data_stride * height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
D3D11_MAPPED_SUBRESOURCE sr;
|
||||||
|
HRESULT hr = m_context->Map(d3d11_texture->GetD3DTexture(), 0, D3D11_MAP_WRITE_DISCARD, 0, &sr);
|
||||||
|
pxAssertMsg(SUCCEEDED(hr), "Failed to map dynamic host display texture");
|
||||||
|
|
||||||
|
char* dst_ptr = static_cast<char*>(sr.pData) + (y * sr.RowPitch) + (x * sizeof(u32));
|
||||||
|
const char* src_ptr = static_cast<const char*>(texture_data);
|
||||||
|
if (sr.RowPitch == texture_data_stride)
|
||||||
|
{
|
||||||
|
std::memcpy(dst_ptr, src_ptr, texture_data_stride * height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (u32 row = 0; row < height; row++)
|
||||||
|
{
|
||||||
|
std::memcpy(dst_ptr, src_ptr, width * sizeof(u32));
|
||||||
|
src_ptr += texture_data_stride;
|
||||||
|
dst_ptr += sr.RowPitch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_context->Unmap(d3d11_texture->GetD3DTexture(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::GetHostRefreshRate(float* refresh_rate)
|
||||||
|
{
|
||||||
|
if (m_swap_chain && IsFullscreen())
|
||||||
|
{
|
||||||
|
DXGI_SWAP_CHAIN_DESC desc;
|
||||||
|
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)) && desc.BufferDesc.RefreshRate.Numerator > 0 &&
|
||||||
|
desc.BufferDesc.RefreshRate.Denominator > 0)
|
||||||
|
{
|
||||||
|
DevCon.WriteLn("using fs rr: %u %u", desc.BufferDesc.RefreshRate.Numerator,
|
||||||
|
desc.BufferDesc.RefreshRate.Denominator);
|
||||||
|
*refresh_rate = static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
|
||||||
|
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return HostDisplay::GetHostRefreshRate(refresh_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D11HostDisplay::SetVSync(VsyncMode mode)
|
||||||
|
{
|
||||||
|
m_vsync = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device)
|
||||||
|
{
|
||||||
|
UINT create_flags = 0;
|
||||||
|
if (debug_device)
|
||||||
|
create_flags |= D3D11_CREATE_DEVICE_DEBUG;
|
||||||
|
|
||||||
|
ComPtr<IDXGIFactory> temp_dxgi_factory;
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
|
HRESULT hr = CreateDXGIFactory(IID_PPV_ARGS(temp_dxgi_factory.GetAddressOf()));
|
||||||
|
#else
|
||||||
|
HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(temp_dxgi_factory.GetAddressOf()));
|
||||||
|
#endif
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Console.Error("Failed to create DXGI factory: 0x%08X", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 adapter_index;
|
||||||
|
if (!adapter_name.empty())
|
||||||
|
{
|
||||||
|
AdapterAndModeList adapter_info(GetAdapterAndModeList(temp_dxgi_factory.Get()));
|
||||||
|
for (adapter_index = 0; adapter_index < static_cast<u32>(adapter_info.adapter_names.size()); adapter_index++)
|
||||||
|
{
|
||||||
|
if (adapter_name == adapter_info.adapter_names[adapter_index])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (adapter_index == static_cast<u32>(adapter_info.adapter_names.size()))
|
||||||
|
{
|
||||||
|
Console.Warning("Could not find adapter '%s', using first (%s)", std::string(adapter_name).c_str(),
|
||||||
|
adapter_info.adapter_names[0].c_str());
|
||||||
|
adapter_index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLn("No adapter selected, using first.");
|
||||||
|
adapter_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComPtr<IDXGIAdapter> dxgi_adapter;
|
||||||
|
hr = temp_dxgi_factory->EnumAdapters(adapter_index, dxgi_adapter.GetAddressOf());
|
||||||
|
if (FAILED(hr))
|
||||||
|
Console.Warning("Failed to enumerate adapter %u, using default", adapter_index);
|
||||||
|
|
||||||
|
static constexpr std::array<D3D_FEATURE_LEVEL, 3> requested_feature_levels = {
|
||||||
|
{D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0}};
|
||||||
|
|
||||||
|
hr =
|
||||||
|
D3D11CreateDevice(dxgi_adapter.Get(), dxgi_adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, nullptr,
|
||||||
|
create_flags, requested_feature_levels.data(), static_cast<UINT>(requested_feature_levels.size()),
|
||||||
|
D3D11_SDK_VERSION, m_device.GetAddressOf(), nullptr, m_context.GetAddressOf());
|
||||||
|
|
||||||
|
// we re-grab these later, see below
|
||||||
|
dxgi_adapter.Reset();
|
||||||
|
temp_dxgi_factory.Reset();
|
||||||
|
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Console.Error("Failed to create D3D device: 0x%08X", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug_device && IsDebuggerPresent())
|
||||||
|
{
|
||||||
|
ComPtr<ID3D11InfoQueue> info;
|
||||||
|
hr = m_device.As(&info);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, TRUE);
|
||||||
|
info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need the specific factory for the device, otherwise MakeWindowAssociation() is flaky.
|
||||||
|
ComPtr<IDXGIDevice> dxgi_device;
|
||||||
|
if (FAILED(m_device.As(&dxgi_device)) || FAILED(dxgi_device->GetParent(IID_PPV_ARGS(dxgi_adapter.GetAddressOf()))) ||
|
||||||
|
FAILED(dxgi_adapter->GetParent(IID_PPV_ARGS(m_dxgi_factory.GetAddressOf()))))
|
||||||
|
{
|
||||||
|
Console.Warning("Failed to get parent adapter/device/factory");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DXGI_ADAPTER_DESC adapter_desc;
|
||||||
|
if (SUCCEEDED(dxgi_adapter->GetDesc(&adapter_desc)))
|
||||||
|
{
|
||||||
|
char adapter_name_buffer[128];
|
||||||
|
const int name_length =
|
||||||
|
WideCharToMultiByte(CP_UTF8, 0, adapter_desc.Description, static_cast<int>(std::wcslen(adapter_desc.Description)),
|
||||||
|
adapter_name_buffer, sizeof(adapter_name_buffer), 0, nullptr);
|
||||||
|
if (name_length >= 0)
|
||||||
|
{
|
||||||
|
adapter_name_buffer[name_length] = 0;
|
||||||
|
Console.WriteLn("D3D Adapter: %s", adapter_name_buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_allow_tearing_supported = false;
|
||||||
|
ComPtr<IDXGIFactory5> dxgi_factory5;
|
||||||
|
hr = m_dxgi_factory.As(&dxgi_factory5);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
BOOL allow_tearing_supported = false;
|
||||||
|
hr = dxgi_factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported,
|
||||||
|
sizeof(allow_tearing_supported));
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
m_allow_tearing_supported = (allow_tearing_supported == TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_window_info = wi;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device)
|
||||||
|
{
|
||||||
|
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain(nullptr))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D11HostDisplay::DestroyRenderDevice()
|
||||||
|
{
|
||||||
|
DestroyRenderSurface();
|
||||||
|
m_context.Reset();
|
||||||
|
m_device.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::MakeRenderContextCurrent()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::DoneRenderContextCurrent()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
|
if (m_window_info.type != WindowInfo::Type::Win32)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_using_flip_model_swap_chain = !EmuConfig.GS.UseBlitSwapChain || fullscreen_mode;
|
||||||
|
|
||||||
|
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
|
||||||
|
RECT client_rc{};
|
||||||
|
GetClientRect(window_hwnd, &client_rc);
|
||||||
|
const u32 width = static_cast<u32>(client_rc.right - client_rc.left);
|
||||||
|
const u32 height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
||||||
|
|
||||||
|
DXGI_SWAP_CHAIN_DESC swap_chain_desc = {};
|
||||||
|
swap_chain_desc.BufferDesc.Width = width;
|
||||||
|
swap_chain_desc.BufferDesc.Height = height;
|
||||||
|
swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
swap_chain_desc.SampleDesc.Count = 1;
|
||||||
|
swap_chain_desc.BufferCount = 3;
|
||||||
|
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||||
|
swap_chain_desc.OutputWindow = window_hwnd;
|
||||||
|
swap_chain_desc.Windowed = TRUE;
|
||||||
|
swap_chain_desc.SwapEffect = m_using_flip_model_swap_chain ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
|
||||||
|
|
||||||
|
m_using_allow_tearing = (m_allow_tearing_supported && m_using_flip_model_swap_chain && !fullscreen_mode);
|
||||||
|
if (m_using_allow_tearing)
|
||||||
|
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
||||||
|
|
||||||
|
if (fullscreen_mode)
|
||||||
|
{
|
||||||
|
swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
||||||
|
swap_chain_desc.Windowed = FALSE;
|
||||||
|
swap_chain_desc.BufferDesc = *fullscreen_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLn("Creating a %dx%d %s %s swap chain", swap_chain_desc.BufferDesc.Width,
|
||||||
|
swap_chain_desc.BufferDesc.Height, m_using_flip_model_swap_chain ? "flip-discard" : "discard",
|
||||||
|
swap_chain_desc.Windowed ? "windowed" : "full-screen");
|
||||||
|
|
||||||
|
hr = m_dxgi_factory->CreateSwapChain(m_device.Get(), &swap_chain_desc, m_swap_chain.GetAddressOf());
|
||||||
|
if (FAILED(hr) && m_using_flip_model_swap_chain)
|
||||||
|
{
|
||||||
|
Console.Warning("Failed to create a flip-discard swap chain, trying discard.");
|
||||||
|
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
|
||||||
|
swap_chain_desc.Flags = 0;
|
||||||
|
m_using_flip_model_swap_chain = false;
|
||||||
|
m_using_allow_tearing = false;
|
||||||
|
|
||||||
|
hr = m_dxgi_factory->CreateSwapChain(m_device.Get(), &swap_chain_desc, m_swap_chain.GetAddressOf());
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Console.Error("CreateSwapChain failed: 0x%08X", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ComPtr<IDXGIFactory> dxgi_factory;
|
||||||
|
hr = m_swap_chain->GetParent(IID_PPV_ARGS(dxgi_factory.GetAddressOf()));
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
hr = dxgi_factory->MakeWindowAssociation(swap_chain_desc.OutputWindow, DXGI_MWA_NO_WINDOW_CHANGES);
|
||||||
|
if (FAILED(hr))
|
||||||
|
Console.Warning("MakeWindowAssociation() to disable ALT+ENTER failed");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (m_window_info.type != WindowInfo::Type::WinRT)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ComPtr<IDXGIFactory2> factory2;
|
||||||
|
hr = m_dxgi_factory.As(&factory2);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Console.Error("Failed to get DXGI factory: %08X", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
|
||||||
|
swap_chain_desc.Width = m_window_info.surface_width;
|
||||||
|
swap_chain_desc.Height = m_window_info.surface_height;
|
||||||
|
swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
swap_chain_desc.SampleDesc.Count = 1;
|
||||||
|
swap_chain_desc.BufferCount = 3;
|
||||||
|
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||||
|
swap_chain_desc.SwapEffect = m_using_flip_model_swap_chain ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
|
||||||
|
|
||||||
|
m_using_allow_tearing = (m_allow_tearing_supported && m_using_flip_model_swap_chain && !fullscreen_mode);
|
||||||
|
if (m_using_allow_tearing)
|
||||||
|
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
||||||
|
|
||||||
|
ComPtr<IDXGISwapChain1> swap_chain1;
|
||||||
|
hr = factory2->CreateSwapChainForCoreWindow(m_device.Get(), static_cast<IUnknown*>(m_window_info.window_handle),
|
||||||
|
&swap_chain_desc, nullptr, swap_chain1.GetAddressOf());
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Console.Error("CreateSwapChainForCoreWindow failed: 0x%08X", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_swap_chain = swap_chain1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return CreateSwapChainRTV();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::CreateSwapChainRTV()
|
||||||
|
{
|
||||||
|
ComPtr<ID3D11Texture2D> backbuffer;
|
||||||
|
HRESULT hr = m_swap_chain->GetBuffer(0, IID_PPV_ARGS(backbuffer.GetAddressOf()));
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Console.Error("GetBuffer for RTV failed: 0x%08X", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D11_TEXTURE2D_DESC backbuffer_desc;
|
||||||
|
backbuffer->GetDesc(&backbuffer_desc);
|
||||||
|
|
||||||
|
CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D, backbuffer_desc.Format, 0, 0,
|
||||||
|
backbuffer_desc.ArraySize);
|
||||||
|
hr = m_device->CreateRenderTargetView(backbuffer.Get(), &rtv_desc, m_swap_chain_rtv.GetAddressOf());
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Console.Error("CreateRenderTargetView for swap chain failed: 0x%08X", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_window_info.surface_width = backbuffer_desc.Width;
|
||||||
|
m_window_info.surface_height = backbuffer_desc.Height;
|
||||||
|
DevCon.WriteLn("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height);
|
||||||
|
|
||||||
|
if (m_window_info.type == WindowInfo::Type::Win32)
|
||||||
|
{
|
||||||
|
BOOL fullscreen = FALSE;
|
||||||
|
DXGI_SWAP_CHAIN_DESC desc;
|
||||||
|
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen &&
|
||||||
|
SUCCEEDED(m_swap_chain->GetDesc(&desc)))
|
||||||
|
{
|
||||||
|
m_window_info.surface_refresh_rate = static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
|
||||||
|
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_window_info.surface_refresh_rate = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::ChangeRenderWindow(const WindowInfo& new_wi)
|
||||||
|
{
|
||||||
|
DestroyRenderSurface();
|
||||||
|
|
||||||
|
m_window_info = new_wi;
|
||||||
|
return CreateSwapChain(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D11HostDisplay::DestroyRenderSurface()
|
||||||
|
{
|
||||||
|
if (IsFullscreen())
|
||||||
|
SetFullscreen(false, 0, 0, 0.0f);
|
||||||
|
|
||||||
|
m_swap_chain_rtv.Reset();
|
||||||
|
m_swap_chain.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D11HostDisplay::ResizeRenderWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
||||||
|
{
|
||||||
|
if (!m_swap_chain)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_window_info.surface_scale = new_window_scale;
|
||||||
|
|
||||||
|
if (m_window_info.surface_width == new_window_width && m_window_info.surface_height == new_window_height)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_swap_chain_rtv.Reset();
|
||||||
|
|
||||||
|
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN,
|
||||||
|
m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
|
||||||
|
if (FAILED(hr))
|
||||||
|
Console.Error("ResizeBuffers() failed: 0x%08X", hr);
|
||||||
|
|
||||||
|
if (!CreateSwapChainRTV())
|
||||||
|
pxFailRel("Failed to recreate swap chain RTV after resize");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::SupportsFullscreen() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::IsFullscreen()
|
||||||
|
{
|
||||||
|
BOOL is_fullscreen = FALSE;
|
||||||
|
return (m_swap_chain && SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
|
||||||
|
{
|
||||||
|
if (!m_swap_chain)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BOOL is_fullscreen = FALSE;
|
||||||
|
HRESULT hr = m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr);
|
||||||
|
if (!fullscreen)
|
||||||
|
{
|
||||||
|
// leaving fullscreen
|
||||||
|
if (is_fullscreen)
|
||||||
|
return SUCCEEDED(m_swap_chain->SetFullscreenState(FALSE, nullptr));
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IDXGIOutput* output;
|
||||||
|
if (FAILED(hr = m_swap_chain->GetContainingOutput(&output)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DXGI_SWAP_CHAIN_DESC current_desc;
|
||||||
|
hr = m_swap_chain->GetDesc(¤t_desc);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DXGI_MODE_DESC new_mode = current_desc.BufferDesc;
|
||||||
|
new_mode.Width = width;
|
||||||
|
new_mode.Height = height;
|
||||||
|
new_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(refresh_rate * 1000.0f));
|
||||||
|
new_mode.RefreshRate.Denominator = 1000u;
|
||||||
|
|
||||||
|
DXGI_MODE_DESC closest_mode;
|
||||||
|
if (FAILED(hr = output->FindClosestMatchingMode(&new_mode, &closest_mode, nullptr)) ||
|
||||||
|
new_mode.Format != current_desc.BufferDesc.Format)
|
||||||
|
{
|
||||||
|
Console.Error("Failed to find closest matching mode, hr=%08X", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_mode.Width == current_desc.BufferDesc.Width && new_mode.Height == current_desc.BufferDesc.Height &&
|
||||||
|
new_mode.RefreshRate.Numerator == current_desc.BufferDesc.RefreshRate.Numerator &&
|
||||||
|
new_mode.RefreshRate.Denominator == current_desc.BufferDesc.RefreshRate.Denominator)
|
||||||
|
{
|
||||||
|
DevCon.WriteLn("Fullscreen mode already set");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_swap_chain_rtv.Reset();
|
||||||
|
m_swap_chain.Reset();
|
||||||
|
|
||||||
|
if (!CreateSwapChain(&closest_mode))
|
||||||
|
{
|
||||||
|
Console.Error("Failed to create a fullscreen swap chain");
|
||||||
|
if (!CreateSwapChain(nullptr))
|
||||||
|
pxFailRel("Failed to recreate windowed swap chain");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::CreateImGuiContext()
|
||||||
|
{
|
||||||
|
return ImGui_ImplDX11_Init(m_device.Get(), m_context.Get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D11HostDisplay::DestroyImGuiContext()
|
||||||
|
{
|
||||||
|
ImGui_ImplDX11_Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::UpdateImGuiFontTexture()
|
||||||
|
{
|
||||||
|
ImGui_ImplDX11_CreateFontsTexture();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11HostDisplay::BeginPresent(bool frame_skip)
|
||||||
|
{
|
||||||
|
if (frame_skip || !m_swap_chain)
|
||||||
|
{
|
||||||
|
ImGui::EndFrame();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::array<float, 4> clear_color = {};
|
||||||
|
m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), clear_color.data());
|
||||||
|
m_context->OMSetRenderTargets(1, m_swap_chain_rtv.GetAddressOf(), nullptr);
|
||||||
|
|
||||||
|
const CD3D11_VIEWPORT vp(0.0f, 0.0f, static_cast<float>(m_window_info.surface_width), static_cast<float>(m_window_info.surface_height));
|
||||||
|
const CD3D11_RECT scissor(0, 0, m_window_info.surface_width, m_window_info.surface_height);
|
||||||
|
m_context->RSSetViewports(1, &vp);
|
||||||
|
m_context->RSSetScissorRects(1, &scissor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D11HostDisplay::EndPresent()
|
||||||
|
{
|
||||||
|
ImGui::Render();
|
||||||
|
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
|
||||||
|
const UINT vsync_rate = static_cast<UINT>(m_vsync != VsyncMode::Off);
|
||||||
|
if (vsync_rate == 0 && m_using_allow_tearing)
|
||||||
|
m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
|
||||||
|
else
|
||||||
|
m_swap_chain->Present(vsync_rate, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
HostDisplay::AdapterAndModeList D3D11HostDisplay::StaticGetAdapterAndModeList()
|
||||||
|
{
|
||||||
|
ComPtr<IDXGIFactory> dxgi_factory;
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
|
HRESULT hr = CreateDXGIFactory(IID_PPV_ARGS(dxgi_factory.GetAddressOf()));
|
||||||
|
#else
|
||||||
|
HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(dxgi_factory.GetAddressOf()));
|
||||||
|
#endif
|
||||||
|
if (FAILED(hr))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return GetAdapterAndModeList(dxgi_factory.Get());
|
||||||
|
}
|
||||||
|
|
||||||
|
HostDisplay::AdapterAndModeList D3D11HostDisplay::GetAdapterAndModeList(IDXGIFactory* dxgi_factory)
|
||||||
|
{
|
||||||
|
AdapterAndModeList adapter_info;
|
||||||
|
ComPtr<IDXGIAdapter> current_adapter;
|
||||||
|
while (SUCCEEDED(dxgi_factory->EnumAdapters(static_cast<UINT>(adapter_info.adapter_names.size()),
|
||||||
|
current_adapter.ReleaseAndGetAddressOf())))
|
||||||
|
{
|
||||||
|
DXGI_ADAPTER_DESC adapter_desc;
|
||||||
|
std::string adapter_name;
|
||||||
|
if (SUCCEEDED(current_adapter->GetDesc(&adapter_desc)))
|
||||||
|
{
|
||||||
|
char adapter_name_buffer[128];
|
||||||
|
const int name_length = WideCharToMultiByte(CP_UTF8, 0, adapter_desc.Description,
|
||||||
|
static_cast<int>(std::wcslen(adapter_desc.Description)),
|
||||||
|
adapter_name_buffer, sizeof(adapter_name_buffer), 0, nullptr);
|
||||||
|
if (name_length >= 0)
|
||||||
|
adapter_name.assign(adapter_name_buffer, static_cast<size_t>(name_length));
|
||||||
|
else
|
||||||
|
adapter_name.assign("(Unknown)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
adapter_name.assign("(Unknown)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (adapter_info.fullscreen_modes.empty())
|
||||||
|
{
|
||||||
|
ComPtr<IDXGIOutput> output;
|
||||||
|
if (SUCCEEDED(current_adapter->EnumOutputs(0, &output)))
|
||||||
|
{
|
||||||
|
UINT num_modes = 0;
|
||||||
|
if (SUCCEEDED(output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, nullptr)))
|
||||||
|
{
|
||||||
|
std::vector<DXGI_MODE_DESC> modes(num_modes);
|
||||||
|
if (SUCCEEDED(output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, modes.data())))
|
||||||
|
{
|
||||||
|
for (const DXGI_MODE_DESC& mode : modes)
|
||||||
|
{
|
||||||
|
adapter_info.fullscreen_modes.push_back(GetFullscreenModeString(
|
||||||
|
mode.Width, mode.Height,
|
||||||
|
static_cast<float>(mode.RefreshRate.Numerator) / static_cast<float>(mode.RefreshRate.Denominator)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle duplicate adapter names
|
||||||
|
if (std::any_of(adapter_info.adapter_names.begin(), adapter_info.adapter_names.end(),
|
||||||
|
[&adapter_name](const std::string& other) { return (adapter_name == other); }))
|
||||||
|
{
|
||||||
|
std::string original_adapter_name = std::move(adapter_name);
|
||||||
|
|
||||||
|
u32 current_extra = 2;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
adapter_name = StringUtil::StdStringFromFormat("%s (%u)", original_adapter_name.c_str(), current_extra);
|
||||||
|
current_extra++;
|
||||||
|
} while (std::any_of(adapter_info.adapter_names.begin(), adapter_info.adapter_names.end(),
|
||||||
|
[&adapter_name](const std::string& other) { return (adapter_name == other); }));
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter_info.adapter_names.push_back(std::move(adapter_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return adapter_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
HostDisplay::AdapterAndModeList D3D11HostDisplay::GetAdapterAndModeList()
|
||||||
|
{
|
||||||
|
return GetAdapterAndModeList(m_dxgi_factory.Get());
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
/* PCSX2 - PS2 Emulator for PCs
|
||||||
|
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||||
|
*
|
||||||
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||||
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||||
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "HostDisplay.h"
|
||||||
|
#include "common/RedtapeWindows.h"
|
||||||
|
#include "common/WindowInfo.h"
|
||||||
|
#include <d3d11.h>
|
||||||
|
#include <dxgi.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include <wrl/client.h>
|
||||||
|
|
||||||
|
class D3D11HostDisplay final : public HostDisplay
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
using ComPtr = Microsoft::WRL::ComPtr<T>;
|
||||||
|
|
||||||
|
D3D11HostDisplay();
|
||||||
|
~D3D11HostDisplay();
|
||||||
|
|
||||||
|
RenderAPI GetRenderAPI() const override;
|
||||||
|
void* GetRenderDevice() const override;
|
||||||
|
void* GetRenderContext() const override;
|
||||||
|
void* GetRenderSurface() const override;
|
||||||
|
|
||||||
|
bool HasRenderDevice() const override;
|
||||||
|
bool HasRenderSurface() const override;
|
||||||
|
|
||||||
|
bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device) override;
|
||||||
|
bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override;
|
||||||
|
void DestroyRenderDevice() override;
|
||||||
|
|
||||||
|
bool MakeRenderContextCurrent() override;
|
||||||
|
bool DoneRenderContextCurrent() override;
|
||||||
|
|
||||||
|
bool ChangeRenderWindow(const WindowInfo& new_wi) override;
|
||||||
|
void ResizeRenderWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
||||||
|
bool SupportsFullscreen() const override;
|
||||||
|
bool IsFullscreen() override;
|
||||||
|
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||||
|
AdapterAndModeList GetAdapterAndModeList() override;
|
||||||
|
void DestroyRenderSurface() override;
|
||||||
|
|
||||||
|
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels,
|
||||||
|
const void* data, u32 data_stride, bool dynamic = false) override;
|
||||||
|
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data,
|
||||||
|
u32 texture_data_stride) override;
|
||||||
|
|
||||||
|
bool GetHostRefreshRate(float* refresh_rate) override;
|
||||||
|
|
||||||
|
void SetVSync(VsyncMode mode) override;
|
||||||
|
|
||||||
|
bool BeginPresent(bool frame_skip) override;
|
||||||
|
void EndPresent() override;
|
||||||
|
|
||||||
|
static AdapterAndModeList StaticGetAdapterAndModeList();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static constexpr u32 DISPLAY_CONSTANT_BUFFER_SIZE = 16;
|
||||||
|
|
||||||
|
static AdapterAndModeList GetAdapterAndModeList(IDXGIFactory* dxgi_factory);
|
||||||
|
|
||||||
|
bool CreateImGuiContext() override;
|
||||||
|
void DestroyImGuiContext() override;
|
||||||
|
bool UpdateImGuiFontTexture() override;
|
||||||
|
|
||||||
|
bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode);
|
||||||
|
bool CreateSwapChainRTV();
|
||||||
|
|
||||||
|
ComPtr<ID3D11Device> m_device;
|
||||||
|
ComPtr<ID3D11DeviceContext> m_context;
|
||||||
|
|
||||||
|
ComPtr<IDXGIFactory> m_dxgi_factory;
|
||||||
|
ComPtr<IDXGISwapChain> m_swap_chain;
|
||||||
|
ComPtr<ID3D11RenderTargetView> m_swap_chain_rtv;
|
||||||
|
|
||||||
|
VsyncMode m_vsync = VsyncMode::Off;
|
||||||
|
bool m_allow_tearing_supported = false;
|
||||||
|
bool m_using_flip_model_swap_chain = true;
|
||||||
|
bool m_using_allow_tearing = false;
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,614 @@
|
||||||
|
/* PCSX2 - PS2 Emulator for PCs
|
||||||
|
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||||
|
*
|
||||||
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||||
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||||
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PrecompiledHeader.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <deque>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "common/StringHelpers.h"
|
||||||
|
#include "common/StringUtil.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
#include "Counters.h"
|
||||||
|
#include "Frontend/ImGuiManager.h"
|
||||||
|
#include "GS.h"
|
||||||
|
#include "GS/GS.h"
|
||||||
|
#include "Host.h"
|
||||||
|
#include "HostDisplay.h"
|
||||||
|
#include "IconsFontAwesome5.h"
|
||||||
|
#include "PerformanceMetrics.h"
|
||||||
|
|
||||||
|
#ifdef PCSX2_CORE
|
||||||
|
#include "VMManager.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void SetImGuiStyle();
|
||||||
|
static bool LoadFontData();
|
||||||
|
static void UnloadFontData();
|
||||||
|
static bool AddImGuiFonts();
|
||||||
|
|
||||||
|
static float s_global_scale = 1.0f;
|
||||||
|
|
||||||
|
static ImFont* s_standard_font;
|
||||||
|
static ImFont* s_fixed_font;
|
||||||
|
|
||||||
|
static std::vector<u8> s_standard_font_data;
|
||||||
|
static std::vector<u8> s_fixed_font_data;
|
||||||
|
static std::vector<u8> s_icon_font_data;
|
||||||
|
|
||||||
|
bool ImGuiManager::Initialize()
|
||||||
|
{
|
||||||
|
if (!LoadFontData())
|
||||||
|
{
|
||||||
|
pxFailRel("Failed to load font data");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HostDisplay* display = Host::GetHostDisplay();
|
||||||
|
|
||||||
|
ImGui::CreateContext();
|
||||||
|
ImGui::GetIO().IniFilename = nullptr;
|
||||||
|
|
||||||
|
s_global_scale = std::max(1.0f, display->GetWindowScale() * static_cast<float>(EmuConfig.GS.OsdScale / 100.0));
|
||||||
|
|
||||||
|
ImGui::GetIO().DisplayFramebufferScale = ImVec2(display->GetWindowScale(), display->GetWindowScale());
|
||||||
|
ImGui::GetIO().DisplaySize.x = static_cast<float>(display->GetWindowWidth());
|
||||||
|
ImGui::GetIO().DisplaySize.y = static_cast<float>(display->GetWindowHeight());
|
||||||
|
ImGui::GetStyle() = ImGuiStyle();
|
||||||
|
ImGui::GetStyle().WindowMinSize = ImVec2(1.0f, 1.0f);
|
||||||
|
SetImGuiStyle();
|
||||||
|
ImGui::GetStyle().ScaleAllSizes(s_global_scale);
|
||||||
|
|
||||||
|
if (!display->CreateImGuiContext())
|
||||||
|
{
|
||||||
|
pxFailRel("Failed to create ImGui device context");
|
||||||
|
ImGui::DestroyContext();
|
||||||
|
UnloadFontData();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AddImGuiFonts() || !display->UpdateImGuiFontTexture())
|
||||||
|
{
|
||||||
|
pxFailRel("Failed to create ImGui font text");
|
||||||
|
display->DestroyImGuiContext();
|
||||||
|
ImGui::DestroyContext();
|
||||||
|
UnloadFontData();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NewFrame();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiManager::Shutdown()
|
||||||
|
{
|
||||||
|
HostDisplay* display = Host::GetHostDisplay();
|
||||||
|
if (display)
|
||||||
|
display->DestroyImGuiContext();
|
||||||
|
if (ImGui::GetCurrentContext())
|
||||||
|
ImGui::DestroyContext();
|
||||||
|
UnloadFontData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiManager::WindowResized()
|
||||||
|
{
|
||||||
|
HostDisplay* display = Host::GetHostDisplay();
|
||||||
|
|
||||||
|
const u32 new_width = display ? display->GetWindowWidth() : 0;
|
||||||
|
const u32 new_height = display ? display->GetWindowHeight() : 0;
|
||||||
|
const float new_scale = (display ? display->GetWindowScale() : 1.0f);
|
||||||
|
|
||||||
|
ImGui::GetIO().DisplaySize = ImVec2(static_cast<float>(new_width), static_cast<float>(new_height));
|
||||||
|
ImGui::GetIO().DisplayFramebufferScale = ImVec2(new_scale, new_scale);
|
||||||
|
|
||||||
|
UpdateScale();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiManager::UpdateScale()
|
||||||
|
{
|
||||||
|
const float scale =
|
||||||
|
std::max(ImGui::GetIO().DisplayFramebufferScale.x * static_cast<float>(EmuConfig.GS.OsdScale / 100.0), 1.0f);
|
||||||
|
if (scale == s_global_scale)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// This is assumed to be called mid-frame.
|
||||||
|
ImGui::EndFrame();
|
||||||
|
|
||||||
|
s_global_scale = scale;
|
||||||
|
|
||||||
|
HostDisplay* display = Host::GetHostDisplay();
|
||||||
|
|
||||||
|
ImGui::GetStyle() = ImGuiStyle();
|
||||||
|
ImGui::GetStyle().WindowMinSize = ImVec2(1.0f, 1.0f);
|
||||||
|
SetImGuiStyle();
|
||||||
|
ImGui::GetStyle().ScaleAllSizes(scale);
|
||||||
|
|
||||||
|
if (!AddImGuiFonts() || !display->UpdateImGuiFontTexture())
|
||||||
|
pxFailRel("Failed to create ImGui font text");
|
||||||
|
|
||||||
|
if (!display->UpdateImGuiFontTexture())
|
||||||
|
pxFailRel("Failed to recreate font texture after scale+resize");
|
||||||
|
|
||||||
|
NewFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiManager::NewFrame()
|
||||||
|
{
|
||||||
|
ImGui::NewFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetImGuiStyle()
|
||||||
|
{
|
||||||
|
ImGuiStyle* style = &ImGui::GetStyle();
|
||||||
|
ImVec4* colors = style->Colors;
|
||||||
|
|
||||||
|
colors[ImGuiCol_Text] = ImVec4(0.95f, 0.96f, 0.98f, 1.00f);
|
||||||
|
colors[ImGuiCol_TextDisabled] = ImVec4(0.36f, 0.42f, 0.47f, 1.00f);
|
||||||
|
colors[ImGuiCol_WindowBg] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
|
||||||
|
colors[ImGuiCol_ChildBg] = ImVec4(0.15f, 0.18f, 0.22f, 1.00f);
|
||||||
|
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
|
||||||
|
colors[ImGuiCol_Border] = ImVec4(0.08f, 0.10f, 0.12f, 1.00f);
|
||||||
|
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||||
|
colors[ImGuiCol_FrameBg] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
|
||||||
|
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.12f, 0.20f, 0.28f, 1.00f);
|
||||||
|
colors[ImGuiCol_FrameBgActive] = ImVec4(0.09f, 0.12f, 0.14f, 1.00f);
|
||||||
|
colors[ImGuiCol_TitleBg] = ImVec4(0.09f, 0.12f, 0.14f, 0.65f);
|
||||||
|
colors[ImGuiCol_TitleBgActive] = ImVec4(0.08f, 0.10f, 0.12f, 1.00f);
|
||||||
|
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
|
||||||
|
colors[ImGuiCol_MenuBarBg] = ImVec4(0.15f, 0.18f, 0.22f, 1.00f);
|
||||||
|
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.39f);
|
||||||
|
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
|
||||||
|
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.18f, 0.22f, 0.25f, 1.00f);
|
||||||
|
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.09f, 0.21f, 0.31f, 1.00f);
|
||||||
|
colors[ImGuiCol_CheckMark] = ImVec4(0.28f, 0.56f, 1.00f, 1.00f);
|
||||||
|
colors[ImGuiCol_SliderGrab] = ImVec4(0.28f, 0.56f, 1.00f, 1.00f);
|
||||||
|
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.37f, 0.61f, 1.00f, 1.00f);
|
||||||
|
colors[ImGuiCol_Button] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
|
||||||
|
colors[ImGuiCol_ButtonHovered] = ImVec4(0.33f, 0.38f, 0.46f, 1.00f);
|
||||||
|
colors[ImGuiCol_ButtonActive] = ImVec4(0.27f, 0.32f, 0.38f, 1.00f);
|
||||||
|
colors[ImGuiCol_Header] = ImVec4(0.20f, 0.25f, 0.29f, 0.55f);
|
||||||
|
colors[ImGuiCol_HeaderHovered] = ImVec4(0.33f, 0.38f, 0.46f, 1.00f);
|
||||||
|
colors[ImGuiCol_HeaderActive] = ImVec4(0.27f, 0.32f, 0.38f, 1.00f);
|
||||||
|
colors[ImGuiCol_Separator] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
|
||||||
|
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.33f, 0.38f, 0.46f, 1.00f);
|
||||||
|
colors[ImGuiCol_SeparatorActive] = ImVec4(0.27f, 0.32f, 0.38f, 1.00f);
|
||||||
|
colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.25f);
|
||||||
|
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.33f, 0.38f, 0.46f, 1.00f);
|
||||||
|
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.27f, 0.32f, 0.38f, 1.00f);
|
||||||
|
colors[ImGuiCol_Tab] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
|
||||||
|
colors[ImGuiCol_TabHovered] = ImVec4(0.33f, 0.38f, 0.46f, 1.00f);
|
||||||
|
colors[ImGuiCol_TabActive] = ImVec4(0.27f, 0.32f, 0.38f, 1.00f);
|
||||||
|
colors[ImGuiCol_TabUnfocused] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
|
||||||
|
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
|
||||||
|
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
|
||||||
|
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
||||||
|
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||||
|
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
|
||||||
|
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
|
||||||
|
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
||||||
|
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
||||||
|
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
||||||
|
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
||||||
|
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LoadFontData()
|
||||||
|
{
|
||||||
|
if (s_standard_font_data.empty())
|
||||||
|
{
|
||||||
|
std::optional<std::vector<u8>> font_data = Host::ReadResourceFile("fonts/Roboto-Regular.ttf");
|
||||||
|
if (!font_data.has_value())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
s_standard_font_data = std::move(font_data.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_fixed_font_data.empty())
|
||||||
|
{
|
||||||
|
std::optional<std::vector<u8>> font_data = Host::ReadResourceFile("fonts/RobotoMono-Medium.ttf");
|
||||||
|
if (!font_data.has_value())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
s_fixed_font_data = std::move(font_data.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_icon_font_data.empty())
|
||||||
|
{
|
||||||
|
std::optional<std::vector<u8>> font_data = Host::ReadResourceFile("fonts/fa-solid-900.ttf");
|
||||||
|
if (!font_data.has_value())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
s_icon_font_data = std::move(font_data.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnloadFontData()
|
||||||
|
{
|
||||||
|
std::vector<u8>().swap(s_standard_font_data);
|
||||||
|
std::vector<u8>().swap(s_fixed_font_data);
|
||||||
|
std::vector<u8>().swap(s_icon_font_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ImFont* AddTextFont(float size /*= 15.0f*/)
|
||||||
|
{
|
||||||
|
static const ImWchar default_ranges[] = {
|
||||||
|
// Basic Latin + Latin Supplement + Central European diacritics
|
||||||
|
0x0020,
|
||||||
|
0x017F,
|
||||||
|
|
||||||
|
// Cyrillic + Cyrillic Supplement
|
||||||
|
0x0400,
|
||||||
|
0x052F,
|
||||||
|
|
||||||
|
// Cyrillic Extended-A
|
||||||
|
0x2DE0,
|
||||||
|
0x2DFF,
|
||||||
|
|
||||||
|
// Cyrillic Extended-B
|
||||||
|
0xA640,
|
||||||
|
0xA69F,
|
||||||
|
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
|
||||||
|
ImFontConfig cfg;
|
||||||
|
cfg.FontDataOwnedByAtlas = false;
|
||||||
|
return ImGui::GetIO().Fonts->AddFontFromMemoryTTF(
|
||||||
|
s_standard_font_data.data(), static_cast<int>(s_standard_font_data.size()), size, &cfg, default_ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ImFont* AddFixedFont(float size)
|
||||||
|
{
|
||||||
|
ImFontConfig cfg;
|
||||||
|
cfg.FontDataOwnedByAtlas = false;
|
||||||
|
return ImGui::GetIO().Fonts->AddFontFromMemoryTTF(s_fixed_font_data.data(),
|
||||||
|
static_cast<int>(s_fixed_font_data.size()), size, &cfg, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool AddIconFonts(float size)
|
||||||
|
{
|
||||||
|
static const ImWchar range_fa[] = {ICON_MIN_FA, ICON_MAX_FA, 0};
|
||||||
|
|
||||||
|
ImFontConfig cfg;
|
||||||
|
cfg.MergeMode = true;
|
||||||
|
cfg.PixelSnapH = true;
|
||||||
|
cfg.GlyphMinAdvanceX = size * 0.75f;
|
||||||
|
cfg.GlyphMaxAdvanceX = size * 0.75f;
|
||||||
|
cfg.FontDataOwnedByAtlas = false;
|
||||||
|
|
||||||
|
return (ImGui::GetIO().Fonts->AddFontFromMemoryTTF(s_icon_font_data.data(), static_cast<int>(s_icon_font_data.size()),
|
||||||
|
size * 0.75f, &cfg, range_fa) != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AddImGuiFonts()
|
||||||
|
{
|
||||||
|
const float standard_font_size = std::ceil(15.0f * s_global_scale);
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.Fonts->Clear();
|
||||||
|
|
||||||
|
s_standard_font = AddTextFont(standard_font_size);
|
||||||
|
if (!s_standard_font || !AddIconFonts(standard_font_size))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
s_fixed_font = AddFixedFont(standard_font_size);
|
||||||
|
if (!s_fixed_font)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return io.Fonts->Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OSDMessage
|
||||||
|
{
|
||||||
|
std::string key;
|
||||||
|
std::string text;
|
||||||
|
std::chrono::steady_clock::time_point time;
|
||||||
|
float duration;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::deque<OSDMessage> s_osd_active_messages;
|
||||||
|
static std::deque<OSDMessage> s_osd_posted_messages;
|
||||||
|
static std::mutex s_osd_messages_lock;
|
||||||
|
|
||||||
|
void Host::AddOSDMessage(std::string message, float duration /*= 2.0f*/)
|
||||||
|
{
|
||||||
|
AddKeyedOSDMessage(std::string(), std::move(message), duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::AddKeyedOSDMessage(std::string key, std::string message, float duration /* = 2.0f */)
|
||||||
|
{
|
||||||
|
OSDMessage msg;
|
||||||
|
msg.key = std::move(key);
|
||||||
|
msg.text = std::move(message);
|
||||||
|
msg.duration = duration;
|
||||||
|
msg.time = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(s_osd_messages_lock);
|
||||||
|
s_osd_posted_messages.push_back(std::move(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::AddFormattedOSDMessage(float duration, const char* format, ...)
|
||||||
|
{
|
||||||
|
std::va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
std::string ret = StringUtil::StdStringFromFormatV(format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return AddKeyedOSDMessage(std::string(), std::move(ret), duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::AddKeyedFormattedOSDMessage(std::string key, float duration, const char* format, ...)
|
||||||
|
{
|
||||||
|
std::va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
std::string ret = StringUtil::StdStringFromFormatV(format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return AddKeyedOSDMessage(std::move(key), std::move(ret), duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::RemoveKeyedOSDMessage(std::string key)
|
||||||
|
{
|
||||||
|
OSDMessage msg;
|
||||||
|
msg.key = std::move(key);
|
||||||
|
msg.duration = 0.0f;
|
||||||
|
msg.time = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(s_osd_messages_lock);
|
||||||
|
s_osd_posted_messages.push_back(std::move(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::ClearOSDMessages()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(s_osd_messages_lock);
|
||||||
|
s_osd_posted_messages.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
s_osd_active_messages.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AcquirePendingOSDMessages()
|
||||||
|
{
|
||||||
|
std::atomic_thread_fence(std::memory_order_consume);
|
||||||
|
if (s_osd_posted_messages.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::unique_lock lock(s_osd_messages_lock);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (s_osd_posted_messages.empty())
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (GSConfig.OsdShowMessages)
|
||||||
|
{
|
||||||
|
OSDMessage& new_msg = s_osd_posted_messages.front();
|
||||||
|
std::deque<OSDMessage>::iterator iter;
|
||||||
|
if (!new_msg.key.empty() && (iter = std::find_if(s_osd_active_messages.begin(), s_osd_active_messages.end(),
|
||||||
|
[&new_msg](const OSDMessage& other) {
|
||||||
|
return new_msg.key == other.key;
|
||||||
|
})) != s_osd_active_messages.end())
|
||||||
|
{
|
||||||
|
iter->text = std::move(new_msg.text);
|
||||||
|
iter->duration = new_msg.duration;
|
||||||
|
iter->time = new_msg.time;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_osd_active_messages.push_back(std::move(new_msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s_osd_posted_messages.pop_front();
|
||||||
|
|
||||||
|
static constexpr size_t MAX_ACTIVE_OSD_MESSAGES = 512;
|
||||||
|
if (s_osd_active_messages.size() > MAX_ACTIVE_OSD_MESSAGES)
|
||||||
|
s_osd_active_messages.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DrawOSDMessages()
|
||||||
|
{
|
||||||
|
ImFont* const font = ImGui::GetFont();
|
||||||
|
const float scale = s_global_scale;
|
||||||
|
const float spacing = std::ceil(5.0f * scale);
|
||||||
|
const float margin = std::ceil(10.0f * scale);
|
||||||
|
const float padding = std::ceil(8.0f * scale);
|
||||||
|
const float rounding = std::ceil(5.0f * scale);
|
||||||
|
const float max_width = ImGui::GetIO().DisplaySize.x - (margin + padding) * 2.0f;
|
||||||
|
float position_x = margin;
|
||||||
|
float position_y = margin;
|
||||||
|
|
||||||
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
auto iter = s_osd_active_messages.begin();
|
||||||
|
while (iter != s_osd_active_messages.end())
|
||||||
|
{
|
||||||
|
const OSDMessage& msg = *iter;
|
||||||
|
const double time = std::chrono::duration<double>(now - msg.time).count();
|
||||||
|
const float time_remaining = static_cast<float>(msg.duration - time);
|
||||||
|
if (time_remaining <= 0.0f)
|
||||||
|
{
|
||||||
|
iter = s_osd_active_messages.erase(iter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
++iter;
|
||||||
|
|
||||||
|
const float opacity = std::min(time_remaining, 1.0f);
|
||||||
|
const u32 alpha = static_cast<u32>(opacity * 255.0f);
|
||||||
|
|
||||||
|
if (position_y >= ImGui::GetIO().DisplaySize.y)
|
||||||
|
break;
|
||||||
|
|
||||||
|
const ImVec2 pos(position_x, position_y);
|
||||||
|
const ImVec2 text_size(
|
||||||
|
font->CalcTextSizeA(font->FontSize, max_width, max_width, msg.text.c_str(), msg.text.c_str() + msg.text.length()));
|
||||||
|
const ImVec2 size(text_size.x + padding * 2.0f, text_size.y + padding * 2.0f);
|
||||||
|
const ImVec4 text_rect(pos.x + padding, pos.y + padding, pos.x + size.x - padding, pos.y + size.y - padding);
|
||||||
|
|
||||||
|
ImDrawList* dl = ImGui::GetBackgroundDrawList();
|
||||||
|
dl->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(0x21, 0x21, 0x21, alpha), rounding);
|
||||||
|
dl->AddRect(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(0x48, 0x48, 0x48, alpha), rounding);
|
||||||
|
dl->AddText(font, font->FontSize, ImVec2(text_rect.x, text_rect.y), IM_COL32(0xff, 0xff, 0xff, alpha),
|
||||||
|
msg.text.c_str(), msg.text.c_str() + msg.text.length(), max_width, &text_rect);
|
||||||
|
position_y += size.y + spacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DrawPerformanceOverlay()
|
||||||
|
{
|
||||||
|
const float scale = s_global_scale;
|
||||||
|
const float shadow_offset = std::ceil(1.0f * scale);
|
||||||
|
const float margin = std::ceil(10.0f * scale);
|
||||||
|
const float spacing = std::ceil(5.0f * scale);
|
||||||
|
float position_y = margin;
|
||||||
|
|
||||||
|
ImDrawList* dl = ImGui::GetBackgroundDrawList();
|
||||||
|
FastFormatAscii text;
|
||||||
|
ImVec2 text_size;
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
|
#define DRAW_LINE(font, text, color) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
text_size = \
|
||||||
|
font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
|
||||||
|
dl->AddText( \
|
||||||
|
font, font->FontSize, \
|
||||||
|
ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x + shadow_offset, position_y + shadow_offset), \
|
||||||
|
IM_COL32(0, 0, 0, 100), (text)); \
|
||||||
|
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), color, \
|
||||||
|
(text)); \
|
||||||
|
position_y += text_size.y + spacing; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#ifdef PCSX2_CORE
|
||||||
|
const bool paused = (VMManager::GetState() == VMState::Paused);
|
||||||
|
#else
|
||||||
|
constexpr bool paused = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!paused)
|
||||||
|
{
|
||||||
|
const float speed = PerformanceMetrics::GetSpeed();
|
||||||
|
if (GSConfig.OsdShowFPS)
|
||||||
|
{
|
||||||
|
text.Write("%.2f", PerformanceMetrics::GetFPS());
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
if (GSConfig.OsdShowSpeed)
|
||||||
|
{
|
||||||
|
text.Write("%s%u%%", first ? "" : " | ", static_cast<u32>(std::round(speed)));
|
||||||
|
|
||||||
|
// We read the main config here, since MTGS doesn't get updated with speed toggles.
|
||||||
|
if (EmuConfig.GS.LimitScalar == 0.0)
|
||||||
|
text.Write(" (Max)");
|
||||||
|
else
|
||||||
|
text.Write(" (%.0f%%)", EmuConfig.GS.LimitScalar * 100.0);
|
||||||
|
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
if (!text.IsEmpty())
|
||||||
|
{
|
||||||
|
ImU32 color;
|
||||||
|
if (speed < 95.0f)
|
||||||
|
color = IM_COL32(255, 100, 100, 255);
|
||||||
|
else if (speed > 105.0f)
|
||||||
|
color = IM_COL32(100, 255, 100, 255);
|
||||||
|
else
|
||||||
|
color = IM_COL32(255, 255, 255, 255);
|
||||||
|
|
||||||
|
DRAW_LINE(s_fixed_font, text.c_str(), color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GSConfig.OsdShowGSStats)
|
||||||
|
{
|
||||||
|
std::string gs_stats;
|
||||||
|
GSgetStats(gs_stats);
|
||||||
|
DRAW_LINE(s_fixed_font, gs_stats.c_str(), IM_COL32(255, 255, 255, 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GSConfig.OsdShowResolution)
|
||||||
|
{
|
||||||
|
int width, height;
|
||||||
|
GSgetInternalResolution(&width, &height);
|
||||||
|
|
||||||
|
text.Clear();
|
||||||
|
text.Write("%dx%d %s %s", width, height, ReportVideoMode(), ReportInterlaceMode());
|
||||||
|
DRAW_LINE(s_fixed_font, text.c_str(), IM_COL32(255, 255, 255, 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GSConfig.OsdShowCPU)
|
||||||
|
{
|
||||||
|
text.Clear();
|
||||||
|
text.Write("%.2fms (%.2fms worst)", PerformanceMetrics::GetAverageFrameTime(),
|
||||||
|
PerformanceMetrics::GetWorstFrameTime());
|
||||||
|
DRAW_LINE(s_fixed_font, text.c_str(), IM_COL32(255, 255, 255, 255));
|
||||||
|
|
||||||
|
text.Clear();
|
||||||
|
if (EmuConfig.Speedhacks.EECycleRate != 0 || EmuConfig.Speedhacks.EECycleSkip != 0)
|
||||||
|
{
|
||||||
|
text.Write("EE[%d/%d]: %.1f%% (%.2fms)", EmuConfig.Speedhacks.EECycleRate, EmuConfig.Speedhacks.EECycleSkip,
|
||||||
|
PerformanceMetrics::GetCPUThreadUsage(),
|
||||||
|
PerformanceMetrics::GetCPUThreadAverageTime());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text.Write("EE: %.1f%% (%.2fms)", PerformanceMetrics::GetCPUThreadUsage(),
|
||||||
|
PerformanceMetrics::GetCPUThreadAverageTime());
|
||||||
|
}
|
||||||
|
DRAW_LINE(s_fixed_font, text.c_str(), IM_COL32(255, 255, 255, 255));
|
||||||
|
|
||||||
|
text.Clear();
|
||||||
|
text.Write("GS: %.1f%% (%.2fms)", PerformanceMetrics::GetGSThreadUsage(),
|
||||||
|
PerformanceMetrics::GetGSThreadAverageTime());
|
||||||
|
DRAW_LINE(s_fixed_font, text.c_str(), IM_COL32(255, 255, 255, 255));
|
||||||
|
|
||||||
|
if (THREAD_VU1)
|
||||||
|
{
|
||||||
|
text.Clear();
|
||||||
|
text.Write("VU: %.1f%% (%.2fms)", PerformanceMetrics::GetVUThreadUsage(),
|
||||||
|
PerformanceMetrics::GetVUThreadAverageTime());
|
||||||
|
DRAW_LINE(s_fixed_font, text.c_str(), IM_COL32(255, 255, 255, 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool is_normal_speed = (EmuConfig.GS.LimitScalar == EmuConfig.Framerate.NominalScalar);
|
||||||
|
if (!is_normal_speed)
|
||||||
|
{
|
||||||
|
const bool is_slowmo = (EmuConfig.GS.LimitScalar < EmuConfig.Framerate.NominalScalar);
|
||||||
|
DRAW_LINE(s_standard_font, is_slowmo ? ICON_FA_FORWARD : ICON_FA_FAST_FORWARD, IM_COL32(255, 255, 255, 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DRAW_LINE(s_standard_font, ICON_FA_PAUSE, IM_COL32(255, 255, 255, 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef DRAW_LINE
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiManager::RenderOSD()
|
||||||
|
{
|
||||||
|
DrawPerformanceOverlay();
|
||||||
|
|
||||||
|
AcquirePendingOSDMessages();
|
||||||
|
DrawOSDMessages();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/* PCSX2 - PS2 Emulator for PCs
|
||||||
|
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||||
|
*
|
||||||
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||||
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||||
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace ImGuiManager
|
||||||
|
{
|
||||||
|
/// Initializes ImGui, creates fonts, etc.
|
||||||
|
bool Initialize();
|
||||||
|
|
||||||
|
/// Frees all ImGui resources.
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
/// Updates internal state when the window is size.
|
||||||
|
void WindowResized();
|
||||||
|
|
||||||
|
/// Updates scaling of the on-screen elements.
|
||||||
|
void UpdateScale();
|
||||||
|
|
||||||
|
/// Call at the beginning of the frame to set up ImGui state.
|
||||||
|
void NewFrame();
|
||||||
|
|
||||||
|
/// Renders any on-screen display elements.
|
||||||
|
void RenderOSD();
|
||||||
|
} // namespace ImGuiManager
|
||||||
|
|
|
@ -0,0 +1,369 @@
|
||||||
|
/* PCSX2 - PS2 Emulator for PCs
|
||||||
|
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||||
|
*
|
||||||
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||||
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||||
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PrecompiledHeader.h"
|
||||||
|
|
||||||
|
#include "OpenGLHostDisplay.h"
|
||||||
|
#include "common/Assertions.h"
|
||||||
|
#include "common/Console.h"
|
||||||
|
#include "common/ScopedGuard.h"
|
||||||
|
#include "common/StringUtil.h"
|
||||||
|
#include "common/GL/Program.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_impl_opengl3.h"
|
||||||
|
#include <array>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
class OpenGLHostDisplayTexture : public HostDisplayTexture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenGLHostDisplayTexture(GLuint texture, u32 width, u32 height, u32 layers, u32 levels)
|
||||||
|
: m_texture(texture)
|
||||||
|
, m_width(width)
|
||||||
|
, m_height(height)
|
||||||
|
, m_layers(layers)
|
||||||
|
, m_levels(levels)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~OpenGLHostDisplayTexture() override = default;
|
||||||
|
|
||||||
|
void* GetHandle() const override { return reinterpret_cast<void*>(static_cast<uintptr_t>(m_texture)); }
|
||||||
|
u32 GetWidth() const override { return m_width; }
|
||||||
|
u32 GetHeight() const override { return m_height; }
|
||||||
|
u32 GetLayers() const override { return m_layers; }
|
||||||
|
u32 GetLevels() const override { return m_levels; }
|
||||||
|
|
||||||
|
GLuint GetGLID() const { return m_texture; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint m_texture;
|
||||||
|
u32 m_width;
|
||||||
|
u32 m_height;
|
||||||
|
u32 m_layers;
|
||||||
|
u32 m_levels;
|
||||||
|
};
|
||||||
|
|
||||||
|
OpenGLHostDisplay::OpenGLHostDisplay() = default;
|
||||||
|
|
||||||
|
OpenGLHostDisplay::~OpenGLHostDisplay()
|
||||||
|
{
|
||||||
|
pxAssertMsg(!m_gl_context, "Context should have been destroyed by now");
|
||||||
|
}
|
||||||
|
|
||||||
|
HostDisplay::RenderAPI OpenGLHostDisplay::GetRenderAPI() const
|
||||||
|
{
|
||||||
|
return m_gl_context->IsGLES() ? RenderAPI::OpenGLES : RenderAPI::OpenGL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* OpenGLHostDisplay::GetRenderDevice() const
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* OpenGLHostDisplay::GetRenderContext() const
|
||||||
|
{
|
||||||
|
return m_gl_context.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void* OpenGLHostDisplay::GetRenderSurface() const
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<HostDisplayTexture> OpenGLHostDisplay::CreateTexture(u32 width, u32 height, u32 layers, u32 levels,
|
||||||
|
const void* data, u32 data_stride, bool dynamic /* = false */)
|
||||||
|
{
|
||||||
|
// clear error
|
||||||
|
glGetError();
|
||||||
|
|
||||||
|
GLuint id;
|
||||||
|
glGenTextures(1, &id);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, id);
|
||||||
|
|
||||||
|
if ((GLAD_GL_ARB_texture_storage || GLAD_GL_ES_VERSION_3_0) && !data)
|
||||||
|
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
|
||||||
|
else
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||||
|
|
||||||
|
GLenum error = glGetError();
|
||||||
|
if (error != GL_NO_ERROR)
|
||||||
|
{
|
||||||
|
Console.Error("Failed to create texture: 0x%X", error);
|
||||||
|
glDeleteTextures(1, &id);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_unique<OpenGLHostDisplayTexture>(id, width, height, layers, levels);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
|
||||||
|
const void* texture_data, u32 texture_data_stride)
|
||||||
|
{
|
||||||
|
OpenGLHostDisplayTexture* tex = static_cast<OpenGLHostDisplayTexture*>(texture);
|
||||||
|
|
||||||
|
GLint alignment;
|
||||||
|
if (texture_data_stride & 1)
|
||||||
|
alignment = 1;
|
||||||
|
else if (texture_data_stride & 2)
|
||||||
|
alignment = 2;
|
||||||
|
else
|
||||||
|
alignment = 4;
|
||||||
|
|
||||||
|
GLint old_texture_binding = 0, old_alignment = 0, old_row_length = 0;
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture_binding);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tex->GetGLID());
|
||||||
|
|
||||||
|
glGetIntegerv(GL_UNPACK_ALIGNMENT, &old_alignment);
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
|
||||||
|
|
||||||
|
glGetIntegerv(GL_UNPACK_ROW_LENGTH, &old_row_length);
|
||||||
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, texture_data_stride / sizeof(u32));
|
||||||
|
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA8, GL_UNSIGNED_BYTE, texture_data);
|
||||||
|
|
||||||
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, old_row_length);
|
||||||
|
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, old_alignment);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, old_texture_binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLHostDisplay::SetVSync(VsyncMode mode)
|
||||||
|
{
|
||||||
|
if (m_gl_context->GetWindowInfo().type == WindowInfo::Type::Surfaceless)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Window framebuffer has to be bound to call SetSwapInterval.
|
||||||
|
GLint current_fbo = 0;
|
||||||
|
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
if (mode != VsyncMode::Adaptive || !m_gl_context->SetSwapInterval(-1))
|
||||||
|
m_gl_context->SetSwapInterval(static_cast<s32>(mode != VsyncMode::Off));
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* OpenGLHostDisplay::GetGLSLVersionString() const
|
||||||
|
{
|
||||||
|
if (GetRenderAPI() == RenderAPI::OpenGLES)
|
||||||
|
{
|
||||||
|
if (GLAD_GL_ES_VERSION_3_0)
|
||||||
|
return "#version 300 es";
|
||||||
|
else
|
||||||
|
return "#version 100";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (GLAD_GL_VERSION_3_3)
|
||||||
|
return "#version 330";
|
||||||
|
else
|
||||||
|
return "#version 130";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string OpenGLHostDisplay::GetGLSLVersionHeader() const
|
||||||
|
{
|
||||||
|
std::string header = GetGLSLVersionString();
|
||||||
|
header += "\n\n";
|
||||||
|
if (GetRenderAPI() == RenderAPI::OpenGLES)
|
||||||
|
{
|
||||||
|
header += "precision highp float;\n";
|
||||||
|
header += "precision highp int;\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLHostDisplay::HasRenderDevice() const
|
||||||
|
{
|
||||||
|
return static_cast<bool>(m_gl_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLHostDisplay::HasRenderSurface() const
|
||||||
|
{
|
||||||
|
return m_window_info.type != WindowInfo::Type::Surfaceless;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLHostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device)
|
||||||
|
{
|
||||||
|
m_gl_context = GL::Context::Create(wi);
|
||||||
|
if (!m_gl_context)
|
||||||
|
{
|
||||||
|
Console.Error("Failed to create any GL context");
|
||||||
|
m_gl_context.reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_window_info = m_gl_context->GetWindowInfo();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLHostDisplay::InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device)
|
||||||
|
{
|
||||||
|
// Start with vsync off.
|
||||||
|
m_gl_context->SetSwapInterval(0);
|
||||||
|
GL::Program::ResetLastProgram();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLHostDisplay::MakeRenderContextCurrent()
|
||||||
|
{
|
||||||
|
if (!m_gl_context->MakeCurrent())
|
||||||
|
{
|
||||||
|
Console.Error("Failed to make GL context current");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLHostDisplay::DoneRenderContextCurrent()
|
||||||
|
{
|
||||||
|
return m_gl_context->DoneCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLHostDisplay::DestroyRenderDevice()
|
||||||
|
{
|
||||||
|
if (!m_gl_context)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_gl_context->DoneCurrent();
|
||||||
|
m_gl_context.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLHostDisplay::ChangeRenderWindow(const WindowInfo& new_wi)
|
||||||
|
{
|
||||||
|
pxAssert(m_gl_context);
|
||||||
|
|
||||||
|
if (!m_gl_context->ChangeSurface(new_wi))
|
||||||
|
{
|
||||||
|
Console.Error("Failed to change surface");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_window_info = m_gl_context->GetWindowInfo();
|
||||||
|
|
||||||
|
if (new_wi.type != WindowInfo::Type::Surfaceless)
|
||||||
|
{
|
||||||
|
// reset vsync rate, since it (usually) gets lost
|
||||||
|
if (m_vsync_mode != VsyncMode::Adaptive || !m_gl_context->SetSwapInterval(-1))
|
||||||
|
m_gl_context->SetSwapInterval(static_cast<s32>(m_vsync_mode != VsyncMode::Off));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLHostDisplay::ResizeRenderWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
||||||
|
{
|
||||||
|
if (!m_gl_context)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_window_info.surface_scale = new_window_scale;
|
||||||
|
if (m_window_info.surface_width == static_cast<u32>(new_window_width) &&
|
||||||
|
m_window_info.surface_height == static_cast<u32>(new_window_height))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_gl_context->ResizeSurface(static_cast<u32>(new_window_width), static_cast<u32>(new_window_height));
|
||||||
|
m_window_info = m_gl_context->GetWindowInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLHostDisplay::SupportsFullscreen() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLHostDisplay::IsFullscreen()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLHostDisplay::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HostDisplay::AdapterAndModeList OpenGLHostDisplay::GetAdapterAndModeList()
|
||||||
|
{
|
||||||
|
AdapterAndModeList aml;
|
||||||
|
|
||||||
|
if (m_gl_context)
|
||||||
|
{
|
||||||
|
for (const GL::Context::FullscreenModeInfo& fmi : m_gl_context->EnumerateFullscreenModes())
|
||||||
|
aml.fullscreen_modes.push_back(GetFullscreenModeString(fmi.width, fmi.height, fmi.refresh_rate));
|
||||||
|
}
|
||||||
|
|
||||||
|
return aml;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLHostDisplay::DestroyRenderSurface()
|
||||||
|
{
|
||||||
|
if (!m_gl_context)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_window_info = {};
|
||||||
|
if (!m_gl_context->ChangeSurface(m_window_info))
|
||||||
|
Console.Error("Failed to switch to surfaceless");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLHostDisplay::CreateImGuiContext()
|
||||||
|
{
|
||||||
|
return ImGui_ImplOpenGL3_Init(GetGLSLVersionString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLHostDisplay::DestroyImGuiContext()
|
||||||
|
{
|
||||||
|
ImGui_ImplOpenGL3_Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLHostDisplay::UpdateImGuiFontTexture()
|
||||||
|
{
|
||||||
|
ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||||
|
return ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLHostDisplay::BeginPresent(bool frame_skip)
|
||||||
|
{
|
||||||
|
if (frame_skip || m_window_info.type == WindowInfo::Type::Surfaceless)
|
||||||
|
{
|
||||||
|
ImGui::EndFrame();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
glViewport(0, 0, m_window_info.surface_width, m_window_info.surface_height);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLHostDisplay::EndPresent()
|
||||||
|
{
|
||||||
|
// clear out pipeline bindings, since imgui doesn't use them
|
||||||
|
glBindProgramPipeline(0);
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glDisable(GL_STENCIL_TEST);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
|
ImGui::Render();
|
||||||
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
GL::Program::ResetLastProgram();
|
||||||
|
|
||||||
|
m_gl_context->SwapBuffers();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
/* PCSX2 - PS2 Emulator for PCs
|
||||||
|
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||||
|
*
|
||||||
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||||
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||||
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glad.h>
|
||||||
|
|
||||||
|
#include "HostDisplay.h"
|
||||||
|
#include "common/GL/Context.h"
|
||||||
|
#include "common/WindowInfo.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class OpenGLHostDisplay final : public HostDisplay
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenGLHostDisplay();
|
||||||
|
~OpenGLHostDisplay();
|
||||||
|
|
||||||
|
RenderAPI GetRenderAPI() const override;
|
||||||
|
void* GetRenderDevice() const override;
|
||||||
|
void* GetRenderContext() const override;
|
||||||
|
void* GetRenderSurface() const override;
|
||||||
|
|
||||||
|
bool HasRenderDevice() const override;
|
||||||
|
bool HasRenderSurface() const override;
|
||||||
|
|
||||||
|
bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device) override;
|
||||||
|
bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override;
|
||||||
|
void DestroyRenderDevice() override;
|
||||||
|
|
||||||
|
bool MakeRenderContextCurrent() override;
|
||||||
|
bool DoneRenderContextCurrent() override;
|
||||||
|
|
||||||
|
bool ChangeRenderWindow(const WindowInfo& new_wi) override;
|
||||||
|
void ResizeRenderWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
||||||
|
bool SupportsFullscreen() const override;
|
||||||
|
bool IsFullscreen() override;
|
||||||
|
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||||
|
AdapterAndModeList GetAdapterAndModeList() override;
|
||||||
|
void DestroyRenderSurface() override;
|
||||||
|
|
||||||
|
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, const void* data,
|
||||||
|
u32 data_stride, bool dynamic) override;
|
||||||
|
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data,
|
||||||
|
u32 texture_data_stride) override;
|
||||||
|
|
||||||
|
void SetVSync(VsyncMode mode) override;
|
||||||
|
|
||||||
|
bool BeginPresent(bool frame_skip) override;
|
||||||
|
void EndPresent() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const char* GetGLSLVersionString() const;
|
||||||
|
std::string GetGLSLVersionHeader() const;
|
||||||
|
|
||||||
|
bool CreateImGuiContext() override;
|
||||||
|
void DestroyImGuiContext() override;
|
||||||
|
bool UpdateImGuiFontTexture() override;
|
||||||
|
|
||||||
|
std::unique_ptr<GL::Context> m_gl_context;
|
||||||
|
|
||||||
|
VsyncMode m_vsync_mode = VsyncMode::Off;
|
||||||
|
};
|
||||||
|
|
55
pcsx2/GS.cpp
55
pcsx2/GS.cpp
|
@ -47,20 +47,31 @@ void gsReset()
|
||||||
|
|
||||||
void gsUpdateFrequency(Pcsx2Config& config)
|
void gsUpdateFrequency(Pcsx2Config& config)
|
||||||
{
|
{
|
||||||
switch (EmuConfig.LimiterMode)
|
if (config.GS.FrameLimitEnable)
|
||||||
{
|
{
|
||||||
case LimiterModeType::Nominal:
|
switch (config.LimiterMode)
|
||||||
config.GS.LimitScalar = EmuConfig.Framerate.NominalScalar;
|
{
|
||||||
break;
|
case LimiterModeType::Nominal:
|
||||||
case LimiterModeType::Slomo:
|
config.GS.LimitScalar = config.Framerate.NominalScalar;
|
||||||
config.GS.LimitScalar = EmuConfig.Framerate.SlomoScalar;
|
break;
|
||||||
break;
|
case LimiterModeType::Slomo:
|
||||||
case LimiterModeType::Turbo:
|
config.GS.LimitScalar = config.Framerate.SlomoScalar;
|
||||||
config.GS.LimitScalar = EmuConfig.Framerate.TurboScalar;
|
break;
|
||||||
break;
|
case LimiterModeType::Turbo:
|
||||||
default:
|
config.GS.LimitScalar = config.Framerate.TurboScalar;
|
||||||
pxAssert("Unknown framelimiter mode!");
|
break;
|
||||||
|
case LimiterModeType::Unlimited:
|
||||||
|
config.GS.LimitScalar = 0.0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pxAssert("Unknown framelimiter mode!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
config.GS.LimitScalar = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
UpdateVSyncRate();
|
UpdateVSyncRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,34 +380,34 @@ void gsIrq() {
|
||||||
// This function does not regulate frame limiting, meaning it does no stalling. Stalling
|
// This function does not regulate frame limiting, meaning it does no stalling. Stalling
|
||||||
// functions are performed by the EE, which itself uses thread sleep logic to avoid spin
|
// functions are performed by the EE, which itself uses thread sleep logic to avoid spin
|
||||||
// waiting as much as possible (maximizes CPU resource availability for the GS).
|
// waiting as much as possible (maximizes CPU resource availability for the GS).
|
||||||
|
static bool s_isSkippingCurrentFrame = false;
|
||||||
|
|
||||||
__fi void gsFrameSkip()
|
__fi void gsFrameSkip()
|
||||||
{
|
{
|
||||||
static int consec_skipped = 0;
|
static int consec_skipped = 0;
|
||||||
static int consec_drawn = 0;
|
static int consec_drawn = 0;
|
||||||
static bool isSkipping = false;
|
|
||||||
|
|
||||||
if( !EmuConfig.GS.FrameSkipEnable )
|
if( !EmuConfig.GS.FrameSkipEnable )
|
||||||
{
|
{
|
||||||
if( isSkipping )
|
if( s_isSkippingCurrentFrame )
|
||||||
{
|
{
|
||||||
// Frameskipping disabled on-the-fly .. make sure the GS is restored to non-skip
|
// Frameskipping disabled on-the-fly .. make sure the GS is restored to non-skip
|
||||||
// behavior.
|
// behavior.
|
||||||
GSsetFrameSkip( false );
|
GSsetFrameSkip( false );
|
||||||
isSkipping = false;
|
s_isSkippingCurrentFrame = false;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GSsetFrameSkip( isSkipping );
|
GSsetFrameSkip( s_isSkippingCurrentFrame );
|
||||||
|
|
||||||
if( isSkipping )
|
if( s_isSkippingCurrentFrame )
|
||||||
{
|
{
|
||||||
++consec_skipped;
|
++consec_skipped;
|
||||||
if( consec_skipped >= EmuConfig.GS.FramesToSkip )
|
if( consec_skipped >= EmuConfig.GS.FramesToSkip )
|
||||||
{
|
{
|
||||||
consec_skipped = 0;
|
consec_skipped = 0;
|
||||||
isSkipping = false;
|
s_isSkippingCurrentFrame = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -405,11 +416,16 @@ __fi void gsFrameSkip()
|
||||||
if( consec_drawn >= EmuConfig.GS.FramesToDraw )
|
if( consec_drawn >= EmuConfig.GS.FramesToDraw )
|
||||||
{
|
{
|
||||||
consec_drawn = 0;
|
consec_drawn = 0;
|
||||||
isSkipping = true;
|
s_isSkippingCurrentFrame = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern bool gsIsSkippingCurrentFrame()
|
||||||
|
{
|
||||||
|
return s_isSkippingCurrentFrame;
|
||||||
|
}
|
||||||
|
|
||||||
//These are done at VSync Start. Drawing is done when VSync is off, then output the screen when Vsync is on
|
//These are done at VSync Start. Drawing is done when VSync is off, then output the screen when Vsync is on
|
||||||
//The GS needs to be told at the start of a vsync else it loses half of its picture (could be responsible for some halfscreen issues)
|
//The GS needs to be told at the start of a vsync else it loses half of its picture (could be responsible for some halfscreen issues)
|
||||||
//We got away with it before i think due to our awful GS timing, but now we have it right (ish)
|
//We got away with it before i think due to our awful GS timing, but now we have it right (ish)
|
||||||
|
@ -436,3 +452,4 @@ void SaveStateBase::gsFreeze()
|
||||||
FreezeMem(PS2MEM_GS, 0x2000);
|
FreezeMem(PS2MEM_GS, 0x2000);
|
||||||
Freeze(gsVideoMode);
|
Freeze(gsVideoMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
25
pcsx2/GS.h
25
pcsx2/GS.h
|
@ -19,6 +19,7 @@
|
||||||
#include "System/SysThreads.h"
|
#include "System/SysThreads.h"
|
||||||
#include "Gif.h"
|
#include "Gif.h"
|
||||||
#include "GS/GS.h"
|
#include "GS/GS.h"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
extern double GetVerticalFrequency();
|
extern double GetVerticalFrequency();
|
||||||
alignas(16) extern u8 g_RealGSMem[Ps2MemSize::GSregs];
|
alignas(16) extern u8 g_RealGSMem[Ps2MemSize::GSregs];
|
||||||
|
@ -295,6 +296,7 @@ enum MTGS_RingCommand
|
||||||
GS_RINGTYPE_MTVU_GSPACKET,
|
GS_RINGTYPE_MTVU_GSPACKET,
|
||||||
GS_RINGTYPE_INIT_READ_FIFO1,
|
GS_RINGTYPE_INIT_READ_FIFO1,
|
||||||
GS_RINGTYPE_INIT_READ_FIFO2,
|
GS_RINGTYPE_INIT_READ_FIFO2,
|
||||||
|
GS_RINGTYPE_ASYNC_CALL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -304,6 +306,14 @@ struct MTGS_FreezeData
|
||||||
s32 retval; // value returned from the call, valid only after an mtgsWaitGS()
|
s32 retval; // value returned from the call, valid only after an mtgsWaitGS()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct MTGS_MemoryScreenshotData
|
||||||
|
{
|
||||||
|
u32 width = 0;
|
||||||
|
u32 height = 0;
|
||||||
|
std::vector<u32> pixels; // width * height
|
||||||
|
bool success = false;
|
||||||
|
};
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// SysMtgsThread
|
// SysMtgsThread
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
@ -312,6 +322,8 @@ class SysMtgsThread : public SysThreadBase
|
||||||
typedef SysThreadBase _parent;
|
typedef SysThreadBase _parent;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
using AsyncCallType = std::function<void()>;
|
||||||
|
|
||||||
// note: when m_ReadPos == m_WritePos, the fifo is empty
|
// note: when m_ReadPos == m_WritePos, the fifo is empty
|
||||||
// Threading info: m_ReadPos is updated by the MTGS thread. m_WritePos is updated by the EE thread
|
// Threading info: m_ReadPos is updated by the MTGS thread. m_WritePos is updated by the EE thread
|
||||||
std::atomic<unsigned int> m_ReadPos; // cur pos gs is reading from
|
std::atomic<unsigned int> m_ReadPos; // cur pos gs is reading from
|
||||||
|
@ -364,7 +376,7 @@ public:
|
||||||
void PrepDataPacket( GIF_PATH pathidx, u32 size );
|
void PrepDataPacket( GIF_PATH pathidx, u32 size );
|
||||||
void SendDataPacket();
|
void SendDataPacket();
|
||||||
void SendGameCRC( u32 crc );
|
void SendGameCRC( u32 crc );
|
||||||
void WaitForOpen();
|
bool WaitForOpen();
|
||||||
void Freeze( FreezeAction mode, MTGS_FreezeData& data );
|
void Freeze( FreezeAction mode, MTGS_FreezeData& data );
|
||||||
|
|
||||||
void SendSimpleGSPacket( MTGS_RingCommand type, u32 offset, u32 size, GIF_PATH path );
|
void SendSimpleGSPacket( MTGS_RingCommand type, u32 offset, u32 size, GIF_PATH path );
|
||||||
|
@ -377,6 +389,16 @@ public:
|
||||||
|
|
||||||
bool IsGSOpened() const { return m_Opened; }
|
bool IsGSOpened() const { return m_Opened; }
|
||||||
|
|
||||||
|
void RunOnGSThread(AsyncCallType func);
|
||||||
|
void ApplySettings();
|
||||||
|
void ResizeDisplayWindow(int width, int height, float scale);
|
||||||
|
void UpdateDisplayWindow();
|
||||||
|
void SetVSync(VsyncMode mode);
|
||||||
|
void SwitchRenderer(GSRendererType renderer, bool display_message = true);
|
||||||
|
void SetSoftwareRendering(bool software, bool display_message = true);
|
||||||
|
void ToggleSoftwareRendering();
|
||||||
|
bool SaveMemorySnapshot(u32 width, u32 height, std::vector<u32>* pixels);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void OpenGS();
|
void OpenGS();
|
||||||
void CloseGS();
|
void CloseGS();
|
||||||
|
@ -413,6 +435,7 @@ extern void gsSetVideoMode(GS_VideoMode mode);
|
||||||
extern void gsResetFrameSkip();
|
extern void gsResetFrameSkip();
|
||||||
extern void gsPostVsyncStart();
|
extern void gsPostVsyncStart();
|
||||||
extern void gsFrameSkip();
|
extern void gsFrameSkip();
|
||||||
|
extern bool gsIsSkippingCurrentFrame();
|
||||||
extern void gsUpdateFrequency(Pcsx2Config& config);
|
extern void gsUpdateFrequency(Pcsx2Config& config);
|
||||||
|
|
||||||
// Some functions shared by both the GS and MTGS
|
// Some functions shared by both the GS and MTGS
|
||||||
|
|
711
pcsx2/GS/GS.cpp
711
pcsx2/GS/GS.cpp
|
@ -14,7 +14,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "PrecompiledHeader.h"
|
#include "PrecompiledHeader.h"
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
|
// NOTE: The include order matters - GS.h includes windows.h
|
||||||
#include "GS/Window/GSwxDialog.h"
|
#include "GS/Window/GSwxDialog.h"
|
||||||
|
#endif
|
||||||
#include "GS.h"
|
#include "GS.h"
|
||||||
#include "GSUtil.h"
|
#include "GSUtil.h"
|
||||||
#include "GSExtra.h"
|
#include "GSExtra.h"
|
||||||
|
@ -26,7 +29,15 @@
|
||||||
#include "GSLzma.h"
|
#include "GSLzma.h"
|
||||||
|
|
||||||
#include "common/pxStreams.h"
|
#include "common/pxStreams.h"
|
||||||
|
#include "common/pxStreams.h"
|
||||||
|
#include "common/Console.h"
|
||||||
|
#include "common/StringUtil.h"
|
||||||
#include "pcsx2/Config.h"
|
#include "pcsx2/Config.h"
|
||||||
|
#include "pcsx2/Host.h"
|
||||||
|
#include "pcsx2/HostDisplay.h"
|
||||||
|
#ifdef PCSX2_CORE
|
||||||
|
#include "pcsx2/HostSettings.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
@ -45,29 +56,10 @@ static HRESULT s_hr = E_FAIL;
|
||||||
// debug obscure compiler errors --govanify
|
// debug obscure compiler errors --govanify
|
||||||
#undef None
|
#undef None
|
||||||
|
|
||||||
static GSRenderer* s_gs = NULL;
|
Pcsx2Config::GSOptions GSConfig;
|
||||||
static u8* s_basemem = NULL;
|
|
||||||
static int s_vsync = 0;
|
|
||||||
static bool s_exclusive = true;
|
|
||||||
static std::string s_renderer_name;
|
|
||||||
bool gsopen_done = false; // crash guard for GSgetTitleInfo2 and GSKeyEvent (replace with lock?)
|
|
||||||
|
|
||||||
#ifndef PCSX2_CORE
|
static std::unique_ptr<GSRenderer> s_gs;
|
||||||
static std::atomic_bool s_gs_window_resized{false};
|
static HostDisplay::RenderAPI s_render_api;
|
||||||
static std::mutex s_gs_window_resized_lock;
|
|
||||||
static int s_new_gs_window_width = 0;
|
|
||||||
static int s_new_gs_window_height = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void GSsetBaseMem(u8* mem)
|
|
||||||
{
|
|
||||||
s_basemem = mem;
|
|
||||||
|
|
||||||
if (s_gs)
|
|
||||||
{
|
|
||||||
s_gs->SetRegsMem(s_basemem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int GSinit()
|
int GSinit()
|
||||||
{
|
{
|
||||||
|
@ -80,8 +72,8 @@ int GSinit()
|
||||||
// can crash if the CPU does not support the instruction set.
|
// can crash if the CPU does not support the instruction set.
|
||||||
// Initialise it here instead - it's not ideal since we have to strip the
|
// Initialise it here instead - it's not ideal since we have to strip the
|
||||||
// const type qualifier from all the affected variables.
|
// const type qualifier from all the affected variables.
|
||||||
theApp.SetConfigDir();
|
GSinitConfig();
|
||||||
theApp.Init();
|
|
||||||
|
|
||||||
|
|
||||||
GSUtil::Init();
|
GSUtil::Init();
|
||||||
|
@ -98,14 +90,31 @@ int GSinit()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GSinitConfig()
|
||||||
|
{
|
||||||
|
static bool config_inited = false;
|
||||||
|
if (config_inited)
|
||||||
|
return;
|
||||||
|
|
||||||
|
config_inited = true;
|
||||||
|
theApp.SetConfigDir();
|
||||||
|
theApp.Init();
|
||||||
|
}
|
||||||
|
|
||||||
void GSshutdown()
|
void GSshutdown()
|
||||||
{
|
{
|
||||||
gsopen_done = false;
|
if (s_gs)
|
||||||
|
{
|
||||||
|
s_gs->Destroy();
|
||||||
|
s_gs.reset();
|
||||||
|
}
|
||||||
|
if (g_gs_device)
|
||||||
|
{
|
||||||
|
g_gs_device->Destroy();
|
||||||
|
g_gs_device.reset();
|
||||||
|
}
|
||||||
|
|
||||||
delete s_gs;
|
Host::ReleaseHostDisplay();
|
||||||
s_gs = nullptr;
|
|
||||||
|
|
||||||
theApp.SetCurrentRendererType(GSRendererType::Undefined);
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (SUCCEEDED(s_hr))
|
if (SUCCEEDED(s_hr))
|
||||||
|
@ -119,190 +128,180 @@ void GSshutdown()
|
||||||
|
|
||||||
void GSclose()
|
void GSclose()
|
||||||
{
|
{
|
||||||
gsopen_done = false;
|
if (s_gs)
|
||||||
|
|
||||||
#ifndef PCSX2_CORE
|
|
||||||
// Make sure we don't have any leftover resize events from our last open.
|
|
||||||
s_gs_window_resized.store(false);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (s_gs == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
s_gs->ResetDevice();
|
|
||||||
|
|
||||||
// Opengl requirement: It must be done before the Detach() of
|
|
||||||
// the context
|
|
||||||
delete s_gs->m_dev;
|
|
||||||
|
|
||||||
s_gs->m_dev = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _GSopen(const WindowInfo& wi, const char* title, GSRendererType renderer, int threads = -1)
|
|
||||||
{
|
|
||||||
GSDevice* dev = NULL;
|
|
||||||
|
|
||||||
// Fresh start up or config file changed
|
|
||||||
if (renderer == GSRendererType::Undefined)
|
|
||||||
{
|
{
|
||||||
renderer = static_cast<GSRendererType>(theApp.GetConfigI("Renderer"));
|
s_gs->Destroy();
|
||||||
if (renderer == GSRendererType::Default)
|
s_gs.reset();
|
||||||
renderer = GSUtil::GetPreferredRenderer();
|
}
|
||||||
|
if (g_gs_device)
|
||||||
|
{
|
||||||
|
g_gs_device->Destroy();
|
||||||
|
g_gs_device.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (threads == -1)
|
Host::ReleaseHostDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
static HostDisplay::RenderAPI GetAPIForRenderer(GSRendererType renderer)
|
||||||
|
{
|
||||||
|
switch (renderer)
|
||||||
{
|
{
|
||||||
threads = theApp.GetConfigI("extrathreads");
|
case GSRendererType::OGL:
|
||||||
|
#ifndef _WIN32
|
||||||
|
default:
|
||||||
|
#endif
|
||||||
|
return HostDisplay::RenderAPI::OpenGL;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
case GSRendererType::DX11:
|
||||||
|
case GSRendererType::SW:
|
||||||
|
default:
|
||||||
|
return HostDisplay::RenderAPI::D3D11;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool DoGSOpen(GSRendererType renderer, u8* basemem)
|
||||||
|
{
|
||||||
|
HostDisplay* display = Host::GetHostDisplay();
|
||||||
|
pxAssert(display);
|
||||||
|
|
||||||
|
s_render_api = Host::GetHostDisplay()->GetRenderAPI();
|
||||||
|
|
||||||
|
switch (display->GetRenderAPI())
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
case HostDisplay::RenderAPI::D3D11:
|
||||||
|
g_gs_device = std::make_unique<GSDevice11>();
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case HostDisplay::RenderAPI::OpenGL:
|
||||||
|
case HostDisplay::RenderAPI::OpenGLES:
|
||||||
|
g_gs_device = std::make_unique<GSDeviceOGL>();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Console.Error("Unknown render API %u", static_cast<unsigned>(display->GetRenderAPI()));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (theApp.GetCurrentRendererType() != renderer)
|
if (!g_gs_device->Create(display))
|
||||||
{
|
{
|
||||||
// Emulator has made a render change request, which requires a completely
|
g_gs_device->Destroy();
|
||||||
// new s_gs -- if the emu doesn't save/restore the GS state across this
|
g_gs_device.reset();
|
||||||
// GSopen call then they'll get corrupted graphics, but that's not my problem.
|
return false;
|
||||||
|
|
||||||
delete s_gs;
|
|
||||||
|
|
||||||
s_gs = NULL;
|
|
||||||
|
|
||||||
theApp.SetCurrentRendererType(renderer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string renderer_name;
|
if (renderer == GSRendererType::Null)
|
||||||
switch (renderer)
|
|
||||||
{
|
{
|
||||||
default:
|
s_gs = std::make_unique<GSRendererNull>();
|
||||||
#ifdef _WIN32
|
|
||||||
case GSRendererType::DX1011_HW:
|
|
||||||
dev = new GSDevice11();
|
|
||||||
s_renderer_name = "D3D11";
|
|
||||||
renderer_name = "Direct3D 11";
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case GSRendererType::OGL_HW:
|
|
||||||
dev = new GSDeviceOGL();
|
|
||||||
s_renderer_name = "OGL";
|
|
||||||
renderer_name = "OpenGL";
|
|
||||||
break;
|
|
||||||
case GSRendererType::OGL_SW:
|
|
||||||
dev = new GSDeviceOGL();
|
|
||||||
s_renderer_name = "SW";
|
|
||||||
renderer_name = "Software";
|
|
||||||
break;
|
|
||||||
case GSRendererType::Null:
|
|
||||||
dev = new GSDeviceNull();
|
|
||||||
s_renderer_name = "NULL";
|
|
||||||
renderer_name = "Null";
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
else if (renderer != GSRendererType::SW)
|
||||||
printf("Current Renderer: %s\n", renderer_name.c_str());
|
|
||||||
|
|
||||||
if (dev == NULL)
|
|
||||||
{
|
{
|
||||||
return -1;
|
s_gs = std::make_unique<GSRendererNew>();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (s_gs == NULL)
|
|
||||||
{
|
{
|
||||||
switch (renderer)
|
const int threads = theApp.GetConfigI("extrathreads");
|
||||||
{
|
s_gs = std::make_unique<GSRendererSW>(threads);
|
||||||
default:
|
|
||||||
case GSRendererType::DX1011_HW:
|
|
||||||
case GSRendererType::OGL_HW:
|
|
||||||
s_gs = (GSRenderer*)new GSRendererNew();
|
|
||||||
break;
|
|
||||||
case GSRendererType::OGL_SW:
|
|
||||||
s_gs = new GSRendererSW(threads);
|
|
||||||
break;
|
|
||||||
case GSRendererType::Null:
|
|
||||||
s_gs = new GSRendererNull();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (s_gs == NULL)
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (std::exception& ex)
|
catch (std::exception& ex)
|
||||||
{
|
{
|
||||||
printf("GS error: Exception caught in GSopen: %s", ex.what());
|
Host::ReportFormattedErrorAsync("GS", "GS error: Exception caught in GSopen: %s", ex.what());
|
||||||
return -1;
|
s_gs.reset();
|
||||||
|
g_gs_device->Destroy();
|
||||||
|
g_gs_device.reset();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
s_gs->SetRegsMem(s_basemem);
|
s_gs->SetRegsMem(basemem);
|
||||||
s_gs->SetVSync(s_vsync);
|
|
||||||
|
|
||||||
if (!s_gs->CreateDevice(dev, wi))
|
display->SetVSync(EmuConfig.GetEffectiveVsyncMode());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GSreopen(bool recreate_display)
|
||||||
|
{
|
||||||
|
Console.WriteLn("Reopening GS with %s display", recreate_display ? "new" : "existing");
|
||||||
|
|
||||||
|
s_gs->Flush();
|
||||||
|
|
||||||
|
freezeData fd = {};
|
||||||
|
if (s_gs->Freeze(&fd, true) != 0)
|
||||||
{
|
{
|
||||||
// This probably means the user has DX11 configured with a video card that is only DX9
|
Console.Error("(GSreopen) Failed to get GS freeze size");
|
||||||
// compliant. Cound mean drivr issues of some sort also, but to be sure, that's the most
|
return false;
|
||||||
// common cause of device creation errors. :) --air
|
|
||||||
|
|
||||||
GSclose();
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
std::unique_ptr<u8[]> fd_data = std::make_unique<u8[]>(fd.size);
|
||||||
}
|
fd.data = fd_data.get();
|
||||||
|
if (s_gs->Freeze(&fd, false) != 0)
|
||||||
void GSosdLog(const char* utf8, u32 color)
|
|
||||||
{
|
|
||||||
if (s_gs && s_gs->m_dev)
|
|
||||||
s_gs->m_dev->m_osd.Log(utf8);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSosdMonitor(const char* key, const char* value, u32 color)
|
|
||||||
{
|
|
||||||
if (s_gs && s_gs->m_dev)
|
|
||||||
s_gs->m_dev->m_osd.Monitor(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GSopen2(const WindowInfo& wi, u32 flags)
|
|
||||||
{
|
|
||||||
static bool stored_toggle_state = false;
|
|
||||||
const bool toggle_state = !!(flags & 4);
|
|
||||||
GSRendererType current_renderer = static_cast<GSRendererType>(flags >> 24);
|
|
||||||
if (current_renderer == GSRendererType::NO_RENDERER)
|
|
||||||
current_renderer = theApp.GetCurrentRendererType();
|
|
||||||
|
|
||||||
if (current_renderer != GSRendererType::Undefined && stored_toggle_state != toggle_state)
|
|
||||||
{
|
{
|
||||||
// SW -> HW and HW -> SW (F9 Switch)
|
Console.Error("(GSreopen) Failed to freeze GS");
|
||||||
switch (current_renderer)
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recreate_display)
|
||||||
|
{
|
||||||
|
g_gs_device->ResetAPIState();
|
||||||
|
if (Host::BeginPresentFrame(true))
|
||||||
|
Host::EndPresentFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* basemem = s_gs->GetRegsMem();
|
||||||
|
const u32 gamecrc = s_gs->GetGameCRC();
|
||||||
|
const int gamecrc_options = s_gs->GetGameCRCOptions();
|
||||||
|
s_gs->Destroy();
|
||||||
|
s_gs.reset();
|
||||||
|
g_gs_device->Destroy();
|
||||||
|
g_gs_device.reset();
|
||||||
|
|
||||||
|
if (recreate_display)
|
||||||
|
{
|
||||||
|
Host::ReleaseHostDisplay();
|
||||||
|
if (!Host::AcquireHostDisplay(GetAPIForRenderer(GSConfig.Renderer)))
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
pxFailRel("(GSreopen) Failed to reacquire host display");
|
||||||
case GSRendererType::DX1011_HW:
|
return false;
|
||||||
current_renderer = GSRendererType::OGL_SW;
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case GSRendererType::OGL_SW:
|
|
||||||
{
|
|
||||||
const auto config_renderer = static_cast<GSRendererType>(theApp.GetConfigI("Renderer"));
|
|
||||||
|
|
||||||
if (current_renderer == config_renderer)
|
|
||||||
current_renderer = GSUtil::GetPreferredRenderer();
|
|
||||||
else
|
|
||||||
current_renderer = config_renderer;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GSRendererType::OGL_HW:
|
|
||||||
current_renderer = GSRendererType::OGL_SW;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
current_renderer = GSRendererType::OGL_SW;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stored_toggle_state = toggle_state;
|
|
||||||
|
|
||||||
int retval = _GSopen(wi, "", current_renderer);
|
if (!DoGSOpen(GSConfig.Renderer, basemem))
|
||||||
|
{
|
||||||
|
pxFailRel("(GSreopen) Failed to recreate GS");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
gsopen_done = true;
|
if (s_gs->Defrost(&fd) != 0)
|
||||||
|
{
|
||||||
|
pxFailRel("(GSreopen) Failed to defrost");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return retval;
|
s_gs->SetGameCRC(gamecrc, gamecrc_options);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* basemem)
|
||||||
|
{
|
||||||
|
if (renderer == GSRendererType::Auto)
|
||||||
|
renderer = GSUtil::GetPreferredRenderer();
|
||||||
|
|
||||||
|
GSConfig = config;
|
||||||
|
GSConfig.Renderer = renderer;
|
||||||
|
GSConfig.MaskUserHacks();
|
||||||
|
|
||||||
|
if (!Host::AcquireHostDisplay(GetAPIForRenderer(renderer)))
|
||||||
|
{
|
||||||
|
Console.Error("Failed to acquire host display");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DoGSOpen(renderer, basemem);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSreset()
|
void GSreset()
|
||||||
|
@ -444,7 +443,7 @@ void GSgifTransfer3(u8* mem, u32 size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSvsync(int field)
|
void GSvsync(u32 field)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -488,20 +487,6 @@ u32 GSmakeSnapshot(char* path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSkeyEvent(const HostKeyEvent& e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (gsopen_done)
|
|
||||||
{
|
|
||||||
s_gs->KeyEvent(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (GSRecoverableError)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int GSfreeze(FreezeAction mode, freezeData* data)
|
int GSfreeze(FreezeAction mode, freezeData* data)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -526,6 +511,20 @@ int GSfreeze(FreezeAction mode, freezeData* data)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
|
|
||||||
|
void GSkeyEvent(const HostKeyEvent& e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (s_gs)
|
||||||
|
s_gs->KeyEvent(e);
|
||||||
|
}
|
||||||
|
catch (GSRecoverableError)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GSconfigure()
|
void GSconfigure()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -540,7 +539,7 @@ void GSconfigure()
|
||||||
{
|
{
|
||||||
theApp.ReloadConfig();
|
theApp.ReloadConfig();
|
||||||
// Force a reload of the gs state
|
// Force a reload of the gs state
|
||||||
theApp.SetCurrentRendererType(GSRendererType::Undefined);
|
//theApp.SetCurrentRendererType(GSRendererType::Undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (GSRecoverableError)
|
catch (GSRecoverableError)
|
||||||
|
@ -556,6 +555,8 @@ int GStest()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
void pt(const char* str)
|
void pt(const char* str)
|
||||||
{
|
{
|
||||||
struct tm* current;
|
struct tm* current;
|
||||||
|
@ -606,72 +607,203 @@ void GSsetGameCRC(u32 crc, int options)
|
||||||
s_gs->SetGameCRC(crc, options);
|
s_gs->SetGameCRC(crc, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSgetTitleInfo2(char* dest, size_t length)
|
|
||||||
{
|
|
||||||
std::string s;
|
|
||||||
s.append(s_renderer_name);
|
|
||||||
// TODO: this gets called from a different thread concurrently with GSOpen (on linux)
|
|
||||||
if (gsopen_done && s_gs != NULL && s_gs->m_GStitleInfoBuffer[0])
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(s_gs->m_pGSsetTitle_Crit);
|
|
||||||
|
|
||||||
s.append(" | ").append(s_gs->m_GStitleInfoBuffer);
|
|
||||||
|
|
||||||
if (s.size() > length - 1)
|
|
||||||
{
|
|
||||||
s = s.substr(0, length - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
strcpy(dest, s.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSsetFrameSkip(int frameskip)
|
void GSsetFrameSkip(int frameskip)
|
||||||
{
|
{
|
||||||
s_gs->SetFrameSkip(frameskip);
|
s_gs->SetFrameSkip(frameskip);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSsetVsync(int vsync)
|
void GSgetInternalResolution(int* width, int* height)
|
||||||
{
|
{
|
||||||
s_vsync = vsync;
|
GSRenderer* gs = s_gs.get();
|
||||||
|
if (!gs)
|
||||||
if (s_gs)
|
|
||||||
{
|
{
|
||||||
s_gs->SetVSync(s_vsync);
|
*width = 0;
|
||||||
|
*height = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GSVector2i res(gs->GetInternalResolution());
|
||||||
|
*width = res.x;
|
||||||
|
*height = res.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSgetStats(std::string& info)
|
||||||
|
{
|
||||||
|
GSPerfMon& pm = s_gs->m_perfmon;
|
||||||
|
|
||||||
|
const char* api_name = HostDisplay::RenderAPIToString(s_render_api);
|
||||||
|
|
||||||
|
if (GSConfig.Renderer == GSRendererType::SW)
|
||||||
|
{
|
||||||
|
int sum = 0;
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
sum += pm.CPU(GSPerfMon::WorkerDraw0 + i);
|
||||||
|
|
||||||
|
const double fps = 1000.0f / pm.Get(GSPerfMon::Frame);
|
||||||
|
const double fillrate = pm.Get(GSPerfMon::Fillrate);
|
||||||
|
info = format("%d S | %d P | %d D | %.2f U | %.2f D | %.2f mpps | %d%% WCPU",
|
||||||
|
(int)pm.Get(GSPerfMon::SyncPoint),
|
||||||
|
(int)pm.Get(GSPerfMon::Prim),
|
||||||
|
(int)pm.Get(GSPerfMon::Draw),
|
||||||
|
pm.Get(GSPerfMon::Swizzle) / 1024,
|
||||||
|
pm.Get(GSPerfMon::Unswizzle) / 1024,
|
||||||
|
fps * fillrate / (1024 * 1024),
|
||||||
|
sum);
|
||||||
|
}
|
||||||
|
else if (GSConfig.Renderer == GSRendererType::Null)
|
||||||
|
{
|
||||||
|
info = format("%s Null", api_name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
info = format("%d S | %d P | %d D | %.2f U | %.2f D",
|
||||||
|
(int)pm.Get(GSPerfMon::SyncPoint),
|
||||||
|
(int)pm.Get(GSPerfMon::Prim),
|
||||||
|
(int)pm.Get(GSPerfMon::Draw),
|
||||||
|
pm.Get(GSPerfMon::Swizzle) / 1024,
|
||||||
|
pm.Get(GSPerfMon::Unswizzle) / 1024);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSsetExclusive(int enabled)
|
void GSgetTitleStats(std::string& info)
|
||||||
{
|
{
|
||||||
s_exclusive = !!enabled;
|
int iwidth, iheight;
|
||||||
|
GSgetInternalResolution(&iwidth, &iheight);
|
||||||
|
|
||||||
if (s_gs)
|
const char* api_name = HostDisplay::RenderAPIToString(s_render_api);
|
||||||
|
const char* hw_sw_name = (GSConfig.Renderer == GSRendererType::Null) ? " Null" : (GSConfig.UseHardwareRenderer() ? " HW" : " SW");
|
||||||
|
const char* interlace_mode = theApp.m_gs_interlace[static_cast<int>(GSConfig.InterlaceMode)].name.c_str();
|
||||||
|
|
||||||
|
info = format("%s%s | %s | %dx%d", api_name, hw_sw_name, interlace_mode, iwidth, iheight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
|
||||||
|
{
|
||||||
|
Pcsx2Config::GSOptions old_config(std::move(GSConfig));
|
||||||
|
GSConfig = new_config;
|
||||||
|
GSConfig.Renderer = (GSConfig.Renderer == GSRendererType::Auto) ? GSUtil::GetPreferredRenderer() : GSConfig.Renderer;
|
||||||
|
GSConfig.MaskUserHacks();
|
||||||
|
if (!s_gs)
|
||||||
|
return;
|
||||||
|
|
||||||
|
HostDisplay* display = Host::GetHostDisplay();
|
||||||
|
|
||||||
|
// Handle OSD scale changes by pushing a window resize through.
|
||||||
|
if (new_config.OsdScale != old_config.OsdScale)
|
||||||
|
Host::ResizeHostDisplay(display->GetWindowWidth(), display->GetWindowHeight(), display->GetWindowScale());
|
||||||
|
|
||||||
|
// Options which need a full teardown/recreate.
|
||||||
|
if (!GSConfig.RestartOptionsAreEqual(old_config))
|
||||||
{
|
{
|
||||||
s_gs->SetVSync(s_vsync);
|
HostDisplay::RenderAPI existing_api = Host::GetHostDisplay()->GetRenderAPI();
|
||||||
|
if (existing_api == HostDisplay::RenderAPI::OpenGLES)
|
||||||
|
existing_api = HostDisplay::RenderAPI::OpenGL;
|
||||||
|
|
||||||
|
const bool do_full_restart = (
|
||||||
|
existing_api != GetAPIForRenderer(GSConfig.Renderer) ||
|
||||||
|
GSConfig.Adapter != old_config.Adapter ||
|
||||||
|
GSConfig.UseDebugDevice != old_config.UseDebugDevice ||
|
||||||
|
GSConfig.UseBlitSwapChain != old_config.UseBlitSwapChain ||
|
||||||
|
GSConfig.DisableShaderCache != old_config.DisableShaderCache
|
||||||
|
);
|
||||||
|
GSreopen(do_full_restart);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Options which aren't using the global struct yet, so we need to recreate all GS objects.
|
||||||
|
if (
|
||||||
|
GSConfig.GPUPaletteConversion != old_config.GPUPaletteConversion ||
|
||||||
|
GSConfig.ConservativeFramebuffer != old_config.ConservativeFramebuffer ||
|
||||||
|
GSConfig.AutoFlushSW != old_config.AutoFlushSW ||
|
||||||
|
GSConfig.PreloadFrameWithGSData != old_config.PreloadFrameWithGSData ||
|
||||||
|
GSConfig.WrapGSMem != old_config.WrapGSMem ||
|
||||||
|
GSConfig.Mipmap != old_config.Mipmap ||
|
||||||
|
GSConfig.AA1 != old_config.AA1 ||
|
||||||
|
GSConfig.UserHacks_AlignSpriteX != old_config.UserHacks_AlignSpriteX ||
|
||||||
|
GSConfig.UserHacks_AutoFlush != old_config.UserHacks_AutoFlush ||
|
||||||
|
GSConfig.UserHacks_CPUFBConversion != old_config.UserHacks_CPUFBConversion ||
|
||||||
|
GSConfig.UserHacks_DisableDepthSupport != old_config.UserHacks_DisableDepthSupport ||
|
||||||
|
GSConfig.UserHacks_DisablePartialInvalidation != old_config.UserHacks_DisablePartialInvalidation ||
|
||||||
|
GSConfig.UserHacks_DisableSafeFeatures != old_config.UserHacks_DisableSafeFeatures ||
|
||||||
|
GSConfig.UserHacks_MergePPSprite != old_config.UserHacks_MergePPSprite ||
|
||||||
|
GSConfig.UserHacks_WildHack != old_config.UserHacks_WildHack ||
|
||||||
|
GSConfig.UserHacks_TextureInsideRt != old_config.UserHacks_TextureInsideRt ||
|
||||||
|
GSConfig.DumpGSData != old_config.DumpGSData ||
|
||||||
|
GSConfig.SaveRT != old_config.SaveRT ||
|
||||||
|
GSConfig.SaveFrame != old_config.SaveFrame ||
|
||||||
|
GSConfig.SaveTexture != old_config.SaveTexture ||
|
||||||
|
GSConfig.SaveDepth != old_config.SaveDepth ||
|
||||||
|
|
||||||
|
GSConfig.UpscaleMultiplier != old_config.UpscaleMultiplier ||
|
||||||
|
GSConfig.HWMipmap != old_config.HWMipmap ||
|
||||||
|
GSConfig.CRCHack != old_config.CRCHack ||
|
||||||
|
GSConfig.MaxAnisotropy != old_config.MaxAnisotropy ||
|
||||||
|
GSConfig.SWExtraThreads != old_config.SWExtraThreads ||
|
||||||
|
GSConfig.SWExtraThreadsHeight != old_config.SWExtraThreadsHeight ||
|
||||||
|
|
||||||
|
GSConfig.UserHacks_HalfBottomOverride != old_config.UserHacks_HalfBottomOverride ||
|
||||||
|
GSConfig.UserHacks_HalfPixelOffset != old_config.UserHacks_HalfPixelOffset ||
|
||||||
|
GSConfig.UserHacks_RoundSprite != old_config.UserHacks_RoundSprite ||
|
||||||
|
GSConfig.UserHacks_TCOffsetX != old_config.UserHacks_TCOffsetX ||
|
||||||
|
GSConfig.UserHacks_TCOffsetY != old_config.UserHacks_TCOffsetY ||
|
||||||
|
|
||||||
|
GSConfig.ShadeBoost_Brightness != old_config.ShadeBoost_Brightness ||
|
||||||
|
GSConfig.ShadeBoost_Contrast != old_config.ShadeBoost_Contrast ||
|
||||||
|
GSConfig.ShadeBoost_Saturation != old_config.ShadeBoost_Saturation ||
|
||||||
|
GSConfig.SaveN != old_config.SaveN ||
|
||||||
|
GSConfig.SaveL != old_config.SaveL ||
|
||||||
|
|
||||||
|
GSConfig.ShaderFX_Conf != old_config.ShaderFX_Conf ||
|
||||||
|
GSConfig.ShaderFX_GLSL != old_config.ShaderFX_GLSL)
|
||||||
|
{
|
||||||
|
GSreopen(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is where we would do finer-grained checks in the future.
|
||||||
|
// For example, flushing the texture cache when mipmap settings change.
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef PCSX2_CORE
|
void GSSwitchRenderer(GSRendererType new_renderer)
|
||||||
void GSResizeWindow(int width, int height)
|
|
||||||
{
|
{
|
||||||
std::unique_lock lock(s_gs_window_resized_lock);
|
if (new_renderer == GSRendererType::Auto)
|
||||||
s_new_gs_window_width = width;
|
new_renderer = GSUtil::GetPreferredRenderer();
|
||||||
s_new_gs_window_height = height;
|
|
||||||
s_gs_window_resized.store(true);
|
if (!s_gs || GSConfig.Renderer == new_renderer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
HostDisplay::RenderAPI existing_api = Host::GetHostDisplay()->GetRenderAPI();
|
||||||
|
if (existing_api == HostDisplay::RenderAPI::OpenGLES)
|
||||||
|
existing_api = HostDisplay::RenderAPI::OpenGL;
|
||||||
|
|
||||||
|
const bool is_software_switch = (new_renderer == GSRendererType::SW || GSConfig.Renderer == GSRendererType::SW);
|
||||||
|
GSConfig.Renderer = new_renderer;
|
||||||
|
GSreopen(!is_software_switch && existing_api != GetAPIForRenderer(new_renderer));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSCheckForWindowResize(int* new_width, int* new_height)
|
void GSResetAPIState()
|
||||||
{
|
{
|
||||||
if (!s_gs_window_resized.load())
|
if (!g_gs_device)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_gs_device->ResetAPIState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSRestoreAPIState()
|
||||||
|
{
|
||||||
|
if (!g_gs_device)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_gs_device->RestoreAPIState();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GSSaveSnapshotToMemory(u32 width, u32 height, std::vector<u32>* pixels)
|
||||||
|
{
|
||||||
|
if (!s_gs)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
std::unique_lock lock(s_gs_window_resized_lock);
|
return s_gs->SaveSnapshotToMemory(width, height, pixels);
|
||||||
*new_width = s_new_gs_window_width;
|
|
||||||
*new_height = s_new_gs_window_height;
|
|
||||||
s_gs_window_resized.store(false);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
std::string format(const char* fmt, ...)
|
std::string format(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
|
@ -885,6 +1017,10 @@ void fifo_free(void* ptr, size_t size, size_t repeat)
|
||||||
|
|
||||||
size_t GSApp::GetIniString(const char* lpAppName, const char* lpKeyName, const char* lpDefault, char* lpReturnedString, size_t nSize, const char* lpFileName)
|
size_t GSApp::GetIniString(const char* lpAppName, const char* lpKeyName, const char* lpDefault, char* lpReturnedString, size_t nSize, const char* lpFileName)
|
||||||
{
|
{
|
||||||
|
#ifdef PCSX2_CORE
|
||||||
|
std::string ret(Host::GetStringSettingValue("EmuCore/GS", lpKeyName, lpDefault));
|
||||||
|
return StringUtil::Strlcpy(lpReturnedString, ret, nSize);
|
||||||
|
#else
|
||||||
BuildConfigurationMap(lpFileName);
|
BuildConfigurationMap(lpFileName);
|
||||||
|
|
||||||
std::string key(lpKeyName);
|
std::string key(lpKeyName);
|
||||||
|
@ -899,10 +1035,12 @@ size_t GSApp::GetIniString(const char* lpAppName, const char* lpKeyName, const c
|
||||||
strcpy(lpReturnedString, value.c_str());
|
strcpy(lpReturnedString, value.c_str());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSApp::WriteIniString(const char* lpAppName, const char* lpKeyName, const char* pString, const char* lpFileName)
|
bool GSApp::WriteIniString(const char* lpAppName, const char* lpKeyName, const char* pString, const char* lpFileName)
|
||||||
{
|
{
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
BuildConfigurationMap(lpFileName);
|
BuildConfigurationMap(lpFileName);
|
||||||
|
|
||||||
std::string key(lpKeyName);
|
std::string key(lpKeyName);
|
||||||
|
@ -931,10 +1069,12 @@ bool GSApp::WriteIniString(const char* lpAppName, const char* lpKeyName, const c
|
||||||
fprintf(f, "%s = %s\n", entry.first.c_str(), entry.second.c_str());
|
fprintf(f, "%s = %s\n", entry.first.c_str(), entry.second.c_str());
|
||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
#endif
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
int GSApp::GetIniInt(const char* lpAppName, const char* lpKeyName, int nDefault, const char* lpFileName)
|
int GSApp::GetIniInt(const char* lpAppName, const char* lpKeyName, int nDefault, const char* lpFileName)
|
||||||
{
|
{
|
||||||
BuildConfigurationMap(lpFileName);
|
BuildConfigurationMap(lpFileName);
|
||||||
|
@ -949,6 +1089,7 @@ int GSApp::GetIniInt(const char* lpAppName, const char* lpKeyName, int nDefault,
|
||||||
else
|
else
|
||||||
return atoi(value.c_str());
|
return atoi(value.c_str());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
GSApp theApp;
|
GSApp theApp;
|
||||||
|
|
||||||
|
@ -969,20 +1110,16 @@ void GSApp::Init()
|
||||||
return;
|
return;
|
||||||
is_initialised = true;
|
is_initialised = true;
|
||||||
|
|
||||||
m_current_renderer_type = GSRendererType::Undefined;
|
|
||||||
|
|
||||||
m_section = "Settings";
|
m_section = "Settings";
|
||||||
|
|
||||||
|
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::Auto), "Automatic", ""));
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::DX1011_HW), "Direct3D 11", ""));
|
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::DX11), "Direct3D 11", ""));
|
||||||
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::OGL_HW), "OpenGL", ""));
|
|
||||||
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::OGL_SW), "Software", ""));
|
|
||||||
#else // Linux
|
|
||||||
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::OGL_HW), "OpenGL", ""));
|
|
||||||
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::OGL_SW), "Software", ""));
|
|
||||||
#endif
|
#endif
|
||||||
|
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::OGL), "OpenGL", ""));
|
||||||
|
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::SW), "Software", ""));
|
||||||
|
|
||||||
// The null renderer goes third, it has use for benchmarking purposes in a release build
|
// The null renderer goes last, it has use for benchmarking purposes in a release build
|
||||||
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::Null), "Null", ""));
|
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::Null), "Null", ""));
|
||||||
|
|
||||||
m_gs_interlace.push_back(GSSetting(0, "None", ""));
|
m_gs_interlace.push_back(GSSetting(0, "None", ""));
|
||||||
|
@ -1018,7 +1155,7 @@ void GSApp::Init()
|
||||||
m_gs_bifilter.push_back(GSSetting(static_cast<u32>(BiFiltering::Forced), "Bilinear", "Forced"));
|
m_gs_bifilter.push_back(GSSetting(static_cast<u32>(BiFiltering::Forced), "Bilinear", "Forced"));
|
||||||
m_gs_bifilter.push_back(GSSetting(static_cast<u32>(BiFiltering::PS2), "Bilinear", "PS2"));
|
m_gs_bifilter.push_back(GSSetting(static_cast<u32>(BiFiltering::PS2), "Bilinear", "PS2"));
|
||||||
|
|
||||||
m_gs_trifilter.push_back(GSSetting(static_cast<u32>(TriFiltering::None), "None", "Default"));
|
m_gs_trifilter.push_back(GSSetting(static_cast<u32>(TriFiltering::Off), "None", "Default"));
|
||||||
m_gs_trifilter.push_back(GSSetting(static_cast<u32>(TriFiltering::PS2), "Trilinear", ""));
|
m_gs_trifilter.push_back(GSSetting(static_cast<u32>(TriFiltering::PS2), "Trilinear", ""));
|
||||||
m_gs_trifilter.push_back(GSSetting(static_cast<u32>(TriFiltering::Forced), "Trilinear", "Ultra/Slow"));
|
m_gs_trifilter.push_back(GSSetting(static_cast<u32>(TriFiltering::Forced), "Trilinear", "Ultra/Slow"));
|
||||||
|
|
||||||
|
@ -1044,7 +1181,7 @@ void GSApp::Init()
|
||||||
|
|
||||||
m_gs_crc_level = {
|
m_gs_crc_level = {
|
||||||
GSSetting(CRCHackLevel::Automatic, "Automatic", "Default"),
|
GSSetting(CRCHackLevel::Automatic, "Automatic", "Default"),
|
||||||
GSSetting(CRCHackLevel::None, "None", "Debug"),
|
GSSetting(CRCHackLevel::Off, "None", "Debug"),
|
||||||
GSSetting(CRCHackLevel::Minimum, "Minimum", "Debug"),
|
GSSetting(CRCHackLevel::Minimum, "Minimum", "Debug"),
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
GSSetting(CRCHackLevel::Partial, "Partial", "OpenGL"),
|
GSSetting(CRCHackLevel::Partial, "Partial", "OpenGL"),
|
||||||
|
@ -1053,14 +1190,14 @@ void GSApp::Init()
|
||||||
GSSetting(CRCHackLevel::Aggressive, "Aggressive", ""),
|
GSSetting(CRCHackLevel::Aggressive, "Aggressive", ""),
|
||||||
};
|
};
|
||||||
|
|
||||||
m_gs_acc_blend_level.push_back(GSSetting(static_cast<u32>(AccBlendLevel::None), "Minimum", "Fastest"));
|
m_gs_acc_blend_level.push_back(GSSetting(static_cast<u32>(AccBlendLevel::Minimum), "Minimum", "Fastest"));
|
||||||
m_gs_acc_blend_level.push_back(GSSetting(static_cast<u32>(AccBlendLevel::Basic), "Basic", "Recommended"));
|
m_gs_acc_blend_level.push_back(GSSetting(static_cast<u32>(AccBlendLevel::Basic), "Basic", "Recommended"));
|
||||||
m_gs_acc_blend_level.push_back(GSSetting(static_cast<u32>(AccBlendLevel::Medium), "Medium", ""));
|
m_gs_acc_blend_level.push_back(GSSetting(static_cast<u32>(AccBlendLevel::Medium), "Medium", ""));
|
||||||
m_gs_acc_blend_level.push_back(GSSetting(static_cast<u32>(AccBlendLevel::High), "High", ""));
|
m_gs_acc_blend_level.push_back(GSSetting(static_cast<u32>(AccBlendLevel::High), "High", ""));
|
||||||
m_gs_acc_blend_level.push_back(GSSetting(static_cast<u32>(AccBlendLevel::Full), "Full", "Very Slow"));
|
m_gs_acc_blend_level.push_back(GSSetting(static_cast<u32>(AccBlendLevel::Full), "Full", "Very Slow"));
|
||||||
m_gs_acc_blend_level.push_back(GSSetting(static_cast<u32>(AccBlendLevel::Ultra), "Ultra", "Ultra Slow"));
|
m_gs_acc_blend_level.push_back(GSSetting(static_cast<u32>(AccBlendLevel::Ultra), "Ultra", "Ultra Slow"));
|
||||||
|
|
||||||
m_gs_acc_blend_level_d3d11.push_back(GSSetting(static_cast<u32>(AccBlendLevel::None), "Minimum", "Fastest"));
|
m_gs_acc_blend_level_d3d11.push_back(GSSetting(static_cast<u32>(AccBlendLevel::Minimum), "Minimum", "Fastest"));
|
||||||
m_gs_acc_blend_level_d3d11.push_back(GSSetting(static_cast<u32>(AccBlendLevel::Basic), "Basic", "Recommended"));
|
m_gs_acc_blend_level_d3d11.push_back(GSSetting(static_cast<u32>(AccBlendLevel::Basic), "Basic", "Recommended"));
|
||||||
m_gs_acc_blend_level_d3d11.push_back(GSSetting(static_cast<u32>(AccBlendLevel::Medium), "Medium", "Debug"));
|
m_gs_acc_blend_level_d3d11.push_back(GSSetting(static_cast<u32>(AccBlendLevel::Medium), "Medium", "Debug"));
|
||||||
m_gs_acc_blend_level_d3d11.push_back(GSSetting(static_cast<u32>(AccBlendLevel::High), "High", "Debug"));
|
m_gs_acc_blend_level_d3d11.push_back(GSSetting(static_cast<u32>(AccBlendLevel::High), "High", "Debug"));
|
||||||
|
@ -1075,9 +1212,9 @@ void GSApp::Init()
|
||||||
// Avoid to clutter the ini file with useless options
|
// Avoid to clutter the ini file with useless options
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Per OS option.
|
// Per OS option.
|
||||||
|
m_default_configuration["Adapter"] = "";
|
||||||
m_default_configuration["CaptureFileName"] = "";
|
m_default_configuration["CaptureFileName"] = "";
|
||||||
m_default_configuration["CaptureVideoCodecDisplayName"] = "";
|
m_default_configuration["CaptureVideoCodecDisplayName"] = "";
|
||||||
m_default_configuration["debug_d3d"] = "0";
|
|
||||||
m_default_configuration["dx_break_on_severity"] = "0";
|
m_default_configuration["dx_break_on_severity"] = "0";
|
||||||
// D3D Blending option
|
// D3D Blending option
|
||||||
m_default_configuration["accurate_blending_unit_d3d11"] = "1";
|
m_default_configuration["accurate_blending_unit_d3d11"] = "1";
|
||||||
|
@ -1097,7 +1234,6 @@ void GSApp::Init()
|
||||||
m_default_configuration["CaptureWidth"] = "640";
|
m_default_configuration["CaptureWidth"] = "640";
|
||||||
m_default_configuration["crc_hack_level"] = std::to_string(static_cast<s8>(CRCHackLevel::Automatic));
|
m_default_configuration["crc_hack_level"] = std::to_string(static_cast<s8>(CRCHackLevel::Automatic));
|
||||||
m_default_configuration["CrcHacksExclusions"] = "";
|
m_default_configuration["CrcHacksExclusions"] = "";
|
||||||
m_default_configuration["debug_opengl"] = "0";
|
|
||||||
m_default_configuration["disable_hw_gl_draw"] = "0";
|
m_default_configuration["disable_hw_gl_draw"] = "0";
|
||||||
m_default_configuration["disable_shader_cache"] = "0";
|
m_default_configuration["disable_shader_cache"] = "0";
|
||||||
m_default_configuration["dithering_ps2"] = "2";
|
m_default_configuration["dithering_ps2"] = "2";
|
||||||
|
@ -1105,7 +1241,10 @@ void GSApp::Init()
|
||||||
m_default_configuration["extrathreads"] = "2";
|
m_default_configuration["extrathreads"] = "2";
|
||||||
m_default_configuration["extrathreads_height"] = "4";
|
m_default_configuration["extrathreads_height"] = "4";
|
||||||
m_default_configuration["filter"] = std::to_string(static_cast<s8>(BiFiltering::PS2));
|
m_default_configuration["filter"] = std::to_string(static_cast<s8>(BiFiltering::PS2));
|
||||||
|
m_default_configuration["FMVSoftwareRendererSwitch"] = "0";
|
||||||
m_default_configuration["fxaa"] = "0";
|
m_default_configuration["fxaa"] = "0";
|
||||||
|
m_default_configuration["HWDisableReadbacks"] = "0";
|
||||||
|
m_default_configuration["IntegerScaling"] = "0";
|
||||||
m_default_configuration["interlace"] = "7";
|
m_default_configuration["interlace"] = "7";
|
||||||
m_default_configuration["conservative_framebuffer"] = "1";
|
m_default_configuration["conservative_framebuffer"] = "1";
|
||||||
m_default_configuration["linear_present"] = "1";
|
m_default_configuration["linear_present"] = "1";
|
||||||
|
@ -1115,20 +1254,13 @@ void GSApp::Init()
|
||||||
m_default_configuration["ModeHeight"] = "480";
|
m_default_configuration["ModeHeight"] = "480";
|
||||||
m_default_configuration["ModeWidth"] = "640";
|
m_default_configuration["ModeWidth"] = "640";
|
||||||
m_default_configuration["NTSC_Saturation"] = "1";
|
m_default_configuration["NTSC_Saturation"] = "1";
|
||||||
#ifdef _WIN32
|
m_default_configuration["OsdShowMessages"] = "1";
|
||||||
m_default_configuration["osd_fontname"] = "C:\\Windows\\Fonts\\my_favorite_font_e_g_tahoma.ttf";
|
m_default_configuration["OsdShowSpeed"] = "0";
|
||||||
#else
|
m_default_configuration["OsdShowFPS"] = "0";
|
||||||
m_default_configuration["osd_fontname"] = "/usr/share/fonts/truetype/my_favorite_font_e_g_DejaVu Sans.ttf";
|
m_default_configuration["OsdShowCPU"] = "0";
|
||||||
#endif
|
m_default_configuration["OsdShowResolution"] = "0";
|
||||||
m_default_configuration["osd_color_r"] = "0";
|
m_default_configuration["OsdShowGSStats"] = "0";
|
||||||
m_default_configuration["osd_color_g"] = "160";
|
m_default_configuration["OsdScale"] = "100";
|
||||||
m_default_configuration["osd_color_b"] = "255";
|
|
||||||
m_default_configuration["osd_color_opacity"] = "100";
|
|
||||||
m_default_configuration["osd_fontsize"] = "25";
|
|
||||||
m_default_configuration["osd_log_enabled"] = "1";
|
|
||||||
m_default_configuration["osd_log_timeout"] = "4";
|
|
||||||
m_default_configuration["osd_monitor_enabled"] = "0";
|
|
||||||
m_default_configuration["osd_max_log_messages"] = "2";
|
|
||||||
m_default_configuration["override_geometry_shader"] = "-1";
|
m_default_configuration["override_geometry_shader"] = "-1";
|
||||||
m_default_configuration["override_GL_ARB_copy_image"] = "-1";
|
m_default_configuration["override_GL_ARB_copy_image"] = "-1";
|
||||||
m_default_configuration["override_GL_ARB_clear_texture"] = "-1";
|
m_default_configuration["override_GL_ARB_clear_texture"] = "-1";
|
||||||
|
@ -1146,7 +1278,7 @@ void GSApp::Init()
|
||||||
m_default_configuration["paltex"] = "0";
|
m_default_configuration["paltex"] = "0";
|
||||||
m_default_configuration["png_compression_level"] = std::to_string(Z_BEST_SPEED);
|
m_default_configuration["png_compression_level"] = std::to_string(Z_BEST_SPEED);
|
||||||
m_default_configuration["preload_frame_with_gs_data"] = "0";
|
m_default_configuration["preload_frame_with_gs_data"] = "0";
|
||||||
m_default_configuration["Renderer"] = std::to_string(static_cast<int>(GSRendererType::Default));
|
m_default_configuration["Renderer"] = std::to_string(static_cast<int>(GSRendererType::Auto));
|
||||||
m_default_configuration["resx"] = "1024";
|
m_default_configuration["resx"] = "1024";
|
||||||
m_default_configuration["resy"] = "1024";
|
m_default_configuration["resy"] = "1024";
|
||||||
m_default_configuration["save"] = "0";
|
m_default_configuration["save"] = "0";
|
||||||
|
@ -1162,8 +1294,13 @@ void GSApp::Init()
|
||||||
m_default_configuration["shaderfx"] = "0";
|
m_default_configuration["shaderfx"] = "0";
|
||||||
m_default_configuration["shaderfx_conf"] = "shaders/GS_FX_Settings.ini";
|
m_default_configuration["shaderfx_conf"] = "shaders/GS_FX_Settings.ini";
|
||||||
m_default_configuration["shaderfx_glsl"] = "shaders/GS.fx";
|
m_default_configuration["shaderfx_glsl"] = "shaders/GS.fx";
|
||||||
|
m_default_configuration["skip_duplicate_frames"] = "0";
|
||||||
|
m_default_configuration["threaded_presentation"] = "0";
|
||||||
|
m_default_configuration["throttle_present_rate"] = "0";
|
||||||
m_default_configuration["TVShader"] = "0";
|
m_default_configuration["TVShader"] = "0";
|
||||||
m_default_configuration["upscale_multiplier"] = "1";
|
m_default_configuration["upscale_multiplier"] = "1";
|
||||||
|
m_default_configuration["UseBlitSwapChain"] = "0";
|
||||||
|
m_default_configuration["UseDebugDevice"] = "0";
|
||||||
m_default_configuration["UserHacks"] = "0";
|
m_default_configuration["UserHacks"] = "0";
|
||||||
m_default_configuration["UserHacks_align_sprite_X"] = "0";
|
m_default_configuration["UserHacks_align_sprite_X"] = "0";
|
||||||
m_default_configuration["UserHacks_AutoFlush"] = "0";
|
m_default_configuration["UserHacks_AutoFlush"] = "0";
|
||||||
|
@ -1180,13 +1317,14 @@ void GSApp::Init()
|
||||||
m_default_configuration["UserHacks_TCOffsetX"] = "0";
|
m_default_configuration["UserHacks_TCOffsetX"] = "0";
|
||||||
m_default_configuration["UserHacks_TCOffsetY"] = "0";
|
m_default_configuration["UserHacks_TCOffsetY"] = "0";
|
||||||
m_default_configuration["UserHacks_TextureInsideRt"] = "0";
|
m_default_configuration["UserHacks_TextureInsideRt"] = "0";
|
||||||
m_default_configuration["UserHacks_TriFilter"] = std::to_string(static_cast<s8>(TriFiltering::None));
|
m_default_configuration["UserHacks_TriFilter"] = std::to_string(static_cast<s8>(TriFiltering::Off));
|
||||||
m_default_configuration["UserHacks_WildHack"] = "0";
|
m_default_configuration["UserHacks_WildHack"] = "0";
|
||||||
m_default_configuration["wrap_gs_mem"] = "0";
|
m_default_configuration["wrap_gs_mem"] = "0";
|
||||||
m_default_configuration["vsync"] = "0";
|
m_default_configuration["vsync"] = "0";
|
||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
void GSApp::ReloadConfig()
|
void GSApp::ReloadConfig()
|
||||||
{
|
{
|
||||||
if (m_configuration_map.empty())
|
if (m_configuration_map.empty())
|
||||||
|
@ -1245,6 +1383,7 @@ void GSApp::BuildConfigurationMap(const char* lpFileName)
|
||||||
m_configuration_map[key] = value;
|
m_configuration_map[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void GSApp::SetConfigDir()
|
void GSApp::SetConfigDir()
|
||||||
{
|
{
|
||||||
|
@ -1284,18 +1423,40 @@ int GSApp::GetConfigI(const char* entry)
|
||||||
|
|
||||||
if (def != m_default_configuration.end())
|
if (def != m_default_configuration.end())
|
||||||
{
|
{
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
return GetIniInt(m_section.c_str(), entry, std::stoi(def->second), m_ini.c_str());
|
return GetIniInt(m_section.c_str(), entry, std::stoi(def->second), m_ini.c_str());
|
||||||
|
#else
|
||||||
|
return Host::GetIntSettingValue("EmuCore/GS", entry, std::stoi(def->second));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Option %s doesn't have a default value\n", entry);
|
fprintf(stderr, "Option %s doesn't have a default value\n", entry);
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
return GetIniInt(m_section.c_str(), entry, 0, m_ini.c_str());
|
return GetIniInt(m_section.c_str(), entry, 0, m_ini.c_str());
|
||||||
|
#else
|
||||||
|
return Host::GetIntSettingValue("EmuCore/GS", entry, 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSApp::GetConfigB(const char* entry)
|
bool GSApp::GetConfigB(const char* entry)
|
||||||
{
|
{
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
return !!GetConfigI(entry);
|
return !!GetConfigI(entry);
|
||||||
|
#else
|
||||||
|
auto def = m_default_configuration.find(entry);
|
||||||
|
|
||||||
|
if (def != m_default_configuration.end())
|
||||||
|
{
|
||||||
|
return Host::GetBoolSettingValue("EmuCore/GS", entry, StringUtil::FromChars<bool>(def->second).value_or(false));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Option %s doesn't have a default value\n", entry);
|
||||||
|
return Host::GetBoolSettingValue("EmuCore/GS", entry, false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSApp::SetConfig(const char* entry, int value)
|
void GSApp::SetConfig(const char* entry, int value)
|
||||||
|
@ -1306,13 +1467,3 @@ void GSApp::SetConfig(const char* entry, int value)
|
||||||
|
|
||||||
SetConfig(entry, buff);
|
SetConfig(entry, buff);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSApp::SetCurrentRendererType(GSRendererType type)
|
|
||||||
{
|
|
||||||
m_current_renderer_type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
GSRendererType GSApp::GetCurrentRendererType() const
|
|
||||||
{
|
|
||||||
return m_current_renderer_type;
|
|
||||||
}
|
|
||||||
|
|
102
pcsx2/GS/GS.h
102
pcsx2/GS/GS.h
|
@ -18,7 +18,7 @@
|
||||||
#include "common/WindowInfo.h"
|
#include "common/WindowInfo.h"
|
||||||
#include "Window/GSSetting.h"
|
#include "Window/GSSetting.h"
|
||||||
#include "SaveState.h"
|
#include "SaveState.h"
|
||||||
#include "Host.h"
|
#include "pcsx2/Config.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
@ -27,18 +27,6 @@
|
||||||
#undef None
|
#undef None
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum class GSRendererType : int8_t
|
|
||||||
{
|
|
||||||
Undefined = -1,
|
|
||||||
NO_RENDERER = 0,
|
|
||||||
DX1011_HW = 3,
|
|
||||||
Null = 11,
|
|
||||||
OGL_HW = 12,
|
|
||||||
OGL_SW = 13,
|
|
||||||
|
|
||||||
Default = Undefined
|
|
||||||
};
|
|
||||||
|
|
||||||
// ST_WRITE is defined in libc, avoid this
|
// ST_WRITE is defined in libc, avoid this
|
||||||
enum stateType
|
enum stateType
|
||||||
{
|
{
|
||||||
|
@ -58,59 +46,18 @@ enum class GSVideoMode : u8
|
||||||
HDTV_1080I
|
HDTV_1080I
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ordering was done to keep compatibility with older ini file.
|
extern Pcsx2Config::GSOptions GSConfig;
|
||||||
enum class BiFiltering : u8
|
|
||||||
{
|
|
||||||
Nearest,
|
|
||||||
Forced,
|
|
||||||
PS2,
|
|
||||||
Forced_But_Sprite,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class TriFiltering : u8
|
struct HostKeyEvent;
|
||||||
{
|
class HostDisplay;
|
||||||
None,
|
|
||||||
PS2,
|
|
||||||
Forced,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class HWMipmapLevel : int
|
|
||||||
{
|
|
||||||
Automatic = -1,
|
|
||||||
Off,
|
|
||||||
Basic,
|
|
||||||
Full
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class CRCHackLevel : s8
|
|
||||||
{
|
|
||||||
Automatic = -1,
|
|
||||||
None,
|
|
||||||
Minimum,
|
|
||||||
Partial,
|
|
||||||
Full,
|
|
||||||
Aggressive
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class AccBlendLevel : u8
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Basic,
|
|
||||||
Medium,
|
|
||||||
High,
|
|
||||||
Full,
|
|
||||||
Ultra,
|
|
||||||
};
|
|
||||||
|
|
||||||
void GSsetBaseMem(u8* mem);
|
|
||||||
int GSinit();
|
int GSinit();
|
||||||
|
void GSinitConfig();
|
||||||
void GSshutdown();
|
void GSshutdown();
|
||||||
void GSclose();
|
bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* basemem);
|
||||||
int _GSopen(const WindowInfo& wi, const char* title, GSRendererType renderer, int threads);
|
bool GSreopen(bool recreate_display);
|
||||||
void GSosdLog(const char* utf8, u32 color);
|
|
||||||
void GSosdMonitor(const char* key, const char* value, u32 color);
|
|
||||||
int GSopen2(const WindowInfo & wi, u32 flags);
|
|
||||||
void GSreset();
|
void GSreset();
|
||||||
|
void GSclose();
|
||||||
void GSgifSoftReset(u32 mask);
|
void GSgifSoftReset(u32 mask);
|
||||||
void GSwriteCSR(u32 csr);
|
void GSwriteCSR(u32 csr);
|
||||||
void GSinitReadFIFO(u8* mem);
|
void GSinitReadFIFO(u8* mem);
|
||||||
|
@ -121,32 +68,34 @@ void GSgifTransfer(const u8* mem, u32 size);
|
||||||
void GSgifTransfer1(u8* mem, u32 addr);
|
void GSgifTransfer1(u8* mem, u32 addr);
|
||||||
void GSgifTransfer2(u8* mem, u32 size);
|
void GSgifTransfer2(u8* mem, u32 size);
|
||||||
void GSgifTransfer3(u8* mem, u32 size);
|
void GSgifTransfer3(u8* mem, u32 size);
|
||||||
void GSvsync(int field);
|
void GSvsync(u32 field);
|
||||||
u32 GSmakeSnapshot(char* path);
|
u32 GSmakeSnapshot(char* path);
|
||||||
void GSkeyEvent(const HostKeyEvent& e);
|
|
||||||
int GSfreeze(FreezeAction mode, freezeData* data);
|
int GSfreeze(FreezeAction mode, freezeData* data);
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
|
void GSkeyEvent(const HostKeyEvent& e);
|
||||||
void GSconfigure();
|
void GSconfigure();
|
||||||
int GStest();
|
int GStest();
|
||||||
|
#endif
|
||||||
bool GSsetupRecording(std::string& filename);
|
bool GSsetupRecording(std::string& filename);
|
||||||
void GSendRecording();
|
void GSendRecording();
|
||||||
void GSsetGameCRC(u32 crc, int options);
|
void GSsetGameCRC(u32 crc, int options);
|
||||||
void GSgetTitleInfo2(char* dest, size_t length);
|
|
||||||
void GSsetFrameSkip(int frameskip);
|
void GSsetFrameSkip(int frameskip);
|
||||||
void GSsetVsync(int vsync);
|
|
||||||
void GSsetExclusive(int enabled);
|
|
||||||
|
|
||||||
#ifndef PCSX2_CORE
|
void GSgetInternalResolution(int* width, int* height);
|
||||||
// Needed for window resizing in wx. Can be safely called from the UI thread.
|
void GSgetStats(std::string& info);
|
||||||
void GSResizeWindow(int width, int height);
|
void GSgetTitleStats(std::string& info);
|
||||||
bool GSCheckForWindowResize(int* new_width, int* new_height);
|
|
||||||
#endif
|
void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config);
|
||||||
|
void GSSwitchRenderer(GSRendererType new_renderer);
|
||||||
|
void GSResetAPIState();
|
||||||
|
void GSRestoreAPIState();
|
||||||
|
bool GSSaveSnapshotToMemory(u32 width, u32 height, std::vector<u32>* pixels);
|
||||||
|
|
||||||
class GSApp
|
class GSApp
|
||||||
{
|
{
|
||||||
std::string m_section;
|
std::string m_section;
|
||||||
std::map<std::string, std::string> m_default_configuration;
|
std::map<std::string, std::string> m_default_configuration;
|
||||||
std::map<std::string, std::string> m_configuration_map;
|
std::map<std::string, std::string> m_configuration_map;
|
||||||
GSRendererType m_current_renderer_type;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::string m_ini;
|
std::string m_ini;
|
||||||
|
@ -154,12 +103,14 @@ public:
|
||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
void BuildConfigurationMap(const char* lpFileName);
|
void BuildConfigurationMap(const char* lpFileName);
|
||||||
void ReloadConfig();
|
void ReloadConfig();
|
||||||
|
int GetIniInt(const char* lpAppName, const char* lpKeyName, int nDefault, const char* lpFileName);
|
||||||
|
#endif
|
||||||
|
|
||||||
size_t GetIniString(const char* lpAppName, const char* lpKeyName, const char* lpDefault, char* lpReturnedString, size_t nSize, const char* lpFileName);
|
size_t GetIniString(const char* lpAppName, const char* lpKeyName, const char* lpDefault, char* lpReturnedString, size_t nSize, const char* lpFileName);
|
||||||
bool WriteIniString(const char* lpAppName, const char* lpKeyName, const char* pString, const char* lpFileName);
|
bool WriteIniString(const char* lpAppName, const char* lpKeyName, const char* pString, const char* lpFileName);
|
||||||
int GetIniInt(const char* lpAppName, const char* lpKeyName, int nDefault, const char* lpFileName);
|
|
||||||
|
|
||||||
void SetConfig(const char* entry, const char* value);
|
void SetConfig(const char* entry, const char* value);
|
||||||
void SetConfig(const char* entry, int value);
|
void SetConfig(const char* entry, int value);
|
||||||
|
@ -173,9 +124,6 @@ public:
|
||||||
bool GetConfigB(const char* entry);
|
bool GetConfigB(const char* entry);
|
||||||
std::string GetConfigS(const char* entry);
|
std::string GetConfigS(const char* entry);
|
||||||
|
|
||||||
void SetCurrentRendererType(GSRendererType type);
|
|
||||||
GSRendererType GetCurrentRendererType() const;
|
|
||||||
|
|
||||||
void SetConfigDir();
|
void SetConfigDir();
|
||||||
|
|
||||||
std::vector<GSSetting> m_gs_renderers;
|
std::vector<GSSetting> m_gs_renderers;
|
||||||
|
@ -206,5 +154,3 @@ struct GSErrorGlVertexArrayTooSmall : GSError
|
||||||
};
|
};
|
||||||
|
|
||||||
extern GSApp theApp;
|
extern GSApp theApp;
|
||||||
|
|
||||||
extern bool gsopen_done;
|
|
||||||
|
|
|
@ -397,11 +397,6 @@ GSCapture::GSCapture()
|
||||||
: m_capturing(false), m_frame(0)
|
: m_capturing(false), m_frame(0)
|
||||||
, m_out_dir("/tmp/GS_Capture") // FIXME Later add an option
|
, m_out_dir("/tmp/GS_Capture") // FIXME Later add an option
|
||||||
{
|
{
|
||||||
m_out_dir = theApp.GetConfigS("capture_out_dir");
|
|
||||||
m_threads = theApp.GetConfigI("capture_threads");
|
|
||||||
#if defined(__unix__)
|
|
||||||
m_compression_level = theApp.GetConfigI("png_compression_level");
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GSCapture::~GSCapture()
|
GSCapture::~GSCapture()
|
||||||
|
@ -418,6 +413,13 @@ bool GSCapture::BeginCapture(float fps, GSVector2i recommendedResolution, float
|
||||||
|
|
||||||
EndCapture();
|
EndCapture();
|
||||||
|
|
||||||
|
// reload settings because they may have changed
|
||||||
|
m_out_dir = theApp.GetConfigS("capture_out_dir");
|
||||||
|
m_threads = theApp.GetConfigI("capture_threads");
|
||||||
|
#if defined(__unix__)
|
||||||
|
m_compression_level = theApp.GetConfigI("png_compression_level");
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
GSCaptureDlg dlg;
|
GSCaptureDlg dlg;
|
||||||
|
|
|
@ -120,7 +120,7 @@ GIFRegTEX0 GSDrawingContext::GetSizeFixedTEX0(const GSVector4& st, bool linear,
|
||||||
th = extend(uv.y, th);
|
th = extend(uv.y, th);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((theApp.GetCurrentRendererType() == GSRendererType::OGL_SW) && ((int)TEX0.TW != tw || (int)TEX0.TH != th))
|
if (GSConfig.Renderer == GSRendererType::SW && ((int)TEX0.TW != tw || (int)TEX0.TH != th))
|
||||||
{
|
{
|
||||||
GL_DBG("FixedTEX0 %05x %d %d tw %d=>%d th %d=>%d st (%.0f,%.0f,%.0f,%.0f) uvmax %d,%d wm %d,%d (%d,%d,%d,%d)",
|
GL_DBG("FixedTEX0 %05x %d %d tw %d=>%d th %d=>%d st (%.0f,%.0f,%.0f,%.0f) uvmax %d,%d wm %d,%d (%d,%d,%d,%d)",
|
||||||
(int)TEX0.TBP0, (int)TEX0.TBW, (int)TEX0.PSM,
|
(int)TEX0.TBP0, (int)TEX0.TBW, (int)TEX0.PSM,
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "GSVector.h"
|
#include "GSVector.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
#include "common/RedtapeWindows.h"
|
||||||
inline std::string convert_utf16_to_utf8(const std::wstring& utf16_string)
|
inline std::string convert_utf16_to_utf8(const std::wstring& utf16_string)
|
||||||
{
|
{
|
||||||
const int size = WideCharToMultiByte(CP_UTF8, 0, utf16_string.c_str(), utf16_string.size(), nullptr, 0, nullptr, nullptr);
|
const int size = WideCharToMultiByte(CP_UTF8, 0, utf16_string.c_str(), utf16_string.size(), nullptr, 0, nullptr, nullptr);
|
||||||
|
|
|
@ -62,14 +62,9 @@ GSLocalMemory::GSLocalMemory()
|
||||||
: m_clut(this)
|
: m_clut(this)
|
||||||
{
|
{
|
||||||
m_use_fifo_alloc = theApp.GetConfigB("UserHacks") && theApp.GetConfigB("wrap_gs_mem");
|
m_use_fifo_alloc = theApp.GetConfigB("UserHacks") && theApp.GetConfigB("wrap_gs_mem");
|
||||||
switch (theApp.GetCurrentRendererType())
|
|
||||||
{
|
if (!GSConfig.UseHardwareRenderer())
|
||||||
case GSRendererType::OGL_SW:
|
m_use_fifo_alloc = true;
|
||||||
m_use_fifo_alloc = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_use_fifo_alloc)
|
if (m_use_fifo_alloc)
|
||||||
m_vm8 = (u8*)fifo_alloc(m_vmsize, 4);
|
m_vm8 = (u8*)fifo_alloc(m_vmsize, 4);
|
||||||
|
|
|
@ -31,7 +31,6 @@ GSState::GSState()
|
||||||
, m_q(1.0f)
|
, m_q(1.0f)
|
||||||
, m_scanmask_used(false)
|
, m_scanmask_used(false)
|
||||||
, m_vt(this)
|
, m_vt(this)
|
||||||
, m_dev(nullptr)
|
|
||||||
, m_regs(NULL)
|
, m_regs(NULL)
|
||||||
, m_crc(0)
|
, m_crc(0)
|
||||||
, m_options(0)
|
, m_options(0)
|
||||||
|
@ -46,15 +45,11 @@ GSState::GSState()
|
||||||
{
|
{
|
||||||
m_userhacks_auto_flush = theApp.GetConfigB("UserHacks_AutoFlush");
|
m_userhacks_auto_flush = theApp.GetConfigB("UserHacks_AutoFlush");
|
||||||
m_userhacks_wildhack = theApp.GetConfigB("UserHacks_WildHack");
|
m_userhacks_wildhack = theApp.GetConfigB("UserHacks_WildHack");
|
||||||
m_userhacks_skipdraw = theApp.GetConfigI("UserHacks_SkipDraw");
|
|
||||||
m_userhacks_skipdraw_offset = theApp.GetConfigI("UserHacks_SkipDraw_Offset");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_userhacks_auto_flush = false;
|
m_userhacks_auto_flush = false;
|
||||||
m_userhacks_wildhack = false;
|
m_userhacks_wildhack = false;
|
||||||
m_userhacks_skipdraw = 0;
|
|
||||||
m_userhacks_skipdraw_offset = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s_n = 0;
|
s_n = 0;
|
||||||
|
@ -76,7 +71,7 @@ GSState::GSState()
|
||||||
|
|
||||||
m_crc_hack_level = theApp.GetConfigT<CRCHackLevel>("crc_hack_level");
|
m_crc_hack_level = theApp.GetConfigT<CRCHackLevel>("crc_hack_level");
|
||||||
if (m_crc_hack_level == CRCHackLevel::Automatic)
|
if (m_crc_hack_level == CRCHackLevel::Automatic)
|
||||||
m_crc_hack_level = GSUtil::GetRecommendedCRCHackLevel(theApp.GetCurrentRendererType());
|
m_crc_hack_level = GSUtil::GetRecommendedCRCHackLevel(GSConfig.Renderer);
|
||||||
|
|
||||||
memset(&m_v, 0, sizeof(m_v));
|
memset(&m_v, 0, sizeof(m_v));
|
||||||
memset(&m_vertex, 0, sizeof(m_vertex));
|
memset(&m_vertex, 0, sizeof(m_vertex));
|
||||||
|
@ -152,13 +147,6 @@ GSState::~GSState()
|
||||||
_aligned_free(m_index.buff);
|
_aligned_free(m_index.buff);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSState::SetRegsMem(u8* basemem)
|
|
||||||
{
|
|
||||||
ASSERT(basemem);
|
|
||||||
|
|
||||||
m_regs = (GSPrivRegSet*)basemem;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSState::SetFrameSkip(int skip)
|
void GSState::SetFrameSkip(int skip)
|
||||||
{
|
{
|
||||||
if (m_frameskip == skip)
|
if (m_frameskip == skip)
|
||||||
|
@ -269,9 +257,7 @@ void GSState::ResetHandlers()
|
||||||
m_fpGIFPackedRegHandlers[GIF_REG_NOP] = &GSState::GIFPackedRegHandlerNOP;
|
m_fpGIFPackedRegHandlers[GIF_REG_NOP] = &GSState::GIFPackedRegHandlerNOP;
|
||||||
|
|
||||||
// swap first/last indices when the provoking vertex is the first (D3D/Vulkan)
|
// swap first/last indices when the provoking vertex is the first (D3D/Vulkan)
|
||||||
const GSRendererType renderer = theApp.GetCurrentRendererType();
|
const bool index_swap = GSConfig.UseHardwareRenderer() && !g_gs_device->Features().provoking_vertex_last;
|
||||||
const bool is_hardware_renderer = (renderer == GSRendererType::DX1011_HW || renderer == GSRendererType::OGL_HW);
|
|
||||||
const bool index_swap = is_hardware_renderer && m_dev && !m_dev->Features().provoking_vertex_last;
|
|
||||||
if (m_userhacks_auto_flush)
|
if (m_userhacks_auto_flush)
|
||||||
index_swap ? SetPrimHandlers<true, true>() : SetPrimHandlers<true, false>();
|
index_swap ? SetPrimHandlers<true, true>() : SetPrimHandlers<true, false>();
|
||||||
else
|
else
|
||||||
|
@ -728,9 +714,7 @@ __forceinline void GSState::ApplyPRIM(u32 prim)
|
||||||
if (GSUtil::GetPrimClass(m_env.PRIM.PRIM) == GSUtil::GetPrimClass(prim & 7)) // NOTE: assume strips/fans are converted to lists
|
if (GSUtil::GetPrimClass(m_env.PRIM.PRIM) == GSUtil::GetPrimClass(prim & 7)) // NOTE: assume strips/fans are converted to lists
|
||||||
{
|
{
|
||||||
u32 prim_mask = 0x7f8;
|
u32 prim_mask = 0x7f8;
|
||||||
const bool is_hardware_renderer =
|
if (GSConfig.UseHardwareRenderer() && GSUtil::GetPrimClass(prim & 7) == GS_TRIANGLE_CLASS)
|
||||||
((theApp.GetCurrentRendererType() == GSRendererType::OGL_HW) || (theApp.GetCurrentRendererType() == GSRendererType::DX1011_HW));
|
|
||||||
if (is_hardware_renderer && GSUtil::GetPrimClass(prim & 7) == GS_TRIANGLE_CLASS)
|
|
||||||
prim_mask &= ~0x80; // Mask out AA1.
|
prim_mask &= ~0x80; // Mask out AA1.
|
||||||
|
|
||||||
if (m_env.PRMODECONT.AC == 1 && (m_env.PRIM.U32[0] ^ prim) & prim_mask) // all fields except PRIM
|
if (m_env.PRMODECONT.AC == 1 && (m_env.PRIM.U32[0] ^ prim) & prim_mask) // all fields except PRIM
|
||||||
|
@ -1067,9 +1051,7 @@ void GSState::GIFRegHandlerPRMODE(const GIFReg* RESTRICT r)
|
||||||
if (!m_env.PRMODECONT.AC)
|
if (!m_env.PRMODECONT.AC)
|
||||||
{
|
{
|
||||||
u32 prim_mask = 0x7f8;
|
u32 prim_mask = 0x7f8;
|
||||||
const bool is_hardware_renderer =
|
if (GSConfig.UseHardwareRenderer() && GSUtil::GetPrimClass(m_env.PRIM.PRIM) == GS_TRIANGLE_CLASS)
|
||||||
((theApp.GetCurrentRendererType() == GSRendererType::OGL_HW) || (theApp.GetCurrentRendererType() == GSRendererType::DX1011_HW));
|
|
||||||
if (is_hardware_renderer && GSUtil::GetPrimClass(m_env.PRIM.PRIM) == GS_TRIANGLE_CLASS)
|
|
||||||
prim_mask &= ~0x80; // Mask out AA1.
|
prim_mask &= ~0x80; // Mask out AA1.
|
||||||
|
|
||||||
if ((m_env.PRIM.U32[0] ^ r->PRMODE.U32[0]) & prim_mask)
|
if ((m_env.PRIM.U32[0] ^ r->PRMODE.U32[0]) & prim_mask)
|
||||||
|
@ -2235,7 +2217,7 @@ void GSState::SetGameCRC(u32 crc, int options)
|
||||||
{
|
{
|
||||||
m_crc = crc;
|
m_crc = crc;
|
||||||
m_options = options;
|
m_options = options;
|
||||||
m_game = CRC::Lookup(m_crc_hack_level != CRCHackLevel::None ? crc : 0);
|
m_game = CRC::Lookup(m_crc_hack_level != CRCHackLevel::Off ? crc : 0);
|
||||||
SetupCrcHack();
|
SetupCrcHack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,8 +155,6 @@ protected:
|
||||||
GetSkipCount m_gsc;
|
GetSkipCount m_gsc;
|
||||||
int m_skip;
|
int m_skip;
|
||||||
int m_skip_offset;
|
int m_skip_offset;
|
||||||
int m_userhacks_skipdraw;
|
|
||||||
int m_userhacks_skipdraw_offset;
|
|
||||||
bool m_userhacks_auto_flush;
|
bool m_userhacks_auto_flush;
|
||||||
|
|
||||||
GSVertex m_v;
|
GSVertex m_v;
|
||||||
|
@ -208,7 +206,6 @@ protected:
|
||||||
GIFRegTEX0 GetTex0Layer(u32 lod);
|
GIFRegTEX0 GetTex0Layer(u32 lod);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GSDevice* m_dev;
|
|
||||||
GIFPath m_path[4];
|
GIFPath m_path[4];
|
||||||
GIFRegPRIM* PRIM;
|
GIFRegPRIM* PRIM;
|
||||||
GSPrivRegSet* m_regs;
|
GSPrivRegSet* m_regs;
|
||||||
|
@ -272,7 +269,13 @@ public:
|
||||||
template<int index> void Transfer(const u8* mem, u32 size);
|
template<int index> void Transfer(const u8* mem, u32 size);
|
||||||
int Freeze(freezeData* fd, bool sizeonly);
|
int Freeze(freezeData* fd, bool sizeonly);
|
||||||
int Defrost(const freezeData* fd);
|
int Defrost(const freezeData* fd);
|
||||||
|
|
||||||
|
u32 GetGameCRC() const { return m_crc; }
|
||||||
|
int GetGameCRCOptions() const { return m_options; }
|
||||||
virtual void SetGameCRC(u32 crc, int options);
|
virtual void SetGameCRC(u32 crc, int options);
|
||||||
|
|
||||||
|
u8* GetRegsMem() const { return reinterpret_cast<u8*>(m_regs); }
|
||||||
|
void SetRegsMem(u8* basemem) { m_regs = reinterpret_cast<GSPrivRegSet*>(basemem); }
|
||||||
|
|
||||||
void SetFrameSkip(int skip);
|
void SetFrameSkip(int skip);
|
||||||
void SetRegsMem(u8* basemem);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -185,28 +185,16 @@ bool GSUtil::CheckSSE()
|
||||||
|
|
||||||
CRCHackLevel GSUtil::GetRecommendedCRCHackLevel(GSRendererType type)
|
CRCHackLevel GSUtil::GetRecommendedCRCHackLevel(GSRendererType type)
|
||||||
{
|
{
|
||||||
return type == GSRendererType::OGL_HW ? CRCHackLevel::Partial : CRCHackLevel::Full;
|
return type == GSRendererType::OGL ? CRCHackLevel::Partial : CRCHackLevel::Full;
|
||||||
}
|
}
|
||||||
|
|
||||||
GSRendererType GSUtil::GetPreferredRenderer()
|
GSRendererType GSUtil::GetPreferredRenderer()
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (D3D::ShouldPreferD3D())
|
if (D3D::ShouldPreferD3D())
|
||||||
return GSRendererType::DX1011_HW;
|
return GSRendererType::DX11;
|
||||||
#endif
|
#endif
|
||||||
return GSRendererType::OGL_HW;
|
return GSRendererType::OGL;
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> GSUtil::GetAdapterList(GSRendererType renderer)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (renderer == GSRendererType::DX1011_HW)
|
|
||||||
{
|
|
||||||
auto factory = D3D::CreateFactory(false);
|
|
||||||
return D3D::GetAdapterList(factory.get());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
|
@ -42,7 +42,6 @@ public:
|
||||||
static bool CheckSSE();
|
static bool CheckSSE();
|
||||||
static CRCHackLevel GetRecommendedCRCHackLevel(GSRendererType type);
|
static CRCHackLevel GetRecommendedCRCHackLevel(GSRendererType type);
|
||||||
static GSRendererType GetPreferredRenderer();
|
static GSRendererType GetPreferredRenderer();
|
||||||
static std::vector<std::string> GetAdapterList(GSRendererType renderer);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
|
@ -41,17 +41,16 @@ const char* shaderName(ShaderConvert value)
|
||||||
case ShaderConvert::RGB5A1_TO_FLOAT16: return "ps_convert_rgb5a1_float16";
|
case ShaderConvert::RGB5A1_TO_FLOAT16: return "ps_convert_rgb5a1_float16";
|
||||||
case ShaderConvert::RGBA_TO_8I: return "ps_convert_rgba_8i";
|
case ShaderConvert::RGBA_TO_8I: return "ps_convert_rgba_8i";
|
||||||
case ShaderConvert::YUV: return "ps_yuv";
|
case ShaderConvert::YUV: return "ps_yuv";
|
||||||
case ShaderConvert::OSD: return "ps_osd";
|
|
||||||
default:
|
default:
|
||||||
ASSERT(0);
|
ASSERT(0);
|
||||||
return "ShaderConvertUnknownShader";
|
return "ShaderConvertUnknownShader";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<GSDevice> g_gs_device;
|
||||||
|
|
||||||
GSDevice::GSDevice()
|
GSDevice::GSDevice()
|
||||||
: m_vsync(false)
|
: m_rbswapped(false)
|
||||||
, m_rbswapped(false)
|
|
||||||
, m_backbuffer(NULL)
|
|
||||||
, m_merge(NULL)
|
, m_merge(NULL)
|
||||||
, m_weavebob(NULL)
|
, m_weavebob(NULL)
|
||||||
, m_blend(NULL)
|
, m_blend(NULL)
|
||||||
|
@ -61,73 +60,47 @@ GSDevice::GSDevice()
|
||||||
{
|
{
|
||||||
memset(&m_vertex, 0, sizeof(m_vertex));
|
memset(&m_vertex, 0, sizeof(m_vertex));
|
||||||
memset(&m_index, 0, sizeof(m_index));
|
memset(&m_index, 0, sizeof(m_index));
|
||||||
m_linear_present = theApp.GetConfigB("linear_present");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GSDevice::~GSDevice()
|
GSDevice::~GSDevice()
|
||||||
{
|
{
|
||||||
PurgePool();
|
PurgePool();
|
||||||
delete m_backbuffer;
|
|
||||||
delete m_merge;
|
delete m_merge;
|
||||||
delete m_weavebob;
|
delete m_weavebob;
|
||||||
delete m_blend;
|
delete m_blend;
|
||||||
delete m_target_tmp;
|
delete m_target_tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSDevice::Create(const WindowInfo& wi)
|
bool GSDevice::Create(HostDisplay* display)
|
||||||
{
|
{
|
||||||
|
m_display = display;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSDevice::Reset(int w, int h)
|
void GSDevice::Destroy()
|
||||||
{
|
{
|
||||||
PurgePool();
|
PurgePool();
|
||||||
|
|
||||||
delete m_backbuffer;
|
|
||||||
delete m_merge;
|
delete m_merge;
|
||||||
delete m_weavebob;
|
delete m_weavebob;
|
||||||
delete m_blend;
|
delete m_blend;
|
||||||
delete m_target_tmp;
|
delete m_target_tmp;
|
||||||
|
|
||||||
m_backbuffer = nullptr;
|
|
||||||
m_merge = nullptr;
|
m_merge = nullptr;
|
||||||
m_weavebob = nullptr;
|
m_weavebob = nullptr;
|
||||||
m_blend = nullptr;
|
m_blend = nullptr;
|
||||||
m_target_tmp = nullptr;
|
m_target_tmp = nullptr;
|
||||||
|
|
||||||
m_current = nullptr; // current is special, points to other textures, no need to delete
|
m_current = nullptr; // current is special, points to other textures, no need to delete
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDevice::Present(const GSVector4i& r, int shader)
|
void GSDevice::ResetAPIState()
|
||||||
{
|
{
|
||||||
GL_PUSH("Present");
|
|
||||||
|
|
||||||
#ifndef PCSX2_CORE
|
|
||||||
int new_width, new_height;
|
|
||||||
if (GSCheckForWindowResize(&new_width, &new_height) && !Reset(new_width, new_height))
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// FIXME is it mandatory, it could be slow
|
|
||||||
ClearRenderTarget(m_backbuffer, 0);
|
|
||||||
|
|
||||||
if (m_current)
|
|
||||||
{
|
|
||||||
static constexpr ShaderConvert s_shader[5] = {ShaderConvert::COPY, ShaderConvert::SCANLINE,
|
|
||||||
ShaderConvert::DIAGONAL_FILTER, ShaderConvert::TRIANGULAR_FILTER,
|
|
||||||
ShaderConvert::COMPLEX_FILTER}; // FIXME
|
|
||||||
|
|
||||||
Present(m_current, m_backbuffer, GSVector4(r), s_shader[shader]);
|
|
||||||
RenderOsd(m_backbuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
Flip();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDevice::Present(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader)
|
void GSDevice::RestoreAPIState()
|
||||||
{
|
{
|
||||||
StretchRect(sTex, dTex, dRect, shader, m_linear_present);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int w, int h, GSTexture::Format format)
|
GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int w, int h, GSTexture::Format format)
|
||||||
|
|
|
@ -21,12 +21,13 @@
|
||||||
#include "GSVertex.h"
|
#include "GSVertex.h"
|
||||||
#include "GS/GSAlignedClass.h"
|
#include "GS/GSAlignedClass.h"
|
||||||
#include "GS/GSExtra.h"
|
#include "GS/GSExtra.h"
|
||||||
#include "GSOsdManager.h"
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <dxgi.h>
|
#include <dxgi.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class HostDisplay;
|
||||||
|
|
||||||
enum class ShaderConvert
|
enum class ShaderConvert
|
||||||
{
|
{
|
||||||
COPY = 0,
|
COPY = 0,
|
||||||
|
@ -48,7 +49,6 @@ enum class ShaderConvert
|
||||||
RGB5A1_TO_FLOAT16,
|
RGB5A1_TO_FLOAT16,
|
||||||
RGBA_TO_8I = 17,
|
RGBA_TO_8I = 17,
|
||||||
YUV,
|
YUV,
|
||||||
OSD,
|
|
||||||
Count
|
Count
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -526,9 +526,8 @@ protected:
|
||||||
static const int m_NO_BLEND = 0;
|
static const int m_NO_BLEND = 0;
|
||||||
static const int m_MERGE_BLEND = m_blendMap.size() - 1;
|
static const int m_MERGE_BLEND = m_blendMap.size() - 1;
|
||||||
|
|
||||||
int m_vsync;
|
|
||||||
bool m_rbswapped;
|
bool m_rbswapped;
|
||||||
GSTexture* m_backbuffer;
|
HostDisplay* m_display;
|
||||||
GSTexture* m_merge;
|
GSTexture* m_merge;
|
||||||
GSTexture* m_weavebob;
|
GSTexture* m_weavebob;
|
||||||
GSTexture* m_blend;
|
GSTexture* m_blend;
|
||||||
|
@ -557,11 +556,11 @@ protected:
|
||||||
virtual u16 ConvertBlendEnum(u16 generic) = 0; // Convert blend factors/ops from the generic enum to DX11/OGl specific.
|
virtual u16 ConvertBlendEnum(u16 generic) = 0; // Convert blend factors/ops from the generic enum to DX11/OGl specific.
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GSOsdManager m_osd;
|
|
||||||
|
|
||||||
GSDevice();
|
GSDevice();
|
||||||
virtual ~GSDevice();
|
virtual ~GSDevice();
|
||||||
|
|
||||||
|
__fi HostDisplay* GetDisplay() const { return m_display; }
|
||||||
|
|
||||||
void Recycle(GSTexture* t);
|
void Recycle(GSTexture* t);
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
@ -571,14 +570,11 @@ public:
|
||||||
DontCare
|
DontCare
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual bool Create(const WindowInfo& wi);
|
virtual bool Create(HostDisplay* display);
|
||||||
virtual bool Reset(int w, int h);
|
virtual void Destroy();
|
||||||
virtual bool IsLost(bool update = false) { return false; }
|
|
||||||
virtual void Present(const GSVector4i& r, int shader);
|
|
||||||
virtual void Present(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY);
|
|
||||||
virtual void Flip() {}
|
|
||||||
|
|
||||||
virtual void SetVSync(int vsync) { m_vsync = vsync; }
|
virtual void ResetAPIState();
|
||||||
|
virtual void RestoreAPIState();
|
||||||
|
|
||||||
virtual void BeginScene() {}
|
virtual void BeginScene() {}
|
||||||
virtual void EndScene();
|
virtual void EndScene();
|
||||||
|
@ -626,7 +622,6 @@ public:
|
||||||
void FXAA();
|
void FXAA();
|
||||||
void ShadeBoost();
|
void ShadeBoost();
|
||||||
void ExternalFX();
|
void ExternalFX();
|
||||||
virtual void RenderOsd(GSTexture* dt) {};
|
|
||||||
|
|
||||||
bool ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h);
|
bool ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h);
|
||||||
bool ResizeTexture(GSTexture** t, int w, int h);
|
bool ResizeTexture(GSTexture** t, int w, int h);
|
||||||
|
@ -634,8 +629,6 @@ public:
|
||||||
bool ResizeTarget(GSTexture** t);
|
bool ResizeTarget(GSTexture** t);
|
||||||
|
|
||||||
bool IsRBSwapped() { return m_rbswapped; }
|
bool IsRBSwapped() { return m_rbswapped; }
|
||||||
int GetBackbufferWidth() const { return m_backbuffer ? m_backbuffer->GetWidth() : 0; }
|
|
||||||
int GetBackbufferHeight() const { return m_backbuffer ? m_backbuffer->GetHeight() : 0; }
|
|
||||||
|
|
||||||
void AgePool();
|
void AgePool();
|
||||||
void PurgePool();
|
void PurgePool();
|
||||||
|
@ -673,3 +666,5 @@ struct GSAdapter
|
||||||
// TODO
|
// TODO
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern std::unique_ptr<GSDevice> g_gs_device;
|
|
@ -1,502 +0,0 @@
|
||||||
/* PCSX2 - PS2 Emulator for PCs
|
|
||||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
|
||||||
*
|
|
||||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
|
||||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
|
||||||
* ation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE. See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
|
||||||
* If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "PrecompiledHeader.h"
|
|
||||||
#include "GSOsdManager.h"
|
|
||||||
#include "GS/GS.h"
|
|
||||||
#include "Host.h"
|
|
||||||
|
|
||||||
#if __GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 4)
|
|
||||||
#include <codecvt>
|
|
||||||
#include <locale>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void GSOsdManager::LoadFont()
|
|
||||||
{
|
|
||||||
FT_Error error = FT_New_Face(m_library, theApp.GetConfigS("osd_fontname").c_str(), 0, &m_face);
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
FT_Error error_load_res = 1;
|
|
||||||
auto buffer = Host::ReadResourceFile("fonts/Roboto-Regular.ttf");
|
|
||||||
if (buffer.has_value())
|
|
||||||
{
|
|
||||||
resource_data_buffer = std::move(*buffer);
|
|
||||||
error_load_res = FT_New_Memory_Face(m_library, resource_data_buffer.data(), resource_data_buffer.size(), 0, &m_face);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error_load_res)
|
|
||||||
{
|
|
||||||
m_face = NULL;
|
|
||||||
fprintf(stderr, "Failed to init freetype face from external and internal resource\n");
|
|
||||||
if (error == FT_Err_Unknown_File_Format)
|
|
||||||
fprintf(stderr, "\tFreetype unknown file format for external file\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSOsdManager::LoadSize()
|
|
||||||
{
|
|
||||||
if (!m_face)
|
|
||||||
return;
|
|
||||||
|
|
||||||
FT_Error error = FT_Set_Pixel_Sizes(m_face, 0, m_size);
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to init the face size\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This is not exact, I'm sure there's some convoluted way to determine these
|
|
||||||
* from FreeType but they don't make it easy. */
|
|
||||||
m_atlas_w = m_size * 96; // random guess
|
|
||||||
m_atlas_h = m_size + 10; // another random guess
|
|
||||||
}
|
|
||||||
|
|
||||||
GSOsdManager::GSOsdManager()
|
|
||||||
: m_atlas_h(0)
|
|
||||||
, m_atlas_w(0)
|
|
||||||
, m_max_width(0)
|
|
||||||
, m_onscreen_messages(0)
|
|
||||||
, m_texture_dirty(true)
|
|
||||||
{
|
|
||||||
m_monitor_enabled = theApp.GetConfigB("osd_monitor_enabled");
|
|
||||||
m_log_enabled = theApp.GetConfigB("osd_log_enabled");
|
|
||||||
m_size = std::clamp(theApp.GetConfigI("osd_fontsize"), 1, 100);
|
|
||||||
m_opacity = std::clamp(theApp.GetConfigI("osd_color_opacity"), 0, 100);
|
|
||||||
m_log_timeout = std::clamp(theApp.GetConfigI("osd_log_timeout"), 2, 10);
|
|
||||||
m_max_onscreen_messages = std::clamp(theApp.GetConfigI("osd_max_log_messages"), 1, 20);
|
|
||||||
|
|
||||||
const int r = std::clamp(theApp.GetConfigI("osd_color_r"), 0, 255);
|
|
||||||
const int g = std::clamp(theApp.GetConfigI("osd_color_g"), 0, 255);
|
|
||||||
const int b = std::clamp(theApp.GetConfigI("osd_color_b"), 0, 255);
|
|
||||||
|
|
||||||
m_color = r | (g << 8) | (b << 16) | (255 << 24);
|
|
||||||
|
|
||||||
if (FT_Init_FreeType(&m_library))
|
|
||||||
{
|
|
||||||
m_face = NULL;
|
|
||||||
fprintf(stderr, "Failed to init the freetype library\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadFont();
|
|
||||||
|
|
||||||
/* The space character's width is used in GeneratePrimitives() */
|
|
||||||
AddGlyph(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
GSOsdManager::~GSOsdManager()
|
|
||||||
{
|
|
||||||
FT_Done_FreeType(m_library);
|
|
||||||
}
|
|
||||||
|
|
||||||
GSVector2i GSOsdManager::get_texture_font_size()
|
|
||||||
{
|
|
||||||
return GSVector2i(m_atlas_w, m_atlas_h);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSOsdManager::upload_texture_atlas(GSTexture* t)
|
|
||||||
{
|
|
||||||
if (!m_face)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_char_info.size() > 96) // we only reserved space for this many glyphs
|
|
||||||
fprintf(stderr, "More than 96 glyphs needed for OSD");
|
|
||||||
|
|
||||||
// This can be sped up a bit by only uploading new glyphs
|
|
||||||
int x = 0;
|
|
||||||
for (auto& pair : m_char_info)
|
|
||||||
{
|
|
||||||
if (FT_Load_Char(m_face, pair.first, FT_LOAD_RENDER))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "failed to load char U%d\n", (int)pair.first);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size of char
|
|
||||||
pair.second.ax = m_face->glyph->advance.x >> 6;
|
|
||||||
pair.second.ay = m_face->glyph->advance.y >> 6;
|
|
||||||
|
|
||||||
pair.second.bw = m_face->glyph->bitmap.width;
|
|
||||||
pair.second.bh = m_face->glyph->bitmap.rows;
|
|
||||||
|
|
||||||
pair.second.bl = m_face->glyph->bitmap_left;
|
|
||||||
pair.second.bt = m_face->glyph->bitmap_top;
|
|
||||||
|
|
||||||
GSVector4i r(x, 0, x + pair.second.bw, pair.second.bh);
|
|
||||||
if (r.width())
|
|
||||||
t->Update(r, m_face->glyph->bitmap.buffer, m_face->glyph->bitmap.pitch);
|
|
||||||
|
|
||||||
if (r.width() > m_max_width)
|
|
||||||
m_max_width = r.width();
|
|
||||||
|
|
||||||
pair.second.tx = (float)x / m_atlas_w;
|
|
||||||
pair.second.ty = (float)pair.second.bh / m_atlas_h;
|
|
||||||
pair.second.tw = (float)pair.second.bw / m_atlas_w;
|
|
||||||
|
|
||||||
x += pair.second.bw;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_texture_dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if __GNUC__ < 5 || (__GNUC__ == 5 && __GNUC_MINOR__ < 4)
|
|
||||||
/* This is dumb in that it doesn't check for malformed UTF8. This function
|
|
||||||
* is not expected to operate on user input, but only on compiled in strings */
|
|
||||||
void dumb_utf8_to_utf32(const char* utf8, char32_t* utf32, unsigned size)
|
|
||||||
{
|
|
||||||
while (*utf8 && --size)
|
|
||||||
{
|
|
||||||
if ((*utf8 & 0xF1) == 0xF0)
|
|
||||||
{
|
|
||||||
*utf32++ = (utf8[0] & 0x07) << 18 | (utf8[1] & 0x3F) << 12 | (utf8[2] & 0x3F) << 6 | utf8[3] & 0x3F;
|
|
||||||
utf8 += 4;
|
|
||||||
}
|
|
||||||
else if ((*utf8 & 0xF0) == 0xE0)
|
|
||||||
{
|
|
||||||
*utf32++ = (utf8[0] & 0x0F) << 12 | (utf8[1] & 0x3F) << 6 | utf8[2] & 0x3F;
|
|
||||||
utf8 += 3;
|
|
||||||
}
|
|
||||||
else if ((*utf8 & 0xE0) == 0xC0)
|
|
||||||
{
|
|
||||||
*utf32++ = (utf8[0] & 0x1F) << 6 | utf8[1] & 0x3F;
|
|
||||||
utf8 += 2;
|
|
||||||
}
|
|
||||||
else if ((*utf8 & 0x80) == 0x00)
|
|
||||||
{
|
|
||||||
*utf32++ = utf8[0] & 0x7F;
|
|
||||||
utf8 += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size)
|
|
||||||
*utf32 = *utf8; // Copy NUL char
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void GSOsdManager::AddGlyph(char32_t codepoint)
|
|
||||||
{
|
|
||||||
if (!m_face)
|
|
||||||
return;
|
|
||||||
if (m_char_info.count(codepoint) == 0)
|
|
||||||
{
|
|
||||||
m_texture_dirty = true;
|
|
||||||
m_char_info[codepoint]; // add it
|
|
||||||
if (FT_HAS_KERNING(m_face))
|
|
||||||
{
|
|
||||||
FT_UInt new_glyph = FT_Get_Char_Index(m_face, codepoint);
|
|
||||||
for (auto pair : m_char_info)
|
|
||||||
{
|
|
||||||
FT_Vector delta;
|
|
||||||
|
|
||||||
FT_UInt glyph_index = FT_Get_Char_Index(m_face, pair.first);
|
|
||||||
FT_Get_Kerning(m_face, glyph_index, new_glyph, FT_KERNING_DEFAULT, &delta);
|
|
||||||
m_kern_info[std::make_pair(pair.first, codepoint)] = delta.x >> 6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSOsdManager::Log(const char* utf8)
|
|
||||||
{
|
|
||||||
if (!m_log_enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
#if __GNUC__ < 5 || (__GNUC__ == 5 && __GNUC_MINOR__ < 4)
|
|
||||||
char32_t buffer[256];
|
|
||||||
dumb_utf8_to_utf32(utf8, buffer, std::size(buffer));
|
|
||||||
for (char32_t* c = buffer; *c; ++c)
|
|
||||||
AddGlyph(*c);
|
|
||||||
#else
|
|
||||||
#if _MSC_VER == 1900
|
|
||||||
std::wstring_convert<std::codecvt_utf8<unsigned int>, unsigned int> conv;
|
|
||||||
#else
|
|
||||||
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> conv;
|
|
||||||
#endif
|
|
||||||
std::u32string buffer = conv.from_bytes(utf8);
|
|
||||||
for (auto const& c : buffer)
|
|
||||||
AddGlyph(c);
|
|
||||||
#endif
|
|
||||||
m_onscreen_messages++;
|
|
||||||
m_log.push_back(log_info{buffer, std::chrono::system_clock::time_point()});
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSOsdManager::Monitor(const char* key, const char* value)
|
|
||||||
{
|
|
||||||
if (!m_monitor_enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (value && *value)
|
|
||||||
{
|
|
||||||
#if __GNUC__ < 5 || (__GNUC__ == 5 && __GNUC_MINOR__ < 4)
|
|
||||||
char32_t buffer[256], vbuffer[256];
|
|
||||||
dumb_utf8_to_utf32(key, buffer, std::size(buffer));
|
|
||||||
dumb_utf8_to_utf32(value, vbuffer, std::size(vbuffer));
|
|
||||||
for (char32_t* c = buffer; *c; ++c)
|
|
||||||
AddGlyph(*c);
|
|
||||||
for (char32_t* c = vbuffer; *c; ++c)
|
|
||||||
AddGlyph(*c);
|
|
||||||
#else
|
|
||||||
#if _MSC_VER == 1900
|
|
||||||
std::wstring_convert<std::codecvt_utf8<unsigned int>, unsigned int> conv;
|
|
||||||
#else
|
|
||||||
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> conv;
|
|
||||||
#endif
|
|
||||||
std::u32string buffer = conv.from_bytes(key);
|
|
||||||
std::u32string vbuffer = conv.from_bytes(value);
|
|
||||||
for (auto const& c : buffer)
|
|
||||||
AddGlyph(c);
|
|
||||||
for (auto const& c : vbuffer)
|
|
||||||
AddGlyph(c);
|
|
||||||
#endif
|
|
||||||
m_monitor[buffer] = vbuffer;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#if __GNUC__ < 5 || (__GNUC__ == 5 && __GNUC_MINOR__ < 4)
|
|
||||||
char32_t buffer[256];
|
|
||||||
dumb_utf8_to_utf32(key, buffer, std::size(buffer));
|
|
||||||
#else
|
|
||||||
#if _MSC_VER == 1900
|
|
||||||
std::wstring_convert<std::codecvt_utf8<unsigned int>, unsigned int> conv;
|
|
||||||
#else
|
|
||||||
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> conv;
|
|
||||||
#endif
|
|
||||||
std::u32string buffer = conv.from_bytes(key);
|
|
||||||
#endif
|
|
||||||
m_monitor.erase(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSOsdManager::RenderGlyph(GSVertexPT1* dst, const glyph_info g, float x, float y, u32 color)
|
|
||||||
{
|
|
||||||
float x2 = x + g.bl * (2.0f / m_real_size.x);
|
|
||||||
float y2 = -y - g.bt * (2.0f / m_real_size.y);
|
|
||||||
float w = g.bw * (2.0f / m_real_size.x);
|
|
||||||
float h = g.bh * (2.0f / m_real_size.y);
|
|
||||||
|
|
||||||
dst->p = GSVector4(x2 , -y2 , 0.0f, 1.0f);
|
|
||||||
dst->t = GSVector2(g.tx , 0.0f);
|
|
||||||
dst->c = color;
|
|
||||||
++dst;
|
|
||||||
dst->p = GSVector4(x2 + w, -y2 , 0.0f, 1.0f);
|
|
||||||
dst->t = GSVector2(g.tx + g.tw, 0.0f);
|
|
||||||
dst->c = color;
|
|
||||||
++dst;
|
|
||||||
dst->p = GSVector4(x2 , -y2 - h, 0.0f, 1.0f);
|
|
||||||
dst->t = GSVector2(g.tx , g.ty);
|
|
||||||
dst->c = color;
|
|
||||||
++dst;
|
|
||||||
dst->p = GSVector4(x2 + w, -y2 , 0.0f, 1.0f);
|
|
||||||
dst->t = GSVector2(g.tx + g.tw, 0.0f);
|
|
||||||
dst->c = color;
|
|
||||||
++dst;
|
|
||||||
dst->p = GSVector4(x2 , -y2 - h, 0.0f, 1.0f);
|
|
||||||
dst->t = GSVector2(g.tx , g.ty);
|
|
||||||
dst->c = color;
|
|
||||||
++dst;
|
|
||||||
dst->p = GSVector4(x2 + w, -y2 - h, 0.0f, 1.0f);
|
|
||||||
dst->t = GSVector2(g.tx + g.tw, g.ty);
|
|
||||||
dst->c = color;
|
|
||||||
++dst;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSOsdManager::RenderString(GSVertexPT1* dst, const std::u32string msg, float x, float y, u32 color)
|
|
||||||
{
|
|
||||||
char32_t p = 0;
|
|
||||||
for (const auto& c : msg)
|
|
||||||
{
|
|
||||||
if (p)
|
|
||||||
{
|
|
||||||
x += m_kern_info[std::make_pair(p, c)] * (2.0f / m_real_size.x);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderGlyph(dst, m_char_info[c], x, y, color);
|
|
||||||
|
|
||||||
/* Advance the cursor to the start of the next character */
|
|
||||||
x += m_char_info[c].ax * (2.0f / m_real_size.x);
|
|
||||||
y += m_char_info[c].ay * (2.0f / m_real_size.y);
|
|
||||||
|
|
||||||
dst += 6;
|
|
||||||
|
|
||||||
p = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GSOsdManager::Size()
|
|
||||||
{
|
|
||||||
size_t sum = 0;
|
|
||||||
|
|
||||||
if (m_log_enabled)
|
|
||||||
{
|
|
||||||
float offset = 0;
|
|
||||||
|
|
||||||
for (auto it = m_log.begin(); it != m_log.end(); ++it)
|
|
||||||
{
|
|
||||||
float y = 1 - ((m_size + 2) * (it - m_log.begin() + 1)) * (2.0f / m_real_size.y);
|
|
||||||
if (y + offset < -1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
std::chrono::duration<float> elapsed;
|
|
||||||
if (it->OnScreen.time_since_epoch().count() == 0)
|
|
||||||
{
|
|
||||||
elapsed = std::chrono::seconds(0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
elapsed = std::chrono::system_clock::now() - it->OnScreen;
|
|
||||||
if (elapsed > std::chrono::seconds(m_log_timeout) || m_onscreen_messages > m_max_onscreen_messages)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float ratio = (elapsed - std::chrono::seconds(m_log_timeout / 2)).count() / std::chrono::seconds(m_log_timeout / 2).count();
|
|
||||||
ratio = ratio > 1.0f ? 1.0f : ratio < 0.0f ? 0.0f : ratio;
|
|
||||||
|
|
||||||
y += offset += ((m_size + 2) * (2.0f / m_real_size.y)) * ratio;
|
|
||||||
sum += it->msg.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_monitor_enabled)
|
|
||||||
{
|
|
||||||
for (const auto& pair : m_monitor)
|
|
||||||
{
|
|
||||||
sum += pair.first.size();
|
|
||||||
sum += pair.second.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sum * 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
float GSOsdManager::StringSize(const std::u32string msg)
|
|
||||||
{
|
|
||||||
char32_t p = 0;
|
|
||||||
float x = 0.0;
|
|
||||||
|
|
||||||
for (auto c : msg)
|
|
||||||
{
|
|
||||||
if (p)
|
|
||||||
{
|
|
||||||
x += m_kern_info[std::make_pair(p, c)] * (2.0f / m_real_size.x);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Advance the cursor to the start of the next character */
|
|
||||||
x += m_char_info[c].ax * (2.0f / m_real_size.x);
|
|
||||||
|
|
||||||
p = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GSOsdManager::GeneratePrimitives(GSVertexPT1* dst, size_t count)
|
|
||||||
{
|
|
||||||
size_t drawn = 0;
|
|
||||||
float opacity = m_opacity * 0.01f;
|
|
||||||
|
|
||||||
if (m_log_enabled)
|
|
||||||
{
|
|
||||||
float offset = 0;
|
|
||||||
|
|
||||||
for (auto it = m_log.begin(); it != m_log.end();)
|
|
||||||
{
|
|
||||||
float x = -1 + 8 * (2.0f / m_real_size.x);
|
|
||||||
float y = 1 - ((m_size + 2) * (it - m_log.begin() + 1)) * (2.0f / m_real_size.y);
|
|
||||||
|
|
||||||
if (y + offset < -1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (it->OnScreen.time_since_epoch().count() == 0)
|
|
||||||
it->OnScreen = std::chrono::system_clock::now();
|
|
||||||
|
|
||||||
std::chrono::duration<float> elapsed = std::chrono::system_clock::now() - it->OnScreen;
|
|
||||||
if (elapsed > std::chrono::seconds(m_log_timeout) || m_onscreen_messages > m_max_onscreen_messages)
|
|
||||||
{
|
|
||||||
m_onscreen_messages--;
|
|
||||||
it = m_log.erase(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (it->msg.size() * 6 > count - drawn)
|
|
||||||
break;
|
|
||||||
|
|
||||||
float ratio = (elapsed - std::chrono::seconds(m_log_timeout / 2)).count() / std::chrono::seconds(m_log_timeout / 2).count();
|
|
||||||
ratio = ratio > 1.0f ? 1.0f : ratio < 0.0f ? 0.0f : ratio;
|
|
||||||
|
|
||||||
y += offset += ((m_size + 2) * (2.0f / m_real_size.y)) * ratio;
|
|
||||||
u32 color = m_color;
|
|
||||||
((u8*)&color)[3] = (u8)(((u8*)&color)[3] * (1.0f - ratio) * opacity);
|
|
||||||
RenderString(dst, it->msg, x, y, color);
|
|
||||||
dst += it->msg.size() * 6;
|
|
||||||
drawn += it->msg.size() * 6;
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_monitor_enabled)
|
|
||||||
{
|
|
||||||
// pair.first is the key and second is the value and color
|
|
||||||
|
|
||||||
// Since the monitor is right justified, but we render from left to right
|
|
||||||
// we need to find the longest string
|
|
||||||
float first_max = 0.0, second_max = 0.0;
|
|
||||||
for (const auto& pair : m_monitor)
|
|
||||||
{
|
|
||||||
float first_len = StringSize(pair.first);
|
|
||||||
float second_len = StringSize(pair.second);
|
|
||||||
|
|
||||||
first_max = first_max < first_len ? first_len : first_max;
|
|
||||||
second_max = second_max < second_len ? second_len : second_max;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t line = 1;
|
|
||||||
for (const auto& pair : m_monitor)
|
|
||||||
{
|
|
||||||
if ((pair.first.size() + pair.second.size()) * 6 > count - drawn)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Calculate where to start rendering from by taking the right most position 1.0
|
|
||||||
// and subtracting (going left) 8 scaled pixels for a margin, then subtracting
|
|
||||||
// the size of the longest key and subtracting a scaled space and finally
|
|
||||||
// subtracting the longest value
|
|
||||||
float x = 1.0f - 8 * (2.0f / m_real_size.x) - first_max - m_char_info[' '].ax * (2.0f / m_real_size.x) - second_max;
|
|
||||||
float y = -1.0f + ((m_size + 2) * (2.0f / m_real_size.y)) * line++;
|
|
||||||
|
|
||||||
u32 color = m_color;
|
|
||||||
((u8*)&color)[3] = (u8)(((u8*)&color)[3] * opacity);
|
|
||||||
|
|
||||||
// Render the key
|
|
||||||
RenderString(dst, pair.first, x, y, color);
|
|
||||||
dst += pair.first.size() * 6;
|
|
||||||
drawn += pair.first.size() * 6;
|
|
||||||
|
|
||||||
// Calculate the position for the value
|
|
||||||
x = 1.0f - 8 * (2.0f / m_real_size.x) - second_max;
|
|
||||||
|
|
||||||
// Render the value
|
|
||||||
RenderString(dst, pair.second, x, y, color);
|
|
||||||
dst += pair.second.size() * 6;
|
|
||||||
drawn += pair.second.size() * 6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return drawn;
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
/* PCSX2 - PS2 Emulator for PCs
|
|
||||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
|
||||||
*
|
|
||||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
|
||||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
|
||||||
* ation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE. See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
|
||||||
* If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "PrecompiledHeader.h"
|
|
||||||
#include "GS/GSVector.h"
|
|
||||||
#include "GSVertex.h"
|
|
||||||
#include "GSTexture.h"
|
|
||||||
#include "ft2build.h"
|
|
||||||
#include FT_FREETYPE_H
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
class GSOsdManager
|
|
||||||
{
|
|
||||||
struct glyph_info
|
|
||||||
{
|
|
||||||
s32 ax; // advance.x
|
|
||||||
s32 ay; // advance.y
|
|
||||||
|
|
||||||
u32 bw; // bitmap.width;
|
|
||||||
u32 bh; // bitmap.rows;
|
|
||||||
|
|
||||||
s32 bl; // bitmap_left;
|
|
||||||
s32 bt; // bitmap_top;
|
|
||||||
|
|
||||||
float tx; // x offset of glyph
|
|
||||||
float ty; // y offset of glyph
|
|
||||||
float tw; // nomalized glyph width
|
|
||||||
};
|
|
||||||
|
|
||||||
std::map<char32_t, glyph_info> m_char_info;
|
|
||||||
std::map<std::pair<char32_t, char32_t>, FT_Pos> m_kern_info;
|
|
||||||
|
|
||||||
FT_Library m_library;
|
|
||||||
FT_Face m_face;
|
|
||||||
FT_UInt m_size;
|
|
||||||
|
|
||||||
u32 m_atlas_h;
|
|
||||||
u32 m_atlas_w;
|
|
||||||
s32 m_max_width;
|
|
||||||
s32 m_onscreen_messages;
|
|
||||||
|
|
||||||
struct log_info
|
|
||||||
{
|
|
||||||
std::u32string msg;
|
|
||||||
std::chrono::system_clock::time_point OnScreen;
|
|
||||||
};
|
|
||||||
std::vector<log_info> m_log;
|
|
||||||
|
|
||||||
std::map<std::u32string, std::u32string> m_monitor;
|
|
||||||
|
|
||||||
void AddGlyph(char32_t codepoint);
|
|
||||||
void RenderGlyph(GSVertexPT1* dst, const glyph_info g, float x, float y, u32 color);
|
|
||||||
void RenderString(GSVertexPT1* dst, const std::u32string msg, float x, float y, u32 color);
|
|
||||||
float StringSize(const std::u32string msg);
|
|
||||||
|
|
||||||
bool m_log_enabled;
|
|
||||||
int m_log_timeout;
|
|
||||||
bool m_monitor_enabled;
|
|
||||||
int m_opacity;
|
|
||||||
u32 m_color;
|
|
||||||
int m_max_onscreen_messages;
|
|
||||||
|
|
||||||
public:
|
|
||||||
GSOsdManager();
|
|
||||||
~GSOsdManager();
|
|
||||||
|
|
||||||
void LoadFont();
|
|
||||||
void LoadSize();
|
|
||||||
|
|
||||||
GSVector2i get_texture_font_size();
|
|
||||||
|
|
||||||
bool m_texture_dirty;
|
|
||||||
void upload_texture_atlas(GSTexture* t);
|
|
||||||
|
|
||||||
void Log(const char* utf8);
|
|
||||||
void Monitor(const char* key, const char* value);
|
|
||||||
|
|
||||||
GSVector2i m_real_size;
|
|
||||||
size_t Size();
|
|
||||||
size_t GeneratePrimitives(GSVertexPT1* dst, size_t count);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<u8> resource_data_buffer;
|
|
||||||
};
|
|
|
@ -15,68 +15,32 @@
|
||||||
|
|
||||||
#include "PrecompiledHeader.h"
|
#include "PrecompiledHeader.h"
|
||||||
#include "GSRenderer.h"
|
#include "GSRenderer.h"
|
||||||
#include "gui/AppConfig.h"
|
|
||||||
#include "GS/GSGL.h"
|
#include "GS/GSGL.h"
|
||||||
|
#include "Host.h"
|
||||||
|
#include "HostDisplay.h"
|
||||||
|
#include "PerformanceMetrics.h"
|
||||||
|
#include "pcsx2/Config.h"
|
||||||
|
#include "common/StringUtil.h"
|
||||||
#if defined(__unix__)
|
#if defined(__unix__)
|
||||||
#include <X11/keysym.h>
|
#include <X11/keysym.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const unsigned int s_interlace_nb = 8;
|
|
||||||
const unsigned int s_post_shader_nb = 5;
|
|
||||||
const unsigned int s_mipmap_nb = 3;
|
const unsigned int s_mipmap_nb = 3;
|
||||||
|
|
||||||
GSRenderer::GSRenderer()
|
GSRenderer::GSRenderer()
|
||||||
: m_shader(0)
|
: m_shift_key(false)
|
||||||
, m_shift_key(false)
|
|
||||||
, m_control_key(false)
|
, m_control_key(false)
|
||||||
, m_texture_shuffle(false)
|
, m_texture_shuffle(false)
|
||||||
, m_real_size(0, 0)
|
, m_real_size(0, 0)
|
||||||
{
|
{
|
||||||
m_GStitleInfoBuffer[0] = 0;
|
|
||||||
|
|
||||||
m_interlace = theApp.GetConfigI("interlace") % s_interlace_nb;
|
|
||||||
m_shader = theApp.GetConfigI("TVShader") % s_post_shader_nb;
|
|
||||||
m_vsync = theApp.GetConfigI("vsync");
|
|
||||||
m_aa1 = theApp.GetConfigB("aa1");
|
|
||||||
m_fxaa = theApp.GetConfigB("fxaa");
|
|
||||||
m_shaderfx = theApp.GetConfigB("shaderfx");
|
|
||||||
m_shadeboost = theApp.GetConfigB("ShadeBoost");
|
|
||||||
m_dithering = theApp.GetConfigI("dithering_ps2"); // 0 off, 1 auto, 2 auto no scale
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GSRenderer::~GSRenderer()
|
GSRenderer::~GSRenderer()
|
||||||
{
|
{
|
||||||
/*if(m_dev)
|
|
||||||
{
|
|
||||||
m_dev->Reset(1, 1, GSDevice::Windowed);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
delete m_dev;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSRenderer::CreateDevice(GSDevice* dev, const WindowInfo& wi)
|
void GSRenderer::Destroy()
|
||||||
{
|
{
|
||||||
ASSERT(dev);
|
|
||||||
ASSERT(!m_dev);
|
|
||||||
|
|
||||||
if (!dev->Create(wi))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_dev = dev;
|
|
||||||
m_dev->SetVSync(m_vsync);
|
|
||||||
|
|
||||||
// reset handlers to pick up index swap if needed
|
|
||||||
ResetHandlers();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSRenderer::ResetDevice()
|
|
||||||
{
|
|
||||||
if (m_dev)
|
|
||||||
m_dev->Reset(1, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSRenderer::Merge(int field)
|
bool GSRenderer::Merge(int field)
|
||||||
|
@ -265,37 +229,37 @@ bool GSRenderer::Merge(int field)
|
||||||
|
|
||||||
GSVector4 c = GSVector4((int)m_regs->BGCOLOR.R, (int)m_regs->BGCOLOR.G, (int)m_regs->BGCOLOR.B, (int)m_regs->PMODE.ALP) / 255;
|
GSVector4 c = GSVector4((int)m_regs->BGCOLOR.R, (int)m_regs->BGCOLOR.G, (int)m_regs->BGCOLOR.B, (int)m_regs->PMODE.ALP) / 255;
|
||||||
|
|
||||||
m_dev->Merge(tex, src_hw, dst, fs, m_regs->PMODE, m_regs->EXTBUF, c);
|
g_gs_device->Merge(tex, src_hw, dst, fs, m_regs->PMODE, m_regs->EXTBUF, c);
|
||||||
|
|
||||||
if (m_regs->SMODE2.INT && m_interlace > 0)
|
if (m_regs->SMODE2.INT && GSConfig.InterlaceMode != GSInterlaceMode::Off)
|
||||||
{
|
{
|
||||||
if (m_interlace == 7 && m_regs->SMODE2.FFMD) // Auto interlace enabled / Odd frame interlace setting
|
if (GSConfig.InterlaceMode == GSInterlaceMode::Automatic && m_regs->SMODE2.FFMD) // Auto interlace enabled / Odd frame interlace setting
|
||||||
{
|
{
|
||||||
int field2 = 0;
|
int field2 = 0;
|
||||||
int mode = 2;
|
int mode = 2;
|
||||||
m_dev->Interlace(ds, field ^ field2, mode, tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y);
|
g_gs_device->Interlace(ds, field ^ field2, mode, tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int field2 = 1 - ((m_interlace - 1) & 1);
|
int field2 = 1 - ((static_cast<int>(GSConfig.InterlaceMode) - 1) & 1);
|
||||||
int mode = (m_interlace - 1) >> 1;
|
int mode = (static_cast<int>(GSConfig.InterlaceMode) - 1) >> 1;
|
||||||
m_dev->Interlace(ds, field ^ field2, mode, tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y);
|
g_gs_device->Interlace(ds, field ^ field2, mode, tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_shadeboost)
|
if (GSConfig.ShadeBoost)
|
||||||
{
|
{
|
||||||
m_dev->ShadeBoost();
|
g_gs_device->ShadeBoost();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_shaderfx)
|
if (GSConfig.ShaderFX)
|
||||||
{
|
{
|
||||||
m_dev->ExternalFX();
|
g_gs_device->ExternalFX();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_fxaa)
|
if (GSConfig.FXAA)
|
||||||
{
|
{
|
||||||
m_dev->FXAA();
|
g_gs_device->FXAA();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,64 +271,118 @@ GSVector2i GSRenderer::GetInternalResolution()
|
||||||
return m_real_size;
|
return m_real_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
GSVector4i GSRenderer::ComputeDrawRectangle(int width, int height) const
|
static float GetCurrentAspectRatioFloat()
|
||||||
{
|
{
|
||||||
const double f_width = static_cast<double>(width);
|
static constexpr std::array<float, static_cast<size_t>(AspectRatioType::MaxCount)> ars = { {4.0f / 3.0f, 4.0f / 3.0f, 16.0f / 9.0f} };
|
||||||
const double f_height = static_cast<double>(height);
|
return ars[static_cast<u32>(GSConfig.AspectRatio)];
|
||||||
const double clientAr = f_width / f_height;
|
}
|
||||||
|
|
||||||
double targetAr = clientAr;
|
static GSVector4 CalculateDrawRect(s32 window_width, s32 window_height, s32 texture_width, s32 texture_height, HostDisplay::Alignment alignment, bool flip_y)
|
||||||
|
{
|
||||||
|
const float f_width = static_cast<float>(window_width);
|
||||||
|
const float f_height = static_cast<float>(window_height);
|
||||||
|
const float clientAr = f_width / f_height;
|
||||||
|
|
||||||
|
float targetAr = clientAr;
|
||||||
if (EmuConfig.CurrentAspectRatio == AspectRatioType::R4_3)
|
if (EmuConfig.CurrentAspectRatio == AspectRatioType::R4_3)
|
||||||
targetAr = 4.0 / 3.0;
|
targetAr = 4.0f / 3.0f;
|
||||||
else if (EmuConfig.CurrentAspectRatio == AspectRatioType::R16_9)
|
else if (EmuConfig.CurrentAspectRatio == AspectRatioType::R16_9)
|
||||||
targetAr = 16.0 / 9.0;
|
targetAr = 16.0f / 9.0f;
|
||||||
|
|
||||||
const double arr = targetAr / clientAr;
|
const double arr = targetAr / clientAr;
|
||||||
double target_width = f_width;
|
float target_width = f_width;
|
||||||
double target_height = f_height;
|
float target_height = f_height;
|
||||||
if (arr < 1)
|
if (arr < 1)
|
||||||
target_width = std::floor(f_width * arr + 0.5);
|
target_width = std::floor(f_width * arr + 0.5f);
|
||||||
else if (arr > 1)
|
else if (arr > 1)
|
||||||
target_height = std::floor(f_height / arr + 0.5);
|
target_height = std::floor(f_height / arr + 0.5f);
|
||||||
|
|
||||||
float zoom = EmuConfig.GS.Zoom / 100.0;
|
float zoom = GSConfig.Zoom / 100.0;
|
||||||
if (zoom == 0) //auto zoom in untill black-bars are gone (while keeping the aspect ratio).
|
if (zoom == 0) //auto zoom in untill black-bars are gone (while keeping the aspect ratio).
|
||||||
zoom = std::max((float)arr, (float)(1.0 / arr));
|
zoom = std::max((float)arr, (float)(1.0 / arr));
|
||||||
|
|
||||||
target_width *= zoom;
|
target_width *= zoom;
|
||||||
target_height *= zoom * EmuConfig.GS.StretchY / 100.0;
|
target_height *= zoom * GSConfig.StretchY / 100.0f;
|
||||||
|
|
||||||
double target_x, target_y;
|
if (GSConfig.IntegerScaling)
|
||||||
if (target_width > f_width)
|
{
|
||||||
target_x = -((target_width - f_width) * 0.5);
|
// make target width/height an integer multiple of the texture width/height
|
||||||
|
const float t_width = static_cast<double>(texture_width);
|
||||||
|
const float t_height = static_cast<double>(texture_height);
|
||||||
|
|
||||||
|
float scale;
|
||||||
|
if ((t_width / t_height) >= 1.0)
|
||||||
|
scale = target_width / t_width;
|
||||||
|
else
|
||||||
|
scale = target_height / t_height;
|
||||||
|
|
||||||
|
if (scale > 1.0)
|
||||||
|
{
|
||||||
|
const float adjust = std::floor(scale) / scale;
|
||||||
|
target_width = target_width * adjust;
|
||||||
|
target_height = target_height * adjust;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float target_x, target_y;
|
||||||
|
if (target_width >= f_width)
|
||||||
|
{
|
||||||
|
target_x = -((target_width - f_width) * 0.5f);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
target_x = (f_width - target_width) * 0.5;
|
{
|
||||||
if (target_height > f_height)
|
switch (alignment)
|
||||||
target_y = -((target_height - f_height) * 0.5);
|
{
|
||||||
|
case HostDisplay::Alignment::Center:
|
||||||
|
target_x = (f_width - target_width) * 0.5f;
|
||||||
|
break;
|
||||||
|
case HostDisplay::Alignment::RightOrBottom:
|
||||||
|
target_x = (f_width - target_width);
|
||||||
|
break;
|
||||||
|
case HostDisplay::Alignment::LeftOrTop:
|
||||||
|
default:
|
||||||
|
target_x = 0.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (target_height >= f_height)
|
||||||
|
{
|
||||||
|
target_y = -((target_height - f_height) * 0.5f);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
target_y = (f_height - target_height) * 0.5;
|
{
|
||||||
|
switch (alignment)
|
||||||
|
{
|
||||||
|
case HostDisplay::Alignment::Center:
|
||||||
|
target_y = (f_height - target_height) * 0.5f;
|
||||||
|
break;
|
||||||
|
case HostDisplay::Alignment::RightOrBottom:
|
||||||
|
target_y = (f_height - target_height);
|
||||||
|
break;
|
||||||
|
case HostDisplay::Alignment::LeftOrTop:
|
||||||
|
default:
|
||||||
|
target_y = 0.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const double unit = .01 * std::min(target_x, target_y);
|
const float unit = .01f * std::min(target_x, target_y);
|
||||||
target_x += unit * EmuConfig.GS.OffsetX;
|
target_x += unit * GSConfig.OffsetX;
|
||||||
target_y += unit * EmuConfig.GS.OffsetY;
|
target_y += unit * GSConfig.OffsetY;
|
||||||
|
|
||||||
return GSVector4i(
|
GSVector4 ret(target_x, target_y, target_x + target_width, target_y + target_height);
|
||||||
static_cast<int>(std::floor(target_x)),
|
|
||||||
static_cast<int>(std::floor(target_y)),
|
if (flip_y)
|
||||||
static_cast<int>(std::round(target_x + target_width)),
|
{
|
||||||
static_cast<int>(std::round(target_y + target_height)));
|
const float height = ret.w - ret.y;
|
||||||
|
ret.y = static_cast<float>(window_height) - ret.w;
|
||||||
|
ret.w = ret.y + height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSRenderer::SetVSync(int vsync)
|
void GSRenderer::VSync(u32 field)
|
||||||
{
|
|
||||||
m_vsync = vsync;
|
|
||||||
|
|
||||||
if (m_dev)
|
|
||||||
m_dev->SetVSync(m_vsync);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSRenderer::VSync(int field)
|
|
||||||
{
|
{
|
||||||
GSPerfMonAutoTimer pmat(&m_perfmon);
|
GSPerfMonAutoTimer pmat(&m_perfmon);
|
||||||
|
|
||||||
|
@ -377,104 +395,43 @@ void GSRenderer::VSync(int field)
|
||||||
m_regs->Dump(root_sw + format("%05d_f%lld_gs_reg.txt", s_n, m_perfmon.GetFrame()));
|
m_regs->Dump(root_sw + format("%05d_f%lld_gs_reg.txt", s_n, m_perfmon.GetFrame()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_dev->IsLost(true))
|
g_gs_device->AgePool();
|
||||||
{
|
|
||||||
if (!Merge(field ? 1 : 0))
|
const bool blank_frame = !Merge(field ? 1 : 0);
|
||||||
{
|
const bool skip_frame = m_frameskip;
|
||||||
return;
|
|
||||||
}
|
if (blank_frame || skip_frame)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ResetDevice();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_dev->AgePool();
|
|
||||||
|
|
||||||
// osd
|
|
||||||
|
|
||||||
if ((m_perfmon.GetFrame() & 0x1f) == 0)
|
|
||||||
{
|
|
||||||
m_perfmon.Update();
|
|
||||||
|
|
||||||
std::string s;
|
|
||||||
|
|
||||||
#ifdef GSTITLEINFO_API_FORCE_VERBOSE
|
|
||||||
{
|
|
||||||
const double fps = 1000.0f / m_perfmon.Get(GSPerfMon::Frame);
|
|
||||||
//GS owns the window's title, be verbose.
|
|
||||||
static const char* aspect_ratio_names[static_cast<int>(AspectRatioType::MaxCount)] = { "Stretch", "4:3", "16:9" };
|
|
||||||
|
|
||||||
std::string s2 = m_regs->SMODE2.INT ? (std::string("Interlaced ") + (m_regs->SMODE2.FFMD ? "(frame)" : "(field)")) : "Progressive";
|
|
||||||
|
|
||||||
s = format(
|
|
||||||
"%lld | %d x %d | %.2f fps (%d%%) | %s - %s | %s | %d S/%d P/%d D | %d%% CPU | %.2f | %.2f",
|
|
||||||
m_perfmon.GetFrame(), GetInternalResolution().x, GetInternalResolution().y, fps, (int)(100.0 * fps / GetTvRefreshRate()),
|
|
||||||
s2.c_str(),
|
|
||||||
theApp.m_gs_interlace[m_interlace].name.c_str(),
|
|
||||||
aspect_ratio_names[static_cast<int>(EmuConfig.GS.AspectRatio)],
|
|
||||||
(int)m_perfmon.Get(GSPerfMon::SyncPoint),
|
|
||||||
(int)m_perfmon.Get(GSPerfMon::Prim),
|
|
||||||
(int)m_perfmon.Get(GSPerfMon::Draw),
|
|
||||||
m_perfmon.CPU(),
|
|
||||||
m_perfmon.Get(GSPerfMon::Swizzle) / 1024,
|
|
||||||
m_perfmon.Get(GSPerfMon::Unswizzle) / 1024);
|
|
||||||
|
|
||||||
double fillrate = m_perfmon.Get(GSPerfMon::Fillrate);
|
|
||||||
|
|
||||||
if (fillrate > 0)
|
|
||||||
{
|
|
||||||
s += format(" | %.2f mpps", fps * fillrate / (1024 * 1024));
|
|
||||||
|
|
||||||
int sum = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
sum += m_perfmon.CPU(GSPerfMon::WorkerDraw0 + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
s += format(" | %d%% CPU", sum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
{
|
|
||||||
// Satisfy PCSX2's request for title info: minimal verbosity due to more external title text
|
|
||||||
|
|
||||||
s = format("%dx%d | %s", GetInternalResolution().x, GetInternalResolution().y, theApp.m_gs_interlace[m_interlace].name.c_str());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (m_capture.IsCapturing())
|
|
||||||
{
|
|
||||||
s += " | Recording...";
|
|
||||||
}
|
|
||||||
|
|
||||||
// note: do not use TryEnterCriticalSection. It is unnecessary code complication in
|
|
||||||
// an area that absolutely does not matter (even if it were 100 times slower, it wouldn't
|
|
||||||
// be noticeable). Besides, these locks are extremely short -- overhead of conditional
|
|
||||||
// is way more expensive than just waiting for the CriticalSection in 1 of 10,000,000 tries. --air
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(m_pGSsetTitle_Crit);
|
|
||||||
|
|
||||||
strncpy(m_GStitleInfoBuffer, s.c_str(), std::size(m_GStitleInfoBuffer) - 1);
|
|
||||||
|
|
||||||
m_GStitleInfoBuffer[sizeof(m_GStitleInfoBuffer) - 1] = 0; // make sure null terminated even if text overflows
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_frameskip)
|
|
||||||
{
|
{
|
||||||
|
g_gs_device->ResetAPIState();
|
||||||
|
if (Host::BeginPresentFrame(skip_frame))
|
||||||
|
Host::EndPresentFrame();
|
||||||
|
g_gs_device->RestoreAPIState();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// present
|
if ((m_perfmon.GetFrame() & 0x1f) == 0)
|
||||||
|
m_perfmon.Update();
|
||||||
|
|
||||||
// This will scale the OSD to the window's size.
|
g_gs_device->ResetAPIState();
|
||||||
// Will maintiain the font size no matter what size the window is.
|
if (Host::BeginPresentFrame(false))
|
||||||
GSVector4i window_size(0, 0, m_dev->GetBackbufferWidth(), m_dev->GetBackbufferHeight());
|
{
|
||||||
m_dev->m_osd.m_real_size.x = window_size.v[2];
|
GSTexture* current = g_gs_device->GetCurrent();
|
||||||
m_dev->m_osd.m_real_size.y = window_size.v[3];
|
if (current)
|
||||||
|
{
|
||||||
|
HostDisplay* const display = g_gs_device->GetDisplay();
|
||||||
|
const GSVector4 draw_rect(CalculateDrawRect(display->GetWindowWidth(), display->GetWindowHeight(),
|
||||||
|
current->GetWidth(), current->GetHeight(), display->GetDisplayAlignment(), display->UsesLowerLeftOrigin()));
|
||||||
|
|
||||||
m_dev->Present(ComputeDrawRectangle(window_size.z, window_size.w), m_shader);
|
static constexpr ShaderConvert s_shader[5] = {ShaderConvert::COPY, ShaderConvert::SCANLINE,
|
||||||
|
ShaderConvert::DIAGONAL_FILTER, ShaderConvert::TRIANGULAR_FILTER,
|
||||||
|
ShaderConvert::COMPLEX_FILTER}; // FIXME
|
||||||
|
|
||||||
|
g_gs_device->StretchRect(current, nullptr, draw_rect, s_shader[GSConfig.TVShader], GSConfig.LinearPresent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Host::EndPresentFrame();
|
||||||
|
}
|
||||||
|
g_gs_device->RestoreAPIState();
|
||||||
|
|
||||||
// snapshot
|
// snapshot
|
||||||
|
|
||||||
|
@ -495,7 +452,7 @@ void GSRenderer::VSync(int field)
|
||||||
delete[] fd.data;
|
delete[] fd.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GSTexture* t = m_dev->GetCurrent())
|
if (GSTexture* t = g_gs_device->GetCurrent())
|
||||||
{
|
{
|
||||||
t->Save(m_snapshot + ".png");
|
t->Save(m_snapshot + ".png");
|
||||||
}
|
}
|
||||||
|
@ -512,21 +469,21 @@ void GSRenderer::VSync(int field)
|
||||||
|
|
||||||
if (m_capture.IsCapturing())
|
if (m_capture.IsCapturing())
|
||||||
{
|
{
|
||||||
if (GSTexture* current = m_dev->GetCurrent())
|
if (GSTexture* current = g_gs_device->GetCurrent())
|
||||||
{
|
{
|
||||||
GSVector2i size = m_capture.GetSize();
|
GSVector2i size = m_capture.GetSize();
|
||||||
|
|
||||||
bool res;
|
bool res;
|
||||||
GSTexture::GSMap m;
|
GSTexture::GSMap m;
|
||||||
if (size == current->GetSize())
|
if (size == current->GetSize())
|
||||||
res = m_dev->DownloadTexture(current, GSVector4i(0, 0, size.x, size.y), m);
|
res = g_gs_device->DownloadTexture(current, GSVector4i(0, 0, size.x, size.y), m);
|
||||||
else
|
else
|
||||||
res = m_dev->DownloadTextureConvert(current, GSVector4(0, 0, 1, 1), size, GSTexture::Format::Color, ShaderConvert::COPY, m, true);
|
res = g_gs_device->DownloadTextureConvert(current, GSVector4(0, 0, 1, 1), size, GSTexture::Format::Color, ShaderConvert::COPY, m, true);
|
||||||
|
|
||||||
if (res)
|
if (res)
|
||||||
{
|
{
|
||||||
m_capture.DeliverFrame(m.bits, m.pitch, !m_dev->IsRBSwapped());
|
m_capture.DeliverFrame(m.bits, m.pitch, !g_gs_device->IsRBSwapped());
|
||||||
m_dev->DownloadTextureComplete();
|
g_gs_device->DownloadTextureComplete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -569,10 +526,7 @@ bool GSRenderer::MakeSnapshot(const std::string& path)
|
||||||
|
|
||||||
bool GSRenderer::BeginCapture(std::string& filename)
|
bool GSRenderer::BeginCapture(std::string& filename)
|
||||||
{
|
{
|
||||||
GSVector4i disp = ComputeDrawRectangle(m_dev->GetBackbufferWidth(), m_dev->GetBackbufferHeight());
|
return m_capture.BeginCapture(GetTvRefreshRate(), GetInternalResolution(), GetCurrentAspectRatioFloat(), filename);
|
||||||
float aspect = (float)disp.width() / std::max(1, disp.height());
|
|
||||||
|
|
||||||
return m_capture.BeginCapture(GetTvRefreshRate(), GetInternalResolution(), aspect, filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSRenderer::EndCapture()
|
void GSRenderer::EndCapture()
|
||||||
|
@ -618,34 +572,24 @@ void GSRenderer::KeyEvent(const HostKeyEvent& e)
|
||||||
switch (e.key)
|
switch (e.key)
|
||||||
{
|
{
|
||||||
case VK_F5:
|
case VK_F5:
|
||||||
m_interlace = (m_interlace + s_interlace_nb + step) % s_interlace_nb;
|
GSConfig.InterlaceMode = static_cast<GSInterlaceMode>((static_cast<int>(GSConfig.InterlaceMode) + static_cast<int>(GSInterlaceMode::Count) + step) % static_cast<int>(GSInterlaceMode::Count));
|
||||||
theApp.SetConfig("interlace", m_interlace);
|
theApp.SetConfig("interlace", static_cast<int>(GSConfig.InterlaceMode));
|
||||||
printf("GS: Set deinterlace mode to %d (%s).\n", m_interlace, theApp.m_gs_interlace.at(m_interlace).name.c_str());
|
printf("GS: Set deinterlace mode to %d (%s).\n", static_cast<int>(GSConfig.InterlaceMode), theApp.m_gs_interlace.at(static_cast<int>(GSConfig.InterlaceMode)).name.c_str());
|
||||||
return;
|
return;
|
||||||
case VK_DELETE:
|
case VK_DELETE:
|
||||||
m_aa1 = !m_aa1;
|
GSConfig.AA1 = !GSConfig.AA1;
|
||||||
theApp.SetConfig("aa1", m_aa1);
|
theApp.SetConfig("aa1", GSConfig.AA1);
|
||||||
printf("GS: (Software) Edge anti-aliasing is now %s.\n", m_aa1 ? "enabled" : "disabled");
|
printf("GS: (Software) Edge anti-aliasing is now %s.\n", GSConfig.AA1 ? "enabled" : "disabled");
|
||||||
return;
|
return;
|
||||||
case VK_INSERT:
|
case VK_INSERT:
|
||||||
m_mipmap = (m_mipmap + s_mipmap_nb + step) % s_mipmap_nb;
|
m_mipmap = (m_mipmap + s_mipmap_nb + step) % s_mipmap_nb;
|
||||||
theApp.SetConfig("mipmap_hw", m_mipmap);
|
theApp.SetConfig("mipmap_hw", m_mipmap);
|
||||||
printf("GS: Mipmapping is now %s.\n", theApp.m_gs_hack.at(m_mipmap).name.c_str());
|
printf("GS: Mipmapping is now %s.\n", theApp.m_gs_hack.at(m_mipmap).name.c_str());
|
||||||
return;
|
return;
|
||||||
case VK_PRIOR:
|
|
||||||
m_fxaa = !m_fxaa;
|
|
||||||
theApp.SetConfig("fxaa", m_fxaa);
|
|
||||||
printf("GS: FXAA anti-aliasing is now %s.\n", m_fxaa ? "enabled" : "disabled");
|
|
||||||
return;
|
|
||||||
case VK_HOME:
|
|
||||||
m_shaderfx = !m_shaderfx;
|
|
||||||
theApp.SetConfig("shaderfx", m_shaderfx);
|
|
||||||
printf("GS: External post-processing is now %s.\n", m_shaderfx ? "enabled" : "disabled");
|
|
||||||
return;
|
|
||||||
case VK_NEXT: // As requested by Prafull, to be removed later
|
case VK_NEXT: // As requested by Prafull, to be removed later
|
||||||
char dither_msg[3][16] = {"disabled", "auto", "auto unscaled"};
|
char dither_msg[3][16] = {"disabled", "auto", "auto unscaled"};
|
||||||
m_dithering = (m_dithering + 1) % 3;
|
GSConfig.Dithering = (GSConfig.Dithering + 1) % 3;
|
||||||
printf("GS: Dithering is now %s.\n", dither_msg[m_dithering]);
|
printf("GS: Dithering is now %s.\n", dither_msg[GSConfig.Dithering]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -654,5 +598,47 @@ void GSRenderer::KeyEvent(const HostKeyEvent& e)
|
||||||
|
|
||||||
void GSRenderer::PurgePool()
|
void GSRenderer::PurgePool()
|
||||||
{
|
{
|
||||||
m_dev->PurgePool();
|
g_gs_device->PurgePool();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GSRenderer::SaveSnapshotToMemory(u32 width, u32 height, std::vector<u32>* pixels)
|
||||||
|
{
|
||||||
|
GSTexture* const current = g_gs_device->GetCurrent();
|
||||||
|
if (!current)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
GSVector4 draw_rect(CalculateDrawRect(width, height, current->GetWidth(), current->GetHeight(),
|
||||||
|
HostDisplay::Alignment::LeftOrTop, false));
|
||||||
|
u32 draw_width = static_cast<u32>(draw_rect.z - draw_rect.x);
|
||||||
|
u32 draw_height = static_cast<u32>(draw_rect.w - draw_rect.y);
|
||||||
|
if (draw_width > width)
|
||||||
|
{
|
||||||
|
draw_width = width;
|
||||||
|
draw_rect.left = 0;
|
||||||
|
draw_rect.right = width;
|
||||||
|
}
|
||||||
|
if (draw_height > height)
|
||||||
|
{
|
||||||
|
draw_height = height;
|
||||||
|
draw_rect.top = 0;
|
||||||
|
draw_rect.bottom = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
GSTexture::GSMap map;
|
||||||
|
const bool result = g_gs_device->DownloadTextureConvert(
|
||||||
|
current, GSVector4(0.0f, 0.0f, 1.0f, 1.0f),
|
||||||
|
GSVector2i(draw_width, draw_height), GSTexture::Format::Color,
|
||||||
|
ShaderConvert::COPY, map, true);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
const u32 pad_x = (width - draw_width) / 2;
|
||||||
|
const u32 pad_y = (height - draw_height) / 2;
|
||||||
|
pixels->resize(width * height, 0);
|
||||||
|
StringUtil::StrideMemCpy(pixels->data() + pad_y * width + pad_x, width * sizeof(u32),
|
||||||
|
map.bits, map.pitch, draw_width * sizeof(u32), draw_height);
|
||||||
|
|
||||||
|
g_gs_device->DownloadTextureComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include "GS/GSState.h"
|
#include "GS/GSState.h"
|
||||||
#include "GS/GSCapture.h"
|
#include "GS/GSCapture.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
struct HostKeyEvent;
|
struct HostKeyEvent;
|
||||||
|
|
||||||
|
@ -24,7 +25,6 @@ class GSRenderer : public GSState
|
||||||
{
|
{
|
||||||
GSCapture m_capture;
|
GSCapture m_capture;
|
||||||
std::string m_snapshot;
|
std::string m_snapshot;
|
||||||
int m_shader;
|
|
||||||
|
|
||||||
bool Merge(int field);
|
bool Merge(int field);
|
||||||
|
|
||||||
|
@ -32,13 +32,6 @@ class GSRenderer : public GSState
|
||||||
bool m_control_key;
|
bool m_control_key;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int m_dithering;
|
|
||||||
int m_interlace;
|
|
||||||
int m_vsync;
|
|
||||||
bool m_aa1;
|
|
||||||
bool m_shaderfx;
|
|
||||||
bool m_fxaa;
|
|
||||||
bool m_shadeboost;
|
|
||||||
bool m_texture_shuffle;
|
bool m_texture_shuffle;
|
||||||
GSVector2i m_real_size;
|
GSVector2i m_real_size;
|
||||||
|
|
||||||
|
@ -49,9 +42,9 @@ public:
|
||||||
GSRenderer();
|
GSRenderer();
|
||||||
virtual ~GSRenderer();
|
virtual ~GSRenderer();
|
||||||
|
|
||||||
virtual bool CreateDevice(GSDevice* dev, const WindowInfo& wi);
|
virtual void Destroy();
|
||||||
virtual void ResetDevice();
|
|
||||||
virtual void VSync(int field);
|
virtual void VSync(u32 field);
|
||||||
virtual bool MakeSnapshot(const std::string& path);
|
virtual bool MakeSnapshot(const std::string& path);
|
||||||
virtual void KeyEvent(const HostKeyEvent& e);
|
virtual void KeyEvent(const HostKeyEvent& e);
|
||||||
virtual bool CanUpscale() { return false; }
|
virtual bool CanUpscale() { return false; }
|
||||||
|
@ -59,17 +52,11 @@ public:
|
||||||
virtual GSVector2i GetCustomResolution() { return GSVector2i(0, 0); }
|
virtual GSVector2i GetCustomResolution() { return GSVector2i(0, 0); }
|
||||||
virtual GSVector2 GetTextureScaleFactor() { return { 1.0f, 1.0f }; }
|
virtual GSVector2 GetTextureScaleFactor() { return { 1.0f, 1.0f }; }
|
||||||
GSVector2i GetInternalResolution();
|
GSVector2i GetInternalResolution();
|
||||||
void SetVSync(int vsync);
|
|
||||||
|
|
||||||
virtual bool BeginCapture(std::string& filename);
|
virtual bool BeginCapture(std::string& filename);
|
||||||
virtual void EndCapture();
|
virtual void EndCapture();
|
||||||
|
|
||||||
void PurgePool();
|
void PurgePool();
|
||||||
|
|
||||||
GSVector4i ComputeDrawRectangle(int width, int height) const;
|
bool SaveSnapshotToMemory(u32 width, u32 height, std::vector<u32>* pixels);
|
||||||
|
|
||||||
public:
|
|
||||||
std::mutex m_pGSsetTitle_Crit;
|
|
||||||
|
|
||||||
char m_GStitleInfoBuffer[128];
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,7 +33,6 @@ public:
|
||||||
DepthStencil,
|
DepthStencil,
|
||||||
Texture,
|
Texture,
|
||||||
Offscreen,
|
Offscreen,
|
||||||
Backbuffer,
|
|
||||||
SparseRenderTarget,
|
SparseRenderTarget,
|
||||||
SparseDepthStencil,
|
SparseDepthStencil,
|
||||||
};
|
};
|
||||||
|
@ -41,7 +40,6 @@ public:
|
||||||
enum class Format
|
enum class Format
|
||||||
{
|
{
|
||||||
Invalid = 0, ///< Used for initialization
|
Invalid = 0, ///< Used for initialization
|
||||||
Backbuffer, ///< For displaying to the screen
|
|
||||||
Color, ///< Standard (RGBA8) color texture
|
Color, ///< Standard (RGBA8) color texture
|
||||||
FloatColor, ///< Float-based color texture for colclip emulation (RGBA32F)
|
FloatColor, ///< Float-based color texture for colclip emulation (RGBA32F)
|
||||||
DepthStencil, ///< Depth stencil texture
|
DepthStencil, ///< Depth stencil texture
|
||||||
|
@ -70,6 +68,9 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the native handle of a texture.
|
||||||
|
virtual void* GetNativeHandle() const = 0;
|
||||||
|
|
||||||
virtual bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) = 0;
|
virtual bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) = 0;
|
||||||
virtual bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) = 0;
|
virtual bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) = 0;
|
||||||
virtual void Unmap() = 0;
|
virtual void Unmap() = 0;
|
||||||
|
|
|
@ -24,7 +24,6 @@ CONSTINIT const GSVector4 GSVertexTrace::s_minmax = GSVector4::cxpr(FLT_MAX, -FL
|
||||||
GSVertexTrace::GSVertexTrace(const GSState* state)
|
GSVertexTrace::GSVertexTrace(const GSState* state)
|
||||||
: m_accurate_stq(false), m_state(state), m_primclass(GS_INVALID_CLASS)
|
: m_accurate_stq(false), m_state(state), m_primclass(GS_INVALID_CLASS)
|
||||||
{
|
{
|
||||||
m_force_filter = static_cast<BiFiltering>(theApp.GetConfigI("filter"));
|
|
||||||
memset(&m_alpha, 0, sizeof(m_alpha));
|
memset(&m_alpha, 0, sizeof(m_alpha));
|
||||||
|
|
||||||
#define InitUpdate3(P, IIP, TME, FST, COLOR) \
|
#define InitUpdate3(P, IIP, TME, FST, COLOR) \
|
||||||
|
@ -126,7 +125,7 @@ void GSVertexTrace::Update(const void* vertex, const u32* index, int v_count, in
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (m_force_filter)
|
switch (GSConfig.TextureFiltering)
|
||||||
{
|
{
|
||||||
case BiFiltering::Nearest:
|
case BiFiltering::Nearest:
|
||||||
m_filter.opt_linear = 0;
|
m_filter.opt_linear = 0;
|
||||||
|
|
|
@ -26,8 +26,6 @@ class GSState;
|
||||||
|
|
||||||
class alignas(32) GSVertexTrace : public GSAlignedClass<32>
|
class alignas(32) GSVertexTrace : public GSAlignedClass<32>
|
||||||
{
|
{
|
||||||
BiFiltering m_force_filter;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct Vertex
|
struct Vertex
|
||||||
{
|
{
|
||||||
|
|
|
@ -49,30 +49,6 @@ namespace D3D
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> GetAdapterList(IDXGIFactory2* factory)
|
|
||||||
{
|
|
||||||
ASSERT(factory);
|
|
||||||
|
|
||||||
UINT index = 0;
|
|
||||||
wil::com_ptr_nothrow<IDXGIAdapter1> adapter;
|
|
||||||
|
|
||||||
std::vector<std::string> adapter_list;
|
|
||||||
while (factory->EnumAdapters1(index, adapter.put()) != DXGI_ERROR_NOT_FOUND)
|
|
||||||
{
|
|
||||||
DXGI_ADAPTER_DESC1 desc;
|
|
||||||
|
|
||||||
std::string name;
|
|
||||||
if (SUCCEEDED(adapter->GetDesc1(&desc)))
|
|
||||||
name = convert_utf16_to_utf8(desc.Description);
|
|
||||||
|
|
||||||
adapter_list.push_back(std::move(name));
|
|
||||||
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return adapter_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
wil::com_ptr_nothrow<IDXGIAdapter1> GetAdapterFromIndex(IDXGIFactory2* factory, int index)
|
wil::com_ptr_nothrow<IDXGIAdapter1> GetAdapterFromIndex(IDXGIFactory2* factory, int index)
|
||||||
{
|
{
|
||||||
ASSERT(factory);
|
ASSERT(factory);
|
||||||
|
|
|
@ -25,9 +25,6 @@ namespace D3D
|
||||||
// create a dxgi factory
|
// create a dxgi factory
|
||||||
wil::com_ptr_nothrow<IDXGIFactory2> CreateFactory(bool debug);
|
wil::com_ptr_nothrow<IDXGIFactory2> CreateFactory(bool debug);
|
||||||
|
|
||||||
// get a list of adapters
|
|
||||||
std::vector<std::string> GetAdapterList(IDXGIFactory2* factory);
|
|
||||||
|
|
||||||
// get an adapter based on position
|
// get an adapter based on position
|
||||||
// assuming no one removes/moves it, it should always have the same id
|
// assuming no one removes/moves it, it should always have the same id
|
||||||
// however in the event that the adapter is not found due to the above, use the default
|
// however in the event that the adapter is not found due to the above, use the default
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "GS/GSExtra.h"
|
#include "GS/GSExtra.h"
|
||||||
#include "GS/GSUtil.h"
|
#include "GS/GSUtil.h"
|
||||||
#include "Host.h"
|
#include "Host.h"
|
||||||
|
#include "HostDisplay.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <VersionHelpers.h>
|
#include <VersionHelpers.h>
|
||||||
|
@ -79,12 +80,10 @@ bool GSDevice11::SetFeatureLevel(D3D_FEATURE_LEVEL level, bool compat_mode)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSDevice11::Create(const WindowInfo& wi)
|
bool GSDevice11::Create(HostDisplay* display)
|
||||||
{
|
{
|
||||||
if (!__super::Create(wi))
|
if (!__super::Create(display))
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
D3D11_BUFFER_DESC bd;
|
D3D11_BUFFER_DESC bd;
|
||||||
D3D11_SAMPLER_DESC sd;
|
D3D11_SAMPLER_DESC sd;
|
||||||
|
@ -92,131 +91,43 @@ bool GSDevice11::Create(const WindowInfo& wi)
|
||||||
D3D11_RASTERIZER_DESC rd;
|
D3D11_RASTERIZER_DESC rd;
|
||||||
D3D11_BLEND_DESC bsd;
|
D3D11_BLEND_DESC bsd;
|
||||||
|
|
||||||
const bool enable_debugging = theApp.GetConfigB("debug_d3d");
|
D3D_FEATURE_LEVEL level;
|
||||||
|
|
||||||
auto factory = D3D::CreateFactory(enable_debugging);
|
if (display->GetRenderAPI() != HostDisplay::RenderAPI::D3D11)
|
||||||
if (!factory)
|
{
|
||||||
|
fprintf(stderr, "Render API is incompatible with D3D11\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dev = static_cast<ID3D11Device*>(display->GetRenderDevice());
|
||||||
|
m_ctx = static_cast<ID3D11DeviceContext*>(display->GetRenderContext());
|
||||||
|
level = m_dev->GetFeatureLevel();
|
||||||
|
|
||||||
|
bool nvidia_vendor = false;
|
||||||
|
{
|
||||||
|
if (auto dxgi_device = m_dev.try_query<IDXGIDevice>())
|
||||||
|
{
|
||||||
|
wil::com_ptr_nothrow<IDXGIAdapter> dxgi_adapter;
|
||||||
|
DXGI_ADAPTER_DESC adapter_desc;
|
||||||
|
if (SUCCEEDED(dxgi_device->GetAdapter(dxgi_adapter.put())) && SUCCEEDED(dxgi_adapter->GetDesc(&adapter_desc)))
|
||||||
|
nvidia_vendor = (adapter_desc.VendorId == 0x10DE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SetFeatureLevel(m_dev->GetFeatureLevel(), true))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// select adapter
|
// Set maximum texture size limit based on supported feature level.
|
||||||
auto adapter = D3D::GetAdapterFromIndex(
|
if (level >= D3D_FEATURE_LEVEL_11_0)
|
||||||
factory.get(), theApp.GetConfigI("adapter_index")
|
m_d3d_texsize = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
||||||
);
|
else
|
||||||
|
m_d3d_texsize = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
||||||
DXGI_ADAPTER_DESC1 adapter_desc = {};
|
|
||||||
if (SUCCEEDED(adapter->GetDesc1(&adapter_desc)))
|
|
||||||
{
|
|
||||||
std::string adapter_name = convert_utf16_to_utf8(
|
|
||||||
adapter_desc.Description
|
|
||||||
);
|
|
||||||
|
|
||||||
fprintf(stderr, "Selected DXGI Adapter\n"
|
|
||||||
"\tName: %s\n"
|
|
||||||
"\tVendor: %x\n", adapter_name.c_str(), adapter_desc.VendorId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// device creation
|
|
||||||
{
|
|
||||||
u32 flags = D3D11_CREATE_DEVICE_SINGLETHREADED;
|
|
||||||
|
|
||||||
if(enable_debugging)
|
|
||||||
flags |= D3D11_CREATE_DEVICE_DEBUG;
|
|
||||||
|
|
||||||
constexpr std::array<D3D_FEATURE_LEVEL, 3> supported_levels = {
|
|
||||||
D3D_FEATURE_LEVEL_11_0,
|
|
||||||
D3D_FEATURE_LEVEL_10_1,
|
|
||||||
D3D_FEATURE_LEVEL_10_0,
|
|
||||||
};
|
|
||||||
|
|
||||||
D3D_FEATURE_LEVEL feature_level;
|
|
||||||
HRESULT result = D3D11CreateDevice(
|
|
||||||
adapter.get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, flags,
|
|
||||||
supported_levels.data(), supported_levels.size(),
|
|
||||||
D3D11_SDK_VERSION, m_dev.put(), &feature_level, m_ctx.put()
|
|
||||||
);
|
|
||||||
|
|
||||||
// if a debug device is requested but not supported, fallback to non-debug device
|
|
||||||
if (FAILED(result) && enable_debugging)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "D3D: failed to create debug device, trying without debugging\n");
|
|
||||||
// clear the debug flag
|
|
||||||
flags = D3D11_CREATE_DEVICE_SINGLETHREADED;
|
|
||||||
|
|
||||||
result = D3D11CreateDevice(
|
|
||||||
adapter.get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, flags,
|
|
||||||
supported_levels.data(), supported_levels.size(),
|
|
||||||
D3D11_SDK_VERSION, m_dev.put(), &feature_level, m_ctx.put()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(result))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "D3D: unable to create D3D11 device (reason %x)\n"
|
|
||||||
"ensure that your gpu supports our minimum requirements:\n"
|
|
||||||
"https://github.com/PCSX2/pcsx2#system-requirements\n", result);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enable_debugging)
|
|
||||||
{
|
|
||||||
if (auto info_queue = m_dev.try_query<ID3D11InfoQueue>())
|
|
||||||
{
|
|
||||||
const int break_on = theApp.GetConfigI("dx_break_on_severity");
|
|
||||||
|
|
||||||
info_queue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, break_on & (1 << 0));
|
|
||||||
info_queue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, break_on & (1 << 1));
|
|
||||||
info_queue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, break_on & (1 << 2));
|
|
||||||
info_queue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_INFO, break_on & (1 << 3));
|
|
||||||
}
|
|
||||||
fprintf(stderr, "D3D: debugging enabled\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SetFeatureLevel(feature_level, true))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "D3D: adapter doesn't have a sufficient feature level\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set maximum texture size limit based on supported feature level.
|
|
||||||
if (feature_level >= D3D_FEATURE_LEVEL_11_0)
|
|
||||||
m_d3d_texsize = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
|
||||||
else
|
|
||||||
m_d3d_texsize = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
// swapchain creation
|
|
||||||
{
|
|
||||||
DXGI_SWAP_CHAIN_DESC1 swapchain_description = {};
|
|
||||||
|
|
||||||
// let the runtime get window size
|
|
||||||
swapchain_description.Width = 0;
|
|
||||||
swapchain_description.Height = 0;
|
|
||||||
|
|
||||||
swapchain_description.BufferCount = 2;
|
|
||||||
swapchain_description.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
||||||
swapchain_description.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
||||||
swapchain_description.SampleDesc.Count = 1;
|
|
||||||
swapchain_description.SampleDesc.Quality = 0;
|
|
||||||
|
|
||||||
// TODO: update swap effect
|
|
||||||
swapchain_description.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
|
|
||||||
|
|
||||||
const HRESULT result = factory->CreateSwapChainForHwnd(
|
|
||||||
m_dev.get(), reinterpret_cast<HWND>(wi.window_handle),
|
|
||||||
&swapchain_description, nullptr, nullptr, m_swapchain.put());
|
|
||||||
|
|
||||||
if (FAILED(result))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "D3D: Failed to create swapchain (reason: %x)\n", result);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// HACK: check nVIDIA
|
// HACK: check nVIDIA
|
||||||
// Note: It can cause issues on several games such as SOTC, Fatal Frame, plus it adds border offset.
|
// Note: It can cause issues on several games such as SOTC, Fatal Frame, plus it adds border offset.
|
||||||
bool disable_safe_features = theApp.GetConfigB("UserHacks") && theApp.GetConfigB("UserHacks_Disable_Safe_Features");
|
const bool disable_safe_features = theApp.GetConfigB("UserHacks") && theApp.GetConfigB("UserHacks_Disable_Safe_Features");
|
||||||
m_hack_topleft_offset = (m_upscale_multiplier != 1 && D3D::IsNvidia(adapter.get()) && !disable_safe_features) ? -0.01f : 0.0f;
|
m_hack_topleft_offset = (m_upscale_multiplier != 1 && nvidia_vendor && !disable_safe_features) ? -0.01f : 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/dx11/tfx.fx");
|
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/dx11/tfx.fx");
|
||||||
|
@ -362,10 +273,8 @@ bool GSDevice11::Create(const WindowInfo& wi)
|
||||||
rd.MultisampleEnable = true;
|
rd.MultisampleEnable = true;
|
||||||
rd.AntialiasedLineEnable = false;
|
rd.AntialiasedLineEnable = false;
|
||||||
|
|
||||||
wil::com_ptr_nothrow<ID3D11RasterizerState> rs;
|
m_dev->CreateRasterizerState(&rd, m_rs.put());
|
||||||
m_dev->CreateRasterizerState(&rd, rs.put());
|
m_ctx->RSSetState(m_rs.get());
|
||||||
|
|
||||||
m_ctx->RSSetState(rs.get());
|
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
@ -388,10 +297,6 @@ bool GSDevice11::Create(const WindowInfo& wi)
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
Reset(wi.surface_width, wi.surface_height);
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
CreateTextureFX();
|
CreateTextureFX();
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -419,48 +324,48 @@ bool GSDevice11::Create(const WindowInfo& wi)
|
||||||
|
|
||||||
m_dev->CreateBlendState(&blend, m_date.bs.put());
|
m_dev->CreateBlendState(&blend, m_date.bs.put());
|
||||||
|
|
||||||
const GSVector2i tex_font = m_osd.get_texture_font_size();
|
|
||||||
|
|
||||||
m_font = std::unique_ptr<GSTexture>(
|
|
||||||
CreateSurface(GSTexture::Type::Texture, tex_font.x, tex_font.y, GSTexture::Format::UNorm8));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSDevice11::Reset(int w, int h)
|
void GSDevice11::ResetAPIState()
|
||||||
{
|
{
|
||||||
if (!__super::Reset(w, h))
|
// Clear out the GS, since the imgui draw doesn't get rid of it.
|
||||||
return false;
|
m_ctx->GSSetShader(nullptr, nullptr, 0);
|
||||||
|
|
||||||
if (m_swapchain)
|
|
||||||
{
|
|
||||||
DXGI_SWAP_CHAIN_DESC scd;
|
|
||||||
|
|
||||||
memset(&scd, 0, sizeof(scd));
|
|
||||||
|
|
||||||
m_swapchain->GetDesc(&scd);
|
|
||||||
m_swapchain->ResizeBuffers(scd.BufferCount, w, h, scd.BufferDesc.Format, 0);
|
|
||||||
|
|
||||||
wil::com_ptr_nothrow<ID3D11Texture2D> backbuffer;
|
|
||||||
if (FAILED(m_swapchain->GetBuffer(0, IID_PPV_ARGS(backbuffer.put()))))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_backbuffer = new GSTexture11(std::move(backbuffer), GSTexture::Format::Backbuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDevice11::SetVSync(int vsync)
|
void GSDevice11::RestoreAPIState()
|
||||||
{
|
{
|
||||||
m_vsync = vsync ? 1 : 0;
|
const UINT vb_stride = static_cast<UINT>(m_state.vb_stride);
|
||||||
}
|
const UINT vb_offset = 0;
|
||||||
|
m_ctx->IASetVertexBuffers(0, 1, &m_state.vb, &vb_stride, &vb_offset);
|
||||||
|
m_ctx->IASetIndexBuffer(m_state.ib, DXGI_FORMAT_R32_UINT, 0);
|
||||||
|
m_ctx->IASetInputLayout(m_state.layout);
|
||||||
|
m_ctx->IASetPrimitiveTopology(m_state.topology);
|
||||||
|
m_ctx->VSSetShader(m_state.vs, nullptr, 0);
|
||||||
|
m_ctx->VSSetConstantBuffers(0, 1, &m_state.vs_cb);
|
||||||
|
m_ctx->GSSetShader(m_state.gs, nullptr, 0);
|
||||||
|
m_ctx->GSSetConstantBuffers(0, 1, &m_state.gs_cb);
|
||||||
|
m_ctx->PSSetShader(m_state.ps, nullptr, 0);
|
||||||
|
m_ctx->PSSetConstantBuffers(0, 1, &m_state.ps_cb);
|
||||||
|
|
||||||
void GSDevice11::Flip()
|
const CD3D11_VIEWPORT vp(m_hack_topleft_offset, m_hack_topleft_offset,
|
||||||
{
|
static_cast<float>(m_state.viewport.x), static_cast<float>(m_state.viewport.y),
|
||||||
m_swapchain->Present(m_vsync, 0);
|
0.0f, 1.0f);
|
||||||
|
m_ctx->RSSetViewports(1, &vp);
|
||||||
|
m_ctx->RSSetScissorRects(1, m_state.scissor);
|
||||||
|
m_ctx->RSSetState(m_rs.get());
|
||||||
|
|
||||||
|
m_ctx->OMSetDepthStencilState(m_state.dss, m_state.sref);
|
||||||
|
|
||||||
|
const float blend_factors[4] = { m_state.bf, m_state.bf, m_state.bf, m_state.bf };
|
||||||
|
m_ctx->OMSetBlendState(m_state.bs, blend_factors, 0xFFFFFFFFu);
|
||||||
|
|
||||||
|
PSUpdateShaderState();
|
||||||
|
|
||||||
|
if (m_state.rt_view)
|
||||||
|
m_ctx->OMSetRenderTargets(1, &m_state.rt_view, m_state.dsv);
|
||||||
|
else
|
||||||
|
m_ctx->OMSetRenderTargets(0, nullptr, m_state.dsv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDevice11::BeforeDraw()
|
void GSDevice11::BeforeDraw()
|
||||||
|
@ -583,7 +488,6 @@ GSTexture* GSDevice11::CreateSurface(GSTexture::Type type, int w, int h, GSTextu
|
||||||
case GSTexture::Format::UInt32: dxformat = DXGI_FORMAT_R32_UINT; break;
|
case GSTexture::Format::UInt32: dxformat = DXGI_FORMAT_R32_UINT; break;
|
||||||
case GSTexture::Format::Int32: dxformat = DXGI_FORMAT_R32_SINT; break;
|
case GSTexture::Format::Int32: dxformat = DXGI_FORMAT_R32_SINT; break;
|
||||||
case GSTexture::Format::Invalid:
|
case GSTexture::Format::Invalid:
|
||||||
case GSTexture::Format::Backbuffer:
|
|
||||||
ASSERT(0);
|
ASSERT(0);
|
||||||
dxformat = DXGI_FORMAT_UNKNOWN;
|
dxformat = DXGI_FORMAT_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
@ -727,11 +631,7 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
||||||
|
|
||||||
void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ID3D11PixelShader* ps, ID3D11Buffer* ps_cb, ID3D11BlendState* bs, bool linear)
|
void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ID3D11PixelShader* ps, ID3D11Buffer* ps_cb, ID3D11BlendState* bs, bool linear)
|
||||||
{
|
{
|
||||||
if (!sTex || !dTex)
|
ASSERT(sTex);
|
||||||
{
|
|
||||||
ASSERT(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool draw_in_depth = ps == m_convert.ps[static_cast<int>(ShaderConvert::RGBA8_TO_FLOAT32)]
|
const bool draw_in_depth = ps == m_convert.ps[static_cast<int>(ShaderConvert::RGBA8_TO_FLOAT32)]
|
||||||
|| ps == m_convert.ps[static_cast<int>(ShaderConvert::RGBA8_TO_FLOAT24)]
|
|| ps == m_convert.ps[static_cast<int>(ShaderConvert::RGBA8_TO_FLOAT24)]
|
||||||
|
@ -740,11 +640,22 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
||||||
|
|
||||||
BeginScene();
|
BeginScene();
|
||||||
|
|
||||||
const GSVector2i ds = dTex->GetSize();
|
GSVector2i ds;
|
||||||
|
if (dTex)
|
||||||
|
{
|
||||||
|
ds = dTex->GetSize();
|
||||||
|
if (draw_in_depth)
|
||||||
|
OMSetRenderTargets(nullptr, dTex);
|
||||||
|
else
|
||||||
|
OMSetRenderTargets(dTex, nullptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ds = GSVector2i(m_display->GetWindowWidth(), m_display->GetWindowHeight());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// om
|
// om
|
||||||
|
|
||||||
|
|
||||||
if (draw_in_depth)
|
if (draw_in_depth)
|
||||||
OMSetDepthStencilState(m_convert.dss_write.get(), 0);
|
OMSetDepthStencilState(m_convert.dss_write.get(), 0);
|
||||||
else
|
else
|
||||||
|
@ -752,10 +663,7 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
||||||
|
|
||||||
OMSetBlendState(bs, 0);
|
OMSetBlendState(bs, 0);
|
||||||
|
|
||||||
if (draw_in_depth)
|
|
||||||
OMSetRenderTargets(nullptr, dTex);
|
|
||||||
else
|
|
||||||
OMSetRenderTargets(dTex, nullptr);
|
|
||||||
|
|
||||||
// ia
|
// ia
|
||||||
|
|
||||||
|
@ -805,48 +713,6 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
||||||
PSSetShaderResources(nullptr, nullptr);
|
PSSetShaderResources(nullptr, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDevice11::RenderOsd(GSTexture* dt)
|
|
||||||
{
|
|
||||||
BeginScene();
|
|
||||||
|
|
||||||
// om
|
|
||||||
OMSetDepthStencilState(m_convert.dss.get(), 0);
|
|
||||||
OMSetBlendState(m_merge.bs.get(), 0);
|
|
||||||
OMSetRenderTargets(dt, nullptr);
|
|
||||||
|
|
||||||
if (m_osd.m_texture_dirty)
|
|
||||||
{
|
|
||||||
m_osd.upload_texture_atlas(m_font.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ps
|
|
||||||
PSSetShaderResource(0, m_font.get());
|
|
||||||
PSSetSamplerState(m_convert.pt.get(), nullptr);
|
|
||||||
PSSetShader(m_convert.ps[static_cast<int>(ShaderConvert::OSD)].get(), nullptr);
|
|
||||||
|
|
||||||
// ia
|
|
||||||
IASetInputLayout(m_convert.il.get());
|
|
||||||
IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
||||||
|
|
||||||
// Note scaling could also be done in shader (require gl3/dx10)
|
|
||||||
size_t count = m_osd.Size();
|
|
||||||
void* dst = nullptr;
|
|
||||||
|
|
||||||
IAMapVertexBuffer(&dst, sizeof(GSVertexPT1), count);
|
|
||||||
count = m_osd.GeneratePrimitives((GSVertexPT1*)dst, count);
|
|
||||||
IAUnmapVertexBuffer();
|
|
||||||
|
|
||||||
// vs
|
|
||||||
VSSetShader(m_convert.vs.get(), nullptr);
|
|
||||||
|
|
||||||
// gs
|
|
||||||
GSSetShader(nullptr, nullptr);
|
|
||||||
|
|
||||||
DrawPrimitive();
|
|
||||||
|
|
||||||
EndScene();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSDevice11::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c)
|
void GSDevice11::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c)
|
||||||
{
|
{
|
||||||
const bool slbg = PMODE.SLBG;
|
const bool slbg = PMODE.SLBG;
|
||||||
|
|
|
@ -119,7 +119,6 @@ private:
|
||||||
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
|
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
|
||||||
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex) final;
|
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex) final;
|
||||||
void DoExternalFX(GSTexture* sTex, GSTexture* dTex) final;
|
void DoExternalFX(GSTexture* sTex, GSTexture* dTex) final;
|
||||||
void RenderOsd(GSTexture* dt);
|
|
||||||
void BeforeDraw();
|
void BeforeDraw();
|
||||||
void AfterDraw();
|
void AfterDraw();
|
||||||
|
|
||||||
|
@ -217,11 +216,11 @@ private:
|
||||||
wil::com_ptr_nothrow<ID3D11SamplerState> m_palette_ss;
|
wil::com_ptr_nothrow<ID3D11SamplerState> m_palette_ss;
|
||||||
std::unordered_map<u32, wil::com_ptr_nothrow<ID3D11DepthStencilState>> m_om_dss;
|
std::unordered_map<u32, wil::com_ptr_nothrow<ID3D11DepthStencilState>> m_om_dss;
|
||||||
std::unordered_map<u32, wil::com_ptr_nothrow<ID3D11BlendState>> m_om_bs;
|
std::unordered_map<u32, wil::com_ptr_nothrow<ID3D11BlendState>> m_om_bs;
|
||||||
|
wil::com_ptr_nothrow<ID3D11RasterizerState> m_rs;
|
||||||
|
|
||||||
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
|
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
|
||||||
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
|
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
|
||||||
|
|
||||||
std::unique_ptr<GSTexture> m_font;
|
|
||||||
std::unique_ptr<GSTexture11> m_download_tex;
|
std::unique_ptr<GSTexture11> m_download_tex;
|
||||||
|
|
||||||
std::string m_tfx_source;
|
std::string m_tfx_source;
|
||||||
|
@ -240,10 +239,10 @@ public:
|
||||||
bool SetFeatureLevel(D3D_FEATURE_LEVEL level, bool compat_mode);
|
bool SetFeatureLevel(D3D_FEATURE_LEVEL level, bool compat_mode);
|
||||||
void GetFeatureLevel(D3D_FEATURE_LEVEL& level) const { level = m_shader.level; }
|
void GetFeatureLevel(D3D_FEATURE_LEVEL& level) const { level = m_shader.level; }
|
||||||
|
|
||||||
bool Create(const WindowInfo& wi);
|
bool Create(HostDisplay* display);
|
||||||
bool Reset(int w, int h);
|
|
||||||
void Flip();
|
void ResetAPIState() override;
|
||||||
void SetVSync(int vsync) final;
|
void RestoreAPIState() override;
|
||||||
|
|
||||||
void DrawPrimitive();
|
void DrawPrimitive();
|
||||||
void DrawIndexedPrimitive();
|
void DrawIndexedPrimitive();
|
||||||
|
|
|
@ -44,6 +44,11 @@ GSTexture11::GSTexture11(wil::com_ptr_nothrow<ID3D11Texture2D> texture, GSTextur
|
||||||
m_max_layer = m_desc.MipLevels;
|
m_max_layer = m_desc.MipLevels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* GSTexture11::GetNativeHandle() const
|
||||||
|
{
|
||||||
|
return static_cast<ID3D11ShaderResourceView*>(*const_cast<GSTexture11*>(this));
|
||||||
|
}
|
||||||
|
|
||||||
bool GSTexture11::Update(const GSVector4i& r, const void* data, int pitch, int layer)
|
bool GSTexture11::Update(const GSVector4i& r, const void* data, int pitch, int layer)
|
||||||
{
|
{
|
||||||
if (layer >= m_max_layer)
|
if (layer >= m_max_layer)
|
||||||
|
|
|
@ -36,6 +36,8 @@ class GSTexture11 : public GSTexture
|
||||||
public:
|
public:
|
||||||
explicit GSTexture11(wil::com_ptr_nothrow<ID3D11Texture2D> texture, GSTexture::Format format);
|
explicit GSTexture11(wil::com_ptr_nothrow<ID3D11Texture2D> texture, GSTexture::Format format);
|
||||||
|
|
||||||
|
void* GetNativeHandle() const override;
|
||||||
|
|
||||||
bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0);
|
bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0);
|
||||||
bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0);
|
bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0);
|
||||||
void Unmap();
|
void Unmap();
|
||||||
|
|
|
@ -225,7 +225,7 @@ void GSDevice11::SetupPS(PSSelector sel, const GSHWDrawConfig::PSConstantBuffer*
|
||||||
|
|
||||||
memset(&sd, 0, sizeof(sd));
|
memset(&sd, 0, sizeof(sd));
|
||||||
|
|
||||||
const int anisotropy = theApp.GetConfigI("MaxAnisotropy");
|
const int anisotropy = GSConfig.MaxAnisotropy;
|
||||||
if (anisotropy && ssel.aniso)
|
if (anisotropy && ssel.aniso)
|
||||||
sd.Filter = D3D11_FILTER_ANISOTROPIC;
|
sd.Filter = D3D11_FILTER_ANISOTROPIC;
|
||||||
else if (ssel.biln)
|
else if (ssel.biln)
|
||||||
|
|
|
@ -1121,7 +1121,7 @@ bool GSState::IsBadFrame()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_skip == 0 && (m_userhacks_skipdraw > 0))
|
if (m_skip == 0 && (GSConfig.SkipDraw > 0))
|
||||||
{
|
{
|
||||||
if (fi.TME)
|
if (fi.TME)
|
||||||
{
|
{
|
||||||
|
@ -1129,8 +1129,8 @@ bool GSState::IsBadFrame()
|
||||||
// General, often problematic post processing
|
// General, often problematic post processing
|
||||||
if (GSLocalMemory::m_psm[fi.TPSM].depth || GSUtil::HasSharedBits(fi.FBP, fi.FPSM, fi.TBP0, fi.TPSM))
|
if (GSLocalMemory::m_psm[fi.TPSM].depth || GSUtil::HasSharedBits(fi.FBP, fi.FPSM, fi.TBP0, fi.TPSM))
|
||||||
{
|
{
|
||||||
m_skip_offset = m_userhacks_skipdraw_offset;
|
m_skip_offset = GSConfig.SkipDrawOffset;
|
||||||
m_skip = std::max(m_userhacks_skipdraw, m_skip_offset);
|
m_skip = std::max(GSConfig.SkipDraw, m_skip_offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,11 @@
|
||||||
#include "GS/GSGL.h"
|
#include "GS/GSGL.h"
|
||||||
|
|
||||||
GSRendererHW::GSRendererHW()
|
GSRendererHW::GSRendererHW()
|
||||||
: m_width(default_rt_size.x)
|
: GSRenderer()
|
||||||
|
, m_width(default_rt_size.x)
|
||||||
, m_height(default_rt_size.y)
|
, m_height(default_rt_size.y)
|
||||||
, m_custom_width(1024)
|
, m_custom_width(1024)
|
||||||
, m_custom_height(1024)
|
, m_custom_height(1024)
|
||||||
, m_reset(false)
|
|
||||||
, m_userhacks_ts_half_bottom(-1)
|
, m_userhacks_ts_half_bottom(-1)
|
||||||
, m_tc(new GSTextureCache(this))
|
, m_tc(new GSTextureCache(this))
|
||||||
, m_src(nullptr)
|
, m_src(nullptr)
|
||||||
|
@ -30,12 +30,12 @@ GSRendererHW::GSRendererHW()
|
||||||
, m_userhacks_tcoffset_x(0)
|
, m_userhacks_tcoffset_x(0)
|
||||||
, m_userhacks_tcoffset_y(0)
|
, m_userhacks_tcoffset_y(0)
|
||||||
, m_channel_shuffle(false)
|
, m_channel_shuffle(false)
|
||||||
|
, m_reset(false)
|
||||||
, m_lod(GSVector2i(0, 0))
|
, m_lod(GSVector2i(0, 0))
|
||||||
{
|
{
|
||||||
m_mipmap = theApp.GetConfigI("mipmap_hw");
|
m_mipmap = theApp.GetConfigI("mipmap_hw");
|
||||||
m_upscale_multiplier = std::max(0, theApp.GetConfigI("upscale_multiplier"));
|
m_upscale_multiplier = std::max(0, theApp.GetConfigI("upscale_multiplier"));
|
||||||
m_conservative_framebuffer = theApp.GetConfigB("conservative_framebuffer");
|
m_conservative_framebuffer = theApp.GetConfigB("conservative_framebuffer");
|
||||||
m_accurate_date = theApp.GetConfigB("accurate_date");
|
|
||||||
|
|
||||||
if (theApp.GetConfigB("UserHacks"))
|
if (theApp.GetConfigB("UserHacks"))
|
||||||
{
|
{
|
||||||
|
@ -45,7 +45,6 @@ GSRendererHW::GSRendererHW()
|
||||||
m_userHacks_merge_sprite = theApp.GetConfigB("UserHacks_merge_pp_sprite");
|
m_userHacks_merge_sprite = theApp.GetConfigB("UserHacks_merge_pp_sprite");
|
||||||
m_userhacks_ts_half_bottom = theApp.GetConfigI("UserHacks_Half_Bottom_Override");
|
m_userhacks_ts_half_bottom = theApp.GetConfigI("UserHacks_Half_Bottom_Override");
|
||||||
m_userhacks_round_sprite_offset = theApp.GetConfigI("UserHacks_round_sprite_offset");
|
m_userhacks_round_sprite_offset = theApp.GetConfigI("UserHacks_round_sprite_offset");
|
||||||
m_userHacks_HPO = theApp.GetConfigI("UserHacks_HalfPixelOffset");
|
|
||||||
m_userhacks_tcoffset_x = theApp.GetConfigI("UserHacks_TCOffsetX") / -1000.0f;
|
m_userhacks_tcoffset_x = theApp.GetConfigI("UserHacks_TCOffsetX") / -1000.0f;
|
||||||
m_userhacks_tcoffset_y = theApp.GetConfigI("UserHacks_TCOffsetY") / -1000.0f;
|
m_userhacks_tcoffset_y = theApp.GetConfigI("UserHacks_TCOffsetY") / -1000.0f;
|
||||||
m_userhacks_tcoffset = m_userhacks_tcoffset_x < 0.0f || m_userhacks_tcoffset_y < 0.0f;
|
m_userhacks_tcoffset = m_userhacks_tcoffset_x < 0.0f || m_userhacks_tcoffset_y < 0.0f;
|
||||||
|
@ -58,7 +57,6 @@ GSRendererHW::GSRendererHW()
|
||||||
m_userHacks_merge_sprite = false;
|
m_userHacks_merge_sprite = false;
|
||||||
m_userhacks_ts_half_bottom = -1;
|
m_userhacks_ts_half_bottom = -1;
|
||||||
m_userhacks_round_sprite_offset = 0;
|
m_userhacks_round_sprite_offset = 0;
|
||||||
m_userHacks_HPO = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_upscale_multiplier) // Custom Resolution
|
if (!m_upscale_multiplier) // Custom Resolution
|
||||||
|
@ -278,7 +276,7 @@ void GSRendererHW::Reset()
|
||||||
GSRenderer::Reset();
|
GSRenderer::Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSRendererHW::VSync(int field)
|
void GSRendererHW::VSync(u32 field)
|
||||||
{
|
{
|
||||||
//Check if the frame buffer width or display width has changed
|
//Check if the frame buffer width or display width has changed
|
||||||
SetScaling();
|
SetScaling();
|
||||||
|
@ -295,19 +293,12 @@ void GSRendererHW::VSync(int field)
|
||||||
m_tc->IncAge();
|
m_tc->IncAge();
|
||||||
|
|
||||||
m_tc->PrintMemoryUsage();
|
m_tc->PrintMemoryUsage();
|
||||||
m_dev->PrintMemoryUsage();
|
g_gs_device->PrintMemoryUsage();
|
||||||
|
|
||||||
m_skip = 0;
|
m_skip = 0;
|
||||||
m_skip_offset = 0;
|
m_skip_offset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSRendererHW::ResetDevice()
|
|
||||||
{
|
|
||||||
m_tc->RemoveAll();
|
|
||||||
|
|
||||||
GSRenderer::ResetDevice();
|
|
||||||
}
|
|
||||||
|
|
||||||
GSTexture* GSRendererHW::GetOutput(int i, int& y_offset)
|
GSTexture* GSRendererHW::GetOutput(int i, int& y_offset)
|
||||||
{
|
{
|
||||||
const GSRegDISPFB& DISPFB = m_regs->DISP[i].DISPFB;
|
const GSRegDISPFB& DISPFB = m_regs->DISP[i].DISPFB;
|
||||||
|
@ -393,7 +384,7 @@ void GSRendererHW::Lines2Sprites()
|
||||||
|
|
||||||
alignas(16) static constexpr std::array<int, 8> tri_normal_indices = {{0, 1, 2, 1, 2, 3}};
|
alignas(16) static constexpr std::array<int, 8> tri_normal_indices = {{0, 1, 2, 1, 2, 3}};
|
||||||
alignas(16) static constexpr std::array<int, 8> tri_swapped_indices = {{0, 1, 2, 1, 2, 3}};
|
alignas(16) static constexpr std::array<int, 8> tri_swapped_indices = {{0, 1, 2, 1, 2, 3}};
|
||||||
const bool index_swap = !m_dev->Features().provoking_vertex_last;
|
const bool index_swap = !g_gs_device->Features().provoking_vertex_last;
|
||||||
const int* tri_indices = index_swap ? tri_swapped_indices.data() : tri_normal_indices.data();
|
const int* tri_indices = index_swap ? tri_swapped_indices.data() : tri_normal_indices.data();
|
||||||
const GSVector4i indices_low(GSVector4i::load<true>(tri_indices));
|
const GSVector4i indices_low(GSVector4i::load<true>(tri_indices));
|
||||||
const GSVector4i indices_high(GSVector4i::loadl(tri_indices + 4));
|
const GSVector4i indices_high(GSVector4i::loadl(tri_indices + 4));
|
||||||
|
@ -641,7 +632,7 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
|
||||||
|
|
||||||
GSVector4 GSRendererHW::RealignTargetTextureCoordinate(const GSTextureCache::Source* tex)
|
GSVector4 GSRendererHW::RealignTargetTextureCoordinate(const GSTextureCache::Source* tex)
|
||||||
{
|
{
|
||||||
if (m_userHacks_HPO <= 1 || GetUpscaleMultiplier() == 1)
|
if (GSConfig.UserHacks_HalfPixelOffset <= 1 || GetUpscaleMultiplier() == 1)
|
||||||
return GSVector4(0.0f);
|
return GSVector4(0.0f);
|
||||||
|
|
||||||
const GSVertex* v = &m_vertex.buff[0];
|
const GSVertex* v = &m_vertex.buff[0];
|
||||||
|
@ -656,7 +647,7 @@ GSVector4 GSRendererHW::RealignTargetTextureCoordinate(const GSTextureCache::Sou
|
||||||
if (PRIM->FST)
|
if (PRIM->FST)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (m_userHacks_HPO == 3)
|
if (GSConfig.UserHacks_HalfPixelOffset == 3)
|
||||||
{
|
{
|
||||||
if (!linear && t_position == 8)
|
if (!linear && t_position == 8)
|
||||||
{
|
{
|
||||||
|
@ -1202,7 +1193,7 @@ void GSRendererHW::RoundSpriteOffset()
|
||||||
|
|
||||||
void GSRendererHW::Draw()
|
void GSRendererHW::Draw()
|
||||||
{
|
{
|
||||||
if (m_dev->IsLost() || IsBadFrame())
|
if (IsBadFrame())
|
||||||
{
|
{
|
||||||
GL_INS("Warning skipping a draw call (%d)", s_n);
|
GL_INS("Warning skipping a draw call (%d)", s_n);
|
||||||
return;
|
return;
|
||||||
|
@ -1508,11 +1499,11 @@ void GSRendererHW::Draw()
|
||||||
{
|
{
|
||||||
const bool is_rt = t == rt;
|
const bool is_rt = t == rt;
|
||||||
t->m_texture = is_rt ?
|
t->m_texture = is_rt ?
|
||||||
m_dev->CreateSparseRenderTarget(new_w, new_h, tex->GetFormat()) :
|
g_gs_device->CreateSparseRenderTarget(new_w, new_h, tex->GetFormat()) :
|
||||||
m_dev->CreateSparseDepthStencil(new_w, new_h, tex->GetFormat());
|
g_gs_device->CreateSparseDepthStencil(new_w, new_h, tex->GetFormat());
|
||||||
const GSVector4i r{ 0, 0, w, h };
|
const GSVector4i r{ 0, 0, w, h };
|
||||||
m_dev->CopyRect(tex, t->m_texture, r);
|
g_gs_device->CopyRect(tex, t->m_texture, r);
|
||||||
m_dev->Recycle(tex);
|
g_gs_device->Recycle(tex);
|
||||||
t->m_texture->SetScale(up_s);
|
t->m_texture->SetScale(up_s);
|
||||||
(is_rt ? rt_tex : ds_tex) = t->m_texture;
|
(is_rt ? rt_tex : ds_tex) = t->m_texture;
|
||||||
}
|
}
|
||||||
|
@ -1832,11 +1823,11 @@ void GSRendererHW::OI_DoubleHalfClear(GSTexture* rt, GSTexture* ds)
|
||||||
{
|
{
|
||||||
// Only pure clear are supported for depth
|
// Only pure clear are supported for depth
|
||||||
ASSERT(color == 0);
|
ASSERT(color == 0);
|
||||||
m_dev->ClearDepth(t);
|
g_gs_device->ClearDepth(t);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_dev->ClearRenderTarget(t, color);
|
g_gs_device->ClearRenderTarget(t, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1949,15 +1940,15 @@ bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Sourc
|
||||||
// Do the blit. With a Copy mess to avoid issue with limited API (dx)
|
// Do the blit. With a Copy mess to avoid issue with limited API (dx)
|
||||||
// m_dev->StretchRect(tex->m_texture, sRect, tex->m_texture, dRect);
|
// m_dev->StretchRect(tex->m_texture, sRect, tex->m_texture, dRect);
|
||||||
const GSVector4i r_full(0, 0, tw, th);
|
const GSVector4i r_full(0, 0, tw, th);
|
||||||
if (GSTexture* rt = m_dev->CreateRenderTarget(tw, th, GSTexture::Format::Color))
|
if (GSTexture* rt = g_gs_device->CreateRenderTarget(tw, th, GSTexture::Format::Color))
|
||||||
{
|
{
|
||||||
m_dev->CopyRect(tex->m_texture, rt, r_full);
|
g_gs_device->CopyRect(tex->m_texture, rt, r_full);
|
||||||
|
|
||||||
m_dev->StretchRect(tex->m_texture, sRect, rt, dRect);
|
g_gs_device->StretchRect(tex->m_texture, sRect, rt, dRect);
|
||||||
|
|
||||||
m_dev->CopyRect(rt, tex->m_texture, r_full);
|
g_gs_device->CopyRect(rt, tex->m_texture, r_full);
|
||||||
|
|
||||||
m_dev->Recycle(rt);
|
g_gs_device->Recycle(rt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy back the texture into the GS mem. I don't know why but it will be
|
// Copy back the texture into the GS mem. I don't know why but it will be
|
||||||
|
@ -2077,9 +2068,9 @@ bool GSRendererHW::OI_FFXII(GSTexture* rt, GSTexture* ds, GSTextureCache::Source
|
||||||
// normally, this step would copy the video onto screen with 512 texture mapped horizontal lines,
|
// normally, this step would copy the video onto screen with 512 texture mapped horizontal lines,
|
||||||
// but we use the stored video data to create a new texture, and replace the lines with two triangles
|
// but we use the stored video data to create a new texture, and replace the lines with two triangles
|
||||||
|
|
||||||
m_dev->Recycle(t->m_texture);
|
g_gs_device->Recycle(t->m_texture);
|
||||||
|
|
||||||
t->m_texture = m_dev->CreateTexture(512, 512, GSTexture::Format::Color);
|
t->m_texture = g_gs_device->CreateTexture(512, 512, GSTexture::Format::Color);
|
||||||
|
|
||||||
t->m_texture->Update(GSVector4i(0, 0, 448, lines), video, 448 * 4);
|
t->m_texture->Update(GSVector4i(0, 0, 448, lines), video, 448 * 4);
|
||||||
|
|
||||||
|
@ -2120,7 +2111,7 @@ bool GSRendererHW::OI_FFX(GSTexture* rt, GSTexture* ds, GSTextureCache::Source*
|
||||||
GL_INS("OI_FFX ZB clear");
|
GL_INS("OI_FFX ZB clear");
|
||||||
if (ds)
|
if (ds)
|
||||||
ds->Commit(); // Don't bother to save few MB for a single game
|
ds->Commit(); // Don't bother to save few MB for a single game
|
||||||
m_dev->ClearDepth(ds);
|
g_gs_device->ClearDepth(ds);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -2172,7 +2163,7 @@ bool GSRendererHW::OI_RozenMaidenGebetGarden(GSTexture* rt, GSTexture* ds, GSTex
|
||||||
{
|
{
|
||||||
GL_INS("OI_RozenMaidenGebetGarden FB clear");
|
GL_INS("OI_RozenMaidenGebetGarden FB clear");
|
||||||
tmp_rt->m_texture->Commit(); // Don't bother to save few MB for a single game
|
tmp_rt->m_texture->Commit(); // Don't bother to save few MB for a single game
|
||||||
m_dev->ClearRenderTarget(tmp_rt->m_texture, 0);
|
g_gs_device->ClearRenderTarget(tmp_rt->m_texture, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -2191,7 +2182,7 @@ bool GSRendererHW::OI_RozenMaidenGebetGarden(GSTexture* rt, GSTexture* ds, GSTex
|
||||||
{
|
{
|
||||||
GL_INS("OI_RozenMaidenGebetGarden ZB clear");
|
GL_INS("OI_RozenMaidenGebetGarden ZB clear");
|
||||||
tmp_ds->m_texture->Commit(); // Don't bother to save few MB for a single game
|
tmp_ds->m_texture->Commit(); // Don't bother to save few MB for a single game
|
||||||
m_dev->ClearDepth(tmp_ds->m_texture);
|
g_gs_device->ClearDepth(tmp_ds->m_texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -2232,7 +2223,7 @@ bool GSRendererHW::OI_SonicUnleashed(GSTexture* rt, GSTexture* ds, GSTextureCach
|
||||||
const GSVector4 sRect(0, 0, 1, 1);
|
const GSVector4 sRect(0, 0, 1, 1);
|
||||||
const GSVector4 dRect(0, 0, size.x, size.y);
|
const GSVector4 dRect(0, 0, size.x, size.y);
|
||||||
|
|
||||||
m_dev->StretchRect(src->m_texture, sRect, rt, dRect, true, true, true, false);
|
g_gs_device->StretchRect(src->m_texture, sRect, rt, dRect, true, true, true, false);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2308,7 +2299,7 @@ bool GSRendererHW::OI_SuperManReturns(GSTexture* rt, GSTexture* ds, GSTextureCac
|
||||||
// Do a direct write
|
// Do a direct write
|
||||||
if (rt)
|
if (rt)
|
||||||
rt->Commit(); // Don't bother to save few MB for a single game
|
rt->Commit(); // Don't bother to save few MB for a single game
|
||||||
m_dev->ClearRenderTarget(rt, GSVector4(m_vt.m_min.c));
|
g_gs_device->ClearRenderTarget(rt, GSVector4(m_vt.m_min.c));
|
||||||
|
|
||||||
m_tc->InvalidateVideoMemType(GSTextureCache::DepthStencil, ctx->FRAME.Block());
|
m_tc->InvalidateVideoMemType(GSTextureCache::DepthStencil, ctx->FRAME.Block());
|
||||||
GL_INS("OI_SuperManReturns");
|
GL_INS("OI_SuperManReturns");
|
||||||
|
@ -2346,7 +2337,7 @@ bool GSRendererHW::OI_ArTonelico2(GSTexture* rt, GSTexture* ds, GSTextureCache::
|
||||||
GL_INS("OI_ArTonelico2");
|
GL_INS("OI_ArTonelico2");
|
||||||
if (ds)
|
if (ds)
|
||||||
ds->Commit(); // Don't bother to save few MB for a single game
|
ds->Commit(); // Don't bother to save few MB for a single game
|
||||||
m_dev->ClearDepth(ds);
|
g_gs_device->ClearDepth(ds);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -26,7 +26,6 @@ private:
|
||||||
int m_height;
|
int m_height;
|
||||||
int m_custom_width;
|
int m_custom_width;
|
||||||
int m_custom_height;
|
int m_custom_height;
|
||||||
bool m_reset;
|
|
||||||
int m_upscale_multiplier;
|
int m_upscale_multiplier;
|
||||||
int m_userhacks_ts_half_bottom;
|
int m_userhacks_ts_half_bottom;
|
||||||
|
|
||||||
|
@ -151,29 +150,26 @@ protected:
|
||||||
virtual void DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) = 0;
|
virtual void DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) = 0;
|
||||||
|
|
||||||
int m_userhacks_round_sprite_offset;
|
int m_userhacks_round_sprite_offset;
|
||||||
int m_userHacks_HPO;
|
|
||||||
bool m_userHacks_enabled_unscale_ptln;
|
bool m_userHacks_enabled_unscale_ptln;
|
||||||
|
|
||||||
bool m_userhacks_tcoffset;
|
bool m_userhacks_tcoffset;
|
||||||
float m_userhacks_tcoffset_x;
|
float m_userhacks_tcoffset_x;
|
||||||
float m_userhacks_tcoffset_y;
|
float m_userhacks_tcoffset_y;
|
||||||
|
|
||||||
bool m_accurate_date;
|
|
||||||
AccBlendLevel m_sw_blending;
|
|
||||||
|
|
||||||
bool m_channel_shuffle;
|
bool m_channel_shuffle;
|
||||||
|
bool m_reset;
|
||||||
|
|
||||||
GSVector2i m_lod; // Min & Max level of detail
|
GSVector2i m_lod; // Min & Max level of detail
|
||||||
void CustomResolutionScaling();
|
void CustomResolutionScaling();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GSRendererHW();
|
GSRendererHW();
|
||||||
virtual ~GSRendererHW();
|
virtual ~GSRendererHW() override;
|
||||||
|
|
||||||
void SetGameCRC(u32 crc, int options);
|
void SetGameCRC(u32 crc, int options) override;
|
||||||
bool CanUpscale();
|
bool CanUpscale() override;
|
||||||
int GetUpscaleMultiplier();
|
int GetUpscaleMultiplier() override;
|
||||||
GSVector2i GetCustomResolution();
|
GSVector2i GetCustomResolution() override;
|
||||||
void SetScaling();
|
void SetScaling();
|
||||||
void Lines2Sprites();
|
void Lines2Sprites();
|
||||||
void EmulateAtst(GSVector4& FogColor_AREF, u8& atst, const bool pass_2);
|
void EmulateAtst(GSVector4& FogColor_AREF, u8& atst, const bool pass_2);
|
||||||
|
@ -181,16 +177,16 @@ public:
|
||||||
GSVector4 RealignTargetTextureCoordinate(const GSTextureCache::Source* tex);
|
GSVector4 RealignTargetTextureCoordinate(const GSTextureCache::Source* tex);
|
||||||
GSVector4i ComputeBoundingBox(const GSVector2& rtscale, const GSVector2i& rtsize);
|
GSVector4i ComputeBoundingBox(const GSVector2& rtscale, const GSVector2i& rtsize);
|
||||||
void MergeSprite(GSTextureCache::Source* tex);
|
void MergeSprite(GSTextureCache::Source* tex);
|
||||||
GSVector2 GetTextureScaleFactor();
|
GSVector2 GetTextureScaleFactor() override;
|
||||||
|
|
||||||
void Reset();
|
void Reset() override;
|
||||||
void VSync(int field);
|
void VSync(u32 field) override;
|
||||||
void ResetDevice();
|
|
||||||
GSTexture* GetOutput(int i, int& y_offset);
|
GSTexture* GetOutput(int i, int& y_offset) override;
|
||||||
GSTexture* GetFeedbackOutput();
|
GSTexture* GetFeedbackOutput() override;
|
||||||
void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r);
|
void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r) override;
|
||||||
void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut = false);
|
void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut = false) override;
|
||||||
void Draw();
|
void Draw() override;
|
||||||
|
|
||||||
// Called by the texture cache to know if current texture is useful
|
// Called by the texture cache to know if current texture is useful
|
||||||
virtual bool IsDummyTexture() const { return false; }
|
virtual bool IsDummyTexture() const { return false; }
|
||||||
|
|
|
@ -18,12 +18,8 @@
|
||||||
#include "GS/GSGL.h"
|
#include "GS/GSGL.h"
|
||||||
|
|
||||||
GSRendererNew::GSRendererNew()
|
GSRendererNew::GSRendererNew()
|
||||||
|
: GSRendererHW()
|
||||||
{
|
{
|
||||||
if (theApp.GetConfigB("UserHacks"))
|
|
||||||
UserHacks_tri_filter = static_cast<TriFiltering>(theApp.GetConfigI("UserHacks_TriFilter"));
|
|
||||||
else
|
|
||||||
UserHacks_tri_filter = TriFiltering::None;
|
|
||||||
|
|
||||||
// Hope nothing requires too many draw calls.
|
// Hope nothing requires too many draw calls.
|
||||||
m_drawlist.reserve(2048);
|
m_drawlist.reserve(2048);
|
||||||
|
|
||||||
|
@ -33,19 +29,6 @@ GSRendererNew::GSRendererNew()
|
||||||
ResetStates();
|
ResetStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSRendererNew::CreateDevice(GSDevice* dev, const WindowInfo& wi)
|
|
||||||
{
|
|
||||||
if (!GSRendererHW::CreateDevice(dev, wi))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (dev->Features().texture_barrier)
|
|
||||||
m_sw_blending = static_cast<AccBlendLevel>(theApp.GetConfigI("accurate_blending_unit"));
|
|
||||||
else
|
|
||||||
m_sw_blending = static_cast<AccBlendLevel>(theApp.GetConfigI("accurate_blending_unit_d3d11"));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSRendererNew::SetupIA(const float& sx, const float& sy)
|
void GSRendererNew::SetupIA(const float& sx, const float& sy)
|
||||||
{
|
{
|
||||||
GL_PUSH("IA");
|
GL_PUSH("IA");
|
||||||
|
@ -56,7 +39,7 @@ void GSRendererNew::SetupIA(const float& sx, const float& sy)
|
||||||
m_vertex.buff[i].UV &= 0x3FEF3FEF;
|
m_vertex.buff[i].UV &= 0x3FEF3FEF;
|
||||||
}
|
}
|
||||||
const bool unscale_pt_ln = m_userHacks_enabled_unscale_ptln && (GetUpscaleMultiplier() != 1);
|
const bool unscale_pt_ln = m_userHacks_enabled_unscale_ptln && (GetUpscaleMultiplier() != 1);
|
||||||
const GSDevice::FeatureSupport features = m_dev->Features();
|
const GSDevice::FeatureSupport features = g_gs_device->Features();
|
||||||
|
|
||||||
switch (m_vt.m_primclass)
|
switch (m_vt.m_primclass)
|
||||||
{
|
{
|
||||||
|
@ -117,7 +100,7 @@ void GSRendererNew::SetupIA(const float& sx, const float& sy)
|
||||||
// the extra validation cost of the extra stage.
|
// the extra validation cost of the extra stage.
|
||||||
//
|
//
|
||||||
// Note: keep Geometry Shader in the replayer to ease debug.
|
// Note: keep Geometry Shader in the replayer to ease debug.
|
||||||
if (m_dev->Features().geometry_shader && !m_vt.m_accurate_stq && (m_vertex.next > 32 || GLLoader::in_replayer)) // <=> 16 sprites (based on Shadow Hearts)
|
if (g_gs_device->Features().geometry_shader && !m_vt.m_accurate_stq && (m_vertex.next > 32 || GLLoader::in_replayer)) // <=> 16 sprites (based on Shadow Hearts)
|
||||||
{
|
{
|
||||||
m_conf.gs.expand = true;
|
m_conf.gs.expand = true;
|
||||||
|
|
||||||
|
@ -200,9 +183,9 @@ void GSRendererNew::EmulateTextureShuffleAndFbmask()
|
||||||
// m_texture_shuffle = false;
|
// m_texture_shuffle = false;
|
||||||
|
|
||||||
bool enable_fbmask_emulation = false;
|
bool enable_fbmask_emulation = false;
|
||||||
if (m_dev->Features().texture_barrier)
|
if (g_gs_device->Features().texture_barrier)
|
||||||
{
|
{
|
||||||
enable_fbmask_emulation = m_sw_blending != AccBlendLevel::None;
|
enable_fbmask_emulation = GSConfig.AccurateBlendingUnit != AccBlendLevel::Minimum;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -211,7 +194,7 @@ void GSRendererNew::EmulateTextureShuffleAndFbmask()
|
||||||
// 1. D3D sucks.
|
// 1. D3D sucks.
|
||||||
// 2. FB copy is slow, especially on triangle primitives which is unplayable with some games.
|
// 2. FB copy is slow, especially on triangle primitives which is unplayable with some games.
|
||||||
// 3. SW blending isn't implemented yet.
|
// 3. SW blending isn't implemented yet.
|
||||||
switch (m_sw_blending)
|
switch (GSConfig.AccurateBlendingUnit)
|
||||||
{
|
{
|
||||||
case AccBlendLevel::Ultra:
|
case AccBlendLevel::Ultra:
|
||||||
case AccBlendLevel::Full:
|
case AccBlendLevel::Full:
|
||||||
|
@ -231,7 +214,7 @@ void GSRendererNew::EmulateTextureShuffleAndFbmask()
|
||||||
// Also exclude fbmask emulation on texture shuffle just in case, it is probably safe tho.
|
// Also exclude fbmask emulation on texture shuffle just in case, it is probably safe tho.
|
||||||
enable_fbmask_emulation = (!m_texture_shuffle && (m_vt.m_primclass != GS_TRIANGLE_CLASS) && (m_context->FRAME.FBMSK != 0x80000000));
|
enable_fbmask_emulation = (!m_texture_shuffle && (m_vt.m_primclass != GS_TRIANGLE_CLASS) && (m_context->FRAME.FBMSK != 0x80000000));
|
||||||
break;
|
break;
|
||||||
case AccBlendLevel::None:
|
case AccBlendLevel::Minimum:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -249,7 +232,7 @@ void GSRendererNew::EmulateTextureShuffleAndFbmask()
|
||||||
// If date is enabled you need to test the green channel instead of the
|
// If date is enabled you need to test the green channel instead of the
|
||||||
// alpha channel. Only enable this code in DATE mode to reduce the number
|
// alpha channel. Only enable this code in DATE mode to reduce the number
|
||||||
// of shader.
|
// of shader.
|
||||||
m_conf.ps.write_rg = !write_ba && m_dev->Features().texture_barrier && m_context->TEST.DATE;
|
m_conf.ps.write_rg = !write_ba && g_gs_device->Features().texture_barrier && m_context->TEST.DATE;
|
||||||
|
|
||||||
m_conf.ps.read_ba = read_ba;
|
m_conf.ps.read_ba = read_ba;
|
||||||
|
|
||||||
|
@ -303,7 +286,7 @@ void GSRendererNew::EmulateTextureShuffleAndFbmask()
|
||||||
m_conf.cb_ps.FbMask.a = ba_mask;
|
m_conf.cb_ps.FbMask.a = ba_mask;
|
||||||
|
|
||||||
// No blending so hit unsafe path.
|
// No blending so hit unsafe path.
|
||||||
if (!PRIM->ABE || !m_dev->Features().texture_barrier)
|
if (!PRIM->ABE || !g_gs_device->Features().texture_barrier)
|
||||||
{
|
{
|
||||||
GL_INS("FBMASK Unsafe SW emulated fb_mask:%x on tex shuffle", fbmask);
|
GL_INS("FBMASK Unsafe SW emulated fb_mask:%x on tex shuffle", fbmask);
|
||||||
m_conf.require_one_barrier = true;
|
m_conf.require_one_barrier = true;
|
||||||
|
@ -354,7 +337,7 @@ void GSRendererNew::EmulateTextureShuffleAndFbmask()
|
||||||
have been invalidated before subsequent Draws are executed.
|
have been invalidated before subsequent Draws are executed.
|
||||||
*/
|
*/
|
||||||
// No blending so hit unsafe path.
|
// No blending so hit unsafe path.
|
||||||
if (!PRIM->ABE || !(~ff_fbmask & ~zero_fbmask & 0x7) || !m_dev->Features().texture_barrier)
|
if (!PRIM->ABE || !(~ff_fbmask & ~zero_fbmask & 0x7) || !g_gs_device->Features().texture_barrier)
|
||||||
{
|
{
|
||||||
GL_INS("FBMASK Unsafe SW emulated fb_mask:%x on %d bits format", m_context->FRAME.FBMSK,
|
GL_INS("FBMASK Unsafe SW emulated fb_mask:%x on %d bits format", m_context->FRAME.FBMSK,
|
||||||
(GSLocalMemory::m_psm[m_context->FRAME.PSM].fmt == 2) ? 16 : 32);
|
(GSLocalMemory::m_psm[m_context->FRAME.PSM].fmt == 2) ? 16 : 32);
|
||||||
|
@ -504,7 +487,7 @@ void GSRendererNew::EmulateChannelShuffle(GSTexture** rt, const GSTextureCache::
|
||||||
if (m_channel_shuffle)
|
if (m_channel_shuffle)
|
||||||
{
|
{
|
||||||
m_conf.raw_tex = tex->m_from_target;
|
m_conf.raw_tex = tex->m_from_target;
|
||||||
if (m_dev->Features().texture_barrier)
|
if (g_gs_device->Features().texture_barrier)
|
||||||
m_conf.require_one_barrier = true;
|
m_conf.require_one_barrier = true;
|
||||||
|
|
||||||
// Replace current draw with a fullscreen sprite
|
// Replace current draw with a fullscreen sprite
|
||||||
|
@ -545,7 +528,7 @@ void GSRendererNew::EmulateBlending(bool& DATE_GL42, bool& DATE_GL45)
|
||||||
// Compute the blending equation to detect special case
|
// Compute the blending equation to detect special case
|
||||||
const GIFRegALPHA& ALPHA = m_context->ALPHA;
|
const GIFRegALPHA& ALPHA = m_context->ALPHA;
|
||||||
u8 blend_index = u8(((ALPHA.A * 3 + ALPHA.B) * 3 + ALPHA.C) * 3 + ALPHA.D);
|
u8 blend_index = u8(((ALPHA.A * 3 + ALPHA.B) * 3 + ALPHA.C) * 3 + ALPHA.D);
|
||||||
const int blend_flag = m_dev->GetBlendFlags(blend_index);
|
const int blend_flag = g_gs_device->GetBlendFlags(blend_index);
|
||||||
|
|
||||||
// Do the multiplication in shader for blending accumulation: Cs*As + Cd or Cs*Af + Cd
|
// Do the multiplication in shader for blending accumulation: Cs*As + Cd or Cs*Af + Cd
|
||||||
bool accumulation_blend = !!(blend_flag & BLEND_ACCU);
|
bool accumulation_blend = !!(blend_flag & BLEND_ACCU);
|
||||||
|
@ -569,9 +552,9 @@ void GSRendererNew::EmulateBlending(bool& DATE_GL42, bool& DATE_GL45)
|
||||||
// Warning no break on purpose
|
// Warning no break on purpose
|
||||||
// Note: the [[fallthrough]] attribute tell compilers not to complain about not having breaks.
|
// Note: the [[fallthrough]] attribute tell compilers not to complain about not having breaks.
|
||||||
bool sw_blending = false;
|
bool sw_blending = false;
|
||||||
if (m_dev->Features().texture_barrier)
|
if (g_gs_device->Features().texture_barrier)
|
||||||
{
|
{
|
||||||
switch (m_sw_blending)
|
switch (GSConfig.AccurateBlendingUnit)
|
||||||
{
|
{
|
||||||
case AccBlendLevel::Ultra:
|
case AccBlendLevel::Ultra:
|
||||||
sw_blending |= true;
|
sw_blending |= true;
|
||||||
|
@ -591,18 +574,18 @@ void GSRendererNew::EmulateBlending(bool& DATE_GL42, bool& DATE_GL45)
|
||||||
case AccBlendLevel::Basic:
|
case AccBlendLevel::Basic:
|
||||||
sw_blending |= impossible_or_free_blend;
|
sw_blending |= impossible_or_free_blend;
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case AccBlendLevel::None:
|
case AccBlendLevel::Minimum:
|
||||||
/*sw_blending |= accumulation_blend*/;
|
/*sw_blending |= accumulation_blend*/;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (static_cast<u8>(m_sw_blending) >= static_cast<u8>(AccBlendLevel::Basic))
|
if (static_cast<u8>(GSConfig.AccurateBlendingUnit) >= static_cast<u8>(AccBlendLevel::Basic))
|
||||||
sw_blending |= accumulation_blend || blend_non_recursive;
|
sw_blending |= accumulation_blend || blend_non_recursive;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not run BLEND MIX if sw blending is already present, it's less accurate
|
// Do not run BLEND MIX if sw blending is already present, it's less accurate
|
||||||
if (m_sw_blending != AccBlendLevel::None)
|
if (GSConfig.AccurateBlendingUnit != AccBlendLevel::Minimum)
|
||||||
{
|
{
|
||||||
blend_mix &= !sw_blending;
|
blend_mix &= !sw_blending;
|
||||||
sw_blending |= blend_mix;
|
sw_blending |= blend_mix;
|
||||||
|
@ -615,7 +598,7 @@ void GSRendererNew::EmulateBlending(bool& DATE_GL42, bool& DATE_GL45)
|
||||||
// fixes shadows in Superman shadows of Apokolips.
|
// fixes shadows in Superman shadows of Apokolips.
|
||||||
const bool sw_fbmask_colclip = !m_conf.require_one_barrier && m_conf.ps.fbmask;
|
const bool sw_fbmask_colclip = !m_conf.require_one_barrier && m_conf.ps.fbmask;
|
||||||
bool free_colclip = false;
|
bool free_colclip = false;
|
||||||
if (m_dev->Features().texture_barrier)
|
if (g_gs_device->Features().texture_barrier)
|
||||||
free_colclip = m_prim_overlap == PRIM_OVERLAP_NO || blend_non_recursive || sw_fbmask_colclip;
|
free_colclip = m_prim_overlap == PRIM_OVERLAP_NO || blend_non_recursive || sw_fbmask_colclip;
|
||||||
else
|
else
|
||||||
free_colclip = blend_non_recursive;
|
free_colclip = blend_non_recursive;
|
||||||
|
@ -637,7 +620,7 @@ void GSRendererNew::EmulateBlending(bool& DATE_GL42, bool& DATE_GL45)
|
||||||
m_conf.ps.hdr = 1;
|
m_conf.ps.hdr = 1;
|
||||||
sw_blending = true; // Enable sw blending for the HDR algo
|
sw_blending = true; // Enable sw blending for the HDR algo
|
||||||
}
|
}
|
||||||
else if (sw_blending && m_dev->Features().texture_barrier)
|
else if (sw_blending && g_gs_device->Features().texture_barrier)
|
||||||
{
|
{
|
||||||
// A slow algo that could requires several passes (barely used)
|
// A slow algo that could requires several passes (barely used)
|
||||||
GL_INS("COLCLIP SW mode ENABLED");
|
GL_INS("COLCLIP SW mode ENABLED");
|
||||||
|
@ -658,7 +641,7 @@ void GSRendererNew::EmulateBlending(bool& DATE_GL42, bool& DATE_GL45)
|
||||||
if (sw_blending)
|
if (sw_blending)
|
||||||
{
|
{
|
||||||
GL_INS("PABE mode ENABLED");
|
GL_INS("PABE mode ENABLED");
|
||||||
if (m_dev->Features().texture_barrier)
|
if (g_gs_device->Features().texture_barrier)
|
||||||
{
|
{
|
||||||
// Disable hw/sw blend and do pure sw blend with reading the framebuffer.
|
// Disable hw/sw blend and do pure sw blend with reading the framebuffer.
|
||||||
accumulation_blend = false;
|
accumulation_blend = false;
|
||||||
|
@ -753,7 +736,7 @@ void GSRendererNew::EmulateBlending(bool& DATE_GL42, bool& DATE_GL45)
|
||||||
m_conf.require_full_barrier |= !blend_non_recursive;
|
m_conf.require_full_barrier |= !blend_non_recursive;
|
||||||
|
|
||||||
// Only BLEND_NO_REC should hit this code path for now
|
// Only BLEND_NO_REC should hit this code path for now
|
||||||
if (!m_dev->Features().texture_barrier)
|
if (!g_gs_device->Features().texture_barrier)
|
||||||
ASSERT(blend_non_recursive);
|
ASSERT(blend_non_recursive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -795,7 +778,7 @@ void GSRendererNew::EmulateTextureSampler(const GSTextureCache::Source* tex)
|
||||||
bool bilinear = m_vt.IsLinear();
|
bool bilinear = m_vt.IsLinear();
|
||||||
int trilinear = 0;
|
int trilinear = 0;
|
||||||
bool trilinear_auto = false;
|
bool trilinear_auto = false;
|
||||||
switch (UserHacks_tri_filter)
|
switch (GSConfig.UserHacks_TriFilter)
|
||||||
{
|
{
|
||||||
case TriFiltering::Forced:
|
case TriFiltering::Forced:
|
||||||
trilinear = static_cast<u8>(GS_MIN_FILTER::Linear_Mipmap_Linear);
|
trilinear = static_cast<u8>(GS_MIN_FILTER::Linear_Mipmap_Linear);
|
||||||
|
@ -810,7 +793,7 @@ void GSRendererNew::EmulateTextureSampler(const GSTextureCache::Source* tex)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TriFiltering::None:
|
case TriFiltering::Off:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -936,7 +919,7 @@ void GSRendererNew::EmulateTextureSampler(const GSTextureCache::Source* tex)
|
||||||
m_conf.ps.tcc = m_context->TEX0.TCC;
|
m_conf.ps.tcc = m_context->TEX0.TCC;
|
||||||
|
|
||||||
m_conf.ps.ltf = bilinear && shader_emulated_sampler;
|
m_conf.ps.ltf = bilinear && shader_emulated_sampler;
|
||||||
m_conf.ps.point_sampler = m_dev->Features().broken_point_sampler && (!bilinear || shader_emulated_sampler);
|
m_conf.ps.point_sampler = g_gs_device->Features().broken_point_sampler && (!bilinear || shader_emulated_sampler);
|
||||||
|
|
||||||
const int w = tex->m_texture->GetWidth();
|
const int w = tex->m_texture->GetWidth();
|
||||||
const int h = tex->m_texture->GetHeight();
|
const int h = tex->m_texture->GetHeight();
|
||||||
|
@ -1200,7 +1183,7 @@ void GSRendererNew::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
|
||||||
m_conf.cb_vs.texture_offset = GSVector2(0, 0);
|
m_conf.cb_vs.texture_offset = GSVector2(0, 0);
|
||||||
m_conf.ps.scanmsk = m_env.SCANMSK.MSK;
|
m_conf.ps.scanmsk = m_env.SCANMSK.MSK;
|
||||||
|
|
||||||
ASSERT(m_dev != NULL);
|
ASSERT(g_gs_device != NULL);
|
||||||
|
|
||||||
// HLE implementation of the channel selection effect
|
// HLE implementation of the channel selection effect
|
||||||
//
|
//
|
||||||
|
@ -1213,13 +1196,13 @@ void GSRendererNew::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
|
||||||
MergeSprite(tex);
|
MergeSprite(tex);
|
||||||
|
|
||||||
// Always check if primitive overlap as it is used in plenty of effects.
|
// Always check if primitive overlap as it is used in plenty of effects.
|
||||||
if (m_dev->Features().texture_barrier)
|
if (g_gs_device->Features().texture_barrier)
|
||||||
m_prim_overlap = PrimitiveOverlap();
|
m_prim_overlap = PrimitiveOverlap();
|
||||||
else
|
else
|
||||||
m_prim_overlap = PRIM_OVERLAP_UNKNOW; // Prim overlap check is useless without texture barrier
|
m_prim_overlap = PRIM_OVERLAP_UNKNOW; // Prim overlap check is useless without texture barrier
|
||||||
|
|
||||||
// Detect framebuffer read that will need special handling
|
// Detect framebuffer read that will need special handling
|
||||||
if (m_dev->Features().texture_barrier && (m_context->FRAME.Block() == m_context->TEX0.TBP0) && PRIM->TME && m_sw_blending != AccBlendLevel::None)
|
if (g_gs_device->Features().texture_barrier && (m_context->FRAME.Block() == m_context->TEX0.TBP0) && PRIM->TME && GSConfig.AccurateBlendingUnit != AccBlendLevel::Minimum)
|
||||||
{
|
{
|
||||||
if ((m_context->FRAME.FBMSK == 0x00FFFFFF) && (m_vt.m_primclass == GS_TRIANGLE_CLASS))
|
if ((m_context->FRAME.FBMSK == 0x00FFFFFF) && (m_vt.m_primclass == GS_TRIANGLE_CLASS))
|
||||||
{
|
{
|
||||||
|
@ -1248,7 +1231,7 @@ void GSRendererNew::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
|
||||||
// It is way too complex to emulate texture shuffle with DATE. So just use
|
// It is way too complex to emulate texture shuffle with DATE. So just use
|
||||||
// the slow but accurate algo
|
// the slow but accurate algo
|
||||||
GL_PERF("DATE: With %s", m_texture_shuffle ? "texture shuffle" : "no prim overlap");
|
GL_PERF("DATE: With %s", m_texture_shuffle ? "texture shuffle" : "no prim overlap");
|
||||||
if (m_dev->Features().texture_barrier)
|
if (g_gs_device->Features().texture_barrier)
|
||||||
{
|
{
|
||||||
m_conf.require_full_barrier = true;
|
m_conf.require_full_barrier = true;
|
||||||
DATE_GL45 = true;
|
DATE_GL45 = true;
|
||||||
|
@ -1276,21 +1259,21 @@ void GSRendererNew::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
|
||||||
// texture barrier will split the draw call into n draw call. It is very efficient for
|
// texture barrier will split the draw call into n draw call. It is very efficient for
|
||||||
// few primitive draws. Otherwise it sucks.
|
// few primitive draws. Otherwise it sucks.
|
||||||
GL_PERF("DATE: Slow with alpha %d-%d", GetAlphaMinMax().min, GetAlphaMinMax().max);
|
GL_PERF("DATE: Slow with alpha %d-%d", GetAlphaMinMax().min, GetAlphaMinMax().max);
|
||||||
if (m_dev->Features().texture_barrier)
|
if (g_gs_device->Features().texture_barrier)
|
||||||
{
|
{
|
||||||
m_conf.require_full_barrier = true;
|
m_conf.require_full_barrier = true;
|
||||||
DATE_GL45 = true;
|
DATE_GL45 = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (m_accurate_date)
|
else if (GSConfig.AccurateDATE)
|
||||||
{
|
{
|
||||||
// Note: Fast level (DATE_one) was removed as it's less accurate.
|
// Note: Fast level (DATE_one) was removed as it's less accurate.
|
||||||
GL_PERF("DATE: Full AD with alpha %d-%d", GetAlphaMinMax().min, GetAlphaMinMax().max);
|
GL_PERF("DATE: Full AD with alpha %d-%d", GetAlphaMinMax().min, GetAlphaMinMax().max);
|
||||||
if (m_dev->Features().image_load_store)
|
if (g_gs_device->Features().image_load_store)
|
||||||
{
|
{
|
||||||
DATE_GL42 = true;
|
DATE_GL42 = true;
|
||||||
}
|
}
|
||||||
else if (m_dev->Features().texture_barrier)
|
else if (g_gs_device->Features().texture_barrier)
|
||||||
{
|
{
|
||||||
m_conf.require_full_barrier = true;
|
m_conf.require_full_barrier = true;
|
||||||
DATE_GL45 = true;
|
DATE_GL45 = true;
|
||||||
|
@ -1371,7 +1354,7 @@ void GSRendererNew::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
|
||||||
//The resulting shifted output aligns better with common blending / corona / blurring effects,
|
//The resulting shifted output aligns better with common blending / corona / blurring effects,
|
||||||
//but introduces a few bad pixels on the edges.
|
//but introduces a few bad pixels on the edges.
|
||||||
|
|
||||||
if (rt && rt->LikelyOffset && m_userHacks_HPO == 1)
|
if (rt && rt->LikelyOffset && GSConfig.UserHacks_HalfPixelOffset == 1)
|
||||||
{
|
{
|
||||||
ox2 *= rt->OffsetHack_modx;
|
ox2 *= rt->OffsetHack_modx;
|
||||||
oy2 *= rt->OffsetHack_mody;
|
oy2 *= rt->OffsetHack_mody;
|
||||||
|
@ -1392,7 +1375,7 @@ void GSRendererNew::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
|
||||||
}
|
}
|
||||||
else if (DATE_one)
|
else if (DATE_one)
|
||||||
{
|
{
|
||||||
if (m_dev->Features().texture_barrier)
|
if (g_gs_device->Features().texture_barrier)
|
||||||
{
|
{
|
||||||
m_conf.require_one_barrier = true;
|
m_conf.require_one_barrier = true;
|
||||||
m_conf.ps.date = 5 + m_context->TEST.DATM;
|
m_conf.ps.date = 5 + m_context->TEST.DATM;
|
||||||
|
@ -1409,13 +1392,13 @@ void GSRendererNew::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
|
||||||
}
|
}
|
||||||
|
|
||||||
m_conf.ps.fba = m_context->FBA.FBA;
|
m_conf.ps.fba = m_context->FBA.FBA;
|
||||||
m_conf.ps.dither = m_dithering > 0 && m_conf.ps.dfmt == 2 && m_env.DTHE.DTHE;
|
m_conf.ps.dither = GSConfig.Dithering > 0 && m_conf.ps.dfmt == 2 && m_env.DTHE.DTHE;
|
||||||
|
|
||||||
if (m_conf.ps.dither)
|
if (m_conf.ps.dither)
|
||||||
{
|
{
|
||||||
GL_DBG("DITHERING mode ENABLED (%d)", m_dithering);
|
GL_DBG("DITHERING mode ENABLED (%d)", m_dithering);
|
||||||
|
|
||||||
m_conf.ps.dither = m_dithering;
|
m_conf.ps.dither = GSConfig.Dithering;
|
||||||
m_conf.cb_ps.DitherMatrix[0] = GSVector4(m_env.DIMX.DM00, m_env.DIMX.DM01, m_env.DIMX.DM02, m_env.DIMX.DM03);
|
m_conf.cb_ps.DitherMatrix[0] = GSVector4(m_env.DIMX.DM00, m_env.DIMX.DM01, m_env.DIMX.DM02, m_env.DIMX.DM03);
|
||||||
m_conf.cb_ps.DitherMatrix[1] = GSVector4(m_env.DIMX.DM10, m_env.DIMX.DM11, m_env.DIMX.DM12, m_env.DIMX.DM13);
|
m_conf.cb_ps.DitherMatrix[1] = GSVector4(m_env.DIMX.DM10, m_env.DIMX.DM11, m_env.DIMX.DM12, m_env.DIMX.DM13);
|
||||||
m_conf.cb_ps.DitherMatrix[2] = GSVector4(m_env.DIMX.DM20, m_env.DIMX.DM21, m_env.DIMX.DM22, m_env.DIMX.DM23);
|
m_conf.cb_ps.DitherMatrix[2] = GSVector4(m_env.DIMX.DM20, m_env.DIMX.DM21, m_env.DIMX.DM22, m_env.DIMX.DM23);
|
||||||
|
@ -1619,12 +1602,12 @@ void GSRendererNew::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
|
||||||
|
|
||||||
m_conf.rt = rt;
|
m_conf.rt = rt;
|
||||||
m_conf.ds = ds;
|
m_conf.ds = ds;
|
||||||
m_dev->RenderHW(m_conf);
|
g_gs_device->RenderHW(m_conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSRendererNew::IsDummyTexture() const
|
bool GSRendererNew::IsDummyTexture() const
|
||||||
{
|
{
|
||||||
// Texture is actually the frame buffer. Stencil emulation to compute shadow (Jak series/tri-ace game)
|
// Texture is actually the frame buffer. Stencil emulation to compute shadow (Jak series/tri-ace game)
|
||||||
// Will hit the "m_ps_sel.tex_is_fb = 1" path in the draw
|
// Will hit the "m_ps_sel.tex_is_fb = 1" path in the draw
|
||||||
return m_dev->Features().texture_barrier && (m_context->FRAME.Block() == m_context->TEX0.TBP0) && PRIM->TME && m_sw_blending != AccBlendLevel::None && m_vt.m_primclass == GS_TRIANGLE_CLASS && (m_context->FRAME.FBMSK == 0x00FFFFFF);
|
return g_gs_device->Features().texture_barrier && (m_context->FRAME.Block() == m_context->TEX0.TBP0) && PRIM->TME && GSConfig.AccurateBlendingUnit != AccBlendLevel::Minimum && m_vt.m_primclass == GS_TRIANGLE_CLASS && (m_context->FRAME.FBMSK == 0x00FFFFFF);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,6 @@ private:
|
||||||
PRIM_OVERLAP m_prim_overlap;
|
PRIM_OVERLAP m_prim_overlap;
|
||||||
std::vector<size_t> m_drawlist;
|
std::vector<size_t> m_drawlist;
|
||||||
|
|
||||||
TriFiltering UserHacks_tri_filter;
|
|
||||||
|
|
||||||
GSHWDrawConfig m_conf;
|
GSHWDrawConfig m_conf;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -49,7 +47,6 @@ public:
|
||||||
GSRendererNew();
|
GSRendererNew();
|
||||||
~GSRendererNew() override {}
|
~GSRendererNew() override {}
|
||||||
|
|
||||||
bool CreateDevice(GSDevice* dev, const WindowInfo& wi) override;
|
|
||||||
void DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) override;
|
void DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) override;
|
||||||
|
|
||||||
PRIM_OVERLAP PrimitiveOverlap();
|
PRIM_OVERLAP PrimitiveOverlap();
|
||||||
|
|
|
@ -49,9 +49,6 @@ GSTextureCache::GSTextureCache(GSRenderer* r)
|
||||||
}
|
}
|
||||||
|
|
||||||
m_paltex = theApp.GetConfigB("paltex");
|
m_paltex = theApp.GetConfigB("paltex");
|
||||||
m_crc_hack_level = theApp.GetConfigT<CRCHackLevel>("crc_hack_level");
|
|
||||||
if (m_crc_hack_level == CRCHackLevel::Automatic)
|
|
||||||
m_crc_hack_level = GSUtil::GetRecommendedCRCHackLevel(theApp.GetCurrentRendererType());
|
|
||||||
|
|
||||||
// In theory 4MB is enough but 9MB is safer for overflow (8MB
|
// In theory 4MB is enough but 9MB is safer for overflow (8MB
|
||||||
// isn't enough in custom resolution)
|
// isn't enough in custom resolution)
|
||||||
|
@ -191,7 +188,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
|
||||||
GL_CACHE("TC depth: ERROR miss (0x%x, %s)", TEX0.TBP0, psm_str(psm));
|
GL_CACHE("TC depth: ERROR miss (0x%x, %s)", TEX0.TBP0, psm_str(psm));
|
||||||
// Possible ? In this case we could call LookupSource
|
// Possible ? In this case we could call LookupSource
|
||||||
// Or just put a basic texture
|
// Or just put a basic texture
|
||||||
// src->m_texture = m_renderer->m_dev->CreateTexture(tw, th);
|
// src->m_texture = g_gs_device->CreateTexture(tw, th);
|
||||||
// In all cases rendering will be broken
|
// In all cases rendering will be broken
|
||||||
//
|
//
|
||||||
// Note: might worth to check previous frame
|
// Note: might worth to check previous frame
|
||||||
|
@ -593,7 +590,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, int
|
||||||
GL_CACHE("TC: Lookup Target(Color) %dx%d, hit Depth (0x%x, %s was %s)", new_w, new_h, bp, psm_str(TEX0.PSM), psm_str(dst_match->m_TEX0.PSM));
|
GL_CACHE("TC: Lookup Target(Color) %dx%d, hit Depth (0x%x, %s was %s)", new_w, new_h, bp, psm_str(TEX0.PSM), psm_str(dst_match->m_TEX0.PSM));
|
||||||
shader = (fmt_16_bits) ? ShaderConvert::FLOAT16_TO_RGB5A1 : ShaderConvert::FLOAT32_TO_RGBA8;
|
shader = (fmt_16_bits) ? ShaderConvert::FLOAT16_TO_RGB5A1 : ShaderConvert::FLOAT32_TO_RGBA8;
|
||||||
}
|
}
|
||||||
m_renderer->m_dev->StretchRect(dst_match->m_texture, sRect, dst->m_texture, dRect, shader, false);
|
g_gs_device->StretchRect(dst_match->m_texture, sRect, dst->m_texture, dRect, shader, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,8 +629,8 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, int
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_OGL_DEBUG
|
#ifdef ENABLE_OGL_DEBUG
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case RenderTarget: m_renderer->m_dev->ClearRenderTarget(dst->m_texture, 0); break;
|
case RenderTarget: g_gs_device->ClearRenderTarget(dst->m_texture, 0); break;
|
||||||
case DepthStencil: m_renderer->m_dev->ClearDepth(dst->m_texture); break;
|
case DepthStencil: g_gs_device->ClearDepth(dst->m_texture); break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -738,7 +735,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, int
|
||||||
dst = CreateTarget(TEX0, w, h, RenderTarget);
|
dst = CreateTarget(TEX0, w, h, RenderTarget);
|
||||||
ScaleTexture(dst->m_texture);
|
ScaleTexture(dst->m_texture);
|
||||||
|
|
||||||
m_renderer->m_dev->ClearRenderTarget(dst->m_texture, 0); // new frame buffers after reset should be cleared, don't display memory garbage
|
g_gs_device->ClearRenderTarget(dst->m_texture, 0); // new frame buffers after reset should be cleared, don't display memory garbage
|
||||||
|
|
||||||
if (m_preload_frame)
|
if (m_preload_frame)
|
||||||
{
|
{
|
||||||
|
@ -857,7 +854,7 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
|
||||||
GL_CACHE("TC: Clear Sub Target(%s) %d (0x%x)", to_string(type),
|
GL_CACHE("TC: Clear Sub Target(%s) %d (0x%x)", to_string(type),
|
||||||
t->m_texture ? t->m_texture->GetID() : 0,
|
t->m_texture ? t->m_texture->GetID() : 0,
|
||||||
t->m_TEX0.TBP0);
|
t->m_TEX0.TBP0);
|
||||||
m_renderer->m_dev->ClearRenderTarget(t->m_texture, 0);
|
g_gs_device->ClearRenderTarget(t->m_texture, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1037,6 +1034,12 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
|
||||||
u32 psm = off.psm();
|
u32 psm = off.psm();
|
||||||
//u32 bw = off->bw;
|
//u32 bw = off->bw;
|
||||||
|
|
||||||
|
if (GSConfig.HWDisableReadbacks)
|
||||||
|
{
|
||||||
|
Console.Error("Skipping readback of %ux%u @ %u,%u", r.width(), r.height(), r.left, r.top);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// No depth handling please.
|
// No depth handling please.
|
||||||
if (psm == PSM_PSMZ32 || psm == PSM_PSMZ24 || psm == PSM_PSMZ16 || psm == PSM_PSMZ16S)
|
if (psm == PSM_PSMZ32 || psm == PSM_PSMZ24 || psm == PSM_PSMZ16 || psm == PSM_PSMZ16S)
|
||||||
{
|
{
|
||||||
|
@ -1294,10 +1297,10 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||||
int h = (int)(scale.y * th);
|
int h = (int)(scale.y * th);
|
||||||
|
|
||||||
GSTexture* sTex = dst->m_texture;
|
GSTexture* sTex = dst->m_texture;
|
||||||
GSTexture* dTex = m_renderer->m_dev->CreateRenderTarget(w, h, GSTexture::Format::Color);
|
GSTexture* dTex = g_gs_device->CreateRenderTarget(w, h, GSTexture::Format::Color);
|
||||||
|
|
||||||
GSVector4i area(x, y, x + w, y + h);
|
GSVector4i area(x, y, x + w, y + h);
|
||||||
m_renderer->m_dev->CopyRect(sTex, dTex, area);
|
g_gs_device->CopyRect(sTex, dTex, area);
|
||||||
|
|
||||||
// Keep a trace of origin of the texture
|
// Keep a trace of origin of the texture
|
||||||
src->m_texture = dTex;
|
src->m_texture = dTex;
|
||||||
|
@ -1326,7 +1329,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||||
// So it could be tricky to put in the middle of the DrawPrims
|
// So it could be tricky to put in the middle of the DrawPrims
|
||||||
|
|
||||||
// Texture is created to keep code compatibility
|
// Texture is created to keep code compatibility
|
||||||
GSTexture* dTex = m_renderer->m_dev->CreateRenderTarget(tw, th, GSTexture::Format::Color);
|
GSTexture* dTex = g_gs_device->CreateRenderTarget(tw, th, GSTexture::Format::Color);
|
||||||
|
|
||||||
// Keep a trace of origin of the texture
|
// Keep a trace of origin of the texture
|
||||||
src->m_texture = dTex;
|
src->m_texture = dTex;
|
||||||
|
@ -1401,7 +1404,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||||
|
|
||||||
//ASSERT(dst->m_TEX0.TBW > TEX0.TBW); // otherwise scale.x need to be reduced to make the larger texture fit (TODO)
|
//ASSERT(dst->m_TEX0.TBW > TEX0.TBW); // otherwise scale.x need to be reduced to make the larger texture fit (TODO)
|
||||||
|
|
||||||
//src->m_texture = m_renderer->m_dev->CreateRenderTarget(dstsize.x, dstsize.y, false);
|
//src->m_texture = g_gs_device->CreateRenderTarget(dstsize.x, dstsize.y, false);
|
||||||
|
|
||||||
//GSVector4 size = GSVector4(dstsize).xyxy();
|
//GSVector4 size = GSVector4(dstsize).xyxy();
|
||||||
//GSVector4 scale = GSVector4(dst->m_texture->GetScale()).xyxy();
|
//GSVector4 scale = GSVector4(dst->m_texture->GetScale()).xyxy();
|
||||||
|
@ -1429,7 +1432,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||||
// GSVector4 sRect = GSVector4(GSVector4i(sx, sy).xyxy() + br) * scale / size;
|
// GSVector4 sRect = GSVector4(GSVector4i(sx, sy).xyxy() + br) * scale / size;
|
||||||
// GSVector4 dRect = GSVector4(GSVector4i(dx, dy).xyxy() + br) * scale;
|
// GSVector4 dRect = GSVector4(GSVector4i(dx, dy).xyxy() + br) * scale;
|
||||||
|
|
||||||
// m_renderer->m_dev->StretchRect(dst->m_texture, sRect, src->m_texture, dRect);
|
// g_gs_device->StretchRect(dst->m_texture, sRect, src->m_texture, dRect);
|
||||||
|
|
||||||
// // TODO: this is quite a lot of StretchRect, do it with one Draw
|
// // TODO: this is quite a lot of StretchRect, do it with one Draw
|
||||||
// }
|
// }
|
||||||
|
@ -1497,7 +1500,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||||
// Don't be fooled by the name. 'dst' is the old target (hence the input)
|
// Don't be fooled by the name. 'dst' is the old target (hence the input)
|
||||||
// 'src' is the new texture cache entry (hence the output)
|
// 'src' is the new texture cache entry (hence the output)
|
||||||
GSTexture* sTex = dst->m_texture;
|
GSTexture* sTex = dst->m_texture;
|
||||||
GSTexture* dTex = m_renderer->m_dev->CreateRenderTarget(w, h, GSTexture::Format::Color);
|
GSTexture* dTex = g_gs_device->CreateRenderTarget(w, h, GSTexture::Format::Color);
|
||||||
src->m_texture = dTex;
|
src->m_texture = dTex;
|
||||||
|
|
||||||
// GH: by default (m_paltex == 0) GS converts texture to the 32 bit format
|
// GH: by default (m_paltex == 0) GS converts texture to the 32 bit format
|
||||||
|
@ -1541,11 +1544,11 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||||
// which is arbitrary set to 1280 (biggest RT used by GS). h/w are based on the input texture
|
// which is arbitrary set to 1280 (biggest RT used by GS). h/w are based on the input texture
|
||||||
// so the only reliable way to find the real size of the target is to use the TBW value.
|
// so the only reliable way to find the real size of the target is to use the TBW value.
|
||||||
float real_width = dst->m_TEX0.TBW * 64u * dst->m_texture->GetScale().x;
|
float real_width = dst->m_TEX0.TBW * 64u * dst->m_texture->GetScale().x;
|
||||||
m_renderer->m_dev->CopyRect(sTex, dTex, GSVector4i((int)(real_width / 2.0f), 0, (int)real_width, h));
|
g_gs_device->CopyRect(sTex, dTex, GSVector4i((int)(real_width / 2.0f), 0, (int)real_width, h));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_renderer->m_dev->CopyRect(sTex, dTex, GSVector4i(0, 0, w, h)); // <= likely wrong dstsize.x could be bigger than w
|
g_gs_device->CopyRect(sTex, dTex, GSVector4i(0, 0, w, h)); // <= likely wrong dstsize.x could be bigger than w
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1559,7 +1562,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||||
sRect.x = sRect.z / 2.0f;
|
sRect.x = sRect.z / 2.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_renderer->m_dev->StretchRect(sTex, sRect, dTex, dRect, shader, linear);
|
g_gs_device->StretchRect(sTex, sRect, dTex, dRect, shader, linear);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src->m_texture)
|
if (src->m_texture)
|
||||||
|
@ -1602,12 +1605,12 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||||
{
|
{
|
||||||
if (m_paltex && psm.pal > 0)
|
if (m_paltex && psm.pal > 0)
|
||||||
{
|
{
|
||||||
src->m_texture = m_renderer->m_dev->CreateTexture(tw, th, GSTexture::Format::UNorm8);
|
src->m_texture = g_gs_device->CreateTexture(tw, th, GSTexture::Format::UNorm8);
|
||||||
AttachPaletteToSource(src, psm.pal, true);
|
AttachPaletteToSource(src, psm.pal, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
src->m_texture = m_renderer->m_dev->CreateTexture(tw, th, GSTexture::Format::Color);
|
src->m_texture = g_gs_device->CreateTexture(tw, th, GSTexture::Format::Color);
|
||||||
if (psm.pal > 0)
|
if (psm.pal > 0)
|
||||||
{
|
{
|
||||||
AttachPaletteToSource(src, psm.pal, false);
|
AttachPaletteToSource(src, psm.pal, false);
|
||||||
|
@ -1634,13 +1637,13 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(const GIFRegTEX0& TEX0, int
|
||||||
|
|
||||||
if (type == RenderTarget)
|
if (type == RenderTarget)
|
||||||
{
|
{
|
||||||
t->m_texture = m_renderer->m_dev->CreateSparseRenderTarget(w, h, GSTexture::Format::Color);
|
t->m_texture = g_gs_device->CreateSparseRenderTarget(w, h, GSTexture::Format::Color);
|
||||||
|
|
||||||
t->m_used = true; // FIXME
|
t->m_used = true; // FIXME
|
||||||
}
|
}
|
||||||
else if (type == DepthStencil)
|
else if (type == DepthStencil)
|
||||||
{
|
{
|
||||||
t->m_texture = m_renderer->m_dev->CreateSparseDepthStencil(w, h, GSTexture::Format::DepthStencil);
|
t->m_texture = g_gs_device->CreateSparseDepthStencil(w, h, GSTexture::Format::DepthStencil);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_dst[type].push_front(t);
|
m_dst[type].push_front(t);
|
||||||
|
@ -1699,9 +1702,9 @@ void GSTextureCache::Read(Target* t, const GSVector4i& r)
|
||||||
GSTexture::GSMap m;
|
GSTexture::GSMap m;
|
||||||
|
|
||||||
if (t->m_texture->GetScale() == GSVector2(1, 1) && ps_shader == ShaderConvert::COPY)
|
if (t->m_texture->GetScale() == GSVector2(1, 1) && ps_shader == ShaderConvert::COPY)
|
||||||
res = m_renderer->m_dev->DownloadTexture(t->m_texture, r, m);
|
res = g_gs_device->DownloadTexture(t->m_texture, r, m);
|
||||||
else
|
else
|
||||||
res = m_renderer->m_dev->DownloadTextureConvert(t->m_texture, src, GSVector2i(r.width(), r.height()), fmt, ps_shader, m, false);
|
res = g_gs_device->DownloadTextureConvert(t->m_texture, src, GSVector2i(r.width(), r.height()), fmt, ps_shader, m, false);
|
||||||
|
|
||||||
if (res)
|
if (res)
|
||||||
{
|
{
|
||||||
|
@ -1728,7 +1731,7 @@ void GSTextureCache::Read(Target* t, const GSVector4i& r)
|
||||||
ASSERT(0);
|
ASSERT(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_renderer->m_dev->DownloadTextureComplete();
|
g_gs_device->DownloadTextureComplete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1737,11 +1740,11 @@ void GSTextureCache::Read(Source* t, const GSVector4i& r)
|
||||||
const GIFRegTEX0& TEX0 = t->m_TEX0;
|
const GIFRegTEX0& TEX0 = t->m_TEX0;
|
||||||
|
|
||||||
GSTexture::GSMap m;
|
GSTexture::GSMap m;
|
||||||
if (m_renderer->m_dev->DownloadTexture(t->m_texture, r, m))
|
if (g_gs_device->DownloadTexture(t->m_texture, r, m))
|
||||||
{
|
{
|
||||||
GSOffset off = m_renderer->m_mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM);
|
GSOffset off = m_renderer->m_mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM);
|
||||||
m_renderer->m_mem.WritePixel32(m.bits, m.pitch, off, r);
|
m_renderer->m_mem.WritePixel32(m.bits, m.pitch, off, r);
|
||||||
m_renderer->m_dev->DownloadTextureComplete();
|
g_gs_device->DownloadTextureComplete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1796,7 +1799,7 @@ GSTextureCache::Surface::~Surface()
|
||||||
// Shared textures are pointers copy. Therefore no allocation
|
// Shared textures are pointers copy. Therefore no allocation
|
||||||
// to recycle.
|
// to recycle.
|
||||||
if (!m_shared_texture)
|
if (!m_shared_texture)
|
||||||
m_renderer->m_dev->Recycle(m_texture);
|
g_gs_device->Recycle(m_texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSTextureCache::Surface::UpdateAge()
|
void GSTextureCache::Surface::UpdateAge()
|
||||||
|
@ -2144,7 +2147,7 @@ void GSTextureCache::Target::Update()
|
||||||
// could be a gs transfer bug too due to unaligned-page transfer.
|
// could be a gs transfer bug too due to unaligned-page transfer.
|
||||||
//
|
//
|
||||||
// So the quick and dirty solution is just to clean the depth buffer.
|
// So the quick and dirty solution is just to clean the depth buffer.
|
||||||
m_renderer->m_dev->ClearDepth(m_texture);
|
g_gs_device->ClearDepth(m_texture);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2157,7 +2160,7 @@ void GSTextureCache::Target::Update()
|
||||||
TEXA.TA0 = 0;
|
TEXA.TA0 = 0;
|
||||||
TEXA.TA1 = 0x80;
|
TEXA.TA1 = 0x80;
|
||||||
|
|
||||||
GSTexture* t = m_renderer->m_dev->CreateTexture(w, h, GSTexture::Format::Color);
|
GSTexture* t = g_gs_device->CreateTexture(w, h, GSTexture::Format::Color);
|
||||||
|
|
||||||
GSOffset off = m_renderer->m_mem.GetOffset(m_TEX0.TBP0, m_TEX0.TBW, m_TEX0.PSM);
|
GSOffset off = m_renderer->m_mem.GetOffset(m_TEX0.TBP0, m_TEX0.TBW, m_TEX0.PSM);
|
||||||
|
|
||||||
|
@ -2185,17 +2188,17 @@ void GSTextureCache::Target::Update()
|
||||||
{
|
{
|
||||||
GL_INS("ERROR: Update RenderTarget 0x%x bw:%d (%d,%d => %d,%d)", m_TEX0.TBP0, m_TEX0.TBW, r.x, r.y, r.z, r.w);
|
GL_INS("ERROR: Update RenderTarget 0x%x bw:%d (%d,%d => %d,%d)", m_TEX0.TBP0, m_TEX0.TBW, r.x, r.y, r.z, r.w);
|
||||||
|
|
||||||
m_renderer->m_dev->StretchRect(t, m_texture, GSVector4(r) * GSVector4(m_texture->GetScale()).xyxy());
|
g_gs_device->StretchRect(t, m_texture, GSVector4(r) * GSVector4(m_texture->GetScale()).xyxy());
|
||||||
}
|
}
|
||||||
else if (m_type == DepthStencil)
|
else if (m_type == DepthStencil)
|
||||||
{
|
{
|
||||||
GL_INS("ERROR: Update DepthStencil 0x%x", m_TEX0.TBP0);
|
GL_INS("ERROR: Update DepthStencil 0x%x", m_TEX0.TBP0);
|
||||||
|
|
||||||
// FIXME linear or not?
|
// FIXME linear or not?
|
||||||
m_renderer->m_dev->StretchRect(t, m_texture, GSVector4(r) * GSVector4(m_texture->GetScale()).xyxy(), ShaderConvert::RGBA8_TO_FLOAT32);
|
g_gs_device->StretchRect(t, m_texture, GSVector4(r) * GSVector4(m_texture->GetScale()).xyxy(), ShaderConvert::RGBA8_TO_FLOAT32);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_renderer->m_dev->Recycle(t);
|
g_gs_device->Recycle(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSTextureCache::Target::UpdateValidity(const GSVector4i& rect)
|
void GSTextureCache::Target::UpdateValidity(const GSVector4i& rect)
|
||||||
|
@ -2294,7 +2297,7 @@ GSTextureCache::Palette::Palette(const GSRenderer* renderer, u16 pal, bool need_
|
||||||
|
|
||||||
GSTextureCache::Palette::~Palette()
|
GSTextureCache::Palette::~Palette()
|
||||||
{
|
{
|
||||||
m_renderer->m_dev->Recycle(m_tex_palette);
|
g_gs_device->Recycle(m_tex_palette);
|
||||||
_aligned_free(m_clut);
|
_aligned_free(m_clut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2317,7 +2320,7 @@ void GSTextureCache::Palette::InitializeTexture()
|
||||||
// sampling such texture are always normalized by 255.
|
// sampling such texture are always normalized by 255.
|
||||||
// This is because indexes are stored as normalized values of an RGBA texture (e.g. index 15 will be read as (15/255),
|
// This is because indexes are stored as normalized values of an RGBA texture (e.g. index 15 will be read as (15/255),
|
||||||
// and therefore will read texel 15/255 * texture size).
|
// and therefore will read texel 15/255 * texture size).
|
||||||
m_tex_palette = m_renderer->m_dev->CreateTexture(256, 1, GSTexture::Format::Color);
|
m_tex_palette = g_gs_device->CreateTexture(256, 1, GSTexture::Format::Color);
|
||||||
m_tex_palette->Update(GSVector4i(0, 0, m_pal, 1), m_clut, m_pal * sizeof(m_clut[0]));
|
m_tex_palette->Update(GSVector4i(0, 0, m_pal, 1), m_clut, m_pal * sizeof(m_clut[0]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,7 +219,6 @@ protected:
|
||||||
u8* m_temp;
|
u8* m_temp;
|
||||||
bool m_can_convert_depth;
|
bool m_can_convert_depth;
|
||||||
bool m_cpu_fb_conversion;
|
bool m_cpu_fb_conversion;
|
||||||
CRCHackLevel m_crc_hack_level;
|
|
||||||
static bool m_disable_partial_invalidation;
|
static bool m_disable_partial_invalidation;
|
||||||
bool m_texture_inside_rt;
|
bool m_texture_inside_rt;
|
||||||
static bool m_wrap_gs_mem;
|
static bool m_wrap_gs_mem;
|
||||||
|
|
|
@ -16,21 +16,6 @@
|
||||||
#include "PrecompiledHeader.h"
|
#include "PrecompiledHeader.h"
|
||||||
#include "GSDeviceNull.h"
|
#include "GSDeviceNull.h"
|
||||||
|
|
||||||
bool GSDeviceNull::Create(const WindowInfo& wi)
|
|
||||||
{
|
|
||||||
if (!GSDevice::Create(wi))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Reset(1, 1);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GSDeviceNull::Reset(int w, int h)
|
|
||||||
{
|
|
||||||
return GSDevice::Reset(w, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
GSTexture* GSDeviceNull::CreateSurface(GSTexture::Type type, int w, int h, GSTexture::Format format)
|
GSTexture* GSDeviceNull::CreateSurface(GSTexture::Type type, int w, int h, GSTexture::Format format)
|
||||||
{
|
{
|
||||||
return new GSTextureNull(type, w, h, format);
|
return new GSTextureNull(type, w, h, format);
|
||||||
|
|
|
@ -29,7 +29,4 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GSDeviceNull() {}
|
GSDeviceNull() {}
|
||||||
|
|
||||||
bool Create(const WindowInfo& wi);
|
|
||||||
bool Reset(int w, int h);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,3 +28,8 @@ GSTextureNull::GSTextureNull(Type type, int w, int h, GSTexture::Format format)
|
||||||
m_desc.h = h;
|
m_desc.h = h;
|
||||||
m_desc.format = format;
|
m_desc.format = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* GSTextureNull::GetNativeHandle() const
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
|
@ -37,4 +37,5 @@ public:
|
||||||
bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) override { return false; }
|
bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) override { return false; }
|
||||||
void Unmap() override {}
|
void Unmap() override {}
|
||||||
bool Save(const std::string& fn) override { return false; }
|
bool Save(const std::string& fn) override { return false; }
|
||||||
|
void* GetNativeHandle() const override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "GLLoader.h"
|
#include "GLLoader.h"
|
||||||
#include "GS/GS.h"
|
#include "GS/GS.h"
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
#include "Host.h"
|
||||||
|
|
||||||
namespace GLExtension
|
namespace GLExtension
|
||||||
{
|
{
|
||||||
|
@ -161,15 +162,15 @@ namespace GLLoader
|
||||||
bool found_GL_ARB_get_texture_sub_image = false;
|
bool found_GL_ARB_get_texture_sub_image = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void mandatory(const std::string& ext)
|
static bool mandatory(const std::string& ext)
|
||||||
{
|
{
|
||||||
if (!GLExtension::Has(ext))
|
if (!GLExtension::Has(ext))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "ERROR: %s is NOT SUPPORTED\n", ext.c_str());
|
Host::ReportFormattedErrorAsync("GS", "ERROR: %s is NOT SUPPORTED\n", ext.c_str());
|
||||||
throw GSRecoverableError();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool optional(const std::string& name)
|
static bool optional(const std::string& name)
|
||||||
|
@ -198,7 +199,7 @@ namespace GLLoader
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_gl_version(int major, int minor)
|
bool check_gl_version(int major, int minor)
|
||||||
{
|
{
|
||||||
const char* vendor = (const char*)glGetString(GL_VENDOR);
|
const char* vendor = (const char*)glGetString(GL_VENDOR);
|
||||||
if (strstr(vendor, "Advanced Micro Devices") || strstr(vendor, "ATI Technologies Inc.") || strstr(vendor, "ATI"))
|
if (strstr(vendor, "Advanced Micro Devices") || strstr(vendor, "ATI Technologies Inc.") || strstr(vendor, "ATI"))
|
||||||
|
@ -226,12 +227,14 @@ namespace GLLoader
|
||||||
glGetIntegerv(GL_MINOR_VERSION, &minor_gl);
|
glGetIntegerv(GL_MINOR_VERSION, &minor_gl);
|
||||||
if ((major_gl < major) || (major_gl == major && minor_gl < minor))
|
if ((major_gl < major) || (major_gl == major && minor_gl < minor))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "OpenGL %d.%d is not supported. Only OpenGL %d.%d\n was found", major, minor, major_gl, minor_gl);
|
Host::ReportFormattedErrorAsync("GS", "OpenGL %d.%d is not supported. Only OpenGL %d.%d\n was found", major, minor, major_gl, minor_gl);
|
||||||
throw GSRecoverableError();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_gl_supported_extension()
|
bool check_gl_supported_extension()
|
||||||
{
|
{
|
||||||
int max_ext = 0;
|
int max_ext = 0;
|
||||||
glGetIntegerv(GL_NUM_EXTENSIONS, &max_ext);
|
glGetIntegerv(GL_NUM_EXTENSIONS, &max_ext);
|
||||||
|
@ -243,24 +246,27 @@ namespace GLLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mandatory for both renderer
|
// Mandatory for both renderer
|
||||||
|
bool ok = true;
|
||||||
{
|
{
|
||||||
// GL4.1
|
// GL4.1
|
||||||
mandatory("GL_ARB_separate_shader_objects");
|
ok = ok && mandatory("GL_ARB_separate_shader_objects");
|
||||||
// GL4.2
|
// GL4.2
|
||||||
mandatory("GL_ARB_shading_language_420pack");
|
ok = ok && mandatory("GL_ARB_shading_language_420pack");
|
||||||
mandatory("GL_ARB_texture_storage");
|
ok = ok && mandatory("GL_ARB_texture_storage");
|
||||||
// GL4.3
|
// GL4.3
|
||||||
mandatory("GL_KHR_debug");
|
ok = ok && mandatory("GL_KHR_debug");
|
||||||
// GL4.4
|
// GL4.4
|
||||||
mandatory("GL_ARB_buffer_storage");
|
ok = ok && mandatory("GL_ARB_buffer_storage");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only for HW renderer
|
// Only for HW renderer
|
||||||
if (theApp.GetCurrentRendererType() == GSRendererType::OGL_HW)
|
if (GSConfig.UseHardwareRenderer())
|
||||||
{
|
{
|
||||||
mandatory("GL_ARB_copy_image");
|
ok = ok && mandatory("GL_ARB_copy_image");
|
||||||
mandatory("GL_ARB_clip_control");
|
ok = ok && mandatory("GL_ARB_clip_control");
|
||||||
}
|
}
|
||||||
|
if (!ok)
|
||||||
|
return false;
|
||||||
|
|
||||||
// Extra
|
// Extra
|
||||||
{
|
{
|
||||||
|
@ -319,6 +325,8 @@ namespace GLLoader
|
||||||
Emulate_DSA::Init();
|
Emulate_DSA::Init();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_sparse2_compatible(const char* name, GLenum internal_fmt, int x_max, int y_max)
|
bool is_sparse2_compatible(const char* name, GLenum internal_fmt, int x_max, int y_max)
|
||||||
|
@ -385,11 +393,13 @@ namespace GLLoader
|
||||||
fprintf_once(stdout, "INFO: sparse depth texture is %s\n", found_compatible_sparse_depth ? "available" : "NOT SUPPORTED");
|
fprintf_once(stdout, "INFO: sparse depth texture is %s\n", found_compatible_sparse_depth ? "available" : "NOT SUPPORTED");
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_gl_requirements()
|
bool check_gl_requirements()
|
||||||
{
|
{
|
||||||
check_gl_version(3, 3);
|
if (!check_gl_version(3, 3))
|
||||||
|
return false;
|
||||||
|
|
||||||
check_gl_supported_extension();
|
if (!check_gl_supported_extension())
|
||||||
|
return false;
|
||||||
|
|
||||||
// Bonus for sparse texture
|
// Bonus for sparse texture
|
||||||
check_sparse_compatibility();
|
check_sparse_compatibility();
|
||||||
|
@ -397,5 +407,6 @@ namespace GLLoader
|
||||||
fprintf_once(stdout, "\n");
|
fprintf_once(stdout, "\n");
|
||||||
|
|
||||||
s_first_load = false;
|
s_first_load = false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
} // namespace GLLoader
|
} // namespace GLLoader
|
||||||
|
|
|
@ -30,7 +30,7 @@ namespace GLExtension
|
||||||
|
|
||||||
namespace GLLoader
|
namespace GLLoader
|
||||||
{
|
{
|
||||||
void check_gl_requirements();
|
bool check_gl_requirements();
|
||||||
|
|
||||||
extern bool vendor_id_amd;
|
extern bool vendor_id_amd;
|
||||||
extern bool vendor_id_nvidia;
|
extern bool vendor_id_nvidia;
|
||||||
|
|
|
@ -52,23 +52,23 @@ namespace GLState
|
||||||
void Clear()
|
void Clear()
|
||||||
{
|
{
|
||||||
fbo = 0;
|
fbo = 0;
|
||||||
viewport = GSVector2i(0, 0);
|
viewport = GSVector2i(1, 1);
|
||||||
scissor = GSVector4i(0, 0, 0, 0);
|
scissor = GSVector4i(0, 0, 1, 1);
|
||||||
|
|
||||||
blend = false;
|
blend = false;
|
||||||
eq_RGB = 0;
|
eq_RGB = GL_FUNC_ADD;
|
||||||
f_sRGB = 0;
|
f_sRGB = GL_ONE;
|
||||||
f_dRGB = 0;
|
f_dRGB = GL_ZERO;
|
||||||
bf = 0;
|
bf = 0;
|
||||||
wrgba = 0xF;
|
wrgba = 0xF;
|
||||||
|
|
||||||
depth = false;
|
depth = false;
|
||||||
depth_func = 0;
|
depth_func = GL_LESS;
|
||||||
depth_mask = true;
|
depth_mask = false;
|
||||||
|
|
||||||
stencil = false;
|
stencil = false;
|
||||||
stencil_func = 0;
|
stencil_func = GL_ALWAYS;
|
||||||
stencil_pass = 0xFFFF; // Note 0 is valid (GL_ZERO)
|
stencil_pass = GL_KEEP;
|
||||||
|
|
||||||
ps_ss = 0;
|
ps_ss = 0;
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "GLState.h"
|
#include "GLState.h"
|
||||||
#include "GS/GSUtil.h"
|
#include "GS/GSUtil.h"
|
||||||
#include "Host.h"
|
#include "Host.h"
|
||||||
|
#include "HostDisplay.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
@ -40,7 +41,6 @@ static constexpr u32 INDEX_BUFFER_SIZE = 16 * 1024 * 1024;
|
||||||
static constexpr u32 VERTEX_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024;
|
static constexpr u32 VERTEX_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024;
|
||||||
static constexpr u32 FRAGMENT_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024;
|
static constexpr u32 FRAGMENT_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024;
|
||||||
|
|
||||||
bool GSDeviceOGL::m_debug_gl_call = false;
|
|
||||||
int GSDeviceOGL::m_shader_inst = 0;
|
int GSDeviceOGL::m_shader_inst = 0;
|
||||||
int GSDeviceOGL::m_shader_reg = 0;
|
int GSDeviceOGL::m_shader_reg = 0;
|
||||||
FILE* GSDeviceOGL::m_debug_gl_file = NULL;
|
FILE* GSDeviceOGL::m_debug_gl_file = NULL;
|
||||||
|
@ -61,25 +61,15 @@ GSDeviceOGL::GSDeviceOGL()
|
||||||
memset(&m_shadeboost, 0, sizeof(m_shadeboost));
|
memset(&m_shadeboost, 0, sizeof(m_shadeboost));
|
||||||
memset(&m_om_dss, 0, sizeof(m_om_dss));
|
memset(&m_om_dss, 0, sizeof(m_om_dss));
|
||||||
memset(&m_profiler, 0, sizeof(m_profiler));
|
memset(&m_profiler, 0, sizeof(m_profiler));
|
||||||
GLState::Clear();
|
|
||||||
|
|
||||||
m_mipmap = theApp.GetConfigI("mipmap");
|
m_mipmap = theApp.GetConfigI("mipmap");
|
||||||
m_upscale_multiplier = std::max(1, theApp.GetConfigI("upscale_multiplier"));
|
m_upscale_multiplier = std::max(1, theApp.GetConfigI("upscale_multiplier"));
|
||||||
if (theApp.GetConfigB("UserHacks"))
|
|
||||||
m_filter = static_cast<TriFiltering>(theApp.GetConfigI("UserHacks_TriFilter"));
|
|
||||||
else
|
|
||||||
m_filter = TriFiltering::None;
|
|
||||||
|
|
||||||
// Reset the debug file
|
// Reset the debug file
|
||||||
#ifdef ENABLE_OGL_DEBUG
|
#ifdef ENABLE_OGL_DEBUG
|
||||||
if (theApp.GetCurrentRendererType() == GSRendererType::OGL_SW)
|
m_debug_gl_file = fopen("GS_opengl_debug.txt", "w");
|
||||||
m_debug_gl_file = fopen("GS_opengl_debug_sw.txt", "w");
|
|
||||||
else
|
|
||||||
m_debug_gl_file = fopen("GS_opengl_debug_hw.txt", "w");
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_debug_gl_call = theApp.GetConfigB("debug_opengl");
|
|
||||||
|
|
||||||
m_disable_hw_gl_draw = theApp.GetConfigB("disable_hw_gl_draw");
|
m_disable_hw_gl_draw = theApp.GetConfigB("disable_hw_gl_draw");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,29 +205,23 @@ GSTexture* GSDeviceOGL::CreateSurface(GSTexture::Type type, int w, int h, GSText
|
||||||
GL_PUSH("Create surface");
|
GL_PUSH("Create surface");
|
||||||
|
|
||||||
// A wrapper to call GSTextureOGL, with the different kind of parameters.
|
// A wrapper to call GSTextureOGL, with the different kind of parameters.
|
||||||
GSTextureOGL* t = new GSTextureOGL(type, w, h, fmt, m_fbo_read, m_mipmap > 1 || m_filter != TriFiltering::None);
|
GSTextureOGL* t = new GSTextureOGL(type, w, h, fmt, m_fbo_read, m_mipmap > 1 || GSConfig.UserHacks_TriFilter != TriFiltering::Off);
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSDeviceOGL::Create(const WindowInfo& wi)
|
bool GSDeviceOGL::Create(HostDisplay* display)
|
||||||
{
|
{
|
||||||
m_gl_context = GL::Context::Create(wi, GL::Context::GetAllVersionsList());
|
if (!GSDevice::Create(display))
|
||||||
if (!m_gl_context || !m_gl_context->MakeCurrent())
|
return false;
|
||||||
|
|
||||||
|
if (display->GetRenderAPI() != HostDisplay::RenderAPI::OpenGL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check openGL requirement as soon as possible so we can switch to another
|
// Check openGL requirement as soon as possible so we can switch to another
|
||||||
// renderer/device
|
// renderer/device
|
||||||
try
|
if (!GLLoader::check_gl_requirements())
|
||||||
{
|
|
||||||
GLLoader::check_gl_requirements();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
printf("GS error: Exception caught in GSDeviceOGL::Create: %s", ex.what());
|
|
||||||
m_gl_context->DoneCurrent();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (!theApp.GetConfigB("disable_shader_cache"))
|
if (!theApp.GetConfigB("disable_shader_cache"))
|
||||||
{
|
{
|
||||||
|
@ -268,16 +252,22 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||||
{
|
{
|
||||||
auto shader = Host::ReadResourceFileToString("shaders/opengl/common_header.glsl");
|
auto shader = Host::ReadResourceFileToString("shaders/opengl/common_header.glsl");
|
||||||
if (!shader.has_value())
|
if (!shader.has_value())
|
||||||
|
{
|
||||||
|
Host::ReportErrorAsync("GS", "Failed to read shaders/opengl/common_header.glsl.");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
m_shader_common_header = std::move(*shader);
|
m_shader_common_header = std::move(*shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// because of fbo bindings below...
|
||||||
|
GLState::Clear();
|
||||||
|
|
||||||
// ****************************************************************
|
// ****************************************************************
|
||||||
// Debug helper
|
// Debug helper
|
||||||
// ****************************************************************
|
// ****************************************************************
|
||||||
#ifdef ENABLE_OGL_DEBUG
|
#ifdef ENABLE_OGL_DEBUG
|
||||||
if (theApp.GetConfigB("debug_opengl"))
|
if (GSConfig.UseDebugDevice)
|
||||||
{
|
{
|
||||||
glDebugMessageCallback((GLDEBUGPROC)DebugOutputToFile, NULL);
|
glDebugMessageCallback((GLDEBUGPROC)DebugOutputToFile, NULL);
|
||||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
|
||||||
|
@ -334,7 +324,7 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||||
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &m_uniform_buffer_alignment);
|
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &m_uniform_buffer_alignment);
|
||||||
if (!m_vertex_stream_buffer || !m_index_stream_buffer || !m_vertex_uniform_stream_buffer || !m_fragment_uniform_stream_buffer)
|
if (!m_vertex_stream_buffer || !m_index_stream_buffer || !m_vertex_uniform_stream_buffer || !m_fragment_uniform_stream_buffer)
|
||||||
{
|
{
|
||||||
Console.Error("Failed to create vertex/index/uniform streaming buffers");
|
Host::ReportErrorAsync("GS", "Failed to create vertex/index/uniform streaming buffers");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,6 +346,10 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||||
glVertexAttribPointer(7, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GSVertex), (const GLvoid*)(28));
|
glVertexAttribPointer(7, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GSVertex), (const GLvoid*)(28));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// must be done after va is created
|
||||||
|
GLState::Clear();
|
||||||
|
RestoreAPIState();
|
||||||
|
|
||||||
// ****************************************************************
|
// ****************************************************************
|
||||||
// Pre Generate the different sampler object
|
// Pre Generate the different sampler object
|
||||||
// ****************************************************************
|
// ****************************************************************
|
||||||
|
@ -377,7 +371,10 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||||
// these all share the same vertex shader
|
// these all share the same vertex shader
|
||||||
const auto shader = Host::ReadResourceFileToString("shaders/opengl/convert.glsl");
|
const auto shader = Host::ReadResourceFileToString("shaders/opengl/convert.glsl");
|
||||||
if (!shader.has_value())
|
if (!shader.has_value())
|
||||||
|
{
|
||||||
|
Host::ReportErrorAsync("GS", "Failed to read shaders/opengl/convert.glsl.");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
m_convert.vs = GetShaderSource("vs_main", GL_VERTEX_SHADER, m_shader_common_header, *shader, {});
|
m_convert.vs = GetShaderSource("vs_main", GL_VERTEX_SHADER, m_shader_common_header, *shader, {});
|
||||||
|
|
||||||
|
@ -417,7 +414,10 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||||
|
|
||||||
const auto shader = Host::ReadResourceFileToString("shaders/opengl/merge.glsl");
|
const auto shader = Host::ReadResourceFileToString("shaders/opengl/merge.glsl");
|
||||||
if (!shader.has_value())
|
if (!shader.has_value())
|
||||||
|
{
|
||||||
|
Host::ReportErrorAsync("GS", "Failed to read shaders/opengl/merge.glsl.");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < std::size(m_merge_obj.ps); i++)
|
for (size_t i = 0; i < std::size(m_merge_obj.ps); i++)
|
||||||
{
|
{
|
||||||
|
@ -437,7 +437,10 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||||
|
|
||||||
const auto shader = Host::ReadResourceFileToString("shaders/opengl/interlace.glsl");
|
const auto shader = Host::ReadResourceFileToString("shaders/opengl/interlace.glsl");
|
||||||
if (!shader.has_value())
|
if (!shader.has_value())
|
||||||
|
{
|
||||||
|
Host::ReportErrorAsync("GS", "Failed to read shaders/opengl/interlace.glsl.");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < std::size(m_interlace.ps); i++)
|
for (size_t i = 0; i < std::size(m_interlace.ps); i++)
|
||||||
{
|
{
|
||||||
|
@ -465,7 +468,10 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||||
|
|
||||||
const auto shader = Host::ReadResourceFileToString("shaders/opengl/shadeboost.glsl");
|
const auto shader = Host::ReadResourceFileToString("shaders/opengl/shadeboost.glsl");
|
||||||
if (!shader.has_value())
|
if (!shader.has_value())
|
||||||
|
{
|
||||||
|
Host::ReportErrorAsync("GS", "Failed to read shaders/opengl/shadeboost.glsl.");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string ps(GetShaderSource("ps_main", GL_FRAGMENT_SHADER, m_shader_common_header, *shader, shade_macro));
|
const std::string ps(GetShaderSource("ps_main", GL_FRAGMENT_SHADER, m_shader_common_header, *shader, shade_macro));
|
||||||
if (!m_shader_cache.GetProgram(&m_shadeboost.ps, m_convert.vs, {}, ps))
|
if (!m_shader_cache.GetProgram(&m_shadeboost.ps, m_convert.vs, {}, ps))
|
||||||
|
@ -563,23 +569,6 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||||
|
|
||||||
fprintf(stdout, "Available VRAM/RAM:%lldMB for textures\n", GLState::available_vram >> 20u);
|
fprintf(stdout, "Available VRAM/RAM:%lldMB for textures\n", GLState::available_vram >> 20u);
|
||||||
|
|
||||||
// ****************************************************************
|
|
||||||
// Texture Font (OSD)
|
|
||||||
// ****************************************************************
|
|
||||||
const GSVector2i tex_font = m_osd.get_texture_font_size();
|
|
||||||
|
|
||||||
m_font = std::unique_ptr<GSTexture>(
|
|
||||||
new GSTextureOGL(GSTexture::Type::Texture, tex_font.x, tex_font.y, GSTexture::Format::UNorm8, m_fbo_read, false)
|
|
||||||
);
|
|
||||||
|
|
||||||
// ****************************************************************
|
|
||||||
// Finish window setup and backbuffer
|
|
||||||
// ****************************************************************
|
|
||||||
if (!GSDevice::Create(wi))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Reset(wi.surface_width, wi.surface_height);
|
|
||||||
|
|
||||||
// Basic to ensure structures are correctly packed
|
// Basic to ensure structures are correctly packed
|
||||||
static_assert(sizeof(VSSelector) == 4, "Wrong VSSelector size");
|
static_assert(sizeof(VSSelector) == 4, "Wrong VSSelector size");
|
||||||
static_assert(sizeof(PSSelector) == 8, "Wrong PSSelector size");
|
static_assert(sizeof(PSSelector) == 8, "Wrong PSSelector size");
|
||||||
|
@ -597,7 +586,10 @@ bool GSDeviceOGL::CreateTextureFX()
|
||||||
auto vertex_shader = Host::ReadResourceFileToString("shaders/opengl/tfx_vgs.glsl");
|
auto vertex_shader = Host::ReadResourceFileToString("shaders/opengl/tfx_vgs.glsl");
|
||||||
auto fragment_shader = Host::ReadResourceFileToString("shaders/opengl/tfx_fs.glsl");
|
auto fragment_shader = Host::ReadResourceFileToString("shaders/opengl/tfx_fs.glsl");
|
||||||
if (!vertex_shader.has_value() || !fragment_shader.has_value())
|
if (!vertex_shader.has_value() || !fragment_shader.has_value())
|
||||||
|
{
|
||||||
|
Host::ReportErrorAsync("GS", "Failed to read shaders/opengl/tfx_{vgs,fs}.glsl.");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
m_shader_tfx_vgs = std::move(*vertex_shader);
|
m_shader_tfx_vgs = std::move(*vertex_shader);
|
||||||
m_shader_tfx_fs = std::move(*fragment_shader);
|
m_shader_tfx_fs = std::move(*fragment_shader);
|
||||||
|
@ -615,37 +607,75 @@ bool GSDeviceOGL::CreateTextureFX()
|
||||||
m_om_dss[key] = CreateDepthStencil(OMDepthStencilSelector(key));
|
m_om_dss[key] = CreateDepthStencil(OMDepthStencilSelector(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GSDeviceOGL::Reset(int w, int h)
|
|
||||||
{
|
|
||||||
if (!GSDevice::Reset(w, h))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Opengl allocate the backbuffer with the window. The render is done in the backbuffer when
|
|
||||||
// there isn't any FBO. Only a dummy texture is created to easily detect when the rendering is done
|
|
||||||
// in the backbuffer
|
|
||||||
m_gl_context->ResizeSurface(w, h);
|
|
||||||
m_backbuffer = new GSTextureOGL(GSTexture::Type::Backbuffer, m_gl_context->GetSurfaceWidth(),
|
|
||||||
m_gl_context->GetSurfaceHeight(), GSTexture::Format::Backbuffer, m_fbo_read, false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSDeviceOGL::SetVSync(int vsync)
|
|
||||||
{
|
|
||||||
m_gl_context->SetSwapInterval(vsync);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSDeviceOGL::Flip()
|
|
||||||
{
|
|
||||||
m_gl_context->SwapBuffers();
|
|
||||||
|
|
||||||
if (GLLoader::in_replayer)
|
if (GLLoader::in_replayer)
|
||||||
{
|
{
|
||||||
glQueryCounter(m_profiler.timer(), GL_TIMESTAMP);
|
glQueryCounter(m_profiler.timer(), GL_TIMESTAMP);
|
||||||
m_profiler.last_query++;
|
m_profiler.last_query++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSDeviceOGL::ResetAPIState()
|
||||||
|
{
|
||||||
|
if (GLState::point_size)
|
||||||
|
glDisable(GL_PROGRAM_POINT_SIZE);
|
||||||
|
if (GLState::line_width != 1.0f)
|
||||||
|
glLineWidth(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSDeviceOGL::RestoreAPIState()
|
||||||
|
{
|
||||||
|
glBindVertexArray(m_vertex_array_object);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLState::fbo);
|
||||||
|
|
||||||
|
glViewportIndexedf(0, 0, 0, static_cast<float>(GLState::viewport.x), static_cast<float>(GLState::viewport.y));
|
||||||
|
glScissorIndexed(0, GLState::scissor.x, GLState::scissor.y, GLState::scissor.width(), GLState::scissor.height());
|
||||||
|
|
||||||
|
glBlendEquationSeparate(GLState::eq_RGB, GL_FUNC_ADD);
|
||||||
|
glBlendFuncSeparate(GLState::f_sRGB, GLState::f_dRGB, GL_ONE, GL_ZERO);
|
||||||
|
|
||||||
|
const float bf = static_cast<float>(GLState::bf) / 128.0f;
|
||||||
|
glBlendColor(bf, bf, bf, bf);
|
||||||
|
|
||||||
|
if (GLState::blend)
|
||||||
|
{
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
const OMColorMaskSelector msel{ GLState::wrgba };
|
||||||
|
glColorMask(msel.wr, msel.wg, msel.wb, msel.wa);
|
||||||
|
|
||||||
|
GLState::depth ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
|
||||||
|
glDepthFunc(GLState::depth_func);
|
||||||
|
glDepthMask(GLState::depth_mask);
|
||||||
|
|
||||||
|
if (GLState::stencil)
|
||||||
|
{
|
||||||
|
glEnable(GL_STENCIL_TEST);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glDisable(GL_STENCIL_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
glStencilFunc(GLState::stencil_func, 1, 1);
|
||||||
|
glStencilOp(GL_KEEP, GL_KEEP, GLState::stencil_pass);
|
||||||
|
|
||||||
|
glBindSampler(0, GLState::ps_ss);
|
||||||
|
|
||||||
|
for (GLuint i = 0; i < sizeof(GLState::tex_unit) / sizeof(GLState::tex_unit[0]); i++)
|
||||||
|
glBindTextureUnit(i, GLState::tex_unit[i]);
|
||||||
|
|
||||||
|
if (GLState::point_size)
|
||||||
|
glEnable(GL_PROGRAM_POINT_SIZE);
|
||||||
|
if (GLState::line_width != 1.0f)
|
||||||
|
glLineWidth(GLState::line_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDeviceOGL::DrawPrimitive()
|
void GSDeviceOGL::DrawPrimitive()
|
||||||
|
@ -680,7 +710,7 @@ void GSDeviceOGL::ClearRenderTarget(GSTexture* t, const GSVector4& c)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
GSTextureOGL* T = static_cast<GSTextureOGL*>(t);
|
GSTextureOGL* T = static_cast<GSTextureOGL*>(t);
|
||||||
if (T->HasBeenCleaned() && !T->IsBackbuffer())
|
if (T->HasBeenCleaned())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Performance note: potentially T->Clear() could be used. Main purpose of
|
// Performance note: potentially T->Clear() could be used. Main purpose of
|
||||||
|
@ -698,21 +728,10 @@ void GSDeviceOGL::ClearRenderTarget(GSTexture* t, const GSVector4& c)
|
||||||
const u32 old_color_mask = GLState::wrgba;
|
const u32 old_color_mask = GLState::wrgba;
|
||||||
OMSetColorMaskState();
|
OMSetColorMaskState();
|
||||||
|
|
||||||
if (T->IsBackbuffer())
|
OMSetFBO(m_fbo);
|
||||||
{
|
OMAttachRt(T);
|
||||||
OMSetFBO(0);
|
|
||||||
|
|
||||||
// glDrawBuffer(GL_BACK); // this is the default when there is no FB
|
glClearBufferfv(GL_COLOR, 0, c.v);
|
||||||
// 0 will select the first drawbuffer ie GL_BACK
|
|
||||||
glClearBufferfv(GL_COLOR, 0, c.v);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OMSetFBO(m_fbo);
|
|
||||||
OMAttachRt(T);
|
|
||||||
|
|
||||||
glClearBufferfv(GL_COLOR, 0, c.v);
|
|
||||||
}
|
|
||||||
|
|
||||||
OMSetColorMaskState(OMColorMaskSelector(old_color_mask));
|
OMSetColorMaskState(OMColorMaskSelector(old_color_mask));
|
||||||
|
|
||||||
|
@ -1167,11 +1186,7 @@ void GSDeviceOGL::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
|
||||||
|
|
||||||
void GSDeviceOGL::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, const GL::Program& ps, int bs, OMColorMaskSelector cms, bool linear)
|
void GSDeviceOGL::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, const GL::Program& ps, int bs, OMColorMaskSelector cms, bool linear)
|
||||||
{
|
{
|
||||||
if (!sTex || !dTex)
|
ASSERT(sTex);
|
||||||
{
|
|
||||||
ASSERT(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool draw_in_depth = ps == m_convert.ps[static_cast<int>(ShaderConvert::RGBA8_TO_FLOAT32)]
|
const bool draw_in_depth = ps == m_convert.ps[static_cast<int>(ShaderConvert::RGBA8_TO_FLOAT32)]
|
||||||
|| ps == m_convert.ps[static_cast<int>(ShaderConvert::RGBA8_TO_FLOAT24)]
|
|| ps == m_convert.ps[static_cast<int>(ShaderConvert::RGBA8_TO_FLOAT24)]
|
||||||
|
@ -1182,15 +1197,27 @@ void GSDeviceOGL::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
|
||||||
// instead to emulate it with shader
|
// instead to emulate it with shader
|
||||||
// see https://www.opengl.org/wiki/Framebuffer#Blitting
|
// see https://www.opengl.org/wiki/Framebuffer#Blitting
|
||||||
|
|
||||||
GL_PUSH("StretchRect from %d to %d", sTex->GetID(), dTex->GetID());
|
|
||||||
|
|
||||||
// ************************************
|
// ************************************
|
||||||
// Init
|
// Init
|
||||||
// ************************************
|
// ************************************
|
||||||
|
|
||||||
BeginScene();
|
BeginScene();
|
||||||
|
|
||||||
GSVector2i ds = dTex->GetSize();
|
GSVector2i ds;
|
||||||
|
if (dTex)
|
||||||
|
{
|
||||||
|
GL_PUSH("StretchRect from %d to %d", sTex->GetID(), dTex->GetID());
|
||||||
|
ds = dTex->GetSize();
|
||||||
|
dTex->CommitRegion(GSVector2i((int)dRect.z + 1, (int)dRect.w + 1));
|
||||||
|
if (draw_in_depth)
|
||||||
|
OMSetRenderTargets(NULL, dTex);
|
||||||
|
else
|
||||||
|
OMSetRenderTargets(dTex, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ds = GSVector2i(m_display->GetWindowWidth(), m_display->GetWindowHeight());
|
||||||
|
}
|
||||||
|
|
||||||
ps.Bind();
|
ps.Bind();
|
||||||
|
|
||||||
|
@ -1203,11 +1230,6 @@ void GSDeviceOGL::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
|
||||||
else
|
else
|
||||||
OMSetDepthStencilState(m_convert.dss);
|
OMSetDepthStencilState(m_convert.dss);
|
||||||
|
|
||||||
if (draw_in_depth)
|
|
||||||
OMSetRenderTargets(NULL, dTex);
|
|
||||||
else
|
|
||||||
OMSetRenderTargets(dTex, NULL);
|
|
||||||
|
|
||||||
OMSetBlendState((u8)bs);
|
OMSetBlendState((u8)bs);
|
||||||
OMSetColorMaskState(cms);
|
OMSetColorMaskState(cms);
|
||||||
|
|
||||||
|
@ -1222,7 +1244,7 @@ void GSDeviceOGL::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
|
||||||
// 2/ in case some GS code expect thing in dx order.
|
// 2/ in case some GS code expect thing in dx order.
|
||||||
// Only flipping the backbuffer is transparent (I hope)...
|
// Only flipping the backbuffer is transparent (I hope)...
|
||||||
GSVector4 flip_sr = sRect;
|
GSVector4 flip_sr = sRect;
|
||||||
if (static_cast<GSTextureOGL*>(dTex)->IsBackbuffer())
|
if (!dTex)
|
||||||
{
|
{
|
||||||
flip_sr.y = sRect.w;
|
flip_sr.y = sRect.w;
|
||||||
flip_sr.w = sRect.y;
|
flip_sr.w = sRect.y;
|
||||||
|
@ -1238,7 +1260,6 @@ void GSDeviceOGL::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
|
||||||
// ************************************
|
// ************************************
|
||||||
// Draw
|
// Draw
|
||||||
// ************************************
|
// ************************************
|
||||||
dTex->CommitRegion(GSVector2i((int)dRect.z + 1, (int)dRect.w + 1));
|
|
||||||
DrawStretchRect(flip_sr, dRect, ds);
|
DrawStretchRect(flip_sr, dRect, ds);
|
||||||
|
|
||||||
// ************************************
|
// ************************************
|
||||||
|
@ -1276,39 +1297,6 @@ void GSDeviceOGL::DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect
|
||||||
DrawPrimitive();
|
DrawPrimitive();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDeviceOGL::RenderOsd(GSTexture* dt)
|
|
||||||
{
|
|
||||||
BeginScene();
|
|
||||||
|
|
||||||
m_convert.ps[static_cast<int>(ShaderConvert::OSD)].Bind();
|
|
||||||
|
|
||||||
OMSetDepthStencilState(m_convert.dss);
|
|
||||||
OMSetBlendState((u8)GSDeviceOGL::m_MERGE_BLEND);
|
|
||||||
OMSetRenderTargets(dt, NULL);
|
|
||||||
|
|
||||||
if (m_osd.m_texture_dirty)
|
|
||||||
{
|
|
||||||
m_osd.upload_texture_atlas(m_font.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
PSSetShaderResource(0, m_font.get());
|
|
||||||
PSSetSamplerState(m_convert.pt);
|
|
||||||
|
|
||||||
IASetPrimitiveTopology(GL_TRIANGLES);
|
|
||||||
|
|
||||||
// Note scaling could also be done in shader (require gl3/dx10)
|
|
||||||
size_t count = m_osd.Size();
|
|
||||||
auto res = m_vertex_stream_buffer->Map(sizeof(GSVertexPT1), static_cast<u32>(count) * sizeof(GSVertexPT1));
|
|
||||||
count = m_osd.GeneratePrimitives(reinterpret_cast<GSVertexPT1*>(res.pointer), count);
|
|
||||||
m_vertex.start = res.index_aligned;
|
|
||||||
m_vertex.count = count;
|
|
||||||
m_vertex_stream_buffer->Unmap(static_cast<u32>(count) * sizeof(GSVertexPT1));
|
|
||||||
|
|
||||||
DrawPrimitive();
|
|
||||||
|
|
||||||
EndScene();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSDeviceOGL::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c)
|
void GSDeviceOGL::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c)
|
||||||
{
|
{
|
||||||
GL_PUSH("DoMerge");
|
GL_PUSH("DoMerge");
|
||||||
|
@ -1705,30 +1693,21 @@ void GSDeviceOGL::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVecto
|
||||||
GSTextureOGL* RT = static_cast<GSTextureOGL*>(rt);
|
GSTextureOGL* RT = static_cast<GSTextureOGL*>(rt);
|
||||||
GSTextureOGL* DS = static_cast<GSTextureOGL*>(ds);
|
GSTextureOGL* DS = static_cast<GSTextureOGL*>(ds);
|
||||||
|
|
||||||
if (rt == NULL || !RT->IsBackbuffer())
|
OMSetFBO(m_fbo);
|
||||||
|
if (rt)
|
||||||
{
|
{
|
||||||
OMSetFBO(m_fbo);
|
OMAttachRt(RT);
|
||||||
if (rt)
|
|
||||||
{
|
|
||||||
OMAttachRt(RT);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OMAttachRt();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: it must be done after OMSetFBO
|
|
||||||
if (ds)
|
|
||||||
OMAttachDs(DS);
|
|
||||||
else
|
|
||||||
OMAttachDs();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Render in the backbuffer
|
OMAttachRt();
|
||||||
OMSetFBO(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: it must be done after OMSetFBO
|
||||||
|
if (ds)
|
||||||
|
OMAttachDs(DS);
|
||||||
|
else
|
||||||
|
OMAttachDs();
|
||||||
|
|
||||||
const GSVector2i size = rt ? rt->GetSize() : ds ? ds->GetSize() : GLState::viewport;
|
const GSVector2i size = rt ? rt->GetSize() : ds ? ds->GetSize() : GLState::viewport;
|
||||||
if (GLState::viewport != size)
|
if (GLState::viewport != size)
|
||||||
|
|
|
@ -213,12 +213,9 @@ private:
|
||||||
// Increment this constant whenever shaders change, to invalidate user's program binary cache.
|
// Increment this constant whenever shaders change, to invalidate user's program binary cache.
|
||||||
static constexpr u32 SHADER_VERSION = 1;
|
static constexpr u32 SHADER_VERSION = 1;
|
||||||
|
|
||||||
std::unique_ptr<GL::Context> m_gl_context;
|
|
||||||
int m_mipmap;
|
int m_mipmap;
|
||||||
int m_upscale_multiplier;
|
int m_upscale_multiplier;
|
||||||
TriFiltering m_filter;
|
|
||||||
|
|
||||||
static bool m_debug_gl_call;
|
|
||||||
static FILE* m_debug_gl_file;
|
static FILE* m_debug_gl_file;
|
||||||
|
|
||||||
bool m_disable_hw_gl_draw;
|
bool m_disable_hw_gl_draw;
|
||||||
|
@ -301,7 +298,6 @@ private:
|
||||||
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
|
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
|
||||||
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
|
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
|
||||||
|
|
||||||
std::unique_ptr<GSTexture> m_font;
|
|
||||||
AlignedBuffer<u8, 32> m_download_buffer;
|
AlignedBuffer<u8, 32> m_download_buffer;
|
||||||
|
|
||||||
GSTexture* CreateSurface(GSTexture::Type type, int w, int h, GSTexture::Format format) final;
|
GSTexture* CreateSurface(GSTexture::Type type, int w, int h, GSTexture::Format format) final;
|
||||||
|
@ -311,7 +307,6 @@ private:
|
||||||
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
|
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
|
||||||
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex) final;
|
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex) final;
|
||||||
void DoExternalFX(GSTexture* sTex, GSTexture* dTex) final;
|
void DoExternalFX(GSTexture* sTex, GSTexture* dTex) final;
|
||||||
void RenderOsd(GSTexture* dt) final;
|
|
||||||
|
|
||||||
void OMAttachRt(GSTextureOGL* rt = NULL);
|
void OMAttachRt(GSTextureOGL* rt = NULL);
|
||||||
void OMAttachDs(GSTextureOGL* ds = NULL);
|
void OMAttachDs(GSTextureOGL* ds = NULL);
|
||||||
|
@ -330,10 +325,10 @@ public:
|
||||||
// Used by OpenGL, so the same calling convention is required.
|
// Used by OpenGL, so the same calling convention is required.
|
||||||
static void APIENTRY DebugOutputToFile(GLenum gl_source, GLenum gl_type, GLuint id, GLenum gl_severity, GLsizei gl_length, const GLchar* gl_message, const void* userParam);
|
static void APIENTRY DebugOutputToFile(GLenum gl_source, GLenum gl_type, GLuint id, GLenum gl_severity, GLsizei gl_length, const GLchar* gl_message, const void* userParam);
|
||||||
|
|
||||||
bool Create(const WindowInfo& wi) override;
|
bool Create(HostDisplay* display) override;
|
||||||
bool Reset(int w, int h) override;
|
|
||||||
void Flip() override;
|
void ResetAPIState() override;
|
||||||
void SetVSync(int vsync) override;
|
void RestoreAPIState() override;
|
||||||
|
|
||||||
void DrawPrimitive();
|
void DrawPrimitive();
|
||||||
void DrawIndexedPrimitive();
|
void DrawIndexedPrimitive();
|
||||||
|
|
|
@ -98,7 +98,7 @@ namespace PboPool
|
||||||
m_map = NULL;
|
m_map = NULL;
|
||||||
m_offset = 0;
|
m_offset = 0;
|
||||||
|
|
||||||
for (GLsync fence : m_fence)
|
for (GLsync& fence : m_fence)
|
||||||
{
|
{
|
||||||
if (fence != 0)
|
if (fence != 0)
|
||||||
{
|
{
|
||||||
|
@ -238,13 +238,6 @@ GSTextureOGL::GSTextureOGL(Type type, int w, int h, Format format, GLuint fbo_re
|
||||||
m_int_shift = 3; // 4 bytes for depth + 4 bytes for stencil by texels
|
m_int_shift = 3; // 4 bytes for depth + 4 bytes for stencil by texels
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Backbuffer
|
|
||||||
case Format::Backbuffer:
|
|
||||||
m_int_format = 0;
|
|
||||||
m_int_type = 0;
|
|
||||||
m_int_shift = 2; // 4 bytes by texels
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Format::Invalid:
|
case Format::Invalid:
|
||||||
m_int_format = 0;
|
m_int_format = 0;
|
||||||
m_int_type = 0;
|
m_int_type = 0;
|
||||||
|
@ -254,8 +247,6 @@ GSTextureOGL::GSTextureOGL(Type type, int w, int h, Format format, GLuint fbo_re
|
||||||
|
|
||||||
switch (m_type)
|
switch (m_type)
|
||||||
{
|
{
|
||||||
case Type::Backbuffer:
|
|
||||||
return; // backbuffer isn't a real texture
|
|
||||||
case Type::Texture:
|
case Type::Texture:
|
||||||
// Only 32 bits input texture will be supported for mipmap
|
// Only 32 bits input texture will be supported for mipmap
|
||||||
m_max_layer = mipmap && m_format == Format::Color ? (int)log2(std::max(w, h)) : 1;
|
m_max_layer = mipmap && m_format == Format::Color ? (int)log2(std::max(w, h)) : 1;
|
||||||
|
@ -279,7 +270,6 @@ GSTextureOGL::GSTextureOGL(Type type, int w, int h, Format format, GLuint fbo_re
|
||||||
case Format::Color:
|
case Format::Color:
|
||||||
case Format::UInt32:
|
case Format::UInt32:
|
||||||
case Format::Int32:
|
case Format::Int32:
|
||||||
case Format::Backbuffer:
|
|
||||||
m_sparse &= GLLoader::found_compatible_GL_ARB_sparse_texture2;
|
m_sparse &= GLLoader::found_compatible_GL_ARB_sparse_texture2;
|
||||||
SetGpuPageSize(GSVector2i(127, 127));
|
SetGpuPageSize(GSVector2i(127, 127));
|
||||||
break;
|
break;
|
||||||
|
@ -357,6 +347,11 @@ GSTextureOGL::~GSTextureOGL()
|
||||||
GLState::available_vram += m_mem_usage;
|
GLState::available_vram += m_mem_usage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* GSTextureOGL::GetNativeHandle() const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<void*>(static_cast<uintptr_t>(m_texture_id));
|
||||||
|
}
|
||||||
|
|
||||||
void GSTextureOGL::Clear(const void* data)
|
void GSTextureOGL::Clear(const void* data)
|
||||||
{
|
{
|
||||||
glClearTexImage(m_texture_id, GL_TEX_LEVEL_0, m_int_format, m_int_type, data);
|
glClearTexImage(m_texture_id, GL_TEX_LEVEL_0, m_int_format, m_int_type, data);
|
||||||
|
@ -587,11 +582,7 @@ bool GSTextureOGL::Save(const std::string& fn)
|
||||||
GSPng::Format fmt = GSPng::RGB_PNG;
|
GSPng::Format fmt = GSPng::RGB_PNG;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (IsBackbuffer())
|
if (IsDss())
|
||||||
{
|
|
||||||
glReadPixels(0, 0, m_committed_size.x, m_committed_size.y, GL_RGBA, GL_UNSIGNED_BYTE, image.get());
|
|
||||||
}
|
|
||||||
else if (IsDss())
|
|
||||||
{
|
{
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,8 @@ public:
|
||||||
explicit GSTextureOGL(Type type, int w, int h, Format format, GLuint fbo_read, bool mipmap);
|
explicit GSTextureOGL(Type type, int w, int h, Format format, GLuint fbo_read, bool mipmap);
|
||||||
virtual ~GSTextureOGL();
|
virtual ~GSTextureOGL();
|
||||||
|
|
||||||
|
void* GetNativeHandle() const override;
|
||||||
|
|
||||||
bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) final;
|
bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) final;
|
||||||
bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) final;
|
bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) final;
|
||||||
void Unmap() final;
|
void Unmap() final;
|
||||||
|
@ -70,7 +72,6 @@ public:
|
||||||
bool Save(const std::string& fn) final;
|
bool Save(const std::string& fn) final;
|
||||||
|
|
||||||
GSMap Read(const GSVector4i& r, AlignedBuffer<u8, 32>& buffer);
|
GSMap Read(const GSVector4i& r, AlignedBuffer<u8, 32>& buffer);
|
||||||
bool IsBackbuffer() { return (m_type == Type::Backbuffer); }
|
|
||||||
bool IsDss() { return (m_type == Type::DepthStencil || m_type == Type::SparseDepthStencil); }
|
bool IsDss() { return (m_type == Type::DepthStencil || m_type == Type::SparseDepthStencil); }
|
||||||
|
|
||||||
u32 GetID() final { return m_texture_id; }
|
u32 GetID() final { return m_texture_id; }
|
||||||
|
|
|
@ -27,7 +27,7 @@ CONSTINIT const GSVector8 GSRendererSW::m_pos_scale2 = GSVector8::cxpr(1.0f / 16
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GSRendererSW::GSRendererSW(int threads)
|
GSRendererSW::GSRendererSW(int threads)
|
||||||
: m_fzb(NULL)
|
: GSRenderer(), m_fzb(NULL)
|
||||||
{
|
{
|
||||||
m_nativeres = true; // ignore ini, sw is always native
|
m_nativeres = true; // ignore ini, sw is always native
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ void GSRendererSW::Reset()
|
||||||
GSRenderer::Reset();
|
GSRenderer::Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSRendererSW::VSync(int field)
|
void GSRendererSW::VSync(u32 field)
|
||||||
{
|
{
|
||||||
Sync(0); // IncAge might delete a cached texture in use
|
Sync(0); // IncAge might delete a cached texture in use
|
||||||
|
|
||||||
|
@ -135,16 +135,6 @@ void GSRendererSW::VSync(int field)
|
||||||
// if((m_perfmon.GetFrame() & 255) == 0) m_rl->PrintStats();
|
// if((m_perfmon.GetFrame() & 255) == 0) m_rl->PrintStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSRendererSW::ResetDevice()
|
|
||||||
{
|
|
||||||
for (GSTexture*& tex : m_texture)
|
|
||||||
{
|
|
||||||
delete tex;
|
|
||||||
|
|
||||||
tex = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GSTexture* GSRendererSW::GetOutput(int i, int& y_offset)
|
GSTexture* GSRendererSW::GetOutput(int i, int& y_offset)
|
||||||
{
|
{
|
||||||
Sync(1);
|
Sync(1);
|
||||||
|
@ -156,7 +146,7 @@ GSTexture* GSRendererSW::GetOutput(int i, int& y_offset)
|
||||||
|
|
||||||
// TODO: round up bottom
|
// TODO: round up bottom
|
||||||
|
|
||||||
if (m_dev->ResizeTexture(&m_texture[i], w, h))
|
if (g_gs_device->ResizeTexture(&m_texture[i], w, h))
|
||||||
{
|
{
|
||||||
static int pitch = 1024 * 4;
|
static int pitch = 1024 * 4;
|
||||||
|
|
||||||
|
@ -1300,7 +1290,7 @@ bool GSRendererSW::GetScanlineGlobalData(SharedData* data)
|
||||||
gd.sel.pabe = 1;
|
gd.sel.pabe = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_aa1 && PRIM->AA1 && (primclass == GS_LINE_CLASS || primclass == GS_TRIANGLE_CLASS))
|
if (GSConfig.AA1 && PRIM->AA1 && (primclass == GS_LINE_CLASS || primclass == GS_TRIANGLE_CLASS))
|
||||||
{
|
{
|
||||||
gd.sel.aa1 = 1;
|
gd.sel.aa1 = 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,17 +79,16 @@ protected:
|
||||||
std::atomic<u32> m_fzb_pages[512]; // u16 frame/zbuf pages interleaved
|
std::atomic<u32> m_fzb_pages[512]; // u16 frame/zbuf pages interleaved
|
||||||
std::atomic<u16> m_tex_pages[512];
|
std::atomic<u16> m_tex_pages[512];
|
||||||
|
|
||||||
void Reset();
|
void Reset() override;
|
||||||
void VSync(int field);
|
void VSync(u32 field) override;
|
||||||
void ResetDevice();
|
GSTexture* GetOutput(int i, int& y_offset) override;
|
||||||
GSTexture* GetOutput(int i, int& y_offset);
|
GSTexture* GetFeedbackOutput() override;
|
||||||
GSTexture* GetFeedbackOutput();
|
|
||||||
|
|
||||||
void Draw();
|
void Draw() override;
|
||||||
void Queue(GSRingHeap::SharedPtr<GSRasterizerData>& item);
|
void Queue(GSRingHeap::SharedPtr<GSRasterizerData>& item);
|
||||||
void Sync(int reason);
|
void Sync(int reason);
|
||||||
void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r);
|
void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r) override;
|
||||||
void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut = false);
|
void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut = false) override;
|
||||||
|
|
||||||
void UsePages(const GSOffset::PageLooper& pages, const int type);
|
void UsePages(const GSOffset::PageLooper& pages, const int type);
|
||||||
void ReleasePages(const GSOffset::PageLooper& pages, const int type);
|
void ReleasePages(const GSOffset::PageLooper& pages, const int type);
|
||||||
|
@ -101,5 +100,5 @@ protected:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GSRendererSW(int threads);
|
GSRendererSW(int threads);
|
||||||
virtual ~GSRendererSW();
|
~GSRendererSW() override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,6 +32,11 @@ GSTextureSW::~GSTextureSW()
|
||||||
_aligned_free(m_data);
|
_aligned_free(m_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* GSTextureSW::GetNativeHandle() const
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool GSTextureSW::Update(const GSVector4i& r, const void* data, int pitch, int layer)
|
bool GSTextureSW::Update(const GSVector4i& r, const void* data, int pitch, int layer)
|
||||||
{
|
{
|
||||||
GSMap m;
|
GSMap m;
|
||||||
|
|
|
@ -33,4 +33,5 @@ public:
|
||||||
bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) override;
|
bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) override;
|
||||||
void Unmap() override;
|
void Unmap() override;
|
||||||
bool Save(const std::string& fn) override;
|
bool Save(const std::string& fn) override;
|
||||||
|
void* GetNativeHandle() const override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -85,8 +85,6 @@ const char* dialog_message(int ID, bool* updateText)
|
||||||
" 0500 0500, fixes Persona 3 minimap, helps Haunting Ground.");
|
" 0500 0500, fixes Persona 3 minimap, helps Haunting Ground.");
|
||||||
case IDC_OSD_LOG:
|
case IDC_OSD_LOG:
|
||||||
return cvtString("Prints log messages from the Function keys onscreen.");
|
return cvtString("Prints log messages from the Function keys onscreen.");
|
||||||
case IDC_OSD_MONITOR:
|
|
||||||
return cvtString("Continuously prints/overlays the FPS counter and the EE ('CPU-usage') ,\nGS ('GPU-usage') and VU(if the MTVU speedhack is enabled) percentages onscreen.");
|
|
||||||
case IDC_PALTEX:
|
case IDC_PALTEX:
|
||||||
return cvtString("Enabled: GPU converts colormap-textures.\n"
|
return cvtString("Enabled: GPU converts colormap-textures.\n"
|
||||||
"Disabled: CPU converts colormap-textures.\n\n"
|
"Disabled: CPU converts colormap-textures.\n\n"
|
||||||
|
@ -167,10 +165,6 @@ const char* dialog_message(int ID, bool* updateText)
|
||||||
case IDC_SPARSE_TEXTURE:
|
case IDC_SPARSE_TEXTURE:
|
||||||
return cvtString("Allows to reduce VRAM usage on the GPU.\n\n"
|
return cvtString("Allows to reduce VRAM usage on the GPU.\n\n"
|
||||||
"Note: Feature is currently experimental and works only on Nvidia GPUs.");
|
"Note: Feature is currently experimental and works only on Nvidia GPUs.");
|
||||||
case IDC_OSD_MAX_LOG_EDIT:
|
|
||||||
case IDC_OSD_MAX_LOG:
|
|
||||||
return cvtString("Sets the maximum number of log messages on the screen or in the buffer at the same time.\n\n"
|
|
||||||
"The maximum number of messages visible on the screen at the same time also depends on the character size.");
|
|
||||||
case IDC_LINEAR_PRESENT:
|
case IDC_LINEAR_PRESENT:
|
||||||
return cvtString("Use bilinear filtering when Upscaling/Downscaling the image to the screen. Disable it if you want a sharper/pixelated output.");
|
return cvtString("Use bilinear filtering when Upscaling/Downscaling the image to the screen. Disable it if you want a sharper/pixelated output.");
|
||||||
// Exclusive for Hardware Renderer
|
// Exclusive for Hardware Renderer
|
||||||
|
|
|
@ -17,6 +17,11 @@
|
||||||
#include "GSwxDialog.h"
|
#include "GSwxDialog.h"
|
||||||
#include "gui/AppConfig.h"
|
#include "gui/AppConfig.h"
|
||||||
#include "GS/GSUtil.h"
|
#include "GS/GSUtil.h"
|
||||||
|
#include "HostDisplay.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "Frontend/D3D11HostDisplay.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace GSSettingsDialog;
|
using namespace GSSettingsDialog;
|
||||||
|
|
||||||
|
@ -317,7 +322,7 @@ RendererTab::RendererTab(wxWindow* parent)
|
||||||
void RendererTab::UpdateBlendMode(GSRendererType renderer)
|
void RendererTab::UpdateBlendMode(GSRendererType renderer)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (renderer == GSRendererType::DX1011_HW)
|
if (renderer == GSRendererType::DX11)
|
||||||
{
|
{
|
||||||
m_blend_mode_d3d11.first ->Show();
|
m_blend_mode_d3d11.first ->Show();
|
||||||
m_blend_mode_d3d11.second->Show();
|
m_blend_mode_d3d11.second->Show();
|
||||||
|
@ -510,30 +515,25 @@ OSDTab::OSDTab(wxWindow* parent)
|
||||||
const int space = wxSizerFlags().Border().GetBorderInPixels();
|
const int space = wxSizerFlags().Border().GetBorderInPixels();
|
||||||
PaddedBoxSizer<wxBoxSizer> tab_box(wxVERTICAL);
|
PaddedBoxSizer<wxBoxSizer> tab_box(wxVERTICAL);
|
||||||
|
|
||||||
CheckboxPrereq monitor_check(m_ui.addCheckBox(tab_box.inner, "Enable Monitor", "osd_monitor_enabled", IDC_OSD_MONITOR));
|
PaddedBoxSizer<wxStaticBoxSizer> font_box(wxVERTICAL, this, "Visuals");
|
||||||
|
|
||||||
PaddedBoxSizer<wxStaticBoxSizer> font_box(wxVERTICAL, this, "Font");
|
|
||||||
auto* font_grid = new wxFlexGridSizer(2, space, space);
|
auto* font_grid = new wxFlexGridSizer(2, space, space);
|
||||||
font_grid->AddGrowableCol(1);
|
font_grid->AddGrowableCol(1);
|
||||||
|
|
||||||
m_ui.addSpinAndLabel(font_grid, "Size:", "osd_fontsize", 1, 100, 25, -1, monitor_check);
|
m_ui.addSliderAndLabel(font_grid, "Scale:", "OsdScale", 50, 300, 100, -1);
|
||||||
|
|
||||||
m_ui.addSliderAndLabel(font_grid, "Red:", "osd_color_r", 0, 255, 0, -1, monitor_check);
|
|
||||||
m_ui.addSliderAndLabel(font_grid, "Green:", "osd_color_g", 0, 255, 0, -1, monitor_check);
|
|
||||||
m_ui.addSliderAndLabel(font_grid, "Blue:", "osd_color_b", 0, 255, 0, -1, monitor_check);
|
|
||||||
m_ui.addSliderAndLabel(font_grid, "Opacity:", "osd_color_opacity", 0, 100, 100, -1, monitor_check);
|
|
||||||
|
|
||||||
font_box->Add(font_grid, wxSizerFlags().Expand());
|
font_box->Add(font_grid, wxSizerFlags().Expand());
|
||||||
tab_box->Add(font_box.outer, wxSizerFlags().Expand());
|
tab_box->Add(font_box.outer, wxSizerFlags().Expand());
|
||||||
|
|
||||||
CheckboxPrereq log_check(m_ui.addCheckBox(tab_box.inner, "Enable Log", "osd_log_enabled", IDC_OSD_LOG));
|
|
||||||
|
|
||||||
PaddedBoxSizer<wxStaticBoxSizer> log_box(wxVERTICAL, this, "Log Messages");
|
PaddedBoxSizer<wxStaticBoxSizer> log_box(wxVERTICAL, this, "Log Messages");
|
||||||
auto* log_grid = new wxFlexGridSizer(2, space, space);
|
auto* log_grid = new wxFlexGridSizer(2, space, space);
|
||||||
log_grid->AddGrowableCol(1);
|
log_grid->AddGrowableCol(1);
|
||||||
|
|
||||||
m_ui.addSpinAndLabel(log_grid, "Timeout (seconds):", "osd_log_timeout", 2, 10, 4, -1, log_check);
|
m_ui.addCheckBox(log_grid, "Show Messages", "OsdShowMessages", -1);
|
||||||
m_ui.addSpinAndLabel(log_grid, "Max On-Screen Messages:", "osd_max_log_messages", 1, 10, 2, IDC_OSD_MAX_LOG, log_check);
|
m_ui.addCheckBox(log_grid, "Show Speed", "OsdShowSpeed", -1);
|
||||||
|
m_ui.addCheckBox(log_grid, "Show FPS", "OsdShowFPS", -1);
|
||||||
|
m_ui.addCheckBox(log_grid, "Show CPU Usage", "OsdShowCPU", -1);
|
||||||
|
m_ui.addCheckBox(log_grid, "Show Resolution", "OsdShowResolution", -1);
|
||||||
|
m_ui.addCheckBox(log_grid, "Show Statistics", "OsdShowGSStats", -1);
|
||||||
|
|
||||||
log_box->Add(log_grid, wxSizerFlags().Expand());
|
log_box->Add(log_grid, wxSizerFlags().Expand());
|
||||||
tab_box->Add(log_box.outer, wxSizerFlags().Expand());
|
tab_box->Add(log_box.outer, wxSizerFlags().Expand());
|
||||||
|
@ -554,11 +554,9 @@ DebugTab::DebugTab(wxWindow* parent)
|
||||||
{
|
{
|
||||||
PaddedBoxSizer<wxStaticBoxSizer> debug_box(wxVERTICAL, this, "Debug");
|
PaddedBoxSizer<wxStaticBoxSizer> debug_box(wxVERTICAL, this, "Debug");
|
||||||
auto* debug_check_box = new wxWrapSizer(wxHORIZONTAL);
|
auto* debug_check_box = new wxWrapSizer(wxHORIZONTAL);
|
||||||
|
m_ui.addCheckBox(debug_check_box, "Use Blit Swap Chain", "UseBlitSwapChain");
|
||||||
m_ui.addCheckBox(debug_check_box, "Disable Shader Cache", "disable_shader_cache");
|
m_ui.addCheckBox(debug_check_box, "Disable Shader Cache", "disable_shader_cache");
|
||||||
m_ui.addCheckBox(debug_check_box, "Print GL error", "debug_opengl");
|
m_ui.addCheckBox(debug_check_box, "Use Debug Device", "UseDebugDevice");
|
||||||
#ifdef _WIN32
|
|
||||||
m_ui.addCheckBox(debug_check_box, "D3D Debug Layer", "debug_d3d");
|
|
||||||
#endif
|
|
||||||
m_ui.addCheckBox(debug_check_box, "Dump GS data", "dump");
|
m_ui.addCheckBox(debug_check_box, "Dump GS data", "dump");
|
||||||
|
|
||||||
auto* debug_save_check_box = new wxWrapSizer(wxHORIZONTAL);
|
auto* debug_save_check_box = new wxWrapSizer(wxHORIZONTAL);
|
||||||
|
@ -667,43 +665,47 @@ void Dialog::OnRendererChange(wxCommandEvent&)
|
||||||
|
|
||||||
GSRendererType Dialog::GetSelectedRendererType()
|
GSRendererType Dialog::GetSelectedRendererType()
|
||||||
{
|
{
|
||||||
int index = m_renderer_select->GetSelection();
|
const int index = m_renderer_select->GetSelection();
|
||||||
|
|
||||||
// there is no currently selected renderer or the combo box has more entries than the renderer list or the current selection is negative
|
// there is no currently selected renderer or the combo box has more entries than the renderer list or the current selection is negative
|
||||||
// make sure you haven't made a mistake initializing everything
|
// make sure you haven't made a mistake initializing everything
|
||||||
ASSERT(index < static_cast<int>(theApp.m_gs_renderers.size()) || index >= 0);
|
ASSERT(index < static_cast<int>(theApp.m_gs_renderers.size()) || index >= 0);
|
||||||
|
|
||||||
const GSRendererType type = static_cast<GSRendererType>(
|
const GSRendererType type = static_cast<GSRendererType>(theApp.m_gs_renderers[index].value);
|
||||||
theApp.m_gs_renderers[index].value
|
return (type == GSRendererType::Auto) ? GSUtil::GetPreferredRenderer() : type;
|
||||||
);
|
|
||||||
|
|
||||||
return type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dialog::RendererChange()
|
void Dialog::RendererChange()
|
||||||
{
|
{
|
||||||
GSRendererType renderer = GetSelectedRendererType();
|
const GSRendererType renderer = GetSelectedRendererType();
|
||||||
std::string current;
|
const std::string current_adapter(theApp.GetConfigS("Adapter"));
|
||||||
int current_sel = m_adapter_select->GetSelection();
|
|
||||||
if (current_sel >= 0 && current_sel < static_cast<int>(m_adapter_arr_string.Count()))
|
|
||||||
current = m_adapter_arr_string[current_sel].ToUTF8();
|
|
||||||
bool explicitly_selected_default = m_adapter_arr_string.Count() > 1 && current_sel == 0;
|
|
||||||
|
|
||||||
std::vector<std::string> adapters = GSUtil::GetAdapterList(renderer);
|
HostDisplay::AdapterAndModeList list;
|
||||||
|
switch (renderer)
|
||||||
m_adapter_arr_string.Clear();
|
|
||||||
m_adapter_arr_string.Add(_("Default Adapter"));
|
|
||||||
int new_sel = theApp.GetConfigI("adapter_index") + 1;
|
|
||||||
if (new_sel < 0 || new_sel >= static_cast<int>(adapters.size() + 1) || explicitly_selected_default)
|
|
||||||
new_sel = 0;
|
|
||||||
for (std::string& adapter : adapters)
|
|
||||||
{
|
{
|
||||||
if (adapter == current)
|
#ifdef _WIN32
|
||||||
new_sel = m_adapter_arr_string.Count();
|
case GSRendererType::DX11:
|
||||||
m_adapter_arr_string.Add(fromUTF8(adapter));
|
list = D3D11HostDisplay::StaticGetAdapterAndModeList();
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
m_adapter_select->Set(m_adapter_arr_string);
|
|
||||||
m_adapter_select->SetSelection(new_sel);
|
m_adapter_select->Clear();
|
||||||
|
m_adapter_select->Insert(_("Default Adapter"), 0);
|
||||||
|
if (current_adapter.empty())
|
||||||
|
m_adapter_select->SetSelection(0);
|
||||||
|
|
||||||
|
for (const std::string& name : list.adapter_names)
|
||||||
|
{
|
||||||
|
m_adapter_select->Insert(fromUTF8(name), m_adapter_select->GetCount());
|
||||||
|
if (current_adapter == name)
|
||||||
|
m_adapter_select->SetSelection(m_adapter_select->GetCount() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_adapter_select->Enable(!list.adapter_names.empty());
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
m_renderer_panel->UpdateBlendMode(renderer);
|
m_renderer_panel->UpdateBlendMode(renderer);
|
||||||
|
|
||||||
|
@ -714,9 +716,8 @@ void Dialog::RendererChange()
|
||||||
void Dialog::Load()
|
void Dialog::Load()
|
||||||
{
|
{
|
||||||
m_ui.Load();
|
m_ui.Load();
|
||||||
GSRendererType renderer = GSRendererType(theApp.GetConfigI("Renderer"));
|
|
||||||
if (renderer == GSRendererType::Undefined)
|
const GSRendererType renderer = GSRendererType(theApp.GetConfigI("Renderer"));
|
||||||
renderer = GSUtil::GetPreferredRenderer();
|
|
||||||
m_renderer_select->SetSelection(get_config_index(theApp.m_gs_renderers, static_cast<int>(renderer)));
|
m_renderer_select->SetSelection(get_config_index(theApp.m_gs_renderers, static_cast<int>(renderer)));
|
||||||
|
|
||||||
RendererChange();
|
RendererChange();
|
||||||
|
@ -735,7 +736,7 @@ void Dialog::Save()
|
||||||
// only save the adapter when it makes sense to
|
// only save the adapter when it makes sense to
|
||||||
// prevents changing the adapter, switching to another renderer and saving
|
// prevents changing the adapter, switching to another renderer and saving
|
||||||
if (m_adapter_select->GetCount() > 1) // First option is system default
|
if (m_adapter_select->GetCount() > 1) // First option is system default
|
||||||
theApp.SetConfig("adapter_index", m_adapter_select->GetSelection() - 1);
|
theApp.SetConfig("Adapter", m_adapter_select->GetStringSelection().c_str());
|
||||||
|
|
||||||
m_hacks_panel->Save();
|
m_hacks_panel->Save();
|
||||||
m_renderer_panel->Save();
|
m_renderer_panel->Save();
|
||||||
|
@ -762,14 +763,14 @@ void Dialog::Update()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// cross-tab dependencies yay
|
// cross-tab dependencies yay
|
||||||
const bool is_hw = renderer == GSRendererType::OGL_HW || renderer == GSRendererType::DX1011_HW;
|
const bool is_hw = renderer == GSRendererType::OGL || renderer == GSRendererType::DX11;
|
||||||
const bool is_upscale = m_renderer_panel->m_internal_resolution->GetSelection() != 0;
|
const bool is_upscale = m_renderer_panel->m_internal_resolution->GetSelection() != 0;
|
||||||
m_hacks_panel->m_is_native_res = !is_hw || !is_upscale;
|
m_hacks_panel->m_is_native_res = !is_hw || !is_upscale;
|
||||||
m_hacks_panel->m_is_hardware = is_hw;
|
m_hacks_panel->m_is_hardware = is_hw;
|
||||||
m_hacks_panel->m_is_ogl_hw = renderer == GSRendererType::OGL_HW;
|
m_hacks_panel->m_is_ogl_hw = renderer == GSRendererType::OGL;
|
||||||
m_renderer_panel->m_is_hardware = is_hw;
|
m_renderer_panel->m_is_hardware = is_hw;
|
||||||
m_renderer_panel->m_is_native_res = !is_hw || !is_upscale;
|
m_renderer_panel->m_is_native_res = !is_hw || !is_upscale;
|
||||||
m_debug_panel->m_is_ogl_hw = renderer == GSRendererType::OGL_HW;
|
m_debug_panel->m_is_ogl_hw = renderer == GSRendererType::OGL;
|
||||||
|
|
||||||
m_ui.Update();
|
m_ui.Update();
|
||||||
m_hacks_panel->DoUpdate();
|
m_hacks_panel->DoUpdate();
|
||||||
|
|
|
@ -187,7 +187,6 @@ namespace GSSettingsDialog
|
||||||
wxChoice* m_renderer_select;
|
wxChoice* m_renderer_select;
|
||||||
wxChoice* m_adapter_select;
|
wxChoice* m_adapter_select;
|
||||||
wxChoice* m_bifilter_select;
|
wxChoice* m_bifilter_select;
|
||||||
wxArrayString m_adapter_arr_string;
|
|
||||||
RendererTab* m_renderer_panel;
|
RendererTab* m_renderer_panel;
|
||||||
HacksTab* m_hacks_panel;
|
HacksTab* m_hacks_panel;
|
||||||
DebugTab* m_debug_panel;
|
DebugTab* m_debug_panel;
|
||||||
|
|
15
pcsx2/Host.h
15
pcsx2/Host.h
|
@ -28,7 +28,12 @@ struct HostKeyEvent
|
||||||
{
|
{
|
||||||
NoEvent = 0,
|
NoEvent = 0,
|
||||||
KeyPressed = 1,
|
KeyPressed = 1,
|
||||||
KeyReleased = 2
|
KeyReleased = 2,
|
||||||
|
MousePressed = 3,
|
||||||
|
MouseReleased = 4,
|
||||||
|
MouseWheelDown = 5,
|
||||||
|
MouseWheelUp = 6,
|
||||||
|
MouseMove = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
Type type;
|
Type type;
|
||||||
|
@ -44,6 +49,14 @@ namespace Host
|
||||||
/// Reads a resource file file from the resources directory as a string.
|
/// Reads a resource file file from the resources directory as a string.
|
||||||
std::optional<std::string> ReadResourceFileToString(const char* filename);
|
std::optional<std::string> ReadResourceFileToString(const char* filename);
|
||||||
|
|
||||||
|
/// Adds OSD messages, duration is in seconds.
|
||||||
|
void AddOSDMessage(std::string message, float duration = 2.0f);
|
||||||
|
void AddKeyedOSDMessage(std::string key, std::string message, float duration = 2.0f);
|
||||||
|
void AddFormattedOSDMessage(float duration, const char* format, ...);
|
||||||
|
void AddKeyedFormattedOSDMessage(std::string key, float duration, const char* format, ...);
|
||||||
|
void RemoveKeyedOSDMessage(std::string key);
|
||||||
|
void ClearOSDMessages();
|
||||||
|
|
||||||
/// Displays an asynchronous error on the UI thread, i.e. doesn't block the caller.
|
/// Displays an asynchronous error on the UI thread, i.e. doesn't block the caller.
|
||||||
void ReportErrorAsync(const std::string_view& title, const std::string_view& message);
|
void ReportErrorAsync(const std::string_view& title, const std::string_view& message);
|
||||||
void ReportFormattedErrorAsync(const std::string_view& title, const char* format, ...);
|
void ReportFormattedErrorAsync(const std::string_view& title, const char* format, ...);
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
/* PCSX2 - PS2 Emulator for PCs
|
||||||
|
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||||
|
*
|
||||||
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||||
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||||
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PrecompiledHeader.h"
|
||||||
|
|
||||||
|
#include "HostDisplay.h"
|
||||||
|
#include "common/Assertions.h"
|
||||||
|
#include "common/Console.h"
|
||||||
|
#include "common/StringUtil.h"
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
HostDisplayTexture::~HostDisplayTexture() = default;
|
||||||
|
|
||||||
|
HostDisplay::~HostDisplay() = default;
|
||||||
|
|
||||||
|
const char* HostDisplay::RenderAPIToString(RenderAPI api)
|
||||||
|
{
|
||||||
|
static const char* names[] = {"None", "D3D11", "Vulkan", "OpenGL", "OpenGLES"};
|
||||||
|
return (static_cast<u32>(api) >= std::size(names)) ? names[0] : names[static_cast<u32>(api)];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HostDisplay::UsesLowerLeftOrigin() const
|
||||||
|
{
|
||||||
|
const RenderAPI api = GetRenderAPI();
|
||||||
|
return (api == RenderAPI::OpenGL || api == RenderAPI::OpenGLES);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HostDisplay::GetHostRefreshRate(float* refresh_rate)
|
||||||
|
{
|
||||||
|
if (m_window_info.surface_refresh_rate > 0.0f)
|
||||||
|
{
|
||||||
|
*refresh_rate = m_window_info.surface_refresh_rate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return WindowInfo::QueryRefreshRateForWindow(m_window_info, refresh_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HostDisplay::ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate)
|
||||||
|
{
|
||||||
|
if (!mode.empty())
|
||||||
|
{
|
||||||
|
std::string_view::size_type sep1 = mode.find('x');
|
||||||
|
if (sep1 != std::string_view::npos)
|
||||||
|
{
|
||||||
|
std::optional<u32> owidth = StringUtil::FromChars<u32>(mode.substr(0, sep1));
|
||||||
|
sep1++;
|
||||||
|
|
||||||
|
while (sep1 < mode.length() && std::isspace(mode[sep1]))
|
||||||
|
sep1++;
|
||||||
|
|
||||||
|
if (owidth.has_value() && sep1 < mode.length())
|
||||||
|
{
|
||||||
|
std::string_view::size_type sep2 = mode.find('@', sep1);
|
||||||
|
if (sep2 != std::string_view::npos)
|
||||||
|
{
|
||||||
|
std::optional<u32> oheight = StringUtil::FromChars<u32>(mode.substr(sep1, sep2 - sep1));
|
||||||
|
sep2++;
|
||||||
|
|
||||||
|
while (sep2 < mode.length() && std::isspace(mode[sep2]))
|
||||||
|
sep2++;
|
||||||
|
|
||||||
|
if (oheight.has_value() && sep2 < mode.length())
|
||||||
|
{
|
||||||
|
std::optional<float> orefresh_rate = StringUtil::FromChars<float>(mode.substr(sep2));
|
||||||
|
if (orefresh_rate.has_value())
|
||||||
|
{
|
||||||
|
*width = owidth.value();
|
||||||
|
*height = oheight.value();
|
||||||
|
*refresh_rate = orefresh_rate.value();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*width = 0;
|
||||||
|
*height = 0;
|
||||||
|
*refresh_rate = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string HostDisplay::GetFullscreenModeString(u32 width, u32 height, float refresh_rate)
|
||||||
|
{
|
||||||
|
return StringUtil::StdStringFromFormat("%u x %u @ %f hz", width, height, refresh_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "Frontend/OpenGLHostDisplay.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "Frontend/D3D11HostDisplay.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::unique_ptr<HostDisplay> HostDisplay::CreateDisplayForAPI(RenderAPI api)
|
||||||
|
{
|
||||||
|
switch (api)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
case HostDisplay::RenderAPI::D3D11:
|
||||||
|
return std::make_unique<D3D11HostDisplay>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case HostDisplay::RenderAPI::OpenGL:
|
||||||
|
case HostDisplay::RenderAPI::OpenGLES:
|
||||||
|
return std::make_unique<OpenGLHostDisplay>();
|
||||||
|
|
||||||
|
default:
|
||||||
|
Console.Error("Unknown render API %u", static_cast<unsigned>(api));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
/* PCSX2 - PS2 Emulator for PCs
|
||||||
|
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||||
|
*
|
||||||
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||||
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||||
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/Pcsx2Defs.h"
|
||||||
|
#include "common/WindowInfo.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Host.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
/// An abstracted RGBA8 texture.
|
||||||
|
class HostDisplayTexture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~HostDisplayTexture();
|
||||||
|
|
||||||
|
virtual void* GetHandle() const = 0;
|
||||||
|
virtual u32 GetWidth() const = 0;
|
||||||
|
virtual u32 GetHeight() const = 0;
|
||||||
|
virtual u32 GetLayers() const = 0;
|
||||||
|
virtual u32 GetLevels() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Interface to the frontend's renderer.
|
||||||
|
class HostDisplay
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class RenderAPI
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
D3D11,
|
||||||
|
Vulkan,
|
||||||
|
OpenGL,
|
||||||
|
OpenGLES
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Alignment
|
||||||
|
{
|
||||||
|
LeftOrTop,
|
||||||
|
Center,
|
||||||
|
RightOrBottom
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AdapterAndModeList
|
||||||
|
{
|
||||||
|
std::vector<std::string> adapter_names;
|
||||||
|
std::vector<std::string> fullscreen_modes;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ~HostDisplay();
|
||||||
|
|
||||||
|
/// Returns a string representing the specified API.
|
||||||
|
static const char* RenderAPIToString(RenderAPI api);
|
||||||
|
|
||||||
|
/// Creates a display for the specified API.
|
||||||
|
static std::unique_ptr<HostDisplay> CreateDisplayForAPI(RenderAPI api);
|
||||||
|
|
||||||
|
/// Parses a fullscreen mode into its components (width * height @ refresh hz)
|
||||||
|
static bool ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate);
|
||||||
|
|
||||||
|
/// Converts a fullscreen mode to a string.
|
||||||
|
static std::string GetFullscreenModeString(u32 width, u32 height, float refresh_rate);
|
||||||
|
|
||||||
|
__fi const WindowInfo& GetWindowInfo() const { return m_window_info; }
|
||||||
|
__fi s32 GetWindowWidth() const { return static_cast<s32>(m_window_info.surface_width); }
|
||||||
|
__fi s32 GetWindowHeight() const { return static_cast<s32>(m_window_info.surface_height); }
|
||||||
|
__fi float GetWindowScale() const { return m_window_info.surface_scale; }
|
||||||
|
|
||||||
|
/// Changes the alignment for this display (screen positioning).
|
||||||
|
__fi Alignment GetDisplayAlignment() const { return m_display_alignment; }
|
||||||
|
__fi void SetDisplayAlignment(Alignment alignment) { m_display_alignment = alignment; }
|
||||||
|
|
||||||
|
virtual RenderAPI GetRenderAPI() const = 0;
|
||||||
|
virtual void* GetRenderDevice() const = 0;
|
||||||
|
virtual void* GetRenderContext() const = 0;
|
||||||
|
virtual void* GetRenderSurface() const = 0;
|
||||||
|
|
||||||
|
virtual bool HasRenderDevice() const = 0;
|
||||||
|
virtual bool HasRenderSurface() const = 0;
|
||||||
|
|
||||||
|
virtual bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device) = 0;
|
||||||
|
virtual bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) = 0;
|
||||||
|
virtual bool MakeRenderContextCurrent() = 0;
|
||||||
|
virtual bool DoneRenderContextCurrent() = 0;
|
||||||
|
virtual void DestroyRenderDevice() = 0;
|
||||||
|
virtual void DestroyRenderSurface() = 0;
|
||||||
|
virtual bool ChangeRenderWindow(const WindowInfo& wi) = 0;
|
||||||
|
virtual bool SupportsFullscreen() const = 0;
|
||||||
|
virtual bool IsFullscreen() = 0;
|
||||||
|
virtual bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) = 0;
|
||||||
|
virtual AdapterAndModeList GetAdapterAndModeList() = 0;
|
||||||
|
|
||||||
|
/// Call when the window size changes externally to recreate any resources.
|
||||||
|
virtual void ResizeRenderWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) = 0;
|
||||||
|
|
||||||
|
/// Creates an abstracted RGBA8 texture. If dynamic, the texture can be updated with UpdateTexture() below.
|
||||||
|
virtual std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, const void* data,
|
||||||
|
u32 data_stride, bool dynamic = false) = 0;
|
||||||
|
virtual void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data,
|
||||||
|
u32 data_stride) = 0;
|
||||||
|
|
||||||
|
/// Returns false if the window was completely occluded. If frame_skip is set, the frame won't be
|
||||||
|
/// displayed, but the GPU command queue will still be flushed.
|
||||||
|
virtual bool BeginPresent(bool frame_skip) = 0;
|
||||||
|
|
||||||
|
/// Presents the frame to the display, and renders OSD elements.
|
||||||
|
virtual void EndPresent() = 0;
|
||||||
|
|
||||||
|
/// Changes vsync mode for this display.
|
||||||
|
virtual void SetVSync(VsyncMode mode) = 0;
|
||||||
|
|
||||||
|
/// ImGui context management, usually called by derived classes.
|
||||||
|
virtual bool CreateImGuiContext() = 0;
|
||||||
|
virtual void DestroyImGuiContext() = 0;
|
||||||
|
virtual bool UpdateImGuiFontTexture() = 0;
|
||||||
|
|
||||||
|
/// Returns the effective refresh rate of this display.
|
||||||
|
virtual bool GetHostRefreshRate(float* refresh_rate);
|
||||||
|
|
||||||
|
/// Returns true if it's an OpenGL-based renderer.
|
||||||
|
bool UsesLowerLeftOrigin() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
WindowInfo m_window_info;
|
||||||
|
Alignment m_display_alignment = Alignment::Center;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Host
|
||||||
|
{
|
||||||
|
/// Creates the host display. This may create a new window. The API used depends on the current configuration.
|
||||||
|
HostDisplay* AcquireHostDisplay(HostDisplay::RenderAPI api);
|
||||||
|
|
||||||
|
/// Destroys the host display. This may close the display window.
|
||||||
|
void ReleaseHostDisplay();
|
||||||
|
|
||||||
|
/// Returns a pointer to the current host display abstraction. Assumes AcquireHostDisplay() has been caled.
|
||||||
|
HostDisplay* GetHostDisplay();
|
||||||
|
|
||||||
|
/// Returns false if the window was completely occluded. If frame_skip is set, the frame won't be
|
||||||
|
/// displayed, but the GPU command queue will still be flushed.
|
||||||
|
bool BeginPresentFrame(bool frame_skip);
|
||||||
|
|
||||||
|
/// Presents the frame to the display, and renders OSD elements.
|
||||||
|
void EndPresentFrame();
|
||||||
|
|
||||||
|
/// Called on the MTGS thread when a resize request is received.
|
||||||
|
void ResizeHostDisplay(u32 new_window_width, u32 new_window_height, float new_window_scale);
|
||||||
|
|
||||||
|
/// Called on the MTGS thread when a request to update the display is received.
|
||||||
|
/// This could be a fullscreen transition, for example.
|
||||||
|
void UpdateHostDisplay();
|
||||||
|
} // namespace Host
|
||||||
|
|
188
pcsx2/MTGS.cpp
188
pcsx2/MTGS.cpp
|
@ -19,15 +19,19 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <wx/datetime.h>
|
#include <wx/datetime.h>
|
||||||
|
|
||||||
|
#include "common/StringUtil.h"
|
||||||
|
|
||||||
#include "GS.h"
|
#include "GS.h"
|
||||||
#include "Gif_Unit.h"
|
#include "Gif_Unit.h"
|
||||||
#include "MTVU.h"
|
#include "MTVU.h"
|
||||||
#include "Elfheader.h"
|
#include "Elfheader.h"
|
||||||
#include "PerformanceMetrics.h"
|
|
||||||
#include "gui/Dialogs/ModalPopups.h"
|
|
||||||
|
|
||||||
#include "common/WindowInfo.h"
|
#include "Host.h"
|
||||||
extern WindowInfo g_gs_window_info;
|
#include "HostDisplay.h"
|
||||||
|
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
|
#include "gui/Dialogs/ModalPopups.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
// Uncomment this to enable profiling of the GS RingBufferCopy function.
|
// Uncomment this to enable profiling of the GS RingBufferCopy function.
|
||||||
//#define PCSX2_GSRING_SAMPLING_STATS
|
//#define PCSX2_GSRING_SAMPLING_STATS
|
||||||
|
@ -48,8 +52,6 @@ using namespace Threading;
|
||||||
// =====================================================================================================
|
// =====================================================================================================
|
||||||
|
|
||||||
alignas(32) MTGS_BufferedData RingBuffer;
|
alignas(32) MTGS_BufferedData RingBuffer;
|
||||||
extern bool renderswitch;
|
|
||||||
std::atomic_bool init_gspanel = true;
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef RINGBUF_DEBUG_STACK
|
#ifdef RINGBUF_DEBUG_STACK
|
||||||
|
@ -68,42 +70,6 @@ SysMtgsThread::SysMtgsThread()
|
||||||
// All other state vars are initialized by OnStart().
|
// All other state vars are initialized by OnStart().
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef void (SysMtgsThread::*FnPtr_MtgsThreadMethod)();
|
|
||||||
|
|
||||||
class SysExecEvent_InvokeMtgsThreadMethod : public SysExecEvent
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
FnPtr_MtgsThreadMethod m_method;
|
|
||||||
bool m_IsCritical;
|
|
||||||
|
|
||||||
public:
|
|
||||||
wxString GetEventName() const { return L"MtgsThreadMethod"; }
|
|
||||||
virtual ~SysExecEvent_InvokeMtgsThreadMethod() = default;
|
|
||||||
SysExecEvent_InvokeMtgsThreadMethod* Clone() const { return new SysExecEvent_InvokeMtgsThreadMethod(*this); }
|
|
||||||
|
|
||||||
bool AllowCancelOnExit() const { return false; }
|
|
||||||
bool IsCriticalEvent() const { return m_IsCritical; }
|
|
||||||
|
|
||||||
SysExecEvent_InvokeMtgsThreadMethod(FnPtr_MtgsThreadMethod method, bool critical = false)
|
|
||||||
{
|
|
||||||
m_method = method;
|
|
||||||
m_IsCritical = critical;
|
|
||||||
}
|
|
||||||
|
|
||||||
SysExecEvent_InvokeMtgsThreadMethod& Critical()
|
|
||||||
{
|
|
||||||
m_IsCritical = true;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void InvokeEvent()
|
|
||||||
{
|
|
||||||
if (m_method)
|
|
||||||
(mtgsThread.*m_method)();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void SysMtgsThread::OnStart()
|
void SysMtgsThread::OnStart()
|
||||||
{
|
{
|
||||||
m_Opened = false;
|
m_Opened = false;
|
||||||
|
@ -135,7 +101,6 @@ SysMtgsThread::~SysMtgsThread()
|
||||||
|
|
||||||
void SysMtgsThread::OnResumeReady()
|
void SysMtgsThread::OnResumeReady()
|
||||||
{
|
{
|
||||||
PerformanceMetrics::Reset();
|
|
||||||
m_sem_OpenDone.Reset();
|
m_sem_OpenDone.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,19 +200,17 @@ void SysMtgsThread::OpenGS()
|
||||||
if (m_Opened)
|
if (m_Opened)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (init_gspanel)
|
|
||||||
sApp.OpenGsPanel();
|
|
||||||
|
|
||||||
memcpy(RingBuffer.Regs, PS2MEM_GS, sizeof(PS2MEM_GS));
|
memcpy(RingBuffer.Regs, PS2MEM_GS, sizeof(PS2MEM_GS));
|
||||||
GSsetBaseMem(RingBuffer.Regs);
|
|
||||||
|
|
||||||
pxAssertMsg((GSopen2(g_gs_window_info, 1 | (renderswitch ? 4 : 0)) == 0), "GS failed to open!");
|
m_Opened = GSopen(EmuConfig.GS, EmuConfig.GS.Renderer, RingBuffer.Regs);
|
||||||
|
|
||||||
GSsetVsync(EmuConfig.GS.GetVsync());
|
|
||||||
|
|
||||||
m_Opened = true;
|
|
||||||
m_sem_OpenDone.Post();
|
m_sem_OpenDone.Post();
|
||||||
|
|
||||||
|
if (!m_Opened)
|
||||||
|
{
|
||||||
|
Console.Error("GS failed to open");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
GSsetGameCRC(ElfCRC, 0);
|
GSsetGameCRC(ElfCRC, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,7 +273,7 @@ void SysMtgsThread::ExecuteTaskInThread()
|
||||||
// is very optimized (only 1 instruction test in most cases), so no point in trying
|
// is very optimized (only 1 instruction test in most cases), so no point in trying
|
||||||
// to avoid it.
|
// to avoid it.
|
||||||
|
|
||||||
m_sem_event.WaitWithoutYield();
|
m_sem_event.Wait();
|
||||||
StateCheckInThread();
|
StateCheckInThread();
|
||||||
busy.Acquire();
|
busy.Acquire();
|
||||||
|
|
||||||
|
@ -480,14 +443,20 @@ void SysMtgsThread::ExecuteTaskInThread()
|
||||||
if (m_VsyncSignalListener.exchange(false))
|
if (m_VsyncSignalListener.exchange(false))
|
||||||
m_sem_Vsync.Post();
|
m_sem_Vsync.Post();
|
||||||
|
|
||||||
PerformanceMetrics::Update();
|
|
||||||
|
|
||||||
// Do not StateCheckInThread() here
|
// Do not StateCheckInThread() here
|
||||||
// Otherwise we could pause while there's still data in the queue
|
// Otherwise we could pause while there's still data in the queue
|
||||||
// Which could make the MTVU thread wait forever for it to empty
|
// Which could make the MTVU thread wait forever for it to empty
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case GS_RINGTYPE_ASYNC_CALL:
|
||||||
|
{
|
||||||
|
AsyncCallType* const func = (AsyncCallType*)tag.pointer;
|
||||||
|
(*func)();
|
||||||
|
delete func;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case GS_RINGTYPE_FRAMESKIP:
|
case GS_RINGTYPE_FRAMESKIP:
|
||||||
MTGS_LOG("(MTGS Packet Read) ringtype=Frameskip");
|
MTGS_LOG("(MTGS Packet Read) ringtype=Frameskip");
|
||||||
_gs_ResetFrameskip();
|
_gs_ResetFrameskip();
|
||||||
|
@ -586,12 +555,14 @@ void SysMtgsThread::ExecuteTaskInThread()
|
||||||
|
|
||||||
void SysMtgsThread::CloseGS()
|
void SysMtgsThread::CloseGS()
|
||||||
{
|
{
|
||||||
if (!m_Opened || GSDump::isRunning)
|
if (!m_Opened)
|
||||||
return;
|
return;
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
|
if (GSDump::isRunning)
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
m_Opened = false;
|
m_Opened = false;
|
||||||
GSclose();
|
GSclose();
|
||||||
if (init_gspanel)
|
|
||||||
sApp.CloseGsPanel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysMtgsThread::OnSuspendInThread()
|
void SysMtgsThread::OnSuspendInThread()
|
||||||
|
@ -893,10 +864,10 @@ void SysMtgsThread::SendGameCRC(u32 crc)
|
||||||
SendSimplePacket(GS_RINGTYPE_CRC, crc, 0, 0);
|
SendSimplePacket(GS_RINGTYPE_CRC, crc, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysMtgsThread::WaitForOpen()
|
bool SysMtgsThread::WaitForOpen()
|
||||||
{
|
{
|
||||||
if (m_Opened)
|
if (m_Opened)
|
||||||
return;
|
return true;
|
||||||
Resume();
|
Resume();
|
||||||
|
|
||||||
// Two-phase timeout on MTGS opening, so that possible errors are handled
|
// Two-phase timeout on MTGS opening, so that possible errors are handled
|
||||||
|
@ -904,6 +875,7 @@ void SysMtgsThread::WaitForOpen()
|
||||||
// another 12 seconds if no errors occurred (this might seem long, but sometimes our
|
// another 12 seconds if no errors occurred (this might seem long, but sometimes our
|
||||||
// GS can be very stubborned, especially in debug mode builds).
|
// GS can be very stubborned, especially in debug mode builds).
|
||||||
|
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
if (!m_sem_OpenDone.Wait(wxTimeSpan(0, 0, 2, 0)))
|
if (!m_sem_OpenDone.Wait(wxTimeSpan(0, 0, 2, 0)))
|
||||||
{
|
{
|
||||||
RethrowException();
|
RethrowException();
|
||||||
|
@ -917,6 +889,16 @@ void SysMtgsThread::WaitForOpen()
|
||||||
}
|
}
|
||||||
|
|
||||||
RethrowException();
|
RethrowException();
|
||||||
|
return m_Opened;
|
||||||
|
#else
|
||||||
|
if (!m_sem_OpenDone.Wait(wxTimeSpan(0, 0, 12, 0)) || !m_Opened)
|
||||||
|
{
|
||||||
|
Suspend(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysMtgsThread::Freeze(FreezeAction mode, MTGS_FreezeData& data)
|
void SysMtgsThread::Freeze(FreezeAction mode, MTGS_FreezeData& data)
|
||||||
|
@ -932,3 +914,89 @@ void SysMtgsThread::Freeze(FreezeAction mode, MTGS_FreezeData& data)
|
||||||
WaitForOpen();
|
WaitForOpen();
|
||||||
WaitGS();
|
WaitGS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SysMtgsThread::RunOnGSThread(AsyncCallType func)
|
||||||
|
{
|
||||||
|
SendPointerPacket(GS_RINGTYPE_ASYNC_CALL, 0, new AsyncCallType(std::move(func)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SysMtgsThread::ApplySettings()
|
||||||
|
{
|
||||||
|
pxAssertRel(IsOpen(), "MTGS is running");
|
||||||
|
|
||||||
|
RunOnGSThread([opts = EmuConfig.GS]() {
|
||||||
|
GSUpdateConfig(opts);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SysMtgsThread::ResizeDisplayWindow(int width, int height, float scale)
|
||||||
|
{
|
||||||
|
pxAssertRel(IsOpen(), "MTGS is running");
|
||||||
|
RunOnGSThread([width, height, scale]() {
|
||||||
|
GSResetAPIState();
|
||||||
|
Host::ResizeHostDisplay(width, height, scale);
|
||||||
|
GSRestoreAPIState();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SysMtgsThread::UpdateDisplayWindow()
|
||||||
|
{
|
||||||
|
pxAssertRel(IsOpen(), "MTGS is running");
|
||||||
|
RunOnGSThread([]() {
|
||||||
|
GSResetAPIState();
|
||||||
|
Host::UpdateHostDisplay();
|
||||||
|
GSRestoreAPIState();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SysMtgsThread::SetVSync(VsyncMode mode)
|
||||||
|
{
|
||||||
|
pxAssertRel(IsOpen(), "MTGS is running");
|
||||||
|
|
||||||
|
RunOnGSThread([mode]() {
|
||||||
|
Host::GetHostDisplay()->SetVSync(mode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SysMtgsThread::SwitchRenderer(GSRendererType renderer, bool display_message /* = true */)
|
||||||
|
{
|
||||||
|
pxAssertRel(IsOpen(), "MTGS is running");
|
||||||
|
|
||||||
|
if (display_message)
|
||||||
|
{
|
||||||
|
Host::AddKeyedFormattedOSDMessage("SwitchRenderer", 10.0f, "Switching to %s renderer...",
|
||||||
|
Pcsx2Config::GSOptions::GetRendererName(renderer));
|
||||||
|
}
|
||||||
|
|
||||||
|
RunOnGSThread([renderer]() {
|
||||||
|
GSSwitchRenderer(renderer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SysMtgsThread::SetSoftwareRendering(bool software, bool display_message /* = true */)
|
||||||
|
{
|
||||||
|
// for hardware, use the chosen api in the base config, or auto if base is set to sw
|
||||||
|
GSRendererType new_renderer;
|
||||||
|
if (!software)
|
||||||
|
new_renderer = EmuConfig.GS.UseHardwareRenderer() ? EmuConfig.GS.Renderer : GSRendererType::Auto;
|
||||||
|
else
|
||||||
|
new_renderer = GSRendererType::SW;
|
||||||
|
|
||||||
|
SwitchRenderer(new_renderer, display_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SysMtgsThread::ToggleSoftwareRendering()
|
||||||
|
{
|
||||||
|
// reading from the GS thread.. but should be okay here
|
||||||
|
SetSoftwareRendering(GSConfig.Renderer != GSRendererType::SW);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SysMtgsThread::SaveMemorySnapshot(u32 width, u32 height, std::vector<u32>* pixels)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
RunOnGSThread([width, height, pixels, &result]() {
|
||||||
|
result = GSSaveSnapshotToMemory(width, height, pixels);
|
||||||
|
});
|
||||||
|
WaitGS(false, false, false);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -29,11 +29,10 @@ struct Component_FileMcd;
|
||||||
|
|
||||||
#include "System.h"
|
#include "System.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
#include "Host.h"
|
||||||
|
|
||||||
#include "svnrev.h"
|
#include "svnrev.h"
|
||||||
|
|
||||||
#include "gui/ConsoleLogger.h"
|
|
||||||
|
|
||||||
#include <wx/ffile.h>
|
#include <wx/ffile.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
@ -526,7 +525,7 @@ s32 FileMemoryCard::Save(uint slot, const u8* src, u32 adr, int size)
|
||||||
{
|
{
|
||||||
wxString name, ext;
|
wxString name, ext;
|
||||||
wxFileName::SplitPath(m_file[slot].GetName(), NULL, NULL, &name, &ext);
|
wxFileName::SplitPath(m_file[slot].GetName(), NULL, NULL, &name, &ext);
|
||||||
OSDlog(Color_StrongYellow, true, "Memory Card %s written.", (const char*)(name + "." + ext).c_str());
|
Host::AddOSDMessage(StringUtil::StdStringFromFormat("Memory Card %s written.", (const char*)(name + "." + ext).c_str()), 10.0f);
|
||||||
last = std::chrono::system_clock::now();
|
last = std::chrono::system_clock::now();
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -22,11 +22,13 @@
|
||||||
#include "common/StringUtil.h"
|
#include "common/StringUtil.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "GS.h"
|
#include "GS.h"
|
||||||
|
#include "HostDisplay.h"
|
||||||
#include "CDVD/CDVDaccess.h"
|
#include "CDVD/CDVDaccess.h"
|
||||||
#include "MemoryCardFile.h"
|
#include "MemoryCardFile.h"
|
||||||
|
|
||||||
#ifndef PCSX2_CORE
|
#ifndef PCSX2_CORE
|
||||||
#include "gui/AppConfig.h"
|
#include "gui/AppConfig.h"
|
||||||
|
#include "GS/GS.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace EmuFolders
|
namespace EmuFolders
|
||||||
|
@ -245,6 +247,152 @@ void Pcsx2Config::CpuOptions::LoadSave(SettingsWrapper& wrap)
|
||||||
Recompiler.LoadSave(wrap);
|
Recompiler.LoadSave(wrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* Pcsx2Config::GSOptions::AspectRatioNames[] = {
|
||||||
|
"Stretch",
|
||||||
|
"4:3",
|
||||||
|
"16:9",
|
||||||
|
nullptr};
|
||||||
|
|
||||||
|
const char* Pcsx2Config::GSOptions::FMVAspectRatioSwitchNames[] = {
|
||||||
|
"Off",
|
||||||
|
"4:3",
|
||||||
|
"16:9",
|
||||||
|
nullptr};
|
||||||
|
|
||||||
|
const char* Pcsx2Config::GSOptions::GetRendererName(GSRendererType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case GSRendererType::Auto: return "Auto";
|
||||||
|
case GSRendererType::DX11: return "Direct3D 11";
|
||||||
|
case GSRendererType::OGL: return "OpenGL";
|
||||||
|
case GSRendererType::SW: return "Software";
|
||||||
|
case GSRendererType::Null: return "Null";
|
||||||
|
default: return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Pcsx2Config::GSOptions::GSOptions()
|
||||||
|
{
|
||||||
|
bitset = 0;
|
||||||
|
|
||||||
|
IntegerScaling = false;
|
||||||
|
LinearPresent = true;
|
||||||
|
UseDebugDevice = false;
|
||||||
|
UseBlitSwapChain = false;
|
||||||
|
DisableShaderCache = false;
|
||||||
|
OsdShowMessages = true;
|
||||||
|
OsdShowSpeed = false;
|
||||||
|
OsdShowFPS = false;
|
||||||
|
OsdShowCPU = false;
|
||||||
|
OsdShowResolution = false;
|
||||||
|
OsdShowGSStats = false;
|
||||||
|
|
||||||
|
HWDisableReadbacks = false;
|
||||||
|
AccurateDATE = true;
|
||||||
|
GPUPaletteConversion = false;
|
||||||
|
ConservativeFramebuffer = true;
|
||||||
|
AutoFlushSW = true;
|
||||||
|
PreloadFrameWithGSData = false;
|
||||||
|
WrapGSMem = false;
|
||||||
|
UserHacks = false;
|
||||||
|
UserHacks_AlignSpriteX = false;
|
||||||
|
UserHacks_AutoFlush = false;
|
||||||
|
UserHacks_CPUFBConversion = false;
|
||||||
|
UserHacks_DisableDepthSupport = false;
|
||||||
|
UserHacks_DisablePartialInvalidation = false;
|
||||||
|
UserHacks_DisableSafeFeatures = false;
|
||||||
|
UserHacks_MergePPSprite = false;
|
||||||
|
UserHacks_WildHack = false;
|
||||||
|
|
||||||
|
ShaderFX_Conf = "shaders/GS_FX_Settings.ini";
|
||||||
|
ShaderFX_GLSL = "shaders/GS.fx";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pcsx2Config::GSOptions::operator==(const GSOptions& right) const
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
OpEqu(SynchronousMTGS) &&
|
||||||
|
OpEqu(VsyncQueueSize) &&
|
||||||
|
|
||||||
|
OpEqu(FrameSkipEnable) &&
|
||||||
|
OpEqu(FrameLimitEnable) &&
|
||||||
|
OpEqu(VsyncEnable) &&
|
||||||
|
|
||||||
|
OpEqu(FramesToDraw) &&
|
||||||
|
OpEqu(FramesToSkip) &&
|
||||||
|
|
||||||
|
OpEqu(LimitScalar) &&
|
||||||
|
OpEqu(FramerateNTSC) &&
|
||||||
|
OpEqu(FrameratePAL) &&
|
||||||
|
|
||||||
|
OpEqu(AspectRatio) &&
|
||||||
|
OpEqu(FMVAspectRatioSwitch) &&
|
||||||
|
|
||||||
|
OptionsAreEqual(right)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
OpEqu(bitset) &&
|
||||||
|
|
||||||
|
OpEqu(InterlaceMode) &&
|
||||||
|
|
||||||
|
OpEqu(Zoom) &&
|
||||||
|
OpEqu(StretchY) &&
|
||||||
|
OpEqu(OffsetX) &&
|
||||||
|
OpEqu(OffsetY) &&
|
||||||
|
OpEqu(OsdScale) &&
|
||||||
|
|
||||||
|
OpEqu(Renderer) &&
|
||||||
|
OpEqu(UpscaleMultiplier) &&
|
||||||
|
|
||||||
|
OpEqu(HWMipmap) &&
|
||||||
|
OpEqu(AccurateBlendingUnit) &&
|
||||||
|
OpEqu(CRCHack) &&
|
||||||
|
OpEqu(TextureFiltering) &&
|
||||||
|
OpEqu(Dithering) &&
|
||||||
|
OpEqu(MaxAnisotropy) &&
|
||||||
|
OpEqu(SWExtraThreads) &&
|
||||||
|
OpEqu(SWExtraThreadsHeight) &&
|
||||||
|
OpEqu(TVShader) &&
|
||||||
|
OpEqu(SkipDraw) &&
|
||||||
|
OpEqu(SkipDrawOffset) &&
|
||||||
|
|
||||||
|
OpEqu(UserHacks_HalfBottomOverride) &&
|
||||||
|
OpEqu(UserHacks_HalfPixelOffset) &&
|
||||||
|
OpEqu(UserHacks_RoundSprite) &&
|
||||||
|
OpEqu(UserHacks_TCOffsetX) &&
|
||||||
|
OpEqu(UserHacks_TCOffsetY) &&
|
||||||
|
OpEqu(UserHacks_TriFilter) &&
|
||||||
|
|
||||||
|
OpEqu(ShadeBoost_Brightness) &&
|
||||||
|
OpEqu(ShadeBoost_Contrast) &&
|
||||||
|
OpEqu(ShadeBoost_Saturation) &&
|
||||||
|
OpEqu(SaveN) &&
|
||||||
|
OpEqu(SaveL) &&
|
||||||
|
OpEqu(Adapter) &&
|
||||||
|
OpEqu(ShaderFX_Conf) &&
|
||||||
|
OpEqu(ShaderFX_GLSL));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pcsx2Config::GSOptions::operator!=(const GSOptions& right) const
|
||||||
|
{
|
||||||
|
return !operator==(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pcsx2Config::GSOptions::RestartOptionsAreEqual(const GSOptions& right) const
|
||||||
|
{
|
||||||
|
return
|
||||||
|
OpEqu(Renderer) &&
|
||||||
|
OpEqu(Adapter) &&
|
||||||
|
OpEqu(UseDebugDevice) &&
|
||||||
|
OpEqu(UseBlitSwapChain) &&
|
||||||
|
OpEqu(DisableShaderCache);
|
||||||
|
}
|
||||||
|
|
||||||
void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
|
void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
|
||||||
{
|
{
|
||||||
SettingsWrapSection("EmuCore/GS");
|
SettingsWrapSection("EmuCore/GS");
|
||||||
|
@ -258,7 +406,7 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
|
||||||
SettingsWrapEntry(FrameSkipEnable);
|
SettingsWrapEntry(FrameSkipEnable);
|
||||||
wrap.EnumEntry(CURRENT_SETTINGS_SECTION, "VsyncEnable", VsyncEnable, NULL, VsyncEnable);
|
wrap.EnumEntry(CURRENT_SETTINGS_SECTION, "VsyncEnable", VsyncEnable, NULL, VsyncEnable);
|
||||||
|
|
||||||
SettingsWrapEntry(LimitScalar);
|
// LimitScalar is set at runtime.
|
||||||
SettingsWrapEntry(FramerateNTSC);
|
SettingsWrapEntry(FramerateNTSC);
|
||||||
SettingsWrapEntry(FrameratePAL);
|
SettingsWrapEntry(FrameratePAL);
|
||||||
|
|
||||||
|
@ -266,48 +414,181 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
|
||||||
SettingsWrapEntry(FramesToSkip);
|
SettingsWrapEntry(FramesToSkip);
|
||||||
|
|
||||||
#ifdef PCSX2_CORE
|
#ifdef PCSX2_CORE
|
||||||
static const char* AspectRatioNames[] =
|
// These are loaded from GSWindow in wx.
|
||||||
{
|
SettingsWrapEnumEx(AspectRatio, "AspectRatio", AspectRatioNames);
|
||||||
"Stretch",
|
SettingsWrapEnumEx(FMVAspectRatioSwitch, "FMVAspectRatioSwitch", FMVAspectRatioSwitchNames);
|
||||||
"4:3",
|
|
||||||
"16:9",
|
|
||||||
// WARNING: array must be NULL terminated to compute it size
|
|
||||||
NULL};
|
|
||||||
|
|
||||||
wrap.EnumEntry("AspectRatio", AspectRatio, AspectRatioNames, AspectRatio);
|
|
||||||
|
|
||||||
static const char* FMVAspectRatioSwitchNames[] =
|
|
||||||
{
|
|
||||||
"Off",
|
|
||||||
"4:3",
|
|
||||||
"16:9",
|
|
||||||
// WARNING: array must be NULL terminated to compute it size
|
|
||||||
NULL};
|
|
||||||
wrap.EnumEntry("FMVAspectRatioSwitch", FMVAspectRatioSwitch, FMVAspectRatioSwitchNames, FMVAspectRatioSwitch);
|
|
||||||
|
|
||||||
SettingsWrapEntry(Zoom);
|
SettingsWrapEntry(Zoom);
|
||||||
|
SettingsWrapEntry(StretchY);
|
||||||
|
SettingsWrapEntry(OffsetX);
|
||||||
|
SettingsWrapEntry(OffsetY);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
|
if (wrap.IsLoading())
|
||||||
|
ReloadIniSettings();
|
||||||
|
#else
|
||||||
|
LoadSaveIniSettings(wrap);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int Pcsx2Config::GSOptions::GetVsync() const
|
#ifdef PCSX2_CORE
|
||||||
|
void Pcsx2Config::GSOptions::LoadSaveIniSettings(SettingsWrapper& wrap)
|
||||||
{
|
{
|
||||||
if (EmuConfig.LimiterMode == LimiterModeType::Turbo || !FrameLimitEnable)
|
SettingsWrapSection("EmuCore/GS");
|
||||||
return 0;
|
|
||||||
|
|
||||||
// D3D only support a boolean state. OpenGL waits a number of vsync
|
#define GSSettingInt(var) SettingsWrapBitfield(var)
|
||||||
// interrupt (negative value for late vsync).
|
#define GSSettingIntEx(var, name) SettingsWrapBitfieldEx(var, name)
|
||||||
switch (VsyncEnable)
|
#define GSSettingBool(var) SettingsWrapBitBool(var)
|
||||||
|
#define GSSettingBoolEx(var, name) SettingsWrapBitBoolEx(var, name)
|
||||||
|
#define GSSettingFloat(var) SettingsWrapBitfield(var)
|
||||||
|
#define GSSettingIntEnumEx(var, name) SettingsWrapIntEnumEx(var, name)
|
||||||
|
#define GSSettingString(var) SettingsWrapEntry(var)
|
||||||
|
#define GSSettingStringEx(var, name) SettingsWrapEntryEx(var, name)
|
||||||
|
#else
|
||||||
|
void Pcsx2Config::GSOptions::ReloadIniSettings()
|
||||||
|
{
|
||||||
|
// ensure theApp is loaded.
|
||||||
|
GSinitConfig();
|
||||||
|
|
||||||
|
#define GSSettingInt(var) var = theApp.GetConfigI(#var)
|
||||||
|
#define GSSettingIntEx(var, name) var = theApp.GetConfigI(name)
|
||||||
|
#define GSSettingBool(var) var = theApp.GetConfigB(#var)
|
||||||
|
#define GSSettingBoolEx(var, name) var = theApp.GetConfigB(name)
|
||||||
|
#define GSSettingFloat(var) var = static_cast<double>(theApp.GetConfigI(#var))
|
||||||
|
#define GSSettingIntEnumEx(var, name) var = static_cast<decltype(var)>(theApp.GetConfigI(name))
|
||||||
|
#define GSSettingString(var) var = theApp.GetConfigS(#var)
|
||||||
|
#define GSSettingStringEx(var, name) var = theApp.GetConfigS(name)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Unfortunately, because code in the GS still reads the setting by key instead of
|
||||||
|
// using these variables, we need to use the old names. Maybe post 2.0 we can change this.
|
||||||
|
GSSettingBool(IntegerScaling);
|
||||||
|
GSSettingBoolEx(LinearPresent, "linear_present");
|
||||||
|
GSSettingBool(UseDebugDevice);
|
||||||
|
GSSettingBool(UseBlitSwapChain);
|
||||||
|
GSSettingBoolEx(DisableShaderCache, "disable_shader_cache");
|
||||||
|
GSSettingBool(OsdShowMessages);
|
||||||
|
GSSettingBool(OsdShowSpeed);
|
||||||
|
GSSettingBool(OsdShowFPS);
|
||||||
|
GSSettingBool(OsdShowCPU);
|
||||||
|
GSSettingBool(OsdShowResolution);
|
||||||
|
GSSettingBool(OsdShowGSStats);
|
||||||
|
|
||||||
|
GSSettingBool(HWDisableReadbacks);
|
||||||
|
GSSettingBoolEx(AccurateDATE, "accurate_date");
|
||||||
|
GSSettingBoolEx(GPUPaletteConversion, "paltex");
|
||||||
|
GSSettingBoolEx(ConservativeFramebuffer, "conservative_framebuffer");
|
||||||
|
GSSettingBoolEx(AutoFlushSW, "autoflush_sw");
|
||||||
|
GSSettingBoolEx(PreloadFrameWithGSData, "preload_frame_with_gs_data");
|
||||||
|
GSSettingBoolEx(WrapGSMem, "wrap_gs_mem");
|
||||||
|
GSSettingBoolEx(Mipmap, "mipmap");
|
||||||
|
GSSettingBoolEx(AA1, "aa1");
|
||||||
|
GSSettingBoolEx(UserHacks, "UserHacks");
|
||||||
|
GSSettingBoolEx(UserHacks_AlignSpriteX, "UserHacks_align_sprite_X");
|
||||||
|
GSSettingBoolEx(UserHacks_AutoFlush, "UserHacks_AutoFlush");
|
||||||
|
GSSettingBoolEx(UserHacks_CPUFBConversion, "UserHacks_CPU_FB_Conversion");
|
||||||
|
GSSettingBoolEx(UserHacks_DisableDepthSupport, "UserHacks_DisableDepthSupport");
|
||||||
|
GSSettingBoolEx(UserHacks_DisablePartialInvalidation, "UserHacks_DisablePartialInvalidation");
|
||||||
|
GSSettingBoolEx(UserHacks_DisableSafeFeatures, "UserHacks_Disable_Safe_Features");
|
||||||
|
GSSettingBoolEx(UserHacks_MergePPSprite, "UserHacks_merge_pp_sprite");
|
||||||
|
GSSettingBoolEx(UserHacks_WildHack, "UserHacks_WildHack");
|
||||||
|
GSSettingBoolEx(UserHacks_TextureInsideRt, "UserHacks_TextureInsideRt");
|
||||||
|
GSSettingBoolEx(FXAA, "fxaa");
|
||||||
|
GSSettingBool(ShadeBoost);
|
||||||
|
GSSettingBoolEx(ShaderFX, "shaderfx");
|
||||||
|
GSSettingBoolEx(DumpGSData, "dump");
|
||||||
|
GSSettingBoolEx(SaveRT, "save");
|
||||||
|
GSSettingBoolEx(SaveFrame, "savef");
|
||||||
|
GSSettingBoolEx(SaveTexture, "savet");
|
||||||
|
GSSettingBoolEx(SaveDepth, "savez");
|
||||||
|
|
||||||
|
GSSettingIntEnumEx(InterlaceMode, "interlace");
|
||||||
|
|
||||||
|
GSSettingFloat(OsdScale);
|
||||||
|
|
||||||
|
GSSettingIntEnumEx(Renderer, "Renderer");
|
||||||
|
GSSettingIntEx(UpscaleMultiplier, "upscale_multiplier");
|
||||||
|
|
||||||
|
GSSettingIntEnumEx(HWMipmap, "mipmap_hw");
|
||||||
|
GSSettingIntEnumEx(AccurateBlendingUnit, "accurate_blending_unit");
|
||||||
|
GSSettingIntEnumEx(CRCHack, "crc_hack_level");
|
||||||
|
GSSettingIntEnumEx(TextureFiltering, "filter");
|
||||||
|
GSSettingIntEx(Dithering, "dithering_ps2");
|
||||||
|
GSSettingIntEx(MaxAnisotropy, "MaxAnisotropy");
|
||||||
|
GSSettingIntEx(SWExtraThreads, "extrathreads");
|
||||||
|
GSSettingIntEx(SWExtraThreadsHeight, "extrathreads_height");
|
||||||
|
GSSettingIntEx(TVShader, "TVShader");
|
||||||
|
GSSettingIntEx(SkipDraw, "UserHacks_SkipDraw");
|
||||||
|
GSSettingIntEx(SkipDrawOffset, "UserHacks_SkipDraw_Offset");
|
||||||
|
|
||||||
|
GSSettingIntEx(UserHacks_HalfBottomOverride, "UserHacks_Half_Bottom_Override");
|
||||||
|
GSSettingIntEx(UserHacks_HalfPixelOffset, "UserHacks_HalfPixelOffset");
|
||||||
|
GSSettingIntEx(UserHacks_RoundSprite, "UserHacks_round_sprite_offset");
|
||||||
|
GSSettingIntEx(UserHacks_TCOffsetX, "UserHacks_TCOffsetX");
|
||||||
|
GSSettingIntEx(UserHacks_TCOffsetY, "UserHacks_TCOffsetY");
|
||||||
|
GSSettingIntEnumEx(UserHacks_TriFilter, "UserHacks_TriFilter");
|
||||||
|
|
||||||
|
GSSettingInt(ShadeBoost_Brightness);
|
||||||
|
GSSettingInt(ShadeBoost_Contrast);
|
||||||
|
GSSettingInt(ShadeBoost_Saturation);
|
||||||
|
GSSettingIntEx(SaveN, "saven");
|
||||||
|
GSSettingIntEx(SaveL, "savel");
|
||||||
|
|
||||||
|
GSSettingString(Adapter);
|
||||||
|
GSSettingStringEx(ShaderFX_Conf, "shaderfx_conf");
|
||||||
|
GSSettingStringEx(ShaderFX_GLSL, "shaderfx_glsl");
|
||||||
|
|
||||||
|
#undef GSSettingInt
|
||||||
|
#undef GSSettingIntEx
|
||||||
|
#undef GSSettingBool
|
||||||
|
#undef GSSettingBoolEx
|
||||||
|
#undef GSSettingFloat
|
||||||
|
#undef GSSettingEnumEx
|
||||||
|
#undef GSSettingIntEnumEx
|
||||||
|
#undef GSSettingString
|
||||||
|
#undef GSSettingStringEx
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pcsx2Config::GSOptions::MaskUserHacks()
|
||||||
|
{
|
||||||
|
if (UserHacks)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UserHacks_AlignSpriteX = false;
|
||||||
|
UserHacks_MergePPSprite = false;
|
||||||
|
UserHacks_DisableSafeFeatures = false;
|
||||||
|
UserHacks_HalfBottomOverride = -1;
|
||||||
|
UserHacks_HalfPixelOffset = 0;
|
||||||
|
UserHacks_RoundSprite = 0;
|
||||||
|
PreloadFrameWithGSData = false;
|
||||||
|
UserHacks_DisablePartialInvalidation = false;
|
||||||
|
UserHacks_DisableDepthSupport = false;
|
||||||
|
UserHacks_CPUFBConversion = false;
|
||||||
|
UserHacks_TextureInsideRt = false;
|
||||||
|
UserHacks_TCOffsetX = 0;
|
||||||
|
UserHacks_TCOffsetY = 0;
|
||||||
|
|
||||||
|
// in wx, we put trilinear filtering behind user hacks, but not in qt.
|
||||||
|
#ifndef PCSX2_CORE
|
||||||
|
UserHacks_TriFilter = TriFiltering::Off;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pcsx2Config::GSOptions::UseHardwareRenderer() const
|
||||||
|
{
|
||||||
|
return (Renderer == GSRendererType::DX11 || Renderer == GSRendererType::OGL);
|
||||||
|
}
|
||||||
|
|
||||||
|
VsyncMode Pcsx2Config::GetEffectiveVsyncMode() const
|
||||||
|
{
|
||||||
|
if (GS.LimitScalar != 1.0)
|
||||||
{
|
{
|
||||||
case VsyncMode::Adaptive:
|
Console.WriteLn("Vsync is OFF");
|
||||||
return -1;
|
return VsyncMode::Off;
|
||||||
case VsyncMode::Off:
|
|
||||||
return 0;
|
|
||||||
case VsyncMode::On:
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Console.WriteLn("Vsync is %s", GS.VsyncEnable == VsyncMode::Off ? "OFF" : (GS.VsyncEnable == VsyncMode::Adaptive ? "ADAPTIVE" : "ON"));
|
||||||
|
return GS.VsyncEnable;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pcsx2Config::SPU2Options::SPU2Options()
|
Pcsx2Config::SPU2Options::SPU2Options()
|
||||||
|
@ -348,6 +629,7 @@ static const char* const tbl_GamefixNames[] =
|
||||||
"FpuMul",
|
"FpuMul",
|
||||||
"FpuNegDiv",
|
"FpuNegDiv",
|
||||||
"GoemonTlb",
|
"GoemonTlb",
|
||||||
|
"SoftwareRendererFMV",
|
||||||
"SkipMPEG",
|
"SkipMPEG",
|
||||||
"OPHFlag",
|
"OPHFlag",
|
||||||
"EETiming",
|
"EETiming",
|
||||||
|
@ -422,6 +704,9 @@ void Pcsx2Config::GamefixOptions::Set(GamefixId id, bool enabled)
|
||||||
case Fix_EETiming:
|
case Fix_EETiming:
|
||||||
EETimingHack = enabled;
|
EETimingHack = enabled;
|
||||||
break;
|
break;
|
||||||
|
case Fix_SoftwareRendererFMV:
|
||||||
|
SoftwareRendererFMVHack = enabled;
|
||||||
|
break;
|
||||||
case Fix_SkipMpeg:
|
case Fix_SkipMpeg:
|
||||||
SkipMPEGHack = enabled;
|
SkipMPEGHack = enabled;
|
||||||
break;
|
break;
|
||||||
|
@ -471,6 +756,8 @@ bool Pcsx2Config::GamefixOptions::Get(GamefixId id) const
|
||||||
return XgKickHack;
|
return XgKickHack;
|
||||||
case Fix_EETiming:
|
case Fix_EETiming:
|
||||||
return EETimingHack;
|
return EETimingHack;
|
||||||
|
case Fix_SoftwareRendererFMV:
|
||||||
|
return SoftwareRendererFMVHack;
|
||||||
case Fix_SkipMpeg:
|
case Fix_SkipMpeg:
|
||||||
return SkipMPEGHack;
|
return SkipMPEGHack;
|
||||||
case Fix_OPHFlag:
|
case Fix_OPHFlag:
|
||||||
|
@ -505,6 +792,7 @@ void Pcsx2Config::GamefixOptions::LoadSave(SettingsWrapper& wrap)
|
||||||
SettingsWrapBitBool(FpuNegDivHack);
|
SettingsWrapBitBool(FpuNegDivHack);
|
||||||
SettingsWrapBitBool(XgKickHack);
|
SettingsWrapBitBool(XgKickHack);
|
||||||
SettingsWrapBitBool(EETimingHack);
|
SettingsWrapBitBool(EETimingHack);
|
||||||
|
SettingsWrapBitBool(SoftwareRendererFMVHack);
|
||||||
SettingsWrapBitBool(SkipMPEGHack);
|
SettingsWrapBitBool(SkipMPEGHack);
|
||||||
SettingsWrapBitBool(OPHFlagHack);
|
SettingsWrapBitBool(OPHFlagHack);
|
||||||
SettingsWrapBitBool(DMABusyHack);
|
SettingsWrapBitBool(DMABusyHack);
|
||||||
|
@ -765,6 +1053,8 @@ void Pcsx2Config::CopyConfig(const Pcsx2Config& cfg)
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
McdCompressNTFS = cfg.McdCompressNTFS;
|
McdCompressNTFS = cfg.McdCompressNTFS;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
LimiterMode = cfg.LimiterMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuFolders::SetDefaults()
|
void EmuFolders::SetDefaults()
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "GS.h" // GSosdlog
|
#include "GS.h" // GSosdlog
|
||||||
#include "gui/App.h" // GetRGBA
|
#include "gui/App.h" // GetRGBA
|
||||||
#include "gui/ConsoleLogger.h"
|
#include "gui/ConsoleLogger.h"
|
||||||
|
#include "Host.h"
|
||||||
|
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ namespace inputRec
|
||||||
recordingConLog(fmt::format("[REC]: {}\n", log));
|
recordingConLog(fmt::format("[REC]: {}\n", log));
|
||||||
|
|
||||||
// NOTE - Color is not currently used for OSD logs
|
// NOTE - Color is not currently used for OSD logs
|
||||||
GSosdLog(log.c_str(), wxGetApp().GetProgramLog()->GetRGBA(ConsoleColors::Color_StrongMagenta));
|
Host::AddOSDMessage(log, 15.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void consoleLog(const std::string& log)
|
void consoleLog(const std::string& log)
|
||||||
|
|
|
@ -104,8 +104,10 @@ void SysCoreThread::OnStart()
|
||||||
|
|
||||||
void SysCoreThread::OnSuspendInThread()
|
void SysCoreThread::OnSuspendInThread()
|
||||||
{
|
{
|
||||||
TearDownSystems(static_cast<SystemsMask>(-1)); // All systems
|
// We deliberately don't tear down GS here, because the state isn't saved.
|
||||||
GetMTGS().Suspend();
|
// Which means when it reopens, you'll lose everything in VRAM. Anything
|
||||||
|
// which needs GS to be torn down should manually save its state.
|
||||||
|
TearDownSystems(static_cast<SystemsMask>(-1 & ~System_GS)); // All systems
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysCoreThread::Start()
|
void SysCoreThread::Start()
|
||||||
|
@ -183,7 +185,19 @@ void SysCoreThread::ApplySettings(const Pcsx2Config& src)
|
||||||
m_resetProfilers = (src.Profiler != EmuConfig.Profiler);
|
m_resetProfilers = (src.Profiler != EmuConfig.Profiler);
|
||||||
m_resetVsyncTimers = (src.GS != EmuConfig.GS);
|
m_resetVsyncTimers = (src.GS != EmuConfig.GS);
|
||||||
|
|
||||||
|
const bool gs_settings_changed = !src.GS.OptionsAreEqual(EmuConfig.GS);
|
||||||
|
|
||||||
EmuConfig.CopyConfig(src);
|
EmuConfig.CopyConfig(src);
|
||||||
|
|
||||||
|
// handle GS setting changes
|
||||||
|
if (GetMTGS().IsOpen() && gs_settings_changed)
|
||||||
|
{
|
||||||
|
// if by change we reopen the GS, the window handles will invalidate.
|
||||||
|
// so, we should block here until GS has finished reinitializing, if needed.
|
||||||
|
Console.WriteLn("Applying GS settings...");
|
||||||
|
GetMTGS().ApplySettings();
|
||||||
|
GetMTGS().WaitGS();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
@ -326,6 +340,7 @@ void SysCoreThread::TearDownSystems(SystemsMask systemsToTearDown)
|
||||||
void SysCoreThread::OnResumeInThread(SystemsMask systemsToReinstate)
|
void SysCoreThread::OnResumeInThread(SystemsMask systemsToReinstate)
|
||||||
{
|
{
|
||||||
PerformanceMetrics::SetCPUThreadTimer(Common::ThreadCPUTimer::GetForCallingThread());
|
PerformanceMetrics::SetCPUThreadTimer(Common::ThreadCPUTimer::GetForCallingThread());
|
||||||
|
PerformanceMetrics::Reset();
|
||||||
|
|
||||||
GetMTGS().WaitForOpen();
|
GetMTGS().WaitForOpen();
|
||||||
if (systemsToReinstate & System_DEV9) DEV9open();
|
if (systemsToReinstate & System_DEV9) DEV9open();
|
||||||
|
|
|
@ -526,6 +526,7 @@ public:
|
||||||
void OpenGsPanel();
|
void OpenGsPanel();
|
||||||
void CloseGsPanel();
|
void CloseGsPanel();
|
||||||
void OnGsFrameClosed(wxWindowID id);
|
void OnGsFrameClosed(wxWindowID id);
|
||||||
|
void OnGsFrameDestroyed(wxWindowID id);
|
||||||
void OnMainFrameClosed(wxWindowID id);
|
void OnMainFrameClosed(wxWindowID id);
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
|
|
|
@ -567,9 +567,6 @@ void AppCoreThread::ApplySettings(const Pcsx2Config& src)
|
||||||
{
|
{
|
||||||
_parent::ApplySettings(fixup);
|
_parent::ApplySettings(fixup);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_ExecMode >= ExecMode_Paused)
|
|
||||||
GSsetVsync(EmuConfig.GS.GetVsync());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -18,9 +18,26 @@
|
||||||
#include "common/Console.h"
|
#include "common/Console.h"
|
||||||
#include "common/FileSystem.h"
|
#include "common/FileSystem.h"
|
||||||
#include "common/Path.h"
|
#include "common/Path.h"
|
||||||
|
#include "common/StringUtil.h"
|
||||||
|
|
||||||
#include "Host.h"
|
#include "Host.h"
|
||||||
#include "HostSettings.h"
|
#include "HostSettings.h"
|
||||||
|
#include "HostDisplay.h"
|
||||||
|
#include "GS/GS.h"
|
||||||
|
|
||||||
|
#include "common/Assertions.h"
|
||||||
|
#include "Frontend/ImGuiManager.h"
|
||||||
|
#include "Frontend/OpenGLHostDisplay.h"
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "Frontend/D3D11HostDisplay.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "gui/App.h"
|
||||||
|
#include "gui/AppHost.h"
|
||||||
|
#include "gui/pxEvents.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include "gui/App.h"
|
#include "gui/App.h"
|
||||||
#include "gui/AppConfig.h"
|
#include "gui/AppConfig.h"
|
||||||
|
@ -89,3 +106,104 @@ void Host::ReportErrorAsync(const std::string_view& title, const std::string_vie
|
||||||
MsgButtons().OK()));
|
MsgButtons().OK()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::unique_ptr<HostDisplay> s_host_display;
|
||||||
|
|
||||||
|
HostDisplay* Host::AcquireHostDisplay(HostDisplay::RenderAPI api)
|
||||||
|
{
|
||||||
|
sApp.OpenGsPanel();
|
||||||
|
|
||||||
|
// can't go anywhere if we don't have a window to render into!
|
||||||
|
if (g_gs_window_info.type == WindowInfo::Type::Surfaceless)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
s_host_display = HostDisplay::CreateDisplayForAPI(api);
|
||||||
|
if (!s_host_display)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!s_host_display->CreateRenderDevice(g_gs_window_info, GSConfig.Adapter, GSConfig.UseDebugDevice) ||
|
||||||
|
!s_host_display->InitializeRenderDevice(StringUtil::wxStringToUTF8String(EmuFolders::Cache.ToString()), GSConfig.UseDebugDevice) ||
|
||||||
|
!ImGuiManager::Initialize())
|
||||||
|
{
|
||||||
|
s_host_display->DestroyRenderDevice();
|
||||||
|
s_host_display.reset();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_host_display.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::ReleaseHostDisplay()
|
||||||
|
{
|
||||||
|
ImGuiManager::Shutdown();
|
||||||
|
|
||||||
|
if (s_host_display)
|
||||||
|
{
|
||||||
|
s_host_display->DestroyRenderDevice();
|
||||||
|
s_host_display.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
sApp.CloseGsPanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
HostDisplay* Host::GetHostDisplay()
|
||||||
|
{
|
||||||
|
return s_host_display.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Host::BeginPresentFrame(bool frame_skip)
|
||||||
|
{
|
||||||
|
CheckForGSWindowResize();
|
||||||
|
return s_host_display->BeginPresent(frame_skip);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::EndPresentFrame()
|
||||||
|
{
|
||||||
|
ImGuiManager::RenderOSD();
|
||||||
|
s_host_display->EndPresent();
|
||||||
|
ImGuiManager::NewFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::UpdateHostDisplay()
|
||||||
|
{
|
||||||
|
// not used for wx
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::ResizeHostDisplay(u32 new_window_width, u32 new_window_height, float new_window_scale)
|
||||||
|
{
|
||||||
|
// not used for wx (except for osd scale changes)
|
||||||
|
ImGuiManager::WindowResized();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::atomic_bool s_gs_window_resized{false};
|
||||||
|
static std::mutex s_gs_window_resized_lock;
|
||||||
|
static int s_new_gs_window_width = 0;
|
||||||
|
static int s_new_gs_window_height = 0;
|
||||||
|
|
||||||
|
void Host::GSWindowResized(int width, int height)
|
||||||
|
{
|
||||||
|
std::unique_lock lock(s_gs_window_resized_lock);
|
||||||
|
s_new_gs_window_width = width;
|
||||||
|
s_new_gs_window_height = height;
|
||||||
|
s_gs_window_resized.store(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::CheckForGSWindowResize()
|
||||||
|
{
|
||||||
|
if (!s_gs_window_resized.load())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
{
|
||||||
|
std::unique_lock lock(s_gs_window_resized_lock);
|
||||||
|
width = s_new_gs_window_width;
|
||||||
|
height = s_new_gs_window_height;
|
||||||
|
s_gs_window_resized.store(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!s_host_display)
|
||||||
|
return;
|
||||||
|
|
||||||
|
s_host_display->ResizeRenderWindow(width, height, s_host_display ? s_host_display->GetWindowScale() : 1.0f);
|
||||||
|
ImGuiManager::WindowResized();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
#include "Host.h"
|
||||||
|
|
||||||
|
namespace Host
|
||||||
|
{
|
||||||
|
// UI thread
|
||||||
|
void GSWindowResized(int width, int height);
|
||||||
|
|
||||||
|
// MTGS thread
|
||||||
|
void CheckForGSWindowResize();
|
||||||
|
} // namespace Host
|
||||||
|
|
|
@ -361,47 +361,12 @@ wxAppTraits* Pcsx2App::CreateTraits()
|
||||||
// LogicalVsync - Event received from the AppCoreThread (EEcore) for each vsync,
|
// LogicalVsync - Event received from the AppCoreThread (EEcore) for each vsync,
|
||||||
// roughly 50/60 times a second when frame limiting is enabled, and up to 10,000
|
// roughly 50/60 times a second when frame limiting is enabled, and up to 10,000
|
||||||
// times a second if not (ok, not quite, but you get the idea... I hope.)
|
// times a second if not (ok, not quite, but you get the idea... I hope.)
|
||||||
extern uint eecount_on_last_vdec;
|
|
||||||
extern bool FMVstarted;
|
|
||||||
extern bool EnableFMV;
|
|
||||||
extern bool renderswitch;
|
|
||||||
|
|
||||||
void Pcsx2App::LogicalVsync()
|
void Pcsx2App::LogicalVsync()
|
||||||
{
|
{
|
||||||
if( AppRpc_TryInvokeAsync( &Pcsx2App::LogicalVsync ) ) return;
|
if( AppRpc_TryInvokeAsync( &Pcsx2App::LogicalVsync ) ) return;
|
||||||
|
|
||||||
if( !SysHasValidState() ) return;
|
if( !SysHasValidState() ) return;
|
||||||
|
|
||||||
// Update / Calculate framerate!
|
|
||||||
|
|
||||||
if (EmuConfig.GS.FMVAspectRatioSwitch != FMVAspectRatioSwitchType::Off) {
|
|
||||||
if (EnableFMV) {
|
|
||||||
DevCon.Warning("FMV on");
|
|
||||||
|
|
||||||
switch (EmuConfig.GS.FMVAspectRatioSwitch)
|
|
||||||
{
|
|
||||||
case FMVAspectRatioSwitchType::R4_3:
|
|
||||||
EmuConfig.CurrentAspectRatio = AspectRatioType::R4_3;
|
|
||||||
break;
|
|
||||||
case FMVAspectRatioSwitchType::R16_9:
|
|
||||||
EmuConfig.CurrentAspectRatio = AspectRatioType::R16_9;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
EnableFMV = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FMVstarted) {
|
|
||||||
int diff = cpuRegs.cycle - eecount_on_last_vdec;
|
|
||||||
if (diff > 60000000 ) {
|
|
||||||
DevCon.Warning("FMV off");
|
|
||||||
EmuConfig.CurrentAspectRatio = EmuConfig.GS.AspectRatio;
|
|
||||||
FMVstarted = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( (wxGetApp().GetGsFramePtr() != NULL) )
|
if( (wxGetApp().GetGsFramePtr() != NULL) )
|
||||||
PADupdate(0);
|
PADupdate(0);
|
||||||
|
|
||||||
|
@ -763,8 +728,6 @@ void Pcsx2App::OpenGsPanel()
|
||||||
gsFrame->SetSize( oldsize );
|
gsFrame->SetSize( oldsize );
|
||||||
}
|
}
|
||||||
|
|
||||||
pxAssertDev( !gsopen_done, "GS must be closed prior to opening a new Gs Panel!" );
|
|
||||||
|
|
||||||
gsFrame->ShowFullScreen(g_Conf->GSWindow.IsFullscreen);
|
gsFrame->ShowFullScreen(g_Conf->GSWindow.IsFullscreen);
|
||||||
wxApp::ProcessPendingEvents();
|
wxApp::ProcessPendingEvents();
|
||||||
|
|
||||||
|
@ -784,10 +747,19 @@ void Pcsx2App::OpenGsPanel()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Pcsx2App::CloseGsPanel()
|
void Pcsx2App::CloseGsPanel()
|
||||||
{
|
{
|
||||||
if (AppRpc_TryInvoke(&Pcsx2App::CloseGsPanel))
|
if (AppRpc_TryInvoke(&Pcsx2App::CloseGsPanel))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
GSFrame* gsFrame = GetGsFramePtr();
|
||||||
|
if (gsFrame)
|
||||||
|
{
|
||||||
|
// we unreference the window first, that way it doesn't try to suspend on close and deadlock
|
||||||
|
OnGsFrameDestroyed(gsFrame->GetId());
|
||||||
|
gsFrame->Destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pcsx2App::OnGsFrameClosed(wxWindowID id)
|
void Pcsx2App::OnGsFrameClosed(wxWindowID id)
|
||||||
|
@ -803,6 +775,16 @@ void Pcsx2App::OnGsFrameClosed(wxWindowID id)
|
||||||
// right now there's no way to resume from suspend without GUI.
|
// right now there's no way to resume from suspend without GUI.
|
||||||
PrepForExit();
|
PrepForExit();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pcsx2App::OnGsFrameDestroyed(wxWindowID id)
|
||||||
|
{
|
||||||
|
if ((m_id_GsFrame == wxID_ANY) || (m_id_GsFrame != id))
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_id_GsFrame = wxID_ANY;
|
||||||
|
g_gs_window_info = {};
|
||||||
|
|
||||||
#ifndef DISABLE_RECORDING
|
#ifndef DISABLE_RECORDING
|
||||||
// Disable recording controls that only make sense if the game is running
|
// Disable recording controls that only make sense if the game is running
|
||||||
sMainFrame.enableRecordingMenuItem(MenuId_Recording_FrameAdvance, false);
|
sMainFrame.enableRecordingMenuItem(MenuId_Recording_FrameAdvance, false);
|
||||||
|
|
|
@ -18,13 +18,13 @@
|
||||||
#include "MainFrame.h"
|
#include "MainFrame.h"
|
||||||
#include "ConsoleLogger.h"
|
#include "ConsoleLogger.h"
|
||||||
#include "MSWstuff.h"
|
#include "MSWstuff.h"
|
||||||
|
#include "Host.h"
|
||||||
|
|
||||||
#include "common/Console.h"
|
#include "common/Console.h"
|
||||||
#include "common/IniInterface.h"
|
#include "common/IniInterface.h"
|
||||||
#include "common/SafeArray.inl"
|
#include "common/SafeArray.inl"
|
||||||
#include "Dialogs/LogOptionsDialog.h"
|
#include "Dialogs/LogOptionsDialog.h"
|
||||||
#include "DebugTools/Debug.h"
|
#include "DebugTools/Debug.h"
|
||||||
|
|
||||||
#include <wx/textfile.h>
|
#include <wx/textfile.h>
|
||||||
|
|
||||||
wxDECLARE_EVENT(pxEvt_SetTitleText, wxCommandEvent);
|
wxDECLARE_EVENT(pxEvt_SetTitleText, wxCommandEvent);
|
||||||
|
@ -1266,13 +1266,8 @@ void Pcsx2App::DisableWindowLogging() const
|
||||||
|
|
||||||
void OSDlog(ConsoleColors color, bool console, const std::string& str)
|
void OSDlog(ConsoleColors color, bool console, const std::string& str)
|
||||||
{
|
{
|
||||||
GSosdLog(str.c_str(), wxGetApp().GetProgramLog()->GetRGBA(color));
|
Host::AddOSDMessage(str, 15.0f);
|
||||||
|
|
||||||
if (console)
|
if (console)
|
||||||
Console.WriteLn(color, str.c_str());
|
Console.WriteLn(color, str.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void OSDmonitor(ConsoleColors color, const std::string key, const std::string value) {
|
|
||||||
GSosdMonitor(key.c_str(), value.c_str(), wxGetApp().GetProgramLog()->GetRGBA(color));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -244,5 +244,4 @@ void OSDlog(ConsoleColors color, bool console, const std::string& format, Args .
|
||||||
OSDlog(color, console, buf.data());
|
OSDlog(color, console, buf.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void OSDmonitor(ConsoleColors color, const std::string key, const std::string value);
|
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "common/EmbeddedImage.h"
|
#include "common/EmbeddedImage.h"
|
||||||
#include "gui/Resources/NoIcon.h"
|
#include "gui/Resources/NoIcon.h"
|
||||||
#include "GS.h"
|
#include "GS.h"
|
||||||
|
#include "HostDisplay.h"
|
||||||
|
|
||||||
#include "PathDefs.h"
|
#include "PathDefs.h"
|
||||||
#include "gui/AppConfig.h"
|
#include "gui/AppConfig.h"
|
||||||
|
@ -199,6 +200,7 @@ void Dialogs::GSDumpDialog::CloseDump(wxCommandEvent& event)
|
||||||
m_gif_packet->DeleteAllItems();
|
m_gif_packet->DeleteAllItems();
|
||||||
m_debug_mode->SetValue(false);
|
m_debug_mode->SetValue(false);
|
||||||
m_run->Enable();
|
m_run->Enable();
|
||||||
|
m_settings->Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
@ -219,6 +221,7 @@ void Dialogs::GSDumpDialog::RunDump(wxCommandEvent& event)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_run->Disable();
|
m_run->Disable();
|
||||||
|
m_settings->Disable();
|
||||||
m_debug_mode->Enable();
|
m_debug_mode->Enable();
|
||||||
m_thread->m_renderer = m_renderer_overrides->GetSelection();
|
m_thread->m_renderer = m_renderer_overrides->GetSelection();
|
||||||
m_thread->Start();
|
m_thread->Start();
|
||||||
|
@ -278,6 +281,9 @@ void Dialogs::GSDumpDialog::ToVSync(wxCommandEvent& event)
|
||||||
void Dialogs::GSDumpDialog::OpenSettings(wxCommandEvent& event)
|
void Dialogs::GSDumpDialog::OpenSettings(wxCommandEvent& event)
|
||||||
{
|
{
|
||||||
GSconfigure();
|
GSconfigure();
|
||||||
|
|
||||||
|
// config has to be reloaded here, otherwise it won't apply when we restart
|
||||||
|
g_Conf->EmuOptions.GS.ReloadIniSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dialogs::GSDumpDialog::ToStart(wxCommandEvent& event)
|
void Dialogs::GSDumpDialog::ToStart(wxCommandEvent& event)
|
||||||
|
@ -654,10 +660,8 @@ void Dialogs::GSDumpDialog::ProcessDumpEvent(const GSData& event, char* regs)
|
||||||
case VSync:
|
case VSync:
|
||||||
{
|
{
|
||||||
GSvsync((*((int*)(regs + 4096)) & 0x2000) > 0 ? (u8)1 : (u8)0);
|
GSvsync((*((int*)(regs + 4096)) & 0x2000) > 0 ? (u8)1 : (u8)0);
|
||||||
|
PerformanceMetrics::Update();
|
||||||
g_FrameCount++;
|
g_FrameCount++;
|
||||||
Pcsx2App* app = (Pcsx2App*)wxApp::GetInstance();
|
|
||||||
if (app)
|
|
||||||
PerformanceMetrics::Update();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ReadFIFO2:
|
case ReadFIFO2:
|
||||||
|
@ -788,8 +792,7 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
|
||||||
g_FrameCount = 0;
|
g_FrameCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
GSsetBaseMem((u8*)regs);
|
if (!GSopen(g_Conf->EmuOptions.GS, static_cast<GSRendererType>(renderer_override), (u8*)regs))
|
||||||
if (GSopen2(g_gs_window_info, (renderer_override<<24)) != 0)
|
|
||||||
{
|
{
|
||||||
OnStop();
|
OnStop();
|
||||||
return;
|
return;
|
||||||
|
@ -801,7 +804,6 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
|
||||||
GSDump::isRunning = false;
|
GSDump::isRunning = false;
|
||||||
GSvsync(1);
|
GSvsync(1);
|
||||||
GSreset();
|
GSreset();
|
||||||
GSsetBaseMem((u8*)regs);
|
|
||||||
GSfreeze(FreezeAction::Load, &fd);
|
GSfreeze(FreezeAction::Load, &fd);
|
||||||
|
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "App.h"
|
#include "App.h"
|
||||||
#include "GSFrame.h"
|
#include "GSFrame.h"
|
||||||
#include "AppAccelerators.h"
|
#include "AppAccelerators.h"
|
||||||
|
#include "AppHost.h"
|
||||||
#include "AppSaveStates.h"
|
#include "AppSaveStates.h"
|
||||||
#include "Counters.h"
|
#include "Counters.h"
|
||||||
#include "GS.h"
|
#include "GS.h"
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
#include "MSWstuff.h"
|
#include "MSWstuff.h"
|
||||||
#include "PAD/Gamepad.h"
|
#include "PAD/Gamepad.h"
|
||||||
#include "PerformanceMetrics.h"
|
#include "PerformanceMetrics.h"
|
||||||
|
#include "common/StringUtil.h"
|
||||||
|
|
||||||
#include "gui/Dialogs/ModalPopups.h"
|
#include "gui/Dialogs/ModalPopups.h"
|
||||||
|
|
||||||
|
@ -59,8 +61,6 @@
|
||||||
|
|
||||||
static const KeyAcceleratorCode FULLSCREEN_TOGGLE_ACCELERATOR_GSPANEL=KeyAcceleratorCode( WXK_RETURN ).Alt();
|
static const KeyAcceleratorCode FULLSCREEN_TOGGLE_ACCELERATOR_GSPANEL=KeyAcceleratorCode( WXK_RETURN ).Alt();
|
||||||
|
|
||||||
extern std::atomic_bool init_gspanel;
|
|
||||||
|
|
||||||
void GSPanel::InitDefaultAccelerators()
|
void GSPanel::InitDefaultAccelerators()
|
||||||
{
|
{
|
||||||
// Note: these override GlobalAccels ( Pcsx2App::InitDefaultGlobalAccelerators() )
|
// Note: these override GlobalAccels ( Pcsx2App::InitDefaultGlobalAccelerators() )
|
||||||
|
@ -211,7 +211,6 @@ GSPanel::GSPanel( wxWindow* parent )
|
||||||
m_CursorShown = false;
|
m_CursorShown = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bind(wxEVT_CLOSE_WINDOW, &GSPanel::OnCloseWindow, this);
|
|
||||||
Bind(wxEVT_SIZE, &GSPanel::OnResize, this);
|
Bind(wxEVT_SIZE, &GSPanel::OnResize, this);
|
||||||
Bind(wxEVT_KEY_UP, &GSPanel::OnKeyDownOrUp, this);
|
Bind(wxEVT_KEY_UP, &GSPanel::OnKeyDownOrUp, this);
|
||||||
Bind(wxEVT_KEY_DOWN, &GSPanel::OnKeyDownOrUp, this);
|
Bind(wxEVT_KEY_DOWN, &GSPanel::OnKeyDownOrUp, this);
|
||||||
|
@ -259,13 +258,40 @@ void GSPanel::DoShowMouse()
|
||||||
m_HideMouseTimer.Start( 1750, true );
|
m_HideMouseTimer.Start( 1750, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
static float GetDpiScaleForWxWindow(wxWindow* window)
|
||||||
|
{
|
||||||
|
static UINT(WINAPI * get_dpi_for_window)(HWND hwnd);
|
||||||
|
if (!get_dpi_for_window)
|
||||||
|
{
|
||||||
|
HMODULE mod = GetModuleHandle(L"user32.dll");
|
||||||
|
if (mod)
|
||||||
|
get_dpi_for_window = reinterpret_cast<decltype(get_dpi_for_window)>(GetProcAddress(mod, "GetDpiForWindow"));
|
||||||
|
}
|
||||||
|
if (!get_dpi_for_window)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
// less than 100% scaling seems unlikely.
|
||||||
|
const UINT dpi = get_dpi_for_window(window->GetHandle());
|
||||||
|
return (dpi > 0) ? std::max(1.0f, static_cast<float>(dpi) / 96.0f) : 1.0f;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
std::optional<WindowInfo> GSPanel::GetWindowInfo()
|
std::optional<WindowInfo> GSPanel::GetWindowInfo()
|
||||||
{
|
{
|
||||||
WindowInfo ret;
|
WindowInfo ret;
|
||||||
|
|
||||||
|
const wxSize gs_vp_size(GetClientSize());
|
||||||
|
ret.surface_scale = static_cast<float>(GetContentScaleFactor());
|
||||||
|
ret.surface_width = static_cast<u32>(gs_vp_size.GetWidth());
|
||||||
|
ret.surface_height = static_cast<u32>(gs_vp_size.GetHeight());
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
ret.type = WindowInfo::Type::Win32;
|
ret.type = WindowInfo::Type::Win32;
|
||||||
ret.window_handle = GetHandle();
|
ret.window_handle = GetHandle();
|
||||||
|
|
||||||
|
// Windows DPI internally uses the higher pixel count, so work out by how much.
|
||||||
|
ret.surface_scale = GetDpiScaleForWxWindow(this);
|
||||||
#elif defined(__WXGTK__)
|
#elif defined(__WXGTK__)
|
||||||
GtkWidget* child_window = GTK_WIDGET(GetHandle());
|
GtkWidget* child_window = GTK_WIDGET(GetHandle());
|
||||||
|
|
||||||
|
@ -320,20 +346,6 @@ std::optional<WindowInfo> GSPanel::GetWindowInfo()
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
const wxSize gs_vp_size(GetClientSize());
|
|
||||||
ret.surface_scale = static_cast<float>(GetContentScaleFactor());
|
|
||||||
ret.surface_width = static_cast<u32>(gs_vp_size.GetWidth());
|
|
||||||
ret.surface_height = static_cast<u32>(gs_vp_size.GetHeight());
|
|
||||||
|
|
||||||
#ifdef __WXGTK__
|
|
||||||
// GTK seems to not scale coordinates?
|
|
||||||
if (ret.type == WindowInfo::Type::X11)
|
|
||||||
{
|
|
||||||
ret.surface_width = static_cast<u32>(ret.surface_width * ret.surface_scale);
|
|
||||||
ret.surface_height = static_cast<u32>(ret.surface_height * ret.surface_scale);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,18 +374,7 @@ void GSPanel::OnResize(wxSizeEvent& event)
|
||||||
g_gs_window_info.surface_height = height;
|
g_gs_window_info.surface_height = height;
|
||||||
g_gs_window_info.surface_scale = scale;
|
g_gs_window_info.surface_scale = scale;
|
||||||
|
|
||||||
GSResizeWindow(width, height);
|
Host::GSWindowResized(width, height);
|
||||||
}
|
|
||||||
|
|
||||||
void GSPanel::OnCloseWindow(wxCloseEvent& evt)
|
|
||||||
{
|
|
||||||
// CoreThread pausing calls MTGS suspend which calls GSPanel close on
|
|
||||||
// the main thread leading to event starvation. This prevents regenerating
|
|
||||||
// a frame handle when the user closes the window, which prevents this race
|
|
||||||
// condition. -- govanify
|
|
||||||
init_gspanel = false;
|
|
||||||
CoreThread.Suspend();
|
|
||||||
evt.Skip(); // and close it.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSPanel::OnMouseEvent( wxMouseEvent& evt )
|
void GSPanel::OnMouseEvent( wxMouseEvent& evt )
|
||||||
|
@ -722,10 +723,24 @@ GSFrame::GSFrame( const wxString& title)
|
||||||
|
|
||||||
void GSFrame::OnCloseWindow(wxCloseEvent& evt)
|
void GSFrame::OnCloseWindow(wxCloseEvent& evt)
|
||||||
{
|
{
|
||||||
// see GSPanel::OnCloseWindow
|
// if a gs dump is running, it cleans up the window once it's hidden.
|
||||||
init_gspanel = false;
|
if (GSDump::isRunning)
|
||||||
|
{
|
||||||
|
Hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// but under normal operation, we want to suspend the core thread, which will hide us
|
||||||
|
// (except if hide-on-escape is enabled, in which case we want to force hide ourself)
|
||||||
sApp.OnGsFrameClosed( GetId() );
|
sApp.OnGsFrameClosed( GetId() );
|
||||||
Hide(); // and don't close it.
|
if (!IsShown())
|
||||||
|
Hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSFrame::OnDestroyWindow(wxWindowDestroyEvent& evt)
|
||||||
|
{
|
||||||
|
sApp.OnGsFrameDestroyed(GetId());
|
||||||
|
evt.Skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSFrame::ShowFullScreen(bool show, bool updateConfig)
|
bool GSFrame::ShowFullScreen(bool show, bool updateConfig)
|
||||||
|
@ -838,12 +853,6 @@ void GSFrame::AppStatusEvent_OnSettingsApplied()
|
||||||
if (!IsFullScreen() && !IsMaximized())
|
if (!IsFullScreen() && !IsMaximized())
|
||||||
SetClientSize(g_Conf->GSWindow.WindowSize);
|
SetClientSize(g_Conf->GSWindow.WindowSize);
|
||||||
Refresh();
|
Refresh();
|
||||||
|
|
||||||
if( g_Conf->GSWindow.CloseOnEsc )
|
|
||||||
{
|
|
||||||
if (IsShown() && !gsopen_done)
|
|
||||||
Show( false );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GSPanel* GSFrame::GetViewport()
|
GSPanel* GSFrame::GetViewport()
|
||||||
|
@ -866,19 +875,9 @@ void GSFrame::OnUpdateTitle( wxTimerEvent& evt )
|
||||||
cpuUsage.Write(L" | GS: %3.0f%%", PerformanceMetrics::GetGSThreadUsage());
|
cpuUsage.Write(L" | GS: %3.0f%%", PerformanceMetrics::GetGSThreadUsage());
|
||||||
|
|
||||||
if (THREAD_VU1)
|
if (THREAD_VU1)
|
||||||
{
|
|
||||||
cpuUsage.Write(L" | VU: %3.0f%%", PerformanceMetrics::GetVUThreadUsage());
|
cpuUsage.Write(L" | VU: %3.0f%%", PerformanceMetrics::GetVUThreadUsage());
|
||||||
OSDmonitor(Color_StrongGreen, "VU:", std::to_string(lround(PerformanceMetrics::GetVUThreadUsage())).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
OSDmonitor(Color_StrongGreen, "EE:", std::to_string(lround(PerformanceMetrics::GetCPUThreadUsage())).c_str());
|
|
||||||
OSDmonitor(Color_StrongGreen, "GS:", std::to_string(lround(PerformanceMetrics::GetGSThreadUsage())).c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream out;
|
|
||||||
out << std::fixed << std::setprecision(2) << PerformanceMetrics::GetFPS();
|
|
||||||
OSDmonitor(Color_StrongGreen, "FPS:", out.str());
|
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
// Important Linux note: When the title is set in fullscreen the window is redrawn. Unfortunately
|
// Important Linux note: When the title is set in fullscreen the window is redrawn. Unfortunately
|
||||||
// an intermediate white screen appears too which leads to a very annoying flickering.
|
// an intermediate white screen appears too which leads to a very annoying flickering.
|
||||||
|
@ -887,10 +886,6 @@ void GSFrame::OnUpdateTitle( wxTimerEvent& evt )
|
||||||
|
|
||||||
AppConfig::UiTemplateOptions& templates = g_Conf->Templates;
|
AppConfig::UiTemplateOptions& templates = g_Conf->Templates;
|
||||||
|
|
||||||
char gsDest[128];
|
|
||||||
gsDest[0] = 0; // No need to set whole array to NULL.
|
|
||||||
GSgetTitleInfo2( gsDest, sizeof(gsDest) );
|
|
||||||
|
|
||||||
wxString limiterStr = templates.LimiterUnlimited;
|
wxString limiterStr = templates.LimiterUnlimited;
|
||||||
|
|
||||||
if( g_Conf->EmuOptions.GS.FrameLimitEnable )
|
if( g_Conf->EmuOptions.GS.FrameLimitEnable )
|
||||||
|
@ -900,6 +895,7 @@ void GSFrame::OnUpdateTitle( wxTimerEvent& evt )
|
||||||
case LimiterModeType::Nominal: limiterStr = templates.LimiterNormal; break;
|
case LimiterModeType::Nominal: limiterStr = templates.LimiterNormal; break;
|
||||||
case LimiterModeType::Turbo: limiterStr = templates.LimiterTurbo; break;
|
case LimiterModeType::Turbo: limiterStr = templates.LimiterTurbo; break;
|
||||||
case LimiterModeType::Slomo: limiterStr = templates.LimiterSlowmo; break;
|
case LimiterModeType::Slomo: limiterStr = templates.LimiterSlowmo; break;
|
||||||
|
case LimiterModeType::Unlimited: limiterStr = templates.LimiterUnlimited; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -922,6 +918,9 @@ void GSFrame::OnUpdateTitle( wxTimerEvent& evt )
|
||||||
wxString title = templates.TitleTemplate;
|
wxString title = templates.TitleTemplate;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
std::string gsStats;
|
||||||
|
GSgetTitleStats(gsStats);
|
||||||
|
|
||||||
title.Replace(L"${slot}", pxsFmt(L"%d", States_GetCurrentSlot()));
|
title.Replace(L"${slot}", pxsFmt(L"%d", States_GetCurrentSlot()));
|
||||||
title.Replace(L"${limiter}", limiterStr);
|
title.Replace(L"${limiter}", limiterStr);
|
||||||
title.Replace(L"${speed}", pxsFmt(L"%3d%%", lround(PerformanceMetrics::GetSpeed())));
|
title.Replace(L"${speed}", pxsFmt(L"%3d%%", lround(PerformanceMetrics::GetSpeed())));
|
||||||
|
@ -929,7 +928,7 @@ void GSFrame::OnUpdateTitle( wxTimerEvent& evt )
|
||||||
title.Replace(L"${cpuusage}", cpuUsage);
|
title.Replace(L"${cpuusage}", cpuUsage);
|
||||||
title.Replace(L"${omodef}", omodef);
|
title.Replace(L"${omodef}", omodef);
|
||||||
title.Replace(L"${omodei}", omodei);
|
title.Replace(L"${omodei}", omodei);
|
||||||
title.Replace(L"${gsdx}", fromUTF8(gsDest));
|
title.Replace(L"${gsdx}", StringUtil::UTF8StringToWxString(gsStats));
|
||||||
title.Replace(L"${videomode}", ReportVideoMode());
|
title.Replace(L"${videomode}", ReportVideoMode());
|
||||||
if (CoreThread.IsPaused() && !GSDump::isRunning)
|
if (CoreThread.IsPaused() && !GSDump::isRunning)
|
||||||
title = templates.Paused + title;
|
title = templates.Paused + title;
|
||||||
|
|
|
@ -61,7 +61,6 @@ public:
|
||||||
protected:
|
protected:
|
||||||
void AppStatusEvent_OnSettingsApplied();
|
void AppStatusEvent_OnSettingsApplied();
|
||||||
|
|
||||||
void OnCloseWindow( wxCloseEvent& evt );
|
|
||||||
void OnResize(wxSizeEvent& event);
|
void OnResize(wxSizeEvent& event);
|
||||||
void OnMouseEvent( wxMouseEvent& evt );
|
void OnMouseEvent( wxMouseEvent& evt );
|
||||||
void OnHideMouseTimeout( wxTimerEvent& evt );
|
void OnHideMouseTimeout( wxTimerEvent& evt );
|
||||||
|
@ -118,6 +117,7 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void OnCloseWindow( wxCloseEvent& evt );
|
void OnCloseWindow( wxCloseEvent& evt );
|
||||||
|
void OnDestroyWindow( wxWindowDestroyEvent& evt );
|
||||||
void OnMove( wxMoveEvent& evt );
|
void OnMove( wxMoveEvent& evt );
|
||||||
void OnResize( wxSizeEvent& evt );
|
void OnResize( wxSizeEvent& evt );
|
||||||
void OnFocus( wxFocusEvent& evt );
|
void OnFocus( wxFocusEvent& evt );
|
||||||
|
|
|
@ -35,9 +35,6 @@
|
||||||
#include "SPU2/spu2.h"
|
#include "SPU2/spu2.h"
|
||||||
#include "gui/Dialogs/ModalPopups.h"
|
#include "gui/Dialogs/ModalPopups.h"
|
||||||
|
|
||||||
// renderswitch - tells GS to go into dx9 sw if "renderswitch" is set.
|
|
||||||
bool renderswitch = false;
|
|
||||||
|
|
||||||
static bool g_Pcsx2Recording = false; // true if recording video and sound
|
static bool g_Pcsx2Recording = false; // true if recording video and sound
|
||||||
|
|
||||||
|
|
||||||
|
@ -92,15 +89,15 @@ namespace Implementations
|
||||||
if (!g_Conf->EmuOptions.GS.FrameLimitEnable)
|
if (!g_Conf->EmuOptions.GS.FrameLimitEnable)
|
||||||
{
|
{
|
||||||
g_Conf->EmuOptions.GS.FrameLimitEnable = true;
|
g_Conf->EmuOptions.GS.FrameLimitEnable = true;
|
||||||
EmuConfig.LimiterMode = LimiterModeType::Turbo;
|
g_Conf->EmuOptions.LimiterMode = LimiterModeType::Turbo;
|
||||||
OSDlog(Color_StrongRed, true, "(FrameLimiter) Turbo + FrameLimit ENABLED.");
|
OSDlog(Color_StrongRed, true, "(FrameLimiter) Turbo + FrameLimit ENABLED.");
|
||||||
g_Conf->EmuOptions.GS.FrameSkipEnable = !!EmuConfig.Framerate.SkipOnTurbo;
|
g_Conf->EmuOptions.GS.FrameSkipEnable = !!EmuConfig.Framerate.SkipOnTurbo;
|
||||||
}
|
}
|
||||||
else if (EmuConfig.LimiterMode == LimiterModeType::Turbo)
|
else if (g_Conf->EmuOptions.LimiterMode == LimiterModeType::Turbo)
|
||||||
{
|
{
|
||||||
EmuConfig.LimiterMode = LimiterModeType::Nominal;
|
g_Conf->EmuOptions.LimiterMode = LimiterModeType::Nominal;
|
||||||
|
|
||||||
if (EmuConfig.Framerate.SkipOnLimit)
|
if (g_Conf->EmuOptions.Framerate.SkipOnLimit)
|
||||||
{
|
{
|
||||||
OSDlog(Color_StrongRed, true, "(FrameLimiter) Turbo DISABLED. Frameskip ENABLED");
|
OSDlog(Color_StrongRed, true, "(FrameLimiter) Turbo DISABLED. Frameskip ENABLED");
|
||||||
g_Conf->EmuOptions.GS.FrameSkipEnable = true;
|
g_Conf->EmuOptions.GS.FrameSkipEnable = true;
|
||||||
|
@ -113,9 +110,9 @@ namespace Implementations
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EmuConfig.LimiterMode = LimiterModeType::Turbo;
|
g_Conf->EmuOptions.LimiterMode = LimiterModeType::Turbo;
|
||||||
|
|
||||||
if (EmuConfig.Framerate.SkipOnTurbo)
|
if (g_Conf->EmuOptions.Framerate.SkipOnTurbo)
|
||||||
{
|
{
|
||||||
OSDlog(Color_StrongRed, true, "(FrameLimiter) Turbo + Frameskip ENABLED.");
|
OSDlog(Color_StrongRed, true, "(FrameLimiter) Turbo + Frameskip ENABLED.");
|
||||||
g_Conf->EmuOptions.GS.FrameSkipEnable = true;
|
g_Conf->EmuOptions.GS.FrameSkipEnable = true;
|
||||||
|
@ -127,8 +124,6 @@ namespace Implementations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gsUpdateFrequency(g_Conf->EmuOptions);
|
|
||||||
|
|
||||||
pauser.AllowResume();
|
pauser.AllowResume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,20 +136,18 @@ namespace Implementations
|
||||||
// out a better consistency approach... -air
|
// out a better consistency approach... -air
|
||||||
|
|
||||||
ScopedCoreThreadPause pauser;
|
ScopedCoreThreadPause pauser;
|
||||||
if (EmuConfig.LimiterMode == LimiterModeType::Slomo)
|
if (g_Conf->EmuOptions.LimiterMode == LimiterModeType::Slomo)
|
||||||
{
|
{
|
||||||
EmuConfig.LimiterMode = LimiterModeType::Nominal;
|
g_Conf->EmuOptions.LimiterMode = LimiterModeType::Nominal;
|
||||||
OSDlog(Color_StrongRed, true, "(FrameLimiter) SlowMotion DISABLED.");
|
OSDlog(Color_StrongRed, true, "(FrameLimiter) SlowMotion DISABLED.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EmuConfig.LimiterMode = LimiterModeType::Slomo;
|
g_Conf->EmuOptions.LimiterMode = LimiterModeType::Slomo;
|
||||||
OSDlog(Color_StrongRed, true, "(FrameLimiter) SlowMotion ENABLED.");
|
OSDlog(Color_StrongRed, true, "(FrameLimiter) SlowMotion ENABLED.");
|
||||||
g_Conf->EmuOptions.GS.FrameLimitEnable = true;
|
g_Conf->EmuOptions.GS.FrameLimitEnable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
gsUpdateFrequency(g_Conf->EmuOptions);
|
|
||||||
|
|
||||||
pauser.AllowResume();
|
pauser.AllowResume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +158,7 @@ namespace Implementations
|
||||||
OSDlog(Color_StrongRed, true, "(FrameLimiter) %s.", g_Conf->EmuOptions.GS.FrameLimitEnable ? "ENABLED" : "DISABLED");
|
OSDlog(Color_StrongRed, true, "(FrameLimiter) %s.", g_Conf->EmuOptions.GS.FrameLimitEnable ? "ENABLED" : "DISABLED");
|
||||||
|
|
||||||
// Turbo/Slowmo don't make sense when framelimiter is toggled
|
// Turbo/Slowmo don't make sense when framelimiter is toggled
|
||||||
EmuConfig.LimiterMode = LimiterModeType::Nominal;
|
g_Conf->EmuOptions.LimiterMode = LimiterModeType::Nominal;
|
||||||
|
|
||||||
pauser.AllowResume();
|
pauser.AllowResume();
|
||||||
}
|
}
|
||||||
|
@ -196,15 +189,22 @@ namespace Implementations
|
||||||
// saved until shutdown, but it matches the behavior pre-settings-move.
|
// saved until shutdown, but it matches the behavior pre-settings-move.
|
||||||
g_Conf->EmuOptions.GS.AspectRatio = art;
|
g_Conf->EmuOptions.GS.AspectRatio = art;
|
||||||
|
|
||||||
|
// Prevent GS reopening for the setting change.
|
||||||
|
EmuConfig.GS.AspectRatio = art;
|
||||||
|
|
||||||
OSDlog(Color_StrongBlue, true, "(GSwindow) Aspect ratio: %s", arts);
|
OSDlog(Color_StrongBlue, true, "(GSwindow) Aspect ratio: %s", arts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: The settings below are super janky and race the GS thread when updating.
|
||||||
|
// But because they don't go through the proper settings update procedure, it's necessary to avoid reopening GS.
|
||||||
void SetOffset(float x, float y)
|
void SetOffset(float x, float y)
|
||||||
{
|
{
|
||||||
EmuConfig.GS.OffsetX = x;
|
|
||||||
EmuConfig.GS.OffsetY = y;
|
|
||||||
g_Conf->EmuOptions.GS.OffsetX = x;
|
g_Conf->EmuOptions.GS.OffsetX = x;
|
||||||
g_Conf->EmuOptions.GS.OffsetY = y;
|
g_Conf->EmuOptions.GS.OffsetY = y;
|
||||||
|
EmuConfig.GS.OffsetX = x;
|
||||||
|
EmuConfig.GS.OffsetY = y;
|
||||||
|
GSConfig.OffsetX = x;
|
||||||
|
GSConfig.OffsetY = y;
|
||||||
OSDlog(Color_StrongBlue, true, "(GSwindow) Offset: x=%f, y=%f", x, y);
|
OSDlog(Color_StrongBlue, true, "(GSwindow) Offset: x=%f, y=%f", x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,8 +237,9 @@ namespace Implementations
|
||||||
{
|
{
|
||||||
if (zoom <= 0)
|
if (zoom <= 0)
|
||||||
return;
|
return;
|
||||||
EmuConfig.GS.StretchY = zoom;
|
|
||||||
g_Conf->EmuOptions.GS.StretchY = zoom;
|
g_Conf->EmuOptions.GS.StretchY = zoom;
|
||||||
|
EmuConfig.GS.StretchY = zoom;
|
||||||
|
GSConfig.StretchY = zoom;
|
||||||
OSDlog(Color_StrongBlue, true, "(GSwindow) Vertical stretch: %f", zoom);
|
OSDlog(Color_StrongBlue, true, "(GSwindow) Vertical stretch: %f", zoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,8 +260,9 @@ namespace Implementations
|
||||||
{
|
{
|
||||||
if (zoom < 0)
|
if (zoom < 0)
|
||||||
return;
|
return;
|
||||||
EmuConfig.GS.Zoom = zoom;
|
|
||||||
g_Conf->EmuOptions.GS.Zoom = zoom;
|
g_Conf->EmuOptions.GS.Zoom = zoom;
|
||||||
|
EmuConfig.GS.Zoom = zoom;
|
||||||
|
GSConfig.Zoom = zoom;
|
||||||
|
|
||||||
if (zoom == 0)
|
if (zoom == 0)
|
||||||
OSDlog(Color_StrongBlue, true, "(GSwindow) Zoom: 0 (auto, no black bars)");
|
OSDlog(Color_StrongBlue, true, "(GSwindow) Zoom: 0 (auto, no black bars)");
|
||||||
|
@ -387,15 +389,7 @@ namespace Implementations
|
||||||
{
|
{
|
||||||
reentrant = true;
|
reentrant = true;
|
||||||
ScopedCoreThreadPause paused_core;
|
ScopedCoreThreadPause paused_core;
|
||||||
freezeData fP = {0, nullptr};
|
GetMTGS().ToggleSoftwareRendering();
|
||||||
MTGS_FreezeData sstate = {&fP, 0};
|
|
||||||
GetMTGS().Freeze(FreezeAction::Size, sstate);
|
|
||||||
fP.data = new u8[fP.size];
|
|
||||||
GetMTGS().Freeze(FreezeAction::Save, sstate);
|
|
||||||
GetMTGS().Suspend(true);
|
|
||||||
renderswitch = !renderswitch;
|
|
||||||
GetMTGS().Freeze(FreezeAction::Load, sstate);
|
|
||||||
delete[] fP.data;
|
|
||||||
paused_core.AllowResume();
|
paused_core.AllowResume();
|
||||||
reentrant = false;
|
reentrant = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,6 @@
|
||||||
#include "Recording/InputRecordingControls.h"
|
#include "Recording/InputRecordingControls.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern std::atomic_bool init_gspanel;
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
wxMenu* MainEmuFrame::MakeStatesSubMenu(int baseid, int loadBackupId) const
|
wxMenu* MainEmuFrame::MakeStatesSubMenu(int baseid, int loadBackupId) const
|
||||||
{
|
{
|
||||||
|
@ -164,7 +162,7 @@ void MainEmuFrame::OnCloseWindow(wxCloseEvent& evt)
|
||||||
{
|
{
|
||||||
// the main thread is busy suspending everything, so let's not try to call it
|
// the main thread is busy suspending everything, so let's not try to call it
|
||||||
// when closing the emulator
|
// when closing the emulator
|
||||||
init_gspanel = false;
|
//init_gspanel = false;
|
||||||
|
|
||||||
if (IsBeingDeleted())
|
if (IsBeingDeleted())
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -47,8 +47,6 @@
|
||||||
|
|
||||||
using namespace Dialogs;
|
using namespace Dialogs;
|
||||||
|
|
||||||
extern std::atomic_bool init_gspanel;
|
|
||||||
|
|
||||||
void MainEmuFrame::Menu_SysSettings_Click(wxCommandEvent& event)
|
void MainEmuFrame::Menu_SysSettings_Click(wxCommandEvent& event)
|
||||||
{
|
{
|
||||||
AppOpenModalDialog<SysConfigDialog>(wxEmptyString, this);
|
AppOpenModalDialog<SysConfigDialog>(wxEmptyString, this);
|
||||||
|
@ -88,29 +86,30 @@ void MainEmuFrame::Menu_PADSettings_Click(wxCommandEvent& event)
|
||||||
|
|
||||||
void MainEmuFrame::Menu_GSSettings_Click(wxCommandEvent& event)
|
void MainEmuFrame::Menu_GSSettings_Click(wxCommandEvent& event)
|
||||||
{
|
{
|
||||||
ScopedCoreThreadPause paused_core;
|
|
||||||
bool is_frame_init = !(wxGetApp().GetGsFramePtr() == nullptr);
|
|
||||||
bool need_shutdown = GetMTGS().IsClosed();
|
|
||||||
init_gspanel = false;
|
|
||||||
freezeData fP = {0, nullptr};
|
|
||||||
MTGS_FreezeData sstate = {&fP, 0};
|
|
||||||
if (is_frame_init)
|
|
||||||
{
|
|
||||||
GetMTGS().Freeze(FreezeAction::Size, sstate);
|
|
||||||
fP.data = new u8[fP.size];
|
|
||||||
GetMTGS().Freeze(FreezeAction::Save, sstate);
|
|
||||||
GetMTGS().Suspend(true);
|
|
||||||
}
|
|
||||||
GSconfigure();
|
GSconfigure();
|
||||||
if (is_frame_init)
|
|
||||||
|
// this is a bit of an ugly hack, but so is the whole of the threading nonsense.
|
||||||
|
// we need to tear down USB/PAD before we apply settings, because the window handle
|
||||||
|
// will change on renderer change. but we can't do that in ApplySettings() because
|
||||||
|
// that happens on the UI thread instead of the core thread....
|
||||||
|
GSFrame* gs_frame = wxGetApp().GetGsFramePtr();
|
||||||
|
const bool gs_frame_open = gs_frame && gs_frame->IsShown();
|
||||||
|
const Pcsx2Config::GSOptions old_options(g_Conf->EmuOptions.GS);
|
||||||
|
g_Conf->EmuOptions.GS.ReloadIniSettings();
|
||||||
|
if (!g_Conf->EmuOptions.GS.RestartOptionsAreEqual(old_options))
|
||||||
{
|
{
|
||||||
GetMTGS().Freeze(FreezeAction::Load, sstate);
|
ScopedCoreThreadPause pauser(static_cast<SystemsMask>(System_USB | System_PAD));
|
||||||
delete[] fP.data;
|
wxGetApp().SysApplySettings();
|
||||||
}
|
}
|
||||||
if (need_shutdown)
|
else
|
||||||
GetMTGS().Suspend(true);
|
{
|
||||||
init_gspanel = true;
|
wxGetApp().SysApplySettings();
|
||||||
paused_core.AllowResume();
|
}
|
||||||
|
|
||||||
|
// re-hide the GS window after changing renderers if we were paused
|
||||||
|
gs_frame = wxGetApp().GetGsFramePtr();
|
||||||
|
if (!gs_frame_open && gs_frame && gs_frame->IsShown())
|
||||||
|
gs_frame->Hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainEmuFrame::Menu_WindowSettings_Click(wxCommandEvent& event)
|
void MainEmuFrame::Menu_WindowSettings_Click(wxCommandEvent& event)
|
||||||
|
|
|
@ -48,6 +48,10 @@ Panels::GameFixesPanel::GameFixesPanel( wxWindow* parent )
|
||||||
_("Preload TLB hack to avoid tlb miss on Goemon"),
|
_("Preload TLB hack to avoid tlb miss on Goemon"),
|
||||||
wxEmptyString
|
wxEmptyString
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
_("Switch to Software renderer for FMVs"),
|
||||||
|
wxEmptyString
|
||||||
|
},
|
||||||
{
|
{
|
||||||
_("Skip MPEG hack - Skips videos/FMVs in games to avoid game hanging/freezes."),
|
_("Skip MPEG hack - Skips videos/FMVs in games to avoid game hanging/freezes."),
|
||||||
wxEmptyString
|
wxEmptyString
|
||||||
|
|
|
@ -35,13 +35,13 @@
|
||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\xbyak;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\xbyak;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\freetype\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\xz\xz\src\liblzma\api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\xz\xz\src\liblzma\api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\baseclasses;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\baseclasses;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\libpng;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\libpng;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\glad\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\glad\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\cubeb\cubeb\include;$(SolutionDir)3rdparty\cubeb\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\cubeb\cubeb\include;$(SolutionDir)3rdparty\cubeb\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\imgui\imgui;$(SolutionDir)3rdparty\imgui\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<ExceptionHandling>Async</ExceptionHandling>
|
<ExceptionHandling>Async</ExceptionHandling>
|
||||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||||
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
|
||||||
|
@ -303,6 +303,9 @@
|
||||||
<ClCompile Include="DEV9\Win32\DEV9WinConfig.cpp" />
|
<ClCompile Include="DEV9\Win32\DEV9WinConfig.cpp" />
|
||||||
<ClCompile Include="DEV9\net.cpp" />
|
<ClCompile Include="DEV9\net.cpp" />
|
||||||
<ClCompile Include="DEV9\Win32\tap-win32.cpp" />
|
<ClCompile Include="DEV9\Win32\tap-win32.cpp" />
|
||||||
|
<ClCompile Include="Frontend\D3D11HostDisplay.cpp" />
|
||||||
|
<ClCompile Include="Frontend\ImGuiManager.cpp" />
|
||||||
|
<ClCompile Include="Frontend\OpenGLHostDisplay.cpp" />
|
||||||
<ClCompile Include="GameDatabase.cpp" />
|
<ClCompile Include="GameDatabase.cpp" />
|
||||||
<ClCompile Include="Gif_Logger.cpp" />
|
<ClCompile Include="Gif_Logger.cpp" />
|
||||||
<ClCompile Include="Gif_Unit.cpp" />
|
<ClCompile Include="Gif_Unit.cpp" />
|
||||||
|
@ -332,6 +335,7 @@
|
||||||
<ClCompile Include="gui\wxAppWithHelpers.cpp" />
|
<ClCompile Include="gui\wxAppWithHelpers.cpp" />
|
||||||
<ClCompile Include="gui\wxSettingsInterface.cpp" />
|
<ClCompile Include="gui\wxSettingsInterface.cpp" />
|
||||||
<ClCompile Include="Host.cpp" />
|
<ClCompile Include="Host.cpp" />
|
||||||
|
<ClCompile Include="HostDisplay.cpp" />
|
||||||
<ClCompile Include="IopGte.cpp" />
|
<ClCompile Include="IopGte.cpp" />
|
||||||
<ClCompile Include="PINE.cpp" />
|
<ClCompile Include="PINE.cpp" />
|
||||||
<ClCompile Include="FW.cpp" />
|
<ClCompile Include="FW.cpp" />
|
||||||
|
@ -483,7 +487,6 @@
|
||||||
<ClCompile Include="GS\GSLocalMemory.cpp" />
|
<ClCompile Include="GS\GSLocalMemory.cpp" />
|
||||||
<ClCompile Include="GS\GSLzma.cpp" />
|
<ClCompile Include="GS\GSLzma.cpp" />
|
||||||
<ClCompile Include="GS\GSPerfMon.cpp" />
|
<ClCompile Include="GS\GSPerfMon.cpp" />
|
||||||
<ClCompile Include="GS\Renderers\Common\GSOsdManager.cpp" />
|
|
||||||
<ClCompile Include="GS\GSPng.cpp" />
|
<ClCompile Include="GS\GSPng.cpp" />
|
||||||
<ClCompile Include="GS\GSRingHeap.cpp" />
|
<ClCompile Include="GS\GSRingHeap.cpp" />
|
||||||
<ClCompile Include="GS\Renderers\SW\GSRasterizer.cpp" />
|
<ClCompile Include="GS\Renderers\SW\GSRasterizer.cpp" />
|
||||||
|
@ -742,6 +745,9 @@
|
||||||
<ClInclude Include="DEV9\smap.h" />
|
<ClInclude Include="DEV9\smap.h" />
|
||||||
<ClInclude Include="DEV9\Win32\pcap_io_win32_funcs.h" />
|
<ClInclude Include="DEV9\Win32\pcap_io_win32_funcs.h" />
|
||||||
<ClInclude Include="DEV9\Win32\tap.h" />
|
<ClInclude Include="DEV9\Win32\tap.h" />
|
||||||
|
<ClInclude Include="Frontend\D3D11HostDisplay.h" />
|
||||||
|
<ClInclude Include="Frontend\ImGuiManager.h" />
|
||||||
|
<ClInclude Include="Frontend\OpenGLHostDisplay.h" />
|
||||||
<ClInclude Include="GameDatabase.h" />
|
<ClInclude Include="GameDatabase.h" />
|
||||||
<ClInclude Include="Gif_Unit.h" />
|
<ClInclude Include="Gif_Unit.h" />
|
||||||
<ClInclude Include="GS\Renderers\DX11\D3D.h" />
|
<ClInclude Include="GS\Renderers\DX11\D3D.h" />
|
||||||
|
@ -768,6 +774,7 @@
|
||||||
<ClInclude Include="gui\wxAppWithHelpers.h" />
|
<ClInclude Include="gui\wxAppWithHelpers.h" />
|
||||||
<ClInclude Include="gui\wxSettingsInterface.h" />
|
<ClInclude Include="gui\wxSettingsInterface.h" />
|
||||||
<ClInclude Include="Host.h" />
|
<ClInclude Include="Host.h" />
|
||||||
|
<ClInclude Include="HostDisplay.h" />
|
||||||
<ClInclude Include="IopGte.h" />
|
<ClInclude Include="IopGte.h" />
|
||||||
<ClInclude Include="PINE.h" />
|
<ClInclude Include="PINE.h" />
|
||||||
<ClInclude Include="FW.h" />
|
<ClInclude Include="FW.h" />
|
||||||
|
@ -843,7 +850,6 @@
|
||||||
<ClInclude Include="GS\GSLocalMemory.h" />
|
<ClInclude Include="GS\GSLocalMemory.h" />
|
||||||
<ClInclude Include="GS\GSLzma.h" />
|
<ClInclude Include="GS\GSLzma.h" />
|
||||||
<ClInclude Include="GS\GSPerfMon.h" />
|
<ClInclude Include="GS\GSPerfMon.h" />
|
||||||
<ClInclude Include="GS\Renderers\Common\GSOsdManager.h" />
|
|
||||||
<ClInclude Include="GS\GSPng.h" />
|
<ClInclude Include="GS\GSPng.h" />
|
||||||
<ClInclude Include="GS\GSRingHeap.h" />
|
<ClInclude Include="GS\GSRingHeap.h" />
|
||||||
<ClInclude Include="GS\Renderers\SW\GSRasterizer.h" />
|
<ClInclude Include="GS\Renderers\SW\GSRasterizer.h" />
|
||||||
|
@ -1107,9 +1113,6 @@
|
||||||
<ProjectReference Include="$(SolutionDir)3rdparty\fmt\fmt.vcxproj">
|
<ProjectReference Include="$(SolutionDir)3rdparty\fmt\fmt.vcxproj">
|
||||||
<Project>{449ad25e-424a-4714-babc-68706cdcc33b}</Project>
|
<Project>{449ad25e-424a-4714-babc-68706cdcc33b}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="$(SolutionDir)3rdparty\freetype\builds\windows\freetype.vcxproj">
|
|
||||||
<Project>{78b079bd-9fc7-4b9e-b4a6-96da0f00248b}</Project>
|
|
||||||
</ProjectReference>
|
|
||||||
<ProjectReference Include="$(SolutionDir)3rdparty\libjpeg\libjpeg.vcxproj">
|
<ProjectReference Include="$(SolutionDir)3rdparty\libjpeg\libjpeg.vcxproj">
|
||||||
<Project>{bc236261-77e8-4567-8d09-45cd02965eb6}</Project>
|
<Project>{bc236261-77e8-4567-8d09-45cd02965eb6}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
@ -1158,6 +1161,9 @@
|
||||||
<ProjectReference Include="$(SolutionDir)3rdparty\rapidyaml\ryml.vcxproj">
|
<ProjectReference Include="$(SolutionDir)3rdparty\rapidyaml\ryml.vcxproj">
|
||||||
<Project>{de9653b6-17dd-356a-9ee0-28a731772587}</Project>
|
<Project>{de9653b6-17dd-356a-9ee0-28a731772587}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="$(SolutionDir)3rdparty\imgui\imgui.vcxproj">
|
||||||
|
<Project>{88fb34ec-845e-4f21-a552-f1573b9ed167}</Project>
|
||||||
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\common\common.vcxproj">
|
<ProjectReference Include="..\common\common.vcxproj">
|
||||||
<Project>{4639972e-424e-4e13-8b07-ca403c481346}</Project>
|
<Project>{4639972e-424e-4e13-8b07-ca403c481346}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
|
|
@ -1583,9 +1583,6 @@
|
||||||
<ClCompile Include="GS\Renderers\Common\GSFunctionMap.cpp">
|
<ClCompile Include="GS\Renderers\Common\GSFunctionMap.cpp">
|
||||||
<Filter>System\Ps2\GS\Renderers\Common</Filter>
|
<Filter>System\Ps2\GS\Renderers\Common</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="GS\Renderers\Common\GSOsdManager.cpp">
|
|
||||||
<Filter>System\Ps2\GS\Renderers\Common</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="GS\GSCapture.cpp">
|
<ClCompile Include="GS\GSCapture.cpp">
|
||||||
<Filter>System\Ps2\GS\Window</Filter>
|
<Filter>System\Ps2\GS\Window</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -1649,6 +1646,21 @@
|
||||||
<ClCompile Include="SPU2\SndOut_Cubeb.cpp">
|
<ClCompile Include="SPU2\SndOut_Cubeb.cpp">
|
||||||
<Filter>System\Ps2\SPU2</Filter>
|
<Filter>System\Ps2\SPU2</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="HostDisplay.cpp">
|
||||||
|
<Filter>Host</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="Frontend\OpenGLHostDisplay.cpp">
|
||||||
|
<Filter>Host</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="Frontend\D3D11HostDisplay.cpp">
|
||||||
|
<Filter>Host</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="gui\AppHost.cpp">
|
||||||
|
<Filter>AppHost</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="Frontend\ImGuiManager.cpp">
|
||||||
|
<Filter>Host</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="Host.cpp">
|
<ClCompile Include="Host.cpp">
|
||||||
<Filter>Host</Filter>
|
<Filter>Host</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -2689,9 +2701,6 @@
|
||||||
<ClInclude Include="GS\Renderers\Common\GSFunctionMap.h">
|
<ClInclude Include="GS\Renderers\Common\GSFunctionMap.h">
|
||||||
<Filter>System\Ps2\GS\Renderers\Common</Filter>
|
<Filter>System\Ps2\GS\Renderers\Common</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="GS\Renderers\Common\GSOsdManager.h">
|
|
||||||
<Filter>System\Ps2\GS\Renderers\Common</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="GS\GSCapture.h">
|
<ClInclude Include="GS\GSCapture.h">
|
||||||
<Filter>System\Ps2\GS\Window</Filter>
|
<Filter>System\Ps2\GS\Window</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -2755,6 +2764,21 @@
|
||||||
<ClInclude Include="Host.h">
|
<ClInclude Include="Host.h">
|
||||||
<Filter>Host</Filter>
|
<Filter>Host</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="HostDisplay.h">
|
||||||
|
<Filter>Host</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="Frontend\OpenGLHostDisplay.h">
|
||||||
|
<Filter>Host</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="Frontend\D3D11HostDisplay.h">
|
||||||
|
<Filter>Host</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="gui\AppHost.h">
|
||||||
|
<Filter>AppHost\Include</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="Frontend\ImGuiManager.h">
|
||||||
|
<Filter>Host</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="windows\wxResources.rc">
|
<ResourceCompile Include="windows\wxResources.rc">
|
||||||
|
|
Loading…
Reference in New Issue