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}
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E} = {D6973076-9317-4EF2-A0B8-B7A18AC0713E}
|
||||
{27F17499-A372-4408-8AFA-4F9F4584FBD3} = {27F17499-A372-4408-8AFA-4F9F4584FBD3}
|
||||
{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B} = {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
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
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "baseclasses", "3rdparty\baseclasses\baseclasses.vcxproj", "{27F17499-A372-4408-8AFA-4F9F4584FBD3}"
|
||||
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}"
|
||||
EndProject
|
||||
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|x64.ActiveCfg = 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.Build.0 = Debug|Win32
|
||||
{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}
|
||||
{0FAE817D-9A32-4830-857E-81DA57246E16} = {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}
|
||||
{449AD25E-424A-4714-BABC-68706CDCC33B} = {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;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -272,13 +272,6 @@ void ps_convert_rgba_8i()
|
|||
}
|
||||
#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
|
||||
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/libsamplerate 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/soundtouch EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(3rdparty/wil EXCLUDE_FROM_ALL)
|
||||
|
@ -26,7 +25,6 @@ else()
|
|||
find_package(PCAP REQUIRED)
|
||||
find_package(LibXml2 REQUIRED)
|
||||
make_imported_target_if_missing(LibXml2::LibXml2 LibXml2)
|
||||
find_package(Freetype REQUIRED) # GS OSD
|
||||
find_package(Gettext) # translation tool
|
||||
find_package(LibLZMA REQUIRED)
|
||||
make_imported_target_if_missing(LibLZMA::LibLZMA LIBLZMA)
|
||||
|
@ -168,12 +166,6 @@ else()
|
|||
if(WAYLAND_API)
|
||||
find_package(Wayland REQUIRED)
|
||||
endif()
|
||||
|
||||
#----------------------------------------
|
||||
# Use system include
|
||||
#----------------------------------------
|
||||
find_package(HarfBuzz)
|
||||
|
||||
endif(WIN32)
|
||||
|
||||
# Require threads on all OSes.
|
||||
|
|
|
@ -103,7 +103,11 @@ protected:
|
|||
|
||||
#define SettingsWrapSection(section) const char* CURRENT_SETTINGS_SECTION = section;
|
||||
#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 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 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
|
||||
GS.cpp
|
||||
GSState.cpp
|
||||
Host.cpp
|
||||
HostDisplay.cpp
|
||||
Hw.cpp
|
||||
HwRead.cpp
|
||||
HwWrite.cpp
|
||||
|
@ -187,6 +189,7 @@ set(pcsx2Headers
|
|||
GS.h
|
||||
Hardware.h
|
||||
Host.h
|
||||
HostDisplay.h
|
||||
Hw.h
|
||||
IopBios.h
|
||||
IopCommon.h
|
||||
|
@ -607,7 +610,6 @@ set(pcsx2GSSources
|
|||
GS/Renderers/Common/GSDevice.cpp
|
||||
GS/Renderers/Common/GSDirtyRect.cpp
|
||||
GS/Renderers/Common/GSFunctionMap.cpp
|
||||
GS/Renderers/Common/GSOsdManager.cpp
|
||||
GS/Renderers/Common/GSRenderer.cpp
|
||||
GS/Renderers/Common/GSTexture.cpp
|
||||
GS/Renderers/Common/GSVertexTrace.cpp
|
||||
|
@ -672,7 +674,6 @@ set(pcsx2GSHeaders
|
|||
GS/Renderers/Common/GSDirtyRect.h
|
||||
GS/Renderers/Common/GSFastList.h
|
||||
GS/Renderers/Common/GSFunctionMap.h
|
||||
GS/Renderers/Common/GSOsdManager.h
|
||||
GS/Renderers/Common/GSRenderer.h
|
||||
GS/Renderers/Common/GSTexture.h
|
||||
GS/Renderers/Common/GSVertex.h
|
||||
|
@ -872,6 +873,27 @@ set(pcsx2DebugToolsHeaders
|
|||
DebugTools/DisVUops.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
|
||||
set(pcsx2GuiSources
|
||||
gui/AppAssert.cpp
|
||||
|
@ -952,6 +974,7 @@ set(pcsx2GuiHeaders
|
|||
gui/AppConfig.h
|
||||
gui/AppCoreThread.h
|
||||
gui/AppEventListeners.h
|
||||
gui/AppHost.h
|
||||
gui/AppForwardDefs.h
|
||||
gui/App.h
|
||||
gui/ApplyState.h
|
||||
|
@ -1296,19 +1319,27 @@ target_sources(PCSX2 PRIVATE
|
|||
${pcsx2GSSources}
|
||||
${pcsx2DebugToolsSources}
|
||||
${pcsx2DebugToolsHeaders}
|
||||
${pcsx2GuiSources}
|
||||
${pcsx2GuiResources}
|
||||
${pcsx2GuiHeaders}
|
||||
${pcsx2FrontendSources}
|
||||
${pcsx2FrontendHeaders}
|
||||
${pcsx2ps2Sources}
|
||||
${pcsx2ps2Headers}
|
||||
${pcsx2RecordingSources}
|
||||
${pcsx2RecordingVirtualPadResources}
|
||||
${pcsx2RecordingHeaders}
|
||||
${pcsx2SystemSources}
|
||||
${pcsx2SystemHeaders}
|
||||
${pcsx2UtilitiesSources}
|
||||
${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
|
||||
# Linux
|
||||
if(Linux)
|
||||
|
@ -1368,6 +1399,7 @@ endif()
|
|||
target_link_libraries(PCSX2_FLAGS INTERFACE
|
||||
common
|
||||
glad
|
||||
imgui
|
||||
fmt::fmt
|
||||
ryml
|
||||
chdr-static
|
||||
|
@ -1376,7 +1408,6 @@ target_link_libraries(PCSX2_FLAGS INTERFACE
|
|||
PkgConfig::SOUNDTOUCH
|
||||
PkgConfig::SAMPLERATE
|
||||
PNG::PNG
|
||||
Freetype::Freetype
|
||||
LibLZMA::LibLZMA
|
||||
${LIBC_LIBRARIES}
|
||||
)
|
||||
|
@ -1410,7 +1441,6 @@ elseif(APPLE)
|
|||
else()
|
||||
target_link_libraries(PCSX2_FLAGS INTERFACE
|
||||
GTK::gtk
|
||||
HarfBuzz::HarfBuzz
|
||||
OpenGL::GL
|
||||
PCAP::PCAP
|
||||
LibXml2::LibXml2
|
||||
|
|
206
pcsx2/Config.h
206
pcsx2/Config.h
|
@ -32,6 +32,7 @@ enum GamefixId
|
|||
Fix_FpuMultiply = GamefixId_FIRST,
|
||||
Fix_FpuNegDiv,
|
||||
Fix_GoemonTlbMiss,
|
||||
Fix_SoftwareRendererFMV,
|
||||
Fix_SkipMpeg,
|
||||
Fix_OPHFlag,
|
||||
Fix_EETiming,
|
||||
|
@ -97,6 +98,73 @@ enum class LimiterModeType : u8
|
|||
Nominal,
|
||||
Turbo,
|
||||
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
|
||||
|
@ -318,6 +386,61 @@ struct Pcsx2Config
|
|||
// ------------------------------------------------------------------------
|
||||
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};
|
||||
|
||||
// forces the MTGS to execute tags/tasks in fully blocking/synchronous
|
||||
|
@ -337,45 +460,73 @@ struct Pcsx2Config
|
|||
|
||||
AspectRatioType AspectRatio{AspectRatioType::R4_3};
|
||||
FMVAspectRatioSwitchType FMVAspectRatioSwitch{FMVAspectRatioSwitchType::Off};
|
||||
GSInterlaceMode InterlaceMode{GSInterlaceMode::Automatic};
|
||||
|
||||
double Zoom{100.0};
|
||||
double StretchY{100.0};
|
||||
double OffsetX{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);
|
||||
|
||||
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
|
||||
{
|
||||
return OpEqu(SynchronousMTGS) &&
|
||||
OpEqu(VsyncQueueSize) &&
|
||||
/// Sets user hack values to defaults when user hacks are not enabled.
|
||||
void MaskUserHacks();
|
||||
|
||||
OpEqu(FrameSkipEnable) &&
|
||||
OpEqu(FrameLimitEnable) &&
|
||||
OpEqu(VsyncEnable) &&
|
||||
/// Returns true if any of the hardware renderers are selected.
|
||||
bool UseHardwareRenderer() const;
|
||||
|
||||
OpEqu(LimitScalar) &&
|
||||
OpEqu(FramerateNTSC) &&
|
||||
OpEqu(FrameratePAL) &&
|
||||
/// Returns false if the compared to the old settings, we need to reopen GS.
|
||||
/// (i.e. renderer change, swap chain mode change, etc.)
|
||||
bool RestartOptionsAreEqual(const GSOptions& right) const;
|
||||
|
||||
OpEqu(FramesToDraw) &&
|
||||
OpEqu(FramesToSkip) &&
|
||||
/// Returns false if any options need to be applied to the MTGS.
|
||||
bool OptionsAreEqual(const GSOptions& right) const;
|
||||
|
||||
OpEqu(AspectRatio) &&
|
||||
OpEqu(FMVAspectRatioSwitch) &&
|
||||
|
||||
OpEqu(Zoom) &&
|
||||
OpEqu(StretchY) &&
|
||||
OpEqu(OffsetX) &&
|
||||
OpEqu(OffsetY);
|
||||
}
|
||||
|
||||
bool operator!=(const GSOptions& right) const
|
||||
{
|
||||
return !this->operator==(right);
|
||||
}
|
||||
bool operator==(const GSOptions& right) const;
|
||||
bool operator!=(const GSOptions& right) const;
|
||||
};
|
||||
|
||||
struct SPU2Options
|
||||
|
@ -463,6 +614,7 @@ struct Pcsx2Config
|
|||
FpuMulHack : 1, // Tales of Destiny hangs.
|
||||
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
|
||||
SoftwareRendererFMVHack : 1, // Switches to software renderer for FMVs
|
||||
SkipMPEGHack : 1, // Skips MPEG videos (Katamari and other games need this)
|
||||
OPHFlagHack : 1, // Bleach Blade Battlers
|
||||
EETimingHack : 1, // General purpose timing hack.
|
||||
|
@ -680,6 +832,8 @@ struct Pcsx2Config
|
|||
|
||||
bool MultitapEnabled(uint port) const;
|
||||
|
||||
VsyncMode GetEffectiveVsyncMode() const;
|
||||
|
||||
bool operator==(const Pcsx2Config& right) const;
|
||||
bool operator!=(const Pcsx2Config& right) const
|
||||
{
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <time.h>
|
||||
#include <cmath>
|
||||
|
||||
#include "gui/App.h"
|
||||
#include "Common.h"
|
||||
#include "R3000A.h"
|
||||
#include "Counters.h"
|
||||
|
@ -28,10 +27,17 @@
|
|||
#include "GS.h"
|
||||
#include "VUmicro.h"
|
||||
#include "PerformanceMetrics.h"
|
||||
#include "Patch.h"
|
||||
|
||||
#include "ps2/HwInternal.h"
|
||||
#include "Sio.h"
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
#include "gui/App.h"
|
||||
#else
|
||||
#include "VMManager.h"
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
# include "Recording/InputRecordingControls.h"
|
||||
#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()
|
||||
{
|
||||
// Note about NTSC/PAL "double strike" modes:
|
||||
|
@ -416,10 +428,68 @@ void frameLimitReset()
|
|||
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.
|
||||
static __fi void frameLimitUpdateCore()
|
||||
{
|
||||
DoFMVSwitch();
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
GetCoreThread().VsyncInThread();
|
||||
#else
|
||||
VMManager::Internal::VSyncOnCPUThread();
|
||||
#endif
|
||||
Cpu->CheckExecutionState();
|
||||
}
|
||||
|
||||
|
@ -429,7 +499,7 @@ static __fi void frameLimitUpdateCore()
|
|||
static __fi void frameLimit()
|
||||
{
|
||||
// Framelimiter off in settings? Framelimiter go brrr.
|
||||
if (!EmuConfig.GS.FrameLimitEnable)
|
||||
if (EmuConfig.GS.LimitScalar == 0.0)
|
||||
{
|
||||
frameLimitUpdateCore();
|
||||
return;
|
||||
|
@ -482,6 +552,8 @@ static __fi void VSyncStart(u32 sCycle)
|
|||
}
|
||||
#endif
|
||||
|
||||
PerformanceMetrics::Update();
|
||||
|
||||
frameLimit(); // limit FPS
|
||||
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
|
||||
|
||||
extern const char* ReportVideoMode();
|
||||
extern const char* ReportInterlaceMode();
|
||||
extern Counter counters[4];
|
||||
extern SyncCounter hsyncCounter;
|
||||
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)
|
||||
{
|
||||
switch (EmuConfig.LimiterMode)
|
||||
if (config.GS.FrameLimitEnable)
|
||||
{
|
||||
case LimiterModeType::Nominal:
|
||||
config.GS.LimitScalar = EmuConfig.Framerate.NominalScalar;
|
||||
break;
|
||||
case LimiterModeType::Slomo:
|
||||
config.GS.LimitScalar = EmuConfig.Framerate.SlomoScalar;
|
||||
break;
|
||||
case LimiterModeType::Turbo:
|
||||
config.GS.LimitScalar = EmuConfig.Framerate.TurboScalar;
|
||||
break;
|
||||
default:
|
||||
pxAssert("Unknown framelimiter mode!");
|
||||
switch (config.LimiterMode)
|
||||
{
|
||||
case LimiterModeType::Nominal:
|
||||
config.GS.LimitScalar = config.Framerate.NominalScalar;
|
||||
break;
|
||||
case LimiterModeType::Slomo:
|
||||
config.GS.LimitScalar = config.Framerate.SlomoScalar;
|
||||
break;
|
||||
case LimiterModeType::Turbo:
|
||||
config.GS.LimitScalar = config.Framerate.TurboScalar;
|
||||
break;
|
||||
case LimiterModeType::Unlimited:
|
||||
config.GS.LimitScalar = 0.0;
|
||||
break;
|
||||
default:
|
||||
pxAssert("Unknown framelimiter mode!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
config.GS.LimitScalar = 0.0;
|
||||
}
|
||||
|
||||
UpdateVSyncRate();
|
||||
}
|
||||
|
||||
|
@ -369,34 +380,34 @@ void gsIrq() {
|
|||
// 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
|
||||
// waiting as much as possible (maximizes CPU resource availability for the GS).
|
||||
static bool s_isSkippingCurrentFrame = false;
|
||||
|
||||
__fi void gsFrameSkip()
|
||||
{
|
||||
static int consec_skipped = 0;
|
||||
static int consec_drawn = 0;
|
||||
static bool isSkipping = false;
|
||||
|
||||
if( !EmuConfig.GS.FrameSkipEnable )
|
||||
{
|
||||
if( isSkipping )
|
||||
if( s_isSkippingCurrentFrame )
|
||||
{
|
||||
// Frameskipping disabled on-the-fly .. make sure the GS is restored to non-skip
|
||||
// behavior.
|
||||
GSsetFrameSkip( false );
|
||||
isSkipping = false;
|
||||
s_isSkippingCurrentFrame = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
GSsetFrameSkip( isSkipping );
|
||||
GSsetFrameSkip( s_isSkippingCurrentFrame );
|
||||
|
||||
if( isSkipping )
|
||||
if( s_isSkippingCurrentFrame )
|
||||
{
|
||||
++consec_skipped;
|
||||
if( consec_skipped >= EmuConfig.GS.FramesToSkip )
|
||||
{
|
||||
consec_skipped = 0;
|
||||
isSkipping = false;
|
||||
s_isSkippingCurrentFrame = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -405,11 +416,16 @@ __fi void gsFrameSkip()
|
|||
if( consec_drawn >= EmuConfig.GS.FramesToDraw )
|
||||
{
|
||||
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
|
||||
//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)
|
||||
|
@ -436,3 +452,4 @@ void SaveStateBase::gsFreeze()
|
|||
FreezeMem(PS2MEM_GS, 0x2000);
|
||||
Freeze(gsVideoMode);
|
||||
}
|
||||
|
||||
|
|
25
pcsx2/GS.h
25
pcsx2/GS.h
|
@ -19,6 +19,7 @@
|
|||
#include "System/SysThreads.h"
|
||||
#include "Gif.h"
|
||||
#include "GS/GS.h"
|
||||
#include <functional>
|
||||
|
||||
extern double GetVerticalFrequency();
|
||||
alignas(16) extern u8 g_RealGSMem[Ps2MemSize::GSregs];
|
||||
|
@ -295,6 +296,7 @@ enum MTGS_RingCommand
|
|||
GS_RINGTYPE_MTVU_GSPACKET,
|
||||
GS_RINGTYPE_INIT_READ_FIFO1,
|
||||
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()
|
||||
};
|
||||
|
||||
struct MTGS_MemoryScreenshotData
|
||||
{
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
std::vector<u32> pixels; // width * height
|
||||
bool success = false;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysMtgsThread
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -312,6 +322,8 @@ class SysMtgsThread : public SysThreadBase
|
|||
typedef SysThreadBase _parent;
|
||||
|
||||
public:
|
||||
using AsyncCallType = std::function<void()>;
|
||||
|
||||
// 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
|
||||
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 SendDataPacket();
|
||||
void SendGameCRC( u32 crc );
|
||||
void WaitForOpen();
|
||||
bool WaitForOpen();
|
||||
void Freeze( FreezeAction mode, MTGS_FreezeData& data );
|
||||
|
||||
void SendSimpleGSPacket( MTGS_RingCommand type, u32 offset, u32 size, GIF_PATH path );
|
||||
|
@ -377,6 +389,16 @@ public:
|
|||
|
||||
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:
|
||||
void OpenGS();
|
||||
void CloseGS();
|
||||
|
@ -413,6 +435,7 @@ extern void gsSetVideoMode(GS_VideoMode mode);
|
|||
extern void gsResetFrameSkip();
|
||||
extern void gsPostVsyncStart();
|
||||
extern void gsFrameSkip();
|
||||
extern bool gsIsSkippingCurrentFrame();
|
||||
extern void gsUpdateFrequency(Pcsx2Config& config);
|
||||
|
||||
// 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"
|
||||
#ifndef PCSX2_CORE
|
||||
// NOTE: The include order matters - GS.h includes windows.h
|
||||
#include "GS/Window/GSwxDialog.h"
|
||||
#endif
|
||||
#include "GS.h"
|
||||
#include "GSUtil.h"
|
||||
#include "GSExtra.h"
|
||||
|
@ -26,7 +29,15 @@
|
|||
#include "GSLzma.h"
|
||||
|
||||
#include "common/pxStreams.h"
|
||||
#include "common/pxStreams.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "pcsx2/Config.h"
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/HostDisplay.h"
|
||||
#ifdef PCSX2_CORE
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
|
@ -45,29 +56,10 @@ static HRESULT s_hr = E_FAIL;
|
|||
// debug obscure compiler errors --govanify
|
||||
#undef None
|
||||
|
||||
static GSRenderer* s_gs = NULL;
|
||||
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?)
|
||||
Pcsx2Config::GSOptions GSConfig;
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
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;
|
||||
#endif
|
||||
|
||||
void GSsetBaseMem(u8* mem)
|
||||
{
|
||||
s_basemem = mem;
|
||||
|
||||
if (s_gs)
|
||||
{
|
||||
s_gs->SetRegsMem(s_basemem);
|
||||
}
|
||||
}
|
||||
static std::unique_ptr<GSRenderer> s_gs;
|
||||
static HostDisplay::RenderAPI s_render_api;
|
||||
|
||||
int GSinit()
|
||||
{
|
||||
|
@ -80,8 +72,8 @@ int GSinit()
|
|||
// 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
|
||||
// const type qualifier from all the affected variables.
|
||||
theApp.SetConfigDir();
|
||||
theApp.Init();
|
||||
GSinitConfig();
|
||||
|
||||
|
||||
|
||||
GSUtil::Init();
|
||||
|
@ -98,14 +90,31 @@ int GSinit()
|
|||
return 0;
|
||||
}
|
||||
|
||||
void GSinitConfig()
|
||||
{
|
||||
static bool config_inited = false;
|
||||
if (config_inited)
|
||||
return;
|
||||
|
||||
config_inited = true;
|
||||
theApp.SetConfigDir();
|
||||
theApp.Init();
|
||||
}
|
||||
|
||||
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;
|
||||
s_gs = nullptr;
|
||||
|
||||
theApp.SetCurrentRendererType(GSRendererType::Undefined);
|
||||
Host::ReleaseHostDisplay();
|
||||
|
||||
#ifdef _WIN32
|
||||
if (SUCCEEDED(s_hr))
|
||||
|
@ -119,190 +128,180 @@ void GSshutdown()
|
|||
|
||||
void GSclose()
|
||||
{
|
||||
gsopen_done = false;
|
||||
|
||||
#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)
|
||||
if (s_gs)
|
||||
{
|
||||
renderer = static_cast<GSRendererType>(theApp.GetConfigI("Renderer"));
|
||||
if (renderer == GSRendererType::Default)
|
||||
renderer = GSUtil::GetPreferredRenderer();
|
||||
s_gs->Destroy();
|
||||
s_gs.reset();
|
||||
}
|
||||
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
|
||||
{
|
||||
if (theApp.GetCurrentRendererType() != renderer)
|
||||
if (!g_gs_device->Create(display))
|
||||
{
|
||||
// Emulator has made a render change request, which requires a completely
|
||||
// new s_gs -- if the emu doesn't save/restore the GS state across this
|
||||
// GSopen call then they'll get corrupted graphics, but that's not my problem.
|
||||
|
||||
delete s_gs;
|
||||
|
||||
s_gs = NULL;
|
||||
|
||||
theApp.SetCurrentRendererType(renderer);
|
||||
g_gs_device->Destroy();
|
||||
g_gs_device.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string renderer_name;
|
||||
switch (renderer)
|
||||
if (renderer == GSRendererType::Null)
|
||||
{
|
||||
default:
|
||||
#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;
|
||||
s_gs = std::make_unique<GSRendererNull>();
|
||||
}
|
||||
|
||||
printf("Current Renderer: %s\n", renderer_name.c_str());
|
||||
|
||||
if (dev == NULL)
|
||||
else if (renderer != GSRendererType::SW)
|
||||
{
|
||||
return -1;
|
||||
s_gs = std::make_unique<GSRendererNew>();
|
||||
}
|
||||
|
||||
if (s_gs == NULL)
|
||||
else
|
||||
{
|
||||
switch (renderer)
|
||||
{
|
||||
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;
|
||||
const int threads = theApp.GetConfigI("extrathreads");
|
||||
s_gs = std::make_unique<GSRendererSW>(threads);
|
||||
}
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
printf("GS error: Exception caught in GSopen: %s", ex.what());
|
||||
return -1;
|
||||
Host::ReportFormattedErrorAsync("GS", "GS error: Exception caught in GSopen: %s", ex.what());
|
||||
s_gs.reset();
|
||||
g_gs_device->Destroy();
|
||||
g_gs_device.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
s_gs->SetRegsMem(s_basemem);
|
||||
s_gs->SetVSync(s_vsync);
|
||||
s_gs->SetRegsMem(basemem);
|
||||
|
||||
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
|
||||
// compliant. Cound mean drivr issues of some sort also, but to be sure, that's the most
|
||||
// common cause of device creation errors. :) --air
|
||||
|
||||
GSclose();
|
||||
|
||||
return -1;
|
||||
Console.Error("(GSreopen) Failed to get GS freeze size");
|
||||
return false;
|
||||
}
|
||||
|
||||
return 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)
|
||||
std::unique_ptr<u8[]> fd_data = std::make_unique<u8[]>(fd.size);
|
||||
fd.data = fd_data.get();
|
||||
if (s_gs->Freeze(&fd, false) != 0)
|
||||
{
|
||||
// SW -> HW and HW -> SW (F9 Switch)
|
||||
switch (current_renderer)
|
||||
Console.Error("(GSreopen) Failed to freeze GS");
|
||||
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
|
||||
case GSRendererType::DX1011_HW:
|
||||
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;
|
||||
pxFailRel("(GSreopen) Failed to reacquire host display");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
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()
|
||||
|
@ -444,7 +443,7 @@ void GSgifTransfer3(u8* mem, u32 size)
|
|||
}
|
||||
}
|
||||
|
||||
void GSvsync(int field)
|
||||
void GSvsync(u32 field)
|
||||
{
|
||||
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)
|
||||
{
|
||||
try
|
||||
|
@ -526,6 +511,20 @@ int GSfreeze(FreezeAction mode, freezeData* data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
|
||||
void GSkeyEvent(const HostKeyEvent& e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (s_gs)
|
||||
s_gs->KeyEvent(e);
|
||||
}
|
||||
catch (GSRecoverableError)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void GSconfigure()
|
||||
{
|
||||
try
|
||||
|
@ -540,7 +539,7 @@ void GSconfigure()
|
|||
{
|
||||
theApp.ReloadConfig();
|
||||
// Force a reload of the gs state
|
||||
theApp.SetCurrentRendererType(GSRendererType::Undefined);
|
||||
//theApp.SetCurrentRendererType(GSRendererType::Undefined);
|
||||
}
|
||||
}
|
||||
catch (GSRecoverableError)
|
||||
|
@ -556,6 +555,8 @@ int GStest()
|
|||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void pt(const char* str)
|
||||
{
|
||||
struct tm* current;
|
||||
|
@ -606,72 +607,203 @@ void GSsetGameCRC(u32 crc, int 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)
|
||||
{
|
||||
s_gs->SetFrameSkip(frameskip);
|
||||
}
|
||||
|
||||
void GSsetVsync(int vsync)
|
||||
void GSgetInternalResolution(int* width, int* height)
|
||||
{
|
||||
s_vsync = vsync;
|
||||
|
||||
if (s_gs)
|
||||
GSRenderer* gs = s_gs.get();
|
||||
if (!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 GSResizeWindow(int width, int height)
|
||||
void GSSwitchRenderer(GSRendererType new_renderer)
|
||||
{
|
||||
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);
|
||||
if (new_renderer == GSRendererType::Auto)
|
||||
new_renderer = GSUtil::GetPreferredRenderer();
|
||||
|
||||
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;
|
||||
|
||||
std::unique_lock lock(s_gs_window_resized_lock);
|
||||
*new_width = s_new_gs_window_width;
|
||||
*new_height = s_new_gs_window_height;
|
||||
s_gs_window_resized.store(false);
|
||||
return true;
|
||||
return s_gs->SaveSnapshotToMemory(width, height, pixels);
|
||||
}
|
||||
#endif
|
||||
|
||||
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)
|
||||
{
|
||||
#ifdef PCSX2_CORE
|
||||
std::string ret(Host::GetStringSettingValue("EmuCore/GS", lpKeyName, lpDefault));
|
||||
return StringUtil::Strlcpy(lpReturnedString, ret, nSize);
|
||||
#else
|
||||
BuildConfigurationMap(lpFileName);
|
||||
|
||||
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());
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GSApp::WriteIniString(const char* lpAppName, const char* lpKeyName, const char* pString, const char* lpFileName)
|
||||
{
|
||||
#ifndef PCSX2_CORE
|
||||
BuildConfigurationMap(lpFileName);
|
||||
|
||||
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());
|
||||
}
|
||||
fclose(f);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
int GSApp::GetIniInt(const char* lpAppName, const char* lpKeyName, int nDefault, const char* lpFileName)
|
||||
{
|
||||
BuildConfigurationMap(lpFileName);
|
||||
|
@ -949,6 +1089,7 @@ int GSApp::GetIniInt(const char* lpAppName, const char* lpKeyName, int nDefault,
|
|||
else
|
||||
return atoi(value.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
GSApp theApp;
|
||||
|
||||
|
@ -969,20 +1110,16 @@ void GSApp::Init()
|
|||
return;
|
||||
is_initialised = true;
|
||||
|
||||
m_current_renderer_type = GSRendererType::Undefined;
|
||||
|
||||
m_section = "Settings";
|
||||
|
||||
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::Auto), "Automatic", ""));
|
||||
#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::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", ""));
|
||||
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::DX11), "Direct3D 11", ""));
|
||||
#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_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::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::Forced), "Trilinear", "Ultra/Slow"));
|
||||
|
||||
|
@ -1044,7 +1181,7 @@ void GSApp::Init()
|
|||
|
||||
m_gs_crc_level = {
|
||||
GSSetting(CRCHackLevel::Automatic, "Automatic", "Default"),
|
||||
GSSetting(CRCHackLevel::None, "None", "Debug"),
|
||||
GSSetting(CRCHackLevel::Off, "None", "Debug"),
|
||||
GSSetting(CRCHackLevel::Minimum, "Minimum", "Debug"),
|
||||
#ifdef _DEBUG
|
||||
GSSetting(CRCHackLevel::Partial, "Partial", "OpenGL"),
|
||||
|
@ -1053,14 +1190,14 @@ void GSApp::Init()
|
|||
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::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::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_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::Medium), "Medium", "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
|
||||
#ifdef _WIN32
|
||||
// Per OS option.
|
||||
m_default_configuration["Adapter"] = "";
|
||||
m_default_configuration["CaptureFileName"] = "";
|
||||
m_default_configuration["CaptureVideoCodecDisplayName"] = "";
|
||||
m_default_configuration["debug_d3d"] = "0";
|
||||
m_default_configuration["dx_break_on_severity"] = "0";
|
||||
// D3D Blending option
|
||||
m_default_configuration["accurate_blending_unit_d3d11"] = "1";
|
||||
|
@ -1097,7 +1234,6 @@ void GSApp::Init()
|
|||
m_default_configuration["CaptureWidth"] = "640";
|
||||
m_default_configuration["crc_hack_level"] = std::to_string(static_cast<s8>(CRCHackLevel::Automatic));
|
||||
m_default_configuration["CrcHacksExclusions"] = "";
|
||||
m_default_configuration["debug_opengl"] = "0";
|
||||
m_default_configuration["disable_hw_gl_draw"] = "0";
|
||||
m_default_configuration["disable_shader_cache"] = "0";
|
||||
m_default_configuration["dithering_ps2"] = "2";
|
||||
|
@ -1105,7 +1241,10 @@ void GSApp::Init()
|
|||
m_default_configuration["extrathreads"] = "2";
|
||||
m_default_configuration["extrathreads_height"] = "4";
|
||||
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["HWDisableReadbacks"] = "0";
|
||||
m_default_configuration["IntegerScaling"] = "0";
|
||||
m_default_configuration["interlace"] = "7";
|
||||
m_default_configuration["conservative_framebuffer"] = "1";
|
||||
m_default_configuration["linear_present"] = "1";
|
||||
|
@ -1115,20 +1254,13 @@ void GSApp::Init()
|
|||
m_default_configuration["ModeHeight"] = "480";
|
||||
m_default_configuration["ModeWidth"] = "640";
|
||||
m_default_configuration["NTSC_Saturation"] = "1";
|
||||
#ifdef _WIN32
|
||||
m_default_configuration["osd_fontname"] = "C:\\Windows\\Fonts\\my_favorite_font_e_g_tahoma.ttf";
|
||||
#else
|
||||
m_default_configuration["osd_fontname"] = "/usr/share/fonts/truetype/my_favorite_font_e_g_DejaVu Sans.ttf";
|
||||
#endif
|
||||
m_default_configuration["osd_color_r"] = "0";
|
||||
m_default_configuration["osd_color_g"] = "160";
|
||||
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["OsdShowMessages"] = "1";
|
||||
m_default_configuration["OsdShowSpeed"] = "0";
|
||||
m_default_configuration["OsdShowFPS"] = "0";
|
||||
m_default_configuration["OsdShowCPU"] = "0";
|
||||
m_default_configuration["OsdShowResolution"] = "0";
|
||||
m_default_configuration["OsdShowGSStats"] = "0";
|
||||
m_default_configuration["OsdScale"] = "100";
|
||||
m_default_configuration["override_geometry_shader"] = "-1";
|
||||
m_default_configuration["override_GL_ARB_copy_image"] = "-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["png_compression_level"] = std::to_string(Z_BEST_SPEED);
|
||||
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["resy"] = "1024";
|
||||
m_default_configuration["save"] = "0";
|
||||
|
@ -1162,8 +1294,13 @@ void GSApp::Init()
|
|||
m_default_configuration["shaderfx"] = "0";
|
||||
m_default_configuration["shaderfx_conf"] = "shaders/GS_FX_Settings.ini";
|
||||
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["upscale_multiplier"] = "1";
|
||||
m_default_configuration["UseBlitSwapChain"] = "0";
|
||||
m_default_configuration["UseDebugDevice"] = "0";
|
||||
m_default_configuration["UserHacks"] = "0";
|
||||
m_default_configuration["UserHacks_align_sprite_X"] = "0";
|
||||
m_default_configuration["UserHacks_AutoFlush"] = "0";
|
||||
|
@ -1180,13 +1317,14 @@ void GSApp::Init()
|
|||
m_default_configuration["UserHacks_TCOffsetX"] = "0";
|
||||
m_default_configuration["UserHacks_TCOffsetY"] = "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["wrap_gs_mem"] = "0";
|
||||
m_default_configuration["vsync"] = "0";
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
void GSApp::ReloadConfig()
|
||||
{
|
||||
if (m_configuration_map.empty())
|
||||
|
@ -1245,6 +1383,7 @@ void GSApp::BuildConfigurationMap(const char* lpFileName)
|
|||
m_configuration_map[key] = value;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void GSApp::SetConfigDir()
|
||||
{
|
||||
|
@ -1284,18 +1423,40 @@ int GSApp::GetConfigI(const char* entry)
|
|||
|
||||
if (def != m_default_configuration.end())
|
||||
{
|
||||
#ifndef PCSX2_CORE
|
||||
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
|
||||
{
|
||||
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());
|
||||
#else
|
||||
return Host::GetIntSettingValue("EmuCore/GS", entry, 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool GSApp::GetConfigB(const char* entry)
|
||||
{
|
||||
#ifndef PCSX2_CORE
|
||||
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)
|
||||
|
@ -1306,13 +1467,3 @@ void GSApp::SetConfig(const char* entry, int value)
|
|||
|
||||
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 "Window/GSSetting.h"
|
||||
#include "SaveState.h"
|
||||
#include "Host.h"
|
||||
#include "pcsx2/Config.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
|
@ -27,18 +27,6 @@
|
|||
#undef None
|
||||
#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
|
||||
enum stateType
|
||||
{
|
||||
|
@ -58,59 +46,18 @@ enum class GSVideoMode : u8
|
|||
HDTV_1080I
|
||||
};
|
||||
|
||||
// Ordering was done to keep compatibility with older ini file.
|
||||
enum class BiFiltering : u8
|
||||
{
|
||||
Nearest,
|
||||
Forced,
|
||||
PS2,
|
||||
Forced_But_Sprite,
|
||||
};
|
||||
extern Pcsx2Config::GSOptions GSConfig;
|
||||
|
||||
enum class TriFiltering : u8
|
||||
{
|
||||
None,
|
||||
PS2,
|
||||
Forced,
|
||||
};
|
||||
struct HostKeyEvent;
|
||||
class HostDisplay;
|
||||
|
||||
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();
|
||||
void GSinitConfig();
|
||||
void GSshutdown();
|
||||
void GSclose();
|
||||
int _GSopen(const WindowInfo& wi, const char* title, GSRendererType renderer, int threads);
|
||||
void GSosdLog(const char* utf8, u32 color);
|
||||
void GSosdMonitor(const char* key, const char* value, u32 color);
|
||||
int GSopen2(const WindowInfo & wi, u32 flags);
|
||||
bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* basemem);
|
||||
bool GSreopen(bool recreate_display);
|
||||
void GSreset();
|
||||
void GSclose();
|
||||
void GSgifSoftReset(u32 mask);
|
||||
void GSwriteCSR(u32 csr);
|
||||
void GSinitReadFIFO(u8* mem);
|
||||
|
@ -121,32 +68,34 @@ void GSgifTransfer(const u8* mem, u32 size);
|
|||
void GSgifTransfer1(u8* mem, u32 addr);
|
||||
void GSgifTransfer2(u8* mem, u32 size);
|
||||
void GSgifTransfer3(u8* mem, u32 size);
|
||||
void GSvsync(int field);
|
||||
void GSvsync(u32 field);
|
||||
u32 GSmakeSnapshot(char* path);
|
||||
void GSkeyEvent(const HostKeyEvent& e);
|
||||
int GSfreeze(FreezeAction mode, freezeData* data);
|
||||
#ifndef PCSX2_CORE
|
||||
void GSkeyEvent(const HostKeyEvent& e);
|
||||
void GSconfigure();
|
||||
int GStest();
|
||||
#endif
|
||||
bool GSsetupRecording(std::string& filename);
|
||||
void GSendRecording();
|
||||
void GSsetGameCRC(u32 crc, int options);
|
||||
void GSgetTitleInfo2(char* dest, size_t length);
|
||||
void GSsetFrameSkip(int frameskip);
|
||||
void GSsetVsync(int vsync);
|
||||
void GSsetExclusive(int enabled);
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
// Needed for window resizing in wx. Can be safely called from the UI thread.
|
||||
void GSResizeWindow(int width, int height);
|
||||
bool GSCheckForWindowResize(int* new_width, int* new_height);
|
||||
#endif
|
||||
void GSgetInternalResolution(int* width, int* height);
|
||||
void GSgetStats(std::string& info);
|
||||
void GSgetTitleStats(std::string& info);
|
||||
|
||||
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
|
||||
{
|
||||
std::string m_section;
|
||||
std::map<std::string, std::string> m_default_configuration;
|
||||
std::map<std::string, std::string> m_configuration_map;
|
||||
GSRendererType m_current_renderer_type;
|
||||
|
||||
public:
|
||||
std::string m_ini;
|
||||
|
@ -154,12 +103,14 @@ public:
|
|||
|
||||
void Init();
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
void BuildConfigurationMap(const char* lpFileName);
|
||||
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);
|
||||
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, int value);
|
||||
|
@ -173,9 +124,6 @@ public:
|
|||
bool GetConfigB(const char* entry);
|
||||
std::string GetConfigS(const char* entry);
|
||||
|
||||
void SetCurrentRendererType(GSRendererType type);
|
||||
GSRendererType GetCurrentRendererType() const;
|
||||
|
||||
void SetConfigDir();
|
||||
|
||||
std::vector<GSSetting> m_gs_renderers;
|
||||
|
@ -206,5 +154,3 @@ struct GSErrorGlVertexArrayTooSmall : GSError
|
|||
};
|
||||
|
||||
extern GSApp theApp;
|
||||
|
||||
extern bool gsopen_done;
|
||||
|
|
|
@ -397,11 +397,6 @@ GSCapture::GSCapture()
|
|||
: m_capturing(false), m_frame(0)
|
||||
, 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()
|
||||
|
@ -418,6 +413,13 @@ bool GSCapture::BeginCapture(float fps, GSVector2i recommendedResolution, float
|
|||
|
||||
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
|
||||
|
||||
GSCaptureDlg dlg;
|
||||
|
|
|
@ -120,7 +120,7 @@ GIFRegTEX0 GSDrawingContext::GetSizeFixedTEX0(const GSVector4& st, bool linear,
|
|||
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)",
|
||||
(int)TEX0.TBP0, (int)TEX0.TBW, (int)TEX0.PSM,
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "GSVector.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "common/RedtapeWindows.h"
|
||||
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);
|
||||
|
|
|
@ -62,14 +62,9 @@ GSLocalMemory::GSLocalMemory()
|
|||
: m_clut(this)
|
||||
{
|
||||
m_use_fifo_alloc = theApp.GetConfigB("UserHacks") && theApp.GetConfigB("wrap_gs_mem");
|
||||
switch (theApp.GetCurrentRendererType())
|
||||
{
|
||||
case GSRendererType::OGL_SW:
|
||||
m_use_fifo_alloc = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!GSConfig.UseHardwareRenderer())
|
||||
m_use_fifo_alloc = true;
|
||||
|
||||
if (m_use_fifo_alloc)
|
||||
m_vm8 = (u8*)fifo_alloc(m_vmsize, 4);
|
||||
|
|
|
@ -31,7 +31,6 @@ GSState::GSState()
|
|||
, m_q(1.0f)
|
||||
, m_scanmask_used(false)
|
||||
, m_vt(this)
|
||||
, m_dev(nullptr)
|
||||
, m_regs(NULL)
|
||||
, m_crc(0)
|
||||
, m_options(0)
|
||||
|
@ -46,15 +45,11 @@ GSState::GSState()
|
|||
{
|
||||
m_userhacks_auto_flush = theApp.GetConfigB("UserHacks_AutoFlush");
|
||||
m_userhacks_wildhack = theApp.GetConfigB("UserHacks_WildHack");
|
||||
m_userhacks_skipdraw = theApp.GetConfigI("UserHacks_SkipDraw");
|
||||
m_userhacks_skipdraw_offset = theApp.GetConfigI("UserHacks_SkipDraw_Offset");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_userhacks_auto_flush = false;
|
||||
m_userhacks_wildhack = false;
|
||||
m_userhacks_skipdraw = 0;
|
||||
m_userhacks_skipdraw_offset = 0;
|
||||
}
|
||||
|
||||
s_n = 0;
|
||||
|
@ -76,7 +71,7 @@ GSState::GSState()
|
|||
|
||||
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());
|
||||
m_crc_hack_level = GSUtil::GetRecommendedCRCHackLevel(GSConfig.Renderer);
|
||||
|
||||
memset(&m_v, 0, sizeof(m_v));
|
||||
memset(&m_vertex, 0, sizeof(m_vertex));
|
||||
|
@ -152,13 +147,6 @@ GSState::~GSState()
|
|||
_aligned_free(m_index.buff);
|
||||
}
|
||||
|
||||
void GSState::SetRegsMem(u8* basemem)
|
||||
{
|
||||
ASSERT(basemem);
|
||||
|
||||
m_regs = (GSPrivRegSet*)basemem;
|
||||
}
|
||||
|
||||
void GSState::SetFrameSkip(int skip)
|
||||
{
|
||||
if (m_frameskip == skip)
|
||||
|
@ -269,9 +257,7 @@ void GSState::ResetHandlers()
|
|||
m_fpGIFPackedRegHandlers[GIF_REG_NOP] = &GSState::GIFPackedRegHandlerNOP;
|
||||
|
||||
// swap first/last indices when the provoking vertex is the first (D3D/Vulkan)
|
||||
const GSRendererType renderer = theApp.GetCurrentRendererType();
|
||||
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;
|
||||
const bool index_swap = GSConfig.UseHardwareRenderer() && !g_gs_device->Features().provoking_vertex_last;
|
||||
if (m_userhacks_auto_flush)
|
||||
index_swap ? SetPrimHandlers<true, true>() : SetPrimHandlers<true, false>();
|
||||
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
|
||||
{
|
||||
u32 prim_mask = 0x7f8;
|
||||
const bool is_hardware_renderer =
|
||||
((theApp.GetCurrentRendererType() == GSRendererType::OGL_HW) || (theApp.GetCurrentRendererType() == GSRendererType::DX1011_HW));
|
||||
if (is_hardware_renderer && GSUtil::GetPrimClass(prim & 7) == GS_TRIANGLE_CLASS)
|
||||
if (GSConfig.UseHardwareRenderer() && GSUtil::GetPrimClass(prim & 7) == GS_TRIANGLE_CLASS)
|
||||
prim_mask &= ~0x80; // Mask out AA1.
|
||||
|
||||
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)
|
||||
{
|
||||
u32 prim_mask = 0x7f8;
|
||||
const bool is_hardware_renderer =
|
||||
((theApp.GetCurrentRendererType() == GSRendererType::OGL_HW) || (theApp.GetCurrentRendererType() == GSRendererType::DX1011_HW));
|
||||
if (is_hardware_renderer && GSUtil::GetPrimClass(m_env.PRIM.PRIM) == GS_TRIANGLE_CLASS)
|
||||
if (GSConfig.UseHardwareRenderer() && GSUtil::GetPrimClass(m_env.PRIM.PRIM) == GS_TRIANGLE_CLASS)
|
||||
prim_mask &= ~0x80; // Mask out AA1.
|
||||
|
||||
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_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();
|
||||
}
|
||||
|
||||
|
|
|
@ -155,8 +155,6 @@ protected:
|
|||
GetSkipCount m_gsc;
|
||||
int m_skip;
|
||||
int m_skip_offset;
|
||||
int m_userhacks_skipdraw;
|
||||
int m_userhacks_skipdraw_offset;
|
||||
bool m_userhacks_auto_flush;
|
||||
|
||||
GSVertex m_v;
|
||||
|
@ -208,7 +206,6 @@ protected:
|
|||
GIFRegTEX0 GetTex0Layer(u32 lod);
|
||||
|
||||
public:
|
||||
GSDevice* m_dev;
|
||||
GIFPath m_path[4];
|
||||
GIFRegPRIM* PRIM;
|
||||
GSPrivRegSet* m_regs;
|
||||
|
@ -272,7 +269,13 @@ public:
|
|||
template<int index> void Transfer(const u8* mem, u32 size);
|
||||
int Freeze(freezeData* fd, bool sizeonly);
|
||||
int Defrost(const freezeData* fd);
|
||||
|
||||
u32 GetGameCRC() const { return m_crc; }
|
||||
int GetGameCRCOptions() const { return m_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 SetRegsMem(u8* basemem);
|
||||
};
|
||||
|
|
|
@ -185,28 +185,16 @@ bool GSUtil::CheckSSE()
|
|||
|
||||
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()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (D3D::ShouldPreferD3D())
|
||||
return GSRendererType::DX1011_HW;
|
||||
return GSRendererType::DX11;
|
||||
#endif
|
||||
return GSRendererType::OGL_HW;
|
||||
}
|
||||
|
||||
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 {};
|
||||
return GSRendererType::OGL;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
|
@ -42,7 +42,6 @@ public:
|
|||
static bool CheckSSE();
|
||||
static CRCHackLevel GetRecommendedCRCHackLevel(GSRendererType type);
|
||||
static GSRendererType GetPreferredRenderer();
|
||||
static std::vector<std::string> GetAdapterList(GSRendererType renderer);
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
|
@ -41,17 +41,16 @@ const char* shaderName(ShaderConvert value)
|
|||
case ShaderConvert::RGB5A1_TO_FLOAT16: return "ps_convert_rgb5a1_float16";
|
||||
case ShaderConvert::RGBA_TO_8I: return "ps_convert_rgba_8i";
|
||||
case ShaderConvert::YUV: return "ps_yuv";
|
||||
case ShaderConvert::OSD: return "ps_osd";
|
||||
default:
|
||||
ASSERT(0);
|
||||
return "ShaderConvertUnknownShader";
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<GSDevice> g_gs_device;
|
||||
|
||||
GSDevice::GSDevice()
|
||||
: m_vsync(false)
|
||||
, m_rbswapped(false)
|
||||
, m_backbuffer(NULL)
|
||||
: m_rbswapped(false)
|
||||
, m_merge(NULL)
|
||||
, m_weavebob(NULL)
|
||||
, m_blend(NULL)
|
||||
|
@ -61,73 +60,47 @@ GSDevice::GSDevice()
|
|||
{
|
||||
memset(&m_vertex, 0, sizeof(m_vertex));
|
||||
memset(&m_index, 0, sizeof(m_index));
|
||||
m_linear_present = theApp.GetConfigB("linear_present");
|
||||
}
|
||||
|
||||
GSDevice::~GSDevice()
|
||||
{
|
||||
PurgePool();
|
||||
delete m_backbuffer;
|
||||
|
||||
delete m_merge;
|
||||
delete m_weavebob;
|
||||
delete m_blend;
|
||||
delete m_target_tmp;
|
||||
}
|
||||
|
||||
bool GSDevice::Create(const WindowInfo& wi)
|
||||
bool GSDevice::Create(HostDisplay* display)
|
||||
{
|
||||
m_display = display;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSDevice::Reset(int w, int h)
|
||||
void GSDevice::Destroy()
|
||||
{
|
||||
PurgePool();
|
||||
|
||||
delete m_backbuffer;
|
||||
delete m_merge;
|
||||
delete m_weavebob;
|
||||
delete m_blend;
|
||||
delete m_target_tmp;
|
||||
|
||||
m_backbuffer = nullptr;
|
||||
m_merge = nullptr;
|
||||
m_weavebob = nullptr;
|
||||
m_blend = nullptr;
|
||||
m_target_tmp = nullptr;
|
||||
|
||||
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)
|
||||
|
|
|
@ -21,12 +21,13 @@
|
|||
#include "GSVertex.h"
|
||||
#include "GS/GSAlignedClass.h"
|
||||
#include "GS/GSExtra.h"
|
||||
#include "GSOsdManager.h"
|
||||
#include <array>
|
||||
#ifdef _WIN32
|
||||
#include <dxgi.h>
|
||||
#endif
|
||||
|
||||
class HostDisplay;
|
||||
|
||||
enum class ShaderConvert
|
||||
{
|
||||
COPY = 0,
|
||||
|
@ -48,7 +49,6 @@ enum class ShaderConvert
|
|||
RGB5A1_TO_FLOAT16,
|
||||
RGBA_TO_8I = 17,
|
||||
YUV,
|
||||
OSD,
|
||||
Count
|
||||
};
|
||||
|
||||
|
@ -526,9 +526,8 @@ protected:
|
|||
static const int m_NO_BLEND = 0;
|
||||
static const int m_MERGE_BLEND = m_blendMap.size() - 1;
|
||||
|
||||
int m_vsync;
|
||||
bool m_rbswapped;
|
||||
GSTexture* m_backbuffer;
|
||||
HostDisplay* m_display;
|
||||
GSTexture* m_merge;
|
||||
GSTexture* m_weavebob;
|
||||
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.
|
||||
|
||||
public:
|
||||
GSOsdManager m_osd;
|
||||
|
||||
GSDevice();
|
||||
virtual ~GSDevice();
|
||||
|
||||
__fi HostDisplay* GetDisplay() const { return m_display; }
|
||||
|
||||
void Recycle(GSTexture* t);
|
||||
|
||||
enum
|
||||
|
@ -571,14 +570,11 @@ public:
|
|||
DontCare
|
||||
};
|
||||
|
||||
virtual bool Create(const WindowInfo& wi);
|
||||
virtual bool Reset(int w, int h);
|
||||
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 bool Create(HostDisplay* display);
|
||||
virtual void Destroy();
|
||||
|
||||
virtual void SetVSync(int vsync) { m_vsync = vsync; }
|
||||
virtual void ResetAPIState();
|
||||
virtual void RestoreAPIState();
|
||||
|
||||
virtual void BeginScene() {}
|
||||
virtual void EndScene();
|
||||
|
@ -626,7 +622,6 @@ public:
|
|||
void FXAA();
|
||||
void ShadeBoost();
|
||||
void ExternalFX();
|
||||
virtual void RenderOsd(GSTexture* dt) {};
|
||||
|
||||
bool ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h);
|
||||
bool ResizeTexture(GSTexture** t, int w, int h);
|
||||
|
@ -634,8 +629,6 @@ public:
|
|||
bool ResizeTarget(GSTexture** t);
|
||||
|
||||
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 PurgePool();
|
||||
|
@ -673,3 +666,5 @@ struct GSAdapter
|
|||
// TODO
|
||||
#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 "GSRenderer.h"
|
||||
#include "gui/AppConfig.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__)
|
||||
#include <X11/keysym.h>
|
||||
#endif
|
||||
|
||||
const unsigned int s_interlace_nb = 8;
|
||||
const unsigned int s_post_shader_nb = 5;
|
||||
const unsigned int s_mipmap_nb = 3;
|
||||
|
||||
GSRenderer::GSRenderer()
|
||||
: m_shader(0)
|
||||
, m_shift_key(false)
|
||||
: m_shift_key(false)
|
||||
, m_control_key(false)
|
||||
, m_texture_shuffle(false)
|
||||
, 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()
|
||||
{
|
||||
/*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)
|
||||
|
@ -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;
|
||||
|
||||
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 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
|
||||
{
|
||||
int field2 = 1 - ((m_interlace - 1) & 1);
|
||||
int mode = (m_interlace - 1) >> 1;
|
||||
m_dev->Interlace(ds, field ^ field2, mode, tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y);
|
||||
int field2 = 1 - ((static_cast<int>(GSConfig.InterlaceMode) - 1) & 1);
|
||||
int mode = (static_cast<int>(GSConfig.InterlaceMode) - 1) >> 1;
|
||||
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;
|
||||
}
|
||||
|
||||
GSVector4i GSRenderer::ComputeDrawRectangle(int width, int height) const
|
||||
static float GetCurrentAspectRatioFloat()
|
||||
{
|
||||
const double f_width = static_cast<double>(width);
|
||||
const double f_height = static_cast<double>(height);
|
||||
const double clientAr = f_width / f_height;
|
||||
static constexpr std::array<float, static_cast<size_t>(AspectRatioType::MaxCount)> ars = { {4.0f / 3.0f, 4.0f / 3.0f, 16.0f / 9.0f} };
|
||||
return ars[static_cast<u32>(GSConfig.AspectRatio)];
|
||||
}
|
||||
|
||||
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)
|
||||
targetAr = 4.0 / 3.0;
|
||||
targetAr = 4.0f / 3.0f;
|
||||
else if (EmuConfig.CurrentAspectRatio == AspectRatioType::R16_9)
|
||||
targetAr = 16.0 / 9.0;
|
||||
targetAr = 16.0f / 9.0f;
|
||||
|
||||
const double arr = targetAr / clientAr;
|
||||
double target_width = f_width;
|
||||
double target_height = f_height;
|
||||
float target_width = f_width;
|
||||
float target_height = f_height;
|
||||
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)
|
||||
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).
|
||||
zoom = std::max((float)arr, (float)(1.0 / arr));
|
||||
|
||||
target_width *= zoom;
|
||||
target_height *= zoom * EmuConfig.GS.StretchY / 100.0;
|
||||
target_height *= zoom * GSConfig.StretchY / 100.0f;
|
||||
|
||||
double target_x, target_y;
|
||||
if (target_width > f_width)
|
||||
target_x = -((target_width - f_width) * 0.5);
|
||||
if (GSConfig.IntegerScaling)
|
||||
{
|
||||
// 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
|
||||
target_x = (f_width - target_width) * 0.5;
|
||||
if (target_height > f_height)
|
||||
target_y = -((target_height - f_height) * 0.5);
|
||||
{
|
||||
switch (alignment)
|
||||
{
|
||||
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
|
||||
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);
|
||||
target_x += unit * EmuConfig.GS.OffsetX;
|
||||
target_y += unit * EmuConfig.GS.OffsetY;
|
||||
const float unit = .01f * std::min(target_x, target_y);
|
||||
target_x += unit * GSConfig.OffsetX;
|
||||
target_y += unit * GSConfig.OffsetY;
|
||||
|
||||
return GSVector4i(
|
||||
static_cast<int>(std::floor(target_x)),
|
||||
static_cast<int>(std::floor(target_y)),
|
||||
static_cast<int>(std::round(target_x + target_width)),
|
||||
static_cast<int>(std::round(target_y + target_height)));
|
||||
GSVector4 ret(target_x, target_y, target_x + target_width, target_y + target_height);
|
||||
|
||||
if (flip_y)
|
||||
{
|
||||
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)
|
||||
{
|
||||
m_vsync = vsync;
|
||||
|
||||
if (m_dev)
|
||||
m_dev->SetVSync(m_vsync);
|
||||
}
|
||||
|
||||
void GSRenderer::VSync(int field)
|
||||
void GSRenderer::VSync(u32 field)
|
||||
{
|
||||
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()));
|
||||
}
|
||||
|
||||
if (!m_dev->IsLost(true))
|
||||
{
|
||||
if (!Merge(field ? 1 : 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
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->AgePool();
|
||||
|
||||
const bool blank_frame = !Merge(field ? 1 : 0);
|
||||
const bool skip_frame = m_frameskip;
|
||||
|
||||
if (blank_frame || skip_frame)
|
||||
{
|
||||
g_gs_device->ResetAPIState();
|
||||
if (Host::BeginPresentFrame(skip_frame))
|
||||
Host::EndPresentFrame();
|
||||
g_gs_device->RestoreAPIState();
|
||||
return;
|
||||
}
|
||||
|
||||
// present
|
||||
if ((m_perfmon.GetFrame() & 0x1f) == 0)
|
||||
m_perfmon.Update();
|
||||
|
||||
// This will scale the OSD to the window's size.
|
||||
// Will maintiain the font size no matter what size the window is.
|
||||
GSVector4i window_size(0, 0, m_dev->GetBackbufferWidth(), m_dev->GetBackbufferHeight());
|
||||
m_dev->m_osd.m_real_size.x = window_size.v[2];
|
||||
m_dev->m_osd.m_real_size.y = window_size.v[3];
|
||||
g_gs_device->ResetAPIState();
|
||||
if (Host::BeginPresentFrame(false))
|
||||
{
|
||||
GSTexture* current = g_gs_device->GetCurrent();
|
||||
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
|
||||
|
||||
|
@ -495,7 +452,7 @@ void GSRenderer::VSync(int field)
|
|||
delete[] fd.data;
|
||||
}
|
||||
|
||||
if (GSTexture* t = m_dev->GetCurrent())
|
||||
if (GSTexture* t = g_gs_device->GetCurrent())
|
||||
{
|
||||
t->Save(m_snapshot + ".png");
|
||||
}
|
||||
|
@ -512,21 +469,21 @@ void GSRenderer::VSync(int field)
|
|||
|
||||
if (m_capture.IsCapturing())
|
||||
{
|
||||
if (GSTexture* current = m_dev->GetCurrent())
|
||||
if (GSTexture* current = g_gs_device->GetCurrent())
|
||||
{
|
||||
GSVector2i size = m_capture.GetSize();
|
||||
|
||||
bool res;
|
||||
GSTexture::GSMap m;
|
||||
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
|
||||
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)
|
||||
{
|
||||
m_capture.DeliverFrame(m.bits, m.pitch, !m_dev->IsRBSwapped());
|
||||
m_dev->DownloadTextureComplete();
|
||||
m_capture.DeliverFrame(m.bits, m.pitch, !g_gs_device->IsRBSwapped());
|
||||
g_gs_device->DownloadTextureComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -569,10 +526,7 @@ bool GSRenderer::MakeSnapshot(const std::string& path)
|
|||
|
||||
bool GSRenderer::BeginCapture(std::string& filename)
|
||||
{
|
||||
GSVector4i disp = ComputeDrawRectangle(m_dev->GetBackbufferWidth(), m_dev->GetBackbufferHeight());
|
||||
float aspect = (float)disp.width() / std::max(1, disp.height());
|
||||
|
||||
return m_capture.BeginCapture(GetTvRefreshRate(), GetInternalResolution(), aspect, filename);
|
||||
return m_capture.BeginCapture(GetTvRefreshRate(), GetInternalResolution(), GetCurrentAspectRatioFloat(), filename);
|
||||
}
|
||||
|
||||
void GSRenderer::EndCapture()
|
||||
|
@ -618,34 +572,24 @@ void GSRenderer::KeyEvent(const HostKeyEvent& e)
|
|||
switch (e.key)
|
||||
{
|
||||
case VK_F5:
|
||||
m_interlace = (m_interlace + s_interlace_nb + step) % s_interlace_nb;
|
||||
theApp.SetConfig("interlace", m_interlace);
|
||||
printf("GS: Set deinterlace mode to %d (%s).\n", m_interlace, theApp.m_gs_interlace.at(m_interlace).name.c_str());
|
||||
GSConfig.InterlaceMode = static_cast<GSInterlaceMode>((static_cast<int>(GSConfig.InterlaceMode) + static_cast<int>(GSInterlaceMode::Count) + step) % static_cast<int>(GSInterlaceMode::Count));
|
||||
theApp.SetConfig("interlace", static_cast<int>(GSConfig.InterlaceMode));
|
||||
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;
|
||||
case VK_DELETE:
|
||||
m_aa1 = !m_aa1;
|
||||
theApp.SetConfig("aa1", m_aa1);
|
||||
printf("GS: (Software) Edge anti-aliasing is now %s.\n", m_aa1 ? "enabled" : "disabled");
|
||||
GSConfig.AA1 = !GSConfig.AA1;
|
||||
theApp.SetConfig("aa1", GSConfig.AA1);
|
||||
printf("GS: (Software) Edge anti-aliasing is now %s.\n", GSConfig.AA1 ? "enabled" : "disabled");
|
||||
return;
|
||||
case VK_INSERT:
|
||||
m_mipmap = (m_mipmap + s_mipmap_nb + step) % s_mipmap_nb;
|
||||
theApp.SetConfig("mipmap_hw", m_mipmap);
|
||||
printf("GS: Mipmapping is now %s.\n", theApp.m_gs_hack.at(m_mipmap).name.c_str());
|
||||
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
|
||||
char dither_msg[3][16] = {"disabled", "auto", "auto unscaled"};
|
||||
m_dithering = (m_dithering + 1) % 3;
|
||||
printf("GS: Dithering is now %s.\n", dither_msg[m_dithering]);
|
||||
GSConfig.Dithering = (GSConfig.Dithering + 1) % 3;
|
||||
printf("GS: Dithering is now %s.\n", dither_msg[GSConfig.Dithering]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -654,5 +598,47 @@ void GSRenderer::KeyEvent(const HostKeyEvent& e)
|
|||
|
||||
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/GSCapture.h"
|
||||
#include <memory>
|
||||
|
||||
struct HostKeyEvent;
|
||||
|
||||
|
@ -24,7 +25,6 @@ class GSRenderer : public GSState
|
|||
{
|
||||
GSCapture m_capture;
|
||||
std::string m_snapshot;
|
||||
int m_shader;
|
||||
|
||||
bool Merge(int field);
|
||||
|
||||
|
@ -32,13 +32,6 @@ class GSRenderer : public GSState
|
|||
bool m_control_key;
|
||||
|
||||
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;
|
||||
GSVector2i m_real_size;
|
||||
|
||||
|
@ -49,9 +42,9 @@ public:
|
|||
GSRenderer();
|
||||
virtual ~GSRenderer();
|
||||
|
||||
virtual bool CreateDevice(GSDevice* dev, const WindowInfo& wi);
|
||||
virtual void ResetDevice();
|
||||
virtual void VSync(int field);
|
||||
virtual void Destroy();
|
||||
|
||||
virtual void VSync(u32 field);
|
||||
virtual bool MakeSnapshot(const std::string& path);
|
||||
virtual void KeyEvent(const HostKeyEvent& e);
|
||||
virtual bool CanUpscale() { return false; }
|
||||
|
@ -59,17 +52,11 @@ public:
|
|||
virtual GSVector2i GetCustomResolution() { return GSVector2i(0, 0); }
|
||||
virtual GSVector2 GetTextureScaleFactor() { return { 1.0f, 1.0f }; }
|
||||
GSVector2i GetInternalResolution();
|
||||
void SetVSync(int vsync);
|
||||
|
||||
virtual bool BeginCapture(std::string& filename);
|
||||
virtual void EndCapture();
|
||||
|
||||
void PurgePool();
|
||||
|
||||
GSVector4i ComputeDrawRectangle(int width, int height) const;
|
||||
|
||||
public:
|
||||
std::mutex m_pGSsetTitle_Crit;
|
||||
|
||||
char m_GStitleInfoBuffer[128];
|
||||
bool SaveSnapshotToMemory(u32 width, u32 height, std::vector<u32>* pixels);
|
||||
};
|
||||
|
|
|
@ -33,7 +33,6 @@ public:
|
|||
DepthStencil,
|
||||
Texture,
|
||||
Offscreen,
|
||||
Backbuffer,
|
||||
SparseRenderTarget,
|
||||
SparseDepthStencil,
|
||||
};
|
||||
|
@ -41,7 +40,6 @@ public:
|
|||
enum class Format
|
||||
{
|
||||
Invalid = 0, ///< Used for initialization
|
||||
Backbuffer, ///< For displaying to the screen
|
||||
Color, ///< Standard (RGBA8) color texture
|
||||
FloatColor, ///< Float-based color texture for colclip emulation (RGBA32F)
|
||||
DepthStencil, ///< Depth stencil texture
|
||||
|
@ -70,6 +68,9 @@ public:
|
|||
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 Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) = 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)
|
||||
: 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));
|
||||
|
||||
#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:
|
||||
m_filter.opt_linear = 0;
|
||||
|
|
|
@ -26,8 +26,6 @@ class GSState;
|
|||
|
||||
class alignas(32) GSVertexTrace : public GSAlignedClass<32>
|
||||
{
|
||||
BiFiltering m_force_filter;
|
||||
|
||||
public:
|
||||
struct Vertex
|
||||
{
|
||||
|
|
|
@ -49,30 +49,6 @@ namespace D3D
|
|||
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)
|
||||
{
|
||||
ASSERT(factory);
|
||||
|
|
|
@ -25,9 +25,6 @@ namespace D3D
|
|||
// create a dxgi factory
|
||||
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
|
||||
// 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
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "GS/GSExtra.h"
|
||||
#include "GS/GSUtil.h"
|
||||
#include "Host.h"
|
||||
#include "HostDisplay.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <VersionHelpers.h>
|
||||
|
@ -79,12 +80,10 @@ bool GSDevice11::SetFeatureLevel(D3D_FEATURE_LEVEL level, bool compat_mode)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSDevice11::Create(const WindowInfo& wi)
|
||||
bool GSDevice11::Create(HostDisplay* display)
|
||||
{
|
||||
if (!__super::Create(wi))
|
||||
{
|
||||
if (!__super::Create(display))
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D11_BUFFER_DESC bd;
|
||||
D3D11_SAMPLER_DESC sd;
|
||||
|
@ -92,131 +91,43 @@ bool GSDevice11::Create(const WindowInfo& wi)
|
|||
D3D11_RASTERIZER_DESC rd;
|
||||
D3D11_BLEND_DESC bsd;
|
||||
|
||||
const bool enable_debugging = theApp.GetConfigB("debug_d3d");
|
||||
D3D_FEATURE_LEVEL level;
|
||||
|
||||
auto factory = D3D::CreateFactory(enable_debugging);
|
||||
if (!factory)
|
||||
if (display->GetRenderAPI() != HostDisplay::RenderAPI::D3D11)
|
||||
{
|
||||
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;
|
||||
|
||||
// select adapter
|
||||
auto adapter = D3D::GetAdapterFromIndex(
|
||||
factory.get(), theApp.GetConfigI("adapter_index")
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
// Set maximum texture size limit based on supported feature level.
|
||||
if (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;
|
||||
|
||||
{
|
||||
// HACK: check nVIDIA
|
||||
// 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");
|
||||
m_hack_topleft_offset = (m_upscale_multiplier != 1 && D3D::IsNvidia(adapter.get()) && !disable_safe_features) ? -0.01f : 0.0f;
|
||||
const bool disable_safe_features = theApp.GetConfigB("UserHacks") && theApp.GetConfigB("UserHacks_Disable_Safe_Features");
|
||||
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");
|
||||
|
@ -362,10 +273,8 @@ bool GSDevice11::Create(const WindowInfo& wi)
|
|||
rd.MultisampleEnable = true;
|
||||
rd.AntialiasedLineEnable = false;
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11RasterizerState> rs;
|
||||
m_dev->CreateRasterizerState(&rd, rs.put());
|
||||
|
||||
m_ctx->RSSetState(rs.get());
|
||||
m_dev->CreateRasterizerState(&rd, m_rs.put());
|
||||
m_ctx->RSSetState(m_rs.get());
|
||||
|
||||
//
|
||||
|
||||
|
@ -388,10 +297,6 @@ bool GSDevice11::Create(const WindowInfo& wi)
|
|||
|
||||
//
|
||||
|
||||
Reset(wi.surface_width, wi.surface_height);
|
||||
|
||||
//
|
||||
|
||||
CreateTextureFX();
|
||||
|
||||
//
|
||||
|
@ -419,48 +324,48 @@ bool GSDevice11::Create(const WindowInfo& wi)
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
bool GSDevice11::Reset(int w, int h)
|
||||
void GSDevice11::ResetAPIState()
|
||||
{
|
||||
if (!__super::Reset(w, h))
|
||||
return false;
|
||||
|
||||
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;
|
||||
// Clear out the GS, since the imgui draw doesn't get rid of it.
|
||||
m_ctx->GSSetShader(nullptr, nullptr, 0);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
m_swapchain->Present(m_vsync, 0);
|
||||
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),
|
||||
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()
|
||||
|
@ -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::Int32: dxformat = DXGI_FORMAT_R32_SINT; break;
|
||||
case GSTexture::Format::Invalid:
|
||||
case GSTexture::Format::Backbuffer:
|
||||
ASSERT(0);
|
||||
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)
|
||||
{
|
||||
if (!sTex || !dTex)
|
||||
{
|
||||
ASSERT(0);
|
||||
return;
|
||||
}
|
||||
ASSERT(sTex);
|
||||
|
||||
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)]
|
||||
|
@ -740,11 +640,22 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
|||
|
||||
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
|
||||
|
||||
|
||||
if (draw_in_depth)
|
||||
OMSetDepthStencilState(m_convert.dss_write.get(), 0);
|
||||
else
|
||||
|
@ -752,10 +663,7 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
|||
|
||||
OMSetBlendState(bs, 0);
|
||||
|
||||
if (draw_in_depth)
|
||||
OMSetRenderTargets(nullptr, dTex);
|
||||
else
|
||||
OMSetRenderTargets(dTex, nullptr);
|
||||
|
||||
|
||||
// ia
|
||||
|
||||
|
@ -805,48 +713,6 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
|||
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)
|
||||
{
|
||||
const bool slbg = PMODE.SLBG;
|
||||
|
|
|
@ -119,7 +119,6 @@ private:
|
|||
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
|
||||
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex) final;
|
||||
void DoExternalFX(GSTexture* sTex, GSTexture* dTex) final;
|
||||
void RenderOsd(GSTexture* dt);
|
||||
void BeforeDraw();
|
||||
void AfterDraw();
|
||||
|
||||
|
@ -217,11 +216,11 @@ private:
|
|||
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<ID3D11BlendState>> m_om_bs;
|
||||
wil::com_ptr_nothrow<ID3D11RasterizerState> m_rs;
|
||||
|
||||
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
|
||||
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
|
||||
|
||||
std::unique_ptr<GSTexture> m_font;
|
||||
std::unique_ptr<GSTexture11> m_download_tex;
|
||||
|
||||
std::string m_tfx_source;
|
||||
|
@ -240,10 +239,10 @@ public:
|
|||
bool SetFeatureLevel(D3D_FEATURE_LEVEL level, bool compat_mode);
|
||||
void GetFeatureLevel(D3D_FEATURE_LEVEL& level) const { level = m_shader.level; }
|
||||
|
||||
bool Create(const WindowInfo& wi);
|
||||
bool Reset(int w, int h);
|
||||
void Flip();
|
||||
void SetVSync(int vsync) final;
|
||||
bool Create(HostDisplay* display);
|
||||
|
||||
void ResetAPIState() override;
|
||||
void RestoreAPIState() override;
|
||||
|
||||
void DrawPrimitive();
|
||||
void DrawIndexedPrimitive();
|
||||
|
|
|
@ -44,6 +44,11 @@ GSTexture11::GSTexture11(wil::com_ptr_nothrow<ID3D11Texture2D> texture, GSTextur
|
|||
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)
|
||||
{
|
||||
if (layer >= m_max_layer)
|
||||
|
|
|
@ -36,6 +36,8 @@ class GSTexture11 : public GSTexture
|
|||
public:
|
||||
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 Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0);
|
||||
void Unmap();
|
||||
|
|
|
@ -225,7 +225,7 @@ void GSDevice11::SetupPS(PSSelector sel, const GSHWDrawConfig::PSConstantBuffer*
|
|||
|
||||
memset(&sd, 0, sizeof(sd));
|
||||
|
||||
const int anisotropy = theApp.GetConfigI("MaxAnisotropy");
|
||||
const int anisotropy = GSConfig.MaxAnisotropy;
|
||||
if (anisotropy && ssel.aniso)
|
||||
sd.Filter = D3D11_FILTER_ANISOTROPIC;
|
||||
else if (ssel.biln)
|
||||
|
|
|
@ -1121,7 +1121,7 @@ bool GSState::IsBadFrame()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (m_skip == 0 && (m_userhacks_skipdraw > 0))
|
||||
if (m_skip == 0 && (GSConfig.SkipDraw > 0))
|
||||
{
|
||||
if (fi.TME)
|
||||
{
|
||||
|
@ -1129,8 +1129,8 @@ bool GSState::IsBadFrame()
|
|||
// General, often problematic post processing
|
||||
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 = std::max(m_userhacks_skipdraw, m_skip_offset);
|
||||
m_skip_offset = GSConfig.SkipDrawOffset;
|
||||
m_skip = std::max(GSConfig.SkipDraw, m_skip_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
#include "GS/GSGL.h"
|
||||
|
||||
GSRendererHW::GSRendererHW()
|
||||
: m_width(default_rt_size.x)
|
||||
: GSRenderer()
|
||||
, m_width(default_rt_size.x)
|
||||
, m_height(default_rt_size.y)
|
||||
, m_custom_width(1024)
|
||||
, m_custom_height(1024)
|
||||
, m_reset(false)
|
||||
, m_userhacks_ts_half_bottom(-1)
|
||||
, m_tc(new GSTextureCache(this))
|
||||
, m_src(nullptr)
|
||||
|
@ -30,12 +30,12 @@ GSRendererHW::GSRendererHW()
|
|||
, m_userhacks_tcoffset_x(0)
|
||||
, m_userhacks_tcoffset_y(0)
|
||||
, m_channel_shuffle(false)
|
||||
, m_reset(false)
|
||||
, m_lod(GSVector2i(0, 0))
|
||||
{
|
||||
m_mipmap = theApp.GetConfigI("mipmap_hw");
|
||||
m_upscale_multiplier = std::max(0, theApp.GetConfigI("upscale_multiplier"));
|
||||
m_conservative_framebuffer = theApp.GetConfigB("conservative_framebuffer");
|
||||
m_accurate_date = theApp.GetConfigB("accurate_date");
|
||||
|
||||
if (theApp.GetConfigB("UserHacks"))
|
||||
{
|
||||
|
@ -45,7 +45,6 @@ GSRendererHW::GSRendererHW()
|
|||
m_userHacks_merge_sprite = theApp.GetConfigB("UserHacks_merge_pp_sprite");
|
||||
m_userhacks_ts_half_bottom = theApp.GetConfigI("UserHacks_Half_Bottom_Override");
|
||||
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_y = theApp.GetConfigI("UserHacks_TCOffsetY") / -1000.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_ts_half_bottom = -1;
|
||||
m_userhacks_round_sprite_offset = 0;
|
||||
m_userHacks_HPO = 0;
|
||||
}
|
||||
|
||||
if (!m_upscale_multiplier) // Custom Resolution
|
||||
|
@ -278,7 +276,7 @@ void GSRendererHW::Reset()
|
|||
GSRenderer::Reset();
|
||||
}
|
||||
|
||||
void GSRendererHW::VSync(int field)
|
||||
void GSRendererHW::VSync(u32 field)
|
||||
{
|
||||
//Check if the frame buffer width or display width has changed
|
||||
SetScaling();
|
||||
|
@ -295,19 +293,12 @@ void GSRendererHW::VSync(int field)
|
|||
m_tc->IncAge();
|
||||
|
||||
m_tc->PrintMemoryUsage();
|
||||
m_dev->PrintMemoryUsage();
|
||||
g_gs_device->PrintMemoryUsage();
|
||||
|
||||
m_skip = 0;
|
||||
m_skip_offset = 0;
|
||||
}
|
||||
|
||||
void GSRendererHW::ResetDevice()
|
||||
{
|
||||
m_tc->RemoveAll();
|
||||
|
||||
GSRenderer::ResetDevice();
|
||||
}
|
||||
|
||||
GSTexture* GSRendererHW::GetOutput(int i, int& y_offset)
|
||||
{
|
||||
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_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 GSVector4i indices_low(GSVector4i::load<true>(tri_indices));
|
||||
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)
|
||||
{
|
||||
if (m_userHacks_HPO <= 1 || GetUpscaleMultiplier() == 1)
|
||||
if (GSConfig.UserHacks_HalfPixelOffset <= 1 || GetUpscaleMultiplier() == 1)
|
||||
return GSVector4(0.0f);
|
||||
|
||||
const GSVertex* v = &m_vertex.buff[0];
|
||||
|
@ -656,7 +647,7 @@ GSVector4 GSRendererHW::RealignTargetTextureCoordinate(const GSTextureCache::Sou
|
|||
if (PRIM->FST)
|
||||
{
|
||||
|
||||
if (m_userHacks_HPO == 3)
|
||||
if (GSConfig.UserHacks_HalfPixelOffset == 3)
|
||||
{
|
||||
if (!linear && t_position == 8)
|
||||
{
|
||||
|
@ -1202,7 +1193,7 @@ void GSRendererHW::RoundSpriteOffset()
|
|||
|
||||
void GSRendererHW::Draw()
|
||||
{
|
||||
if (m_dev->IsLost() || IsBadFrame())
|
||||
if (IsBadFrame())
|
||||
{
|
||||
GL_INS("Warning skipping a draw call (%d)", s_n);
|
||||
return;
|
||||
|
@ -1508,11 +1499,11 @@ void GSRendererHW::Draw()
|
|||
{
|
||||
const bool is_rt = t == rt;
|
||||
t->m_texture = is_rt ?
|
||||
m_dev->CreateSparseRenderTarget(new_w, new_h, tex->GetFormat()) :
|
||||
m_dev->CreateSparseDepthStencil(new_w, new_h, tex->GetFormat());
|
||||
g_gs_device->CreateSparseRenderTarget(new_w, new_h, tex->GetFormat()) :
|
||||
g_gs_device->CreateSparseDepthStencil(new_w, new_h, tex->GetFormat());
|
||||
const GSVector4i r{ 0, 0, w, h };
|
||||
m_dev->CopyRect(tex, t->m_texture, r);
|
||||
m_dev->Recycle(tex);
|
||||
g_gs_device->CopyRect(tex, t->m_texture, r);
|
||||
g_gs_device->Recycle(tex);
|
||||
t->m_texture->SetScale(up_s);
|
||||
(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
|
||||
ASSERT(color == 0);
|
||||
m_dev->ClearDepth(t);
|
||||
g_gs_device->ClearDepth(t);
|
||||
}
|
||||
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)
|
||||
// m_dev->StretchRect(tex->m_texture, sRect, tex->m_texture, dRect);
|
||||
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
|
||||
|
@ -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,
|
||||
// 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);
|
||||
|
||||
|
@ -2120,7 +2111,7 @@ bool GSRendererHW::OI_FFX(GSTexture* rt, GSTexture* ds, GSTextureCache::Source*
|
|||
GL_INS("OI_FFX ZB clear");
|
||||
if (ds)
|
||||
ds->Commit(); // Don't bother to save few MB for a single game
|
||||
m_dev->ClearDepth(ds);
|
||||
g_gs_device->ClearDepth(ds);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -2172,7 +2163,7 @@ bool GSRendererHW::OI_RozenMaidenGebetGarden(GSTexture* rt, GSTexture* ds, GSTex
|
|||
{
|
||||
GL_INS("OI_RozenMaidenGebetGarden FB clear");
|
||||
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;
|
||||
|
@ -2191,7 +2182,7 @@ bool GSRendererHW::OI_RozenMaidenGebetGarden(GSTexture* rt, GSTexture* ds, GSTex
|
|||
{
|
||||
GL_INS("OI_RozenMaidenGebetGarden ZB clear");
|
||||
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;
|
||||
|
@ -2232,7 +2223,7 @@ bool GSRendererHW::OI_SonicUnleashed(GSTexture* rt, GSTexture* ds, GSTextureCach
|
|||
const GSVector4 sRect(0, 0, 1, 1);
|
||||
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;
|
||||
}
|
||||
|
@ -2308,7 +2299,7 @@ bool GSRendererHW::OI_SuperManReturns(GSTexture* rt, GSTexture* ds, GSTextureCac
|
|||
// Do a direct write
|
||||
if (rt)
|
||||
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());
|
||||
GL_INS("OI_SuperManReturns");
|
||||
|
@ -2346,7 +2337,7 @@ bool GSRendererHW::OI_ArTonelico2(GSTexture* rt, GSTexture* ds, GSTextureCache::
|
|||
GL_INS("OI_ArTonelico2");
|
||||
if (ds)
|
||||
ds->Commit(); // Don't bother to save few MB for a single game
|
||||
m_dev->ClearDepth(ds);
|
||||
g_gs_device->ClearDepth(ds);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -26,7 +26,6 @@ private:
|
|||
int m_height;
|
||||
int m_custom_width;
|
||||
int m_custom_height;
|
||||
bool m_reset;
|
||||
int m_upscale_multiplier;
|
||||
int m_userhacks_ts_half_bottom;
|
||||
|
||||
|
@ -151,29 +150,26 @@ protected:
|
|||
virtual void DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) = 0;
|
||||
|
||||
int m_userhacks_round_sprite_offset;
|
||||
int m_userHacks_HPO;
|
||||
bool m_userHacks_enabled_unscale_ptln;
|
||||
|
||||
bool m_userhacks_tcoffset;
|
||||
float m_userhacks_tcoffset_x;
|
||||
float m_userhacks_tcoffset_y;
|
||||
|
||||
bool m_accurate_date;
|
||||
AccBlendLevel m_sw_blending;
|
||||
|
||||
bool m_channel_shuffle;
|
||||
bool m_reset;
|
||||
|
||||
GSVector2i m_lod; // Min & Max level of detail
|
||||
void CustomResolutionScaling();
|
||||
|
||||
public:
|
||||
GSRendererHW();
|
||||
virtual ~GSRendererHW();
|
||||
virtual ~GSRendererHW() override;
|
||||
|
||||
void SetGameCRC(u32 crc, int options);
|
||||
bool CanUpscale();
|
||||
int GetUpscaleMultiplier();
|
||||
GSVector2i GetCustomResolution();
|
||||
void SetGameCRC(u32 crc, int options) override;
|
||||
bool CanUpscale() override;
|
||||
int GetUpscaleMultiplier() override;
|
||||
GSVector2i GetCustomResolution() override;
|
||||
void SetScaling();
|
||||
void Lines2Sprites();
|
||||
void EmulateAtst(GSVector4& FogColor_AREF, u8& atst, const bool pass_2);
|
||||
|
@ -181,16 +177,16 @@ public:
|
|||
GSVector4 RealignTargetTextureCoordinate(const GSTextureCache::Source* tex);
|
||||
GSVector4i ComputeBoundingBox(const GSVector2& rtscale, const GSVector2i& rtsize);
|
||||
void MergeSprite(GSTextureCache::Source* tex);
|
||||
GSVector2 GetTextureScaleFactor();
|
||||
GSVector2 GetTextureScaleFactor() override;
|
||||
|
||||
void Reset();
|
||||
void VSync(int field);
|
||||
void ResetDevice();
|
||||
GSTexture* GetOutput(int i, int& y_offset);
|
||||
GSTexture* GetFeedbackOutput();
|
||||
void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r);
|
||||
void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut = false);
|
||||
void Draw();
|
||||
void Reset() override;
|
||||
void VSync(u32 field) override;
|
||||
|
||||
GSTexture* GetOutput(int i, int& y_offset) override;
|
||||
GSTexture* GetFeedbackOutput() override;
|
||||
void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r) override;
|
||||
void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut = false) override;
|
||||
void Draw() override;
|
||||
|
||||
// Called by the texture cache to know if current texture is useful
|
||||
virtual bool IsDummyTexture() const { return false; }
|
||||
|
|
|
@ -18,12 +18,8 @@
|
|||
#include "GS/GSGL.h"
|
||||
|
||||
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.
|
||||
m_drawlist.reserve(2048);
|
||||
|
||||
|
@ -33,19 +29,6 @@ GSRendererNew::GSRendererNew()
|
|||
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)
|
||||
{
|
||||
GL_PUSH("IA");
|
||||
|
@ -56,7 +39,7 @@ void GSRendererNew::SetupIA(const float& sx, const float& sy)
|
|||
m_vertex.buff[i].UV &= 0x3FEF3FEF;
|
||||
}
|
||||
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)
|
||||
{
|
||||
|
@ -117,7 +100,7 @@ void GSRendererNew::SetupIA(const float& sx, const float& sy)
|
|||
// the extra validation cost of the extra stage.
|
||||
//
|
||||
// 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;
|
||||
|
||||
|
@ -200,9 +183,9 @@ void GSRendererNew::EmulateTextureShuffleAndFbmask()
|
|||
// m_texture_shuffle = 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
|
||||
{
|
||||
|
@ -211,7 +194,7 @@ void GSRendererNew::EmulateTextureShuffleAndFbmask()
|
|||
// 1. D3D sucks.
|
||||
// 2. FB copy is slow, especially on triangle primitives which is unplayable with some games.
|
||||
// 3. SW blending isn't implemented yet.
|
||||
switch (m_sw_blending)
|
||||
switch (GSConfig.AccurateBlendingUnit)
|
||||
{
|
||||
case AccBlendLevel::Ultra:
|
||||
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.
|
||||
enable_fbmask_emulation = (!m_texture_shuffle && (m_vt.m_primclass != GS_TRIANGLE_CLASS) && (m_context->FRAME.FBMSK != 0x80000000));
|
||||
break;
|
||||
case AccBlendLevel::None:
|
||||
case AccBlendLevel::Minimum:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -249,7 +232,7 @@ void GSRendererNew::EmulateTextureShuffleAndFbmask()
|
|||
// 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
|
||||
// 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;
|
||||
|
||||
|
@ -303,7 +286,7 @@ void GSRendererNew::EmulateTextureShuffleAndFbmask()
|
|||
m_conf.cb_ps.FbMask.a = ba_mask;
|
||||
|
||||
// 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);
|
||||
m_conf.require_one_barrier = true;
|
||||
|
@ -354,7 +337,7 @@ void GSRendererNew::EmulateTextureShuffleAndFbmask()
|
|||
have been invalidated before subsequent Draws are executed.
|
||||
*/
|
||||
// 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,
|
||||
(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)
|
||||
{
|
||||
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;
|
||||
|
||||
// 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
|
||||
const GIFRegALPHA& ALPHA = m_context->ALPHA;
|
||||
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
|
||||
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
|
||||
// Note: the [[fallthrough]] attribute tell compilers not to complain about not having breaks.
|
||||
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:
|
||||
sw_blending |= true;
|
||||
|
@ -591,18 +574,18 @@ void GSRendererNew::EmulateBlending(bool& DATE_GL42, bool& DATE_GL45)
|
|||
case AccBlendLevel::Basic:
|
||||
sw_blending |= impossible_or_free_blend;
|
||||
[[fallthrough]];
|
||||
case AccBlendLevel::None:
|
||||
case AccBlendLevel::Minimum:
|
||||
/*sw_blending |= accumulation_blend*/;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
sw_blending |= blend_mix;
|
||||
|
@ -615,7 +598,7 @@ void GSRendererNew::EmulateBlending(bool& DATE_GL42, bool& DATE_GL45)
|
|||
// fixes shadows in Superman shadows of Apokolips.
|
||||
const bool sw_fbmask_colclip = !m_conf.require_one_barrier && m_conf.ps.fbmask;
|
||||
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;
|
||||
else
|
||||
free_colclip = blend_non_recursive;
|
||||
|
@ -637,7 +620,7 @@ void GSRendererNew::EmulateBlending(bool& DATE_GL42, bool& DATE_GL45)
|
|||
m_conf.ps.hdr = 1;
|
||||
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)
|
||||
GL_INS("COLCLIP SW mode ENABLED");
|
||||
|
@ -658,7 +641,7 @@ void GSRendererNew::EmulateBlending(bool& DATE_GL42, bool& DATE_GL45)
|
|||
if (sw_blending)
|
||||
{
|
||||
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.
|
||||
accumulation_blend = false;
|
||||
|
@ -753,7 +736,7 @@ void GSRendererNew::EmulateBlending(bool& DATE_GL42, bool& DATE_GL45)
|
|||
m_conf.require_full_barrier |= !blend_non_recursive;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -795,7 +778,7 @@ void GSRendererNew::EmulateTextureSampler(const GSTextureCache::Source* tex)
|
|||
bool bilinear = m_vt.IsLinear();
|
||||
int trilinear = 0;
|
||||
bool trilinear_auto = false;
|
||||
switch (UserHacks_tri_filter)
|
||||
switch (GSConfig.UserHacks_TriFilter)
|
||||
{
|
||||
case TriFiltering::Forced:
|
||||
trilinear = static_cast<u8>(GS_MIN_FILTER::Linear_Mipmap_Linear);
|
||||
|
@ -810,7 +793,7 @@ void GSRendererNew::EmulateTextureSampler(const GSTextureCache::Source* tex)
|
|||
}
|
||||
break;
|
||||
|
||||
case TriFiltering::None:
|
||||
case TriFiltering::Off:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -936,7 +919,7 @@ void GSRendererNew::EmulateTextureSampler(const GSTextureCache::Source* tex)
|
|||
m_conf.ps.tcc = m_context->TEX0.TCC;
|
||||
|
||||
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 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.ps.scanmsk = m_env.SCANMSK.MSK;
|
||||
|
||||
ASSERT(m_dev != NULL);
|
||||
ASSERT(g_gs_device != NULL);
|
||||
|
||||
// HLE implementation of the channel selection effect
|
||||
//
|
||||
|
@ -1213,13 +1196,13 @@ void GSRendererNew::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
|
|||
MergeSprite(tex);
|
||||
|
||||
// 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();
|
||||
else
|
||||
m_prim_overlap = PRIM_OVERLAP_UNKNOW; // Prim overlap check is useless without texture barrier
|
||||
|
||||
// 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))
|
||||
{
|
||||
|
@ -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
|
||||
// the slow but accurate algo
|
||||
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;
|
||||
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
|
||||
// few primitive draws. Otherwise it sucks.
|
||||
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;
|
||||
DATE_GL45 = true;
|
||||
}
|
||||
}
|
||||
else if (m_accurate_date)
|
||||
else if (GSConfig.AccurateDATE)
|
||||
{
|
||||
// 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);
|
||||
if (m_dev->Features().image_load_store)
|
||||
if (g_gs_device->Features().image_load_store)
|
||||
{
|
||||
DATE_GL42 = true;
|
||||
}
|
||||
else if (m_dev->Features().texture_barrier)
|
||||
else if (g_gs_device->Features().texture_barrier)
|
||||
{
|
||||
m_conf.require_full_barrier = 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,
|
||||
//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;
|
||||
oy2 *= rt->OffsetHack_mody;
|
||||
|
@ -1392,7 +1375,7 @@ void GSRendererNew::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
|
|||
}
|
||||
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.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.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)
|
||||
{
|
||||
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[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);
|
||||
|
@ -1619,12 +1602,12 @@ void GSRendererNew::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
|
|||
|
||||
m_conf.rt = rt;
|
||||
m_conf.ds = ds;
|
||||
m_dev->RenderHW(m_conf);
|
||||
g_gs_device->RenderHW(m_conf);
|
||||
}
|
||||
|
||||
bool GSRendererNew::IsDummyTexture() const
|
||||
{
|
||||
// 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
|
||||
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;
|
||||
std::vector<size_t> m_drawlist;
|
||||
|
||||
TriFiltering UserHacks_tri_filter;
|
||||
|
||||
GSHWDrawConfig m_conf;
|
||||
|
||||
private:
|
||||
|
@ -49,7 +47,6 @@ public:
|
|||
GSRendererNew();
|
||||
~GSRendererNew() override {}
|
||||
|
||||
bool CreateDevice(GSDevice* dev, const WindowInfo& wi) override;
|
||||
void DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) override;
|
||||
|
||||
PRIM_OVERLAP PrimitiveOverlap();
|
||||
|
|
|
@ -49,9 +49,6 @@ GSTextureCache::GSTextureCache(GSRenderer* r)
|
|||
}
|
||||
|
||||
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
|
||||
// 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));
|
||||
// Possible ? In this case we could call LookupSource
|
||||
// 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
|
||||
//
|
||||
// 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));
|
||||
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
|
||||
switch (type) {
|
||||
case RenderTarget: m_renderer->m_dev->ClearRenderTarget(dst->m_texture, 0); break;
|
||||
case DepthStencil: m_renderer->m_dev->ClearDepth(dst->m_texture); break;
|
||||
case RenderTarget: g_gs_device->ClearRenderTarget(dst->m_texture, 0); break;
|
||||
case DepthStencil: g_gs_device->ClearDepth(dst->m_texture); break;
|
||||
default: break;
|
||||
}
|
||||
#endif
|
||||
|
@ -738,7 +735,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, int
|
|||
dst = CreateTarget(TEX0, w, h, RenderTarget);
|
||||
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)
|
||||
{
|
||||
|
@ -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),
|
||||
t->m_texture ? t->m_texture->GetID() : 0,
|
||||
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 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.
|
||||
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);
|
||||
|
||||
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);
|
||||
m_renderer->m_dev->CopyRect(sTex, dTex, area);
|
||||
g_gs_device->CopyRect(sTex, dTex, area);
|
||||
|
||||
// Keep a trace of origin of the texture
|
||||
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
|
||||
|
||||
// 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
|
||||
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)
|
||||
|
||||
//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 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 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
|
||||
// }
|
||||
|
@ -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)
|
||||
// 'src' is the new texture cache entry (hence the output)
|
||||
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;
|
||||
|
||||
// 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
|
||||
// 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;
|
||||
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
|
||||
{
|
||||
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
|
||||
|
@ -1559,7 +1562,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
|||
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)
|
||||
|
@ -1602,12 +1605,12 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
|||
{
|
||||
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);
|
||||
}
|
||||
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)
|
||||
{
|
||||
AttachPaletteToSource(src, psm.pal, false);
|
||||
|
@ -1634,13 +1637,13 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(const GIFRegTEX0& TEX0, int
|
|||
|
||||
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
|
||||
}
|
||||
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);
|
||||
|
@ -1699,9 +1702,9 @@ void GSTextureCache::Read(Target* t, const GSVector4i& r)
|
|||
GSTexture::GSMap m;
|
||||
|
||||
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
|
||||
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)
|
||||
{
|
||||
|
@ -1728,7 +1731,7 @@ void GSTextureCache::Read(Target* t, const GSVector4i& r)
|
|||
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;
|
||||
|
||||
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);
|
||||
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
|
||||
// to recycle.
|
||||
if (!m_shared_texture)
|
||||
m_renderer->m_dev->Recycle(m_texture);
|
||||
g_gs_device->Recycle(m_texture);
|
||||
}
|
||||
|
||||
void GSTextureCache::Surface::UpdateAge()
|
||||
|
@ -2144,7 +2147,7 @@ void GSTextureCache::Target::Update()
|
|||
// 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.
|
||||
m_renderer->m_dev->ClearDepth(m_texture);
|
||||
g_gs_device->ClearDepth(m_texture);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2157,7 +2160,7 @@ void GSTextureCache::Target::Update()
|
|||
TEXA.TA0 = 0;
|
||||
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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
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)
|
||||
{
|
||||
GL_INS("ERROR: Update DepthStencil 0x%x", m_TEX0.TBP0);
|
||||
|
||||
// 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)
|
||||
|
@ -2294,7 +2297,7 @@ GSTextureCache::Palette::Palette(const GSRenderer* renderer, u16 pal, bool need_
|
|||
|
||||
GSTextureCache::Palette::~Palette()
|
||||
{
|
||||
m_renderer->m_dev->Recycle(m_tex_palette);
|
||||
g_gs_device->Recycle(m_tex_palette);
|
||||
_aligned_free(m_clut);
|
||||
}
|
||||
|
||||
|
@ -2317,7 +2320,7 @@ void GSTextureCache::Palette::InitializeTexture()
|
|||
// 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),
|
||||
// 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]));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -219,7 +219,6 @@ protected:
|
|||
u8* m_temp;
|
||||
bool m_can_convert_depth;
|
||||
bool m_cpu_fb_conversion;
|
||||
CRCHackLevel m_crc_hack_level;
|
||||
static bool m_disable_partial_invalidation;
|
||||
bool m_texture_inside_rt;
|
||||
static bool m_wrap_gs_mem;
|
||||
|
|
|
@ -16,21 +16,6 @@
|
|||
#include "PrecompiledHeader.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)
|
||||
{
|
||||
return new GSTextureNull(type, w, h, format);
|
||||
|
|
|
@ -29,7 +29,4 @@ private:
|
|||
|
||||
public:
|
||||
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.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; }
|
||||
void Unmap() override {}
|
||||
bool Save(const std::string& fn) override { return false; }
|
||||
void* GetNativeHandle() const override;
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "GLLoader.h"
|
||||
#include "GS/GS.h"
|
||||
#include <unordered_set>
|
||||
#include "Host.h"
|
||||
|
||||
namespace GLExtension
|
||||
{
|
||||
|
@ -161,15 +162,15 @@ namespace GLLoader
|
|||
bool found_GL_ARB_get_texture_sub_image = false;
|
||||
#endif
|
||||
|
||||
static void mandatory(const std::string& ext)
|
||||
static bool mandatory(const std::string& ext)
|
||||
{
|
||||
if (!GLExtension::Has(ext))
|
||||
{
|
||||
fprintf(stderr, "ERROR: %s is NOT SUPPORTED\n", ext.c_str());
|
||||
throw GSRecoverableError();
|
||||
Host::ReportFormattedErrorAsync("GS", "ERROR: %s is NOT SUPPORTED\n", ext.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool optional(const std::string& name)
|
||||
|
@ -198,7 +199,7 @@ namespace GLLoader
|
|||
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);
|
||||
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);
|
||||
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);
|
||||
throw GSRecoverableError();
|
||||
Host::ReportFormattedErrorAsync("GS", "OpenGL %d.%d is not supported. Only OpenGL %d.%d\n was found", major, minor, major_gl, minor_gl);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_gl_supported_extension()
|
||||
bool check_gl_supported_extension()
|
||||
{
|
||||
int max_ext = 0;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &max_ext);
|
||||
|
@ -243,24 +246,27 @@ namespace GLLoader
|
|||
}
|
||||
|
||||
// Mandatory for both renderer
|
||||
bool ok = true;
|
||||
{
|
||||
// GL4.1
|
||||
mandatory("GL_ARB_separate_shader_objects");
|
||||
ok = ok && mandatory("GL_ARB_separate_shader_objects");
|
||||
// GL4.2
|
||||
mandatory("GL_ARB_shading_language_420pack");
|
||||
mandatory("GL_ARB_texture_storage");
|
||||
ok = ok && mandatory("GL_ARB_shading_language_420pack");
|
||||
ok = ok && mandatory("GL_ARB_texture_storage");
|
||||
// GL4.3
|
||||
mandatory("GL_KHR_debug");
|
||||
ok = ok && mandatory("GL_KHR_debug");
|
||||
// GL4.4
|
||||
mandatory("GL_ARB_buffer_storage");
|
||||
ok = ok && mandatory("GL_ARB_buffer_storage");
|
||||
}
|
||||
|
||||
// Only for HW renderer
|
||||
if (theApp.GetCurrentRendererType() == GSRendererType::OGL_HW)
|
||||
if (GSConfig.UseHardwareRenderer())
|
||||
{
|
||||
mandatory("GL_ARB_copy_image");
|
||||
mandatory("GL_ARB_clip_control");
|
||||
ok = ok && mandatory("GL_ARB_copy_image");
|
||||
ok = ok && mandatory("GL_ARB_clip_control");
|
||||
}
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
// Extra
|
||||
{
|
||||
|
@ -319,6 +325,8 @@ namespace GLLoader
|
|||
Emulate_DSA::Init();
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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
|
||||
check_sparse_compatibility();
|
||||
|
@ -397,5 +407,6 @@ namespace GLLoader
|
|||
fprintf_once(stdout, "\n");
|
||||
|
||||
s_first_load = false;
|
||||
return true;
|
||||
}
|
||||
} // namespace GLLoader
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace GLExtension
|
|||
|
||||
namespace GLLoader
|
||||
{
|
||||
void check_gl_requirements();
|
||||
bool check_gl_requirements();
|
||||
|
||||
extern bool vendor_id_amd;
|
||||
extern bool vendor_id_nvidia;
|
||||
|
|
|
@ -52,23 +52,23 @@ namespace GLState
|
|||
void Clear()
|
||||
{
|
||||
fbo = 0;
|
||||
viewport = GSVector2i(0, 0);
|
||||
scissor = GSVector4i(0, 0, 0, 0);
|
||||
viewport = GSVector2i(1, 1);
|
||||
scissor = GSVector4i(0, 0, 1, 1);
|
||||
|
||||
blend = false;
|
||||
eq_RGB = 0;
|
||||
f_sRGB = 0;
|
||||
f_dRGB = 0;
|
||||
eq_RGB = GL_FUNC_ADD;
|
||||
f_sRGB = GL_ONE;
|
||||
f_dRGB = GL_ZERO;
|
||||
bf = 0;
|
||||
wrgba = 0xF;
|
||||
|
||||
depth = false;
|
||||
depth_func = 0;
|
||||
depth_mask = true;
|
||||
depth_func = GL_LESS;
|
||||
depth_mask = false;
|
||||
|
||||
stencil = false;
|
||||
stencil_func = 0;
|
||||
stencil_pass = 0xFFFF; // Note 0 is valid (GL_ZERO)
|
||||
stencil_func = GL_ALWAYS;
|
||||
stencil_pass = GL_KEEP;
|
||||
|
||||
ps_ss = 0;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "GLState.h"
|
||||
#include "GS/GSUtil.h"
|
||||
#include "Host.h"
|
||||
#include "HostDisplay.h"
|
||||
#include <fstream>
|
||||
#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 FRAGMENT_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024;
|
||||
|
||||
bool GSDeviceOGL::m_debug_gl_call = false;
|
||||
int GSDeviceOGL::m_shader_inst = 0;
|
||||
int GSDeviceOGL::m_shader_reg = 0;
|
||||
FILE* GSDeviceOGL::m_debug_gl_file = NULL;
|
||||
|
@ -61,25 +61,15 @@ GSDeviceOGL::GSDeviceOGL()
|
|||
memset(&m_shadeboost, 0, sizeof(m_shadeboost));
|
||||
memset(&m_om_dss, 0, sizeof(m_om_dss));
|
||||
memset(&m_profiler, 0, sizeof(m_profiler));
|
||||
GLState::Clear();
|
||||
|
||||
m_mipmap = theApp.GetConfigI("mipmap");
|
||||
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
|
||||
#ifdef ENABLE_OGL_DEBUG
|
||||
if (theApp.GetCurrentRendererType() == GSRendererType::OGL_SW)
|
||||
m_debug_gl_file = fopen("GS_opengl_debug_sw.txt", "w");
|
||||
else
|
||||
m_debug_gl_file = fopen("GS_opengl_debug_hw.txt", "w");
|
||||
m_debug_gl_file = fopen("GS_opengl_debug.txt", "w");
|
||||
#endif
|
||||
|
||||
m_debug_gl_call = theApp.GetConfigB("debug_opengl");
|
||||
|
||||
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");
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
bool GSDeviceOGL::Create(const WindowInfo& wi)
|
||||
bool GSDeviceOGL::Create(HostDisplay* display)
|
||||
{
|
||||
m_gl_context = GL::Context::Create(wi, GL::Context::GetAllVersionsList());
|
||||
if (!m_gl_context || !m_gl_context->MakeCurrent())
|
||||
if (!GSDevice::Create(display))
|
||||
return false;
|
||||
|
||||
if (display->GetRenderAPI() != HostDisplay::RenderAPI::OpenGL)
|
||||
return false;
|
||||
|
||||
// Check openGL requirement as soon as possible so we can switch to another
|
||||
// renderer/device
|
||||
try
|
||||
{
|
||||
GLLoader::check_gl_requirements();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
printf("GS error: Exception caught in GSDeviceOGL::Create: %s", ex.what());
|
||||
m_gl_context->DoneCurrent();
|
||||
if (!GLLoader::check_gl_requirements())
|
||||
return false;
|
||||
}
|
||||
|
||||
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");
|
||||
if (!shader.has_value())
|
||||
{
|
||||
Host::ReportErrorAsync("GS", "Failed to read shaders/opengl/common_header.glsl.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_shader_common_header = std::move(*shader);
|
||||
}
|
||||
|
||||
// because of fbo bindings below...
|
||||
GLState::Clear();
|
||||
|
||||
// ****************************************************************
|
||||
// Debug helper
|
||||
// ****************************************************************
|
||||
#ifdef ENABLE_OGL_DEBUG
|
||||
if (theApp.GetConfigB("debug_opengl"))
|
||||
if (GSConfig.UseDebugDevice)
|
||||
{
|
||||
glDebugMessageCallback((GLDEBUGPROC)DebugOutputToFile, NULL);
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -356,6 +346,10 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
|||
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
|
||||
// ****************************************************************
|
||||
|
@ -377,7 +371,10 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
|||
// these all share the same vertex shader
|
||||
const auto shader = Host::ReadResourceFileToString("shaders/opengl/convert.glsl");
|
||||
if (!shader.has_value())
|
||||
{
|
||||
Host::ReportErrorAsync("GS", "Failed to read shaders/opengl/convert.glsl.");
|
||||
return false;
|
||||
}
|
||||
|
||||
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");
|
||||
if (!shader.has_value())
|
||||
{
|
||||
Host::ReportErrorAsync("GS", "Failed to read shaders/opengl/merge.glsl.");
|
||||
return false;
|
||||
}
|
||||
|
||||
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");
|
||||
if (!shader.has_value())
|
||||
{
|
||||
Host::ReportErrorAsync("GS", "Failed to read shaders/opengl/interlace.glsl.");
|
||||
return false;
|
||||
}
|
||||
|
||||
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");
|
||||
if (!shader.has_value())
|
||||
{
|
||||
Host::ReportErrorAsync("GS", "Failed to read shaders/opengl/shadeboost.glsl.");
|
||||
return false;
|
||||
}
|
||||
|
||||
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))
|
||||
|
@ -563,23 +569,6 @@ bool GSDeviceOGL::Create(const WindowInfo& wi)
|
|||
|
||||
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
|
||||
static_assert(sizeof(VSSelector) == 4, "Wrong VSSelector 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 fragment_shader = Host::ReadResourceFileToString("shaders/opengl/tfx_fs.glsl");
|
||||
if (!vertex_shader.has_value() || !fragment_shader.has_value())
|
||||
{
|
||||
Host::ReportErrorAsync("GS", "Failed to read shaders/opengl/tfx_{vgs,fs}.glsl.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_shader_tfx_vgs = std::move(*vertex_shader);
|
||||
m_shader_tfx_fs = std::move(*fragment_shader);
|
||||
|
@ -615,37 +607,75 @@ bool GSDeviceOGL::CreateTextureFX()
|
|||
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)
|
||||
{
|
||||
glQueryCounter(m_profiler.timer(), GL_TIMESTAMP);
|
||||
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()
|
||||
|
@ -680,7 +710,7 @@ void GSDeviceOGL::ClearRenderTarget(GSTexture* t, const GSVector4& c)
|
|||
return;
|
||||
|
||||
GSTextureOGL* T = static_cast<GSTextureOGL*>(t);
|
||||
if (T->HasBeenCleaned() && !T->IsBackbuffer())
|
||||
if (T->HasBeenCleaned())
|
||||
return;
|
||||
|
||||
// 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;
|
||||
OMSetColorMaskState();
|
||||
|
||||
if (T->IsBackbuffer())
|
||||
{
|
||||
OMSetFBO(0);
|
||||
OMSetFBO(m_fbo);
|
||||
OMAttachRt(T);
|
||||
|
||||
// glDrawBuffer(GL_BACK); // this is the default when there is no FB
|
||||
// 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);
|
||||
}
|
||||
glClearBufferfv(GL_COLOR, 0, c.v);
|
||||
|
||||
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)
|
||||
{
|
||||
if (!sTex || !dTex)
|
||||
{
|
||||
ASSERT(0);
|
||||
return;
|
||||
}
|
||||
ASSERT(sTex);
|
||||
|
||||
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)]
|
||||
|
@ -1182,15 +1197,27 @@ void GSDeviceOGL::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
|
|||
// instead to emulate it with shader
|
||||
// see https://www.opengl.org/wiki/Framebuffer#Blitting
|
||||
|
||||
GL_PUSH("StretchRect from %d to %d", sTex->GetID(), dTex->GetID());
|
||||
|
||||
// ************************************
|
||||
// Init
|
||||
// ************************************
|
||||
|
||||
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();
|
||||
|
||||
|
@ -1203,11 +1230,6 @@ void GSDeviceOGL::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
|
|||
else
|
||||
OMSetDepthStencilState(m_convert.dss);
|
||||
|
||||
if (draw_in_depth)
|
||||
OMSetRenderTargets(NULL, dTex);
|
||||
else
|
||||
OMSetRenderTargets(dTex, NULL);
|
||||
|
||||
OMSetBlendState((u8)bs);
|
||||
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.
|
||||
// Only flipping the backbuffer is transparent (I hope)...
|
||||
GSVector4 flip_sr = sRect;
|
||||
if (static_cast<GSTextureOGL*>(dTex)->IsBackbuffer())
|
||||
if (!dTex)
|
||||
{
|
||||
flip_sr.y = sRect.w;
|
||||
flip_sr.w = sRect.y;
|
||||
|
@ -1238,7 +1260,6 @@ void GSDeviceOGL::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
|
|||
// ************************************
|
||||
// Draw
|
||||
// ************************************
|
||||
dTex->CommitRegion(GSVector2i((int)dRect.z + 1, (int)dRect.w + 1));
|
||||
DrawStretchRect(flip_sr, dRect, ds);
|
||||
|
||||
// ************************************
|
||||
|
@ -1276,39 +1297,6 @@ void GSDeviceOGL::DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect
|
|||
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)
|
||||
{
|
||||
GL_PUSH("DoMerge");
|
||||
|
@ -1705,30 +1693,21 @@ void GSDeviceOGL::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVecto
|
|||
GSTextureOGL* RT = static_cast<GSTextureOGL*>(rt);
|
||||
GSTextureOGL* DS = static_cast<GSTextureOGL*>(ds);
|
||||
|
||||
if (rt == NULL || !RT->IsBackbuffer())
|
||||
OMSetFBO(m_fbo);
|
||||
if (rt)
|
||||
{
|
||||
OMSetFBO(m_fbo);
|
||||
if (rt)
|
||||
{
|
||||
OMAttachRt(RT);
|
||||
}
|
||||
else
|
||||
{
|
||||
OMAttachRt();
|
||||
}
|
||||
|
||||
// Note: it must be done after OMSetFBO
|
||||
if (ds)
|
||||
OMAttachDs(DS);
|
||||
else
|
||||
OMAttachDs();
|
||||
OMAttachRt(RT);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Render in the backbuffer
|
||||
OMSetFBO(0);
|
||||
OMAttachRt();
|
||||
}
|
||||
|
||||
// Note: it must be done after OMSetFBO
|
||||
if (ds)
|
||||
OMAttachDs(DS);
|
||||
else
|
||||
OMAttachDs();
|
||||
|
||||
const GSVector2i size = rt ? rt->GetSize() : ds ? ds->GetSize() : GLState::viewport;
|
||||
if (GLState::viewport != size)
|
||||
|
|
|
@ -213,12 +213,9 @@ private:
|
|||
// Increment this constant whenever shaders change, to invalidate user's program binary cache.
|
||||
static constexpr u32 SHADER_VERSION = 1;
|
||||
|
||||
std::unique_ptr<GL::Context> m_gl_context;
|
||||
int m_mipmap;
|
||||
int m_upscale_multiplier;
|
||||
TriFiltering m_filter;
|
||||
|
||||
static bool m_debug_gl_call;
|
||||
static FILE* m_debug_gl_file;
|
||||
|
||||
bool m_disable_hw_gl_draw;
|
||||
|
@ -301,7 +298,6 @@ private:
|
|||
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
|
||||
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
|
||||
|
||||
std::unique_ptr<GSTexture> m_font;
|
||||
AlignedBuffer<u8, 32> m_download_buffer;
|
||||
|
||||
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 DoShadeBoost(GSTexture* sTex, GSTexture* dTex) final;
|
||||
void DoExternalFX(GSTexture* sTex, GSTexture* dTex) final;
|
||||
void RenderOsd(GSTexture* dt) final;
|
||||
|
||||
void OMAttachRt(GSTextureOGL* rt = NULL);
|
||||
void OMAttachDs(GSTextureOGL* ds = NULL);
|
||||
|
@ -330,10 +325,10 @@ public:
|
|||
// 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);
|
||||
|
||||
bool Create(const WindowInfo& wi) override;
|
||||
bool Reset(int w, int h) override;
|
||||
void Flip() override;
|
||||
void SetVSync(int vsync) override;
|
||||
bool Create(HostDisplay* display) override;
|
||||
|
||||
void ResetAPIState() override;
|
||||
void RestoreAPIState() override;
|
||||
|
||||
void DrawPrimitive();
|
||||
void DrawIndexedPrimitive();
|
||||
|
|
|
@ -98,7 +98,7 @@ namespace PboPool
|
|||
m_map = NULL;
|
||||
m_offset = 0;
|
||||
|
||||
for (GLsync fence : m_fence)
|
||||
for (GLsync& fence : m_fence)
|
||||
{
|
||||
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
|
||||
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:
|
||||
m_int_format = 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)
|
||||
{
|
||||
case Type::Backbuffer:
|
||||
return; // backbuffer isn't a real texture
|
||||
case Type::Texture:
|
||||
// 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;
|
||||
|
@ -279,7 +270,6 @@ GSTextureOGL::GSTextureOGL(Type type, int w, int h, Format format, GLuint fbo_re
|
|||
case Format::Color:
|
||||
case Format::UInt32:
|
||||
case Format::Int32:
|
||||
case Format::Backbuffer:
|
||||
m_sparse &= GLLoader::found_compatible_GL_ARB_sparse_texture2;
|
||||
SetGpuPageSize(GSVector2i(127, 127));
|
||||
break;
|
||||
|
@ -357,6 +347,11 @@ GSTextureOGL::~GSTextureOGL()
|
|||
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)
|
||||
{
|
||||
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;
|
||||
#endif
|
||||
|
||||
if (IsBackbuffer())
|
||||
{
|
||||
glReadPixels(0, 0, m_committed_size.x, m_committed_size.y, GL_RGBA, GL_UNSIGNED_BYTE, image.get());
|
||||
}
|
||||
else if (IsDss())
|
||||
if (IsDss())
|
||||
{
|
||||
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);
|
||||
virtual ~GSTextureOGL();
|
||||
|
||||
void* GetNativeHandle() const override;
|
||||
|
||||
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;
|
||||
void Unmap() final;
|
||||
|
@ -70,7 +72,6 @@ public:
|
|||
bool Save(const std::string& fn) final;
|
||||
|
||||
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); }
|
||||
|
||||
u32 GetID() final { return m_texture_id; }
|
||||
|
|
|
@ -27,7 +27,7 @@ CONSTINIT const GSVector8 GSRendererSW::m_pos_scale2 = GSVector8::cxpr(1.0f / 16
|
|||
#endif
|
||||
|
||||
GSRendererSW::GSRendererSW(int threads)
|
||||
: m_fzb(NULL)
|
||||
: GSRenderer(), m_fzb(NULL)
|
||||
{
|
||||
m_nativeres = true; // ignore ini, sw is always native
|
||||
|
||||
|
@ -91,7 +91,7 @@ void GSRendererSW::Reset()
|
|||
GSRenderer::Reset();
|
||||
}
|
||||
|
||||
void GSRendererSW::VSync(int field)
|
||||
void GSRendererSW::VSync(u32 field)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
void GSRendererSW::ResetDevice()
|
||||
{
|
||||
for (GSTexture*& tex : m_texture)
|
||||
{
|
||||
delete tex;
|
||||
|
||||
tex = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
GSTexture* GSRendererSW::GetOutput(int i, int& y_offset)
|
||||
{
|
||||
Sync(1);
|
||||
|
@ -156,7 +146,7 @@ GSTexture* GSRendererSW::GetOutput(int i, int& y_offset)
|
|||
|
||||
// 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;
|
||||
|
||||
|
@ -1300,7 +1290,7 @@ bool GSRendererSW::GetScanlineGlobalData(SharedData* data)
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -79,17 +79,16 @@ protected:
|
|||
std::atomic<u32> m_fzb_pages[512]; // u16 frame/zbuf pages interleaved
|
||||
std::atomic<u16> m_tex_pages[512];
|
||||
|
||||
void Reset();
|
||||
void VSync(int field);
|
||||
void ResetDevice();
|
||||
GSTexture* GetOutput(int i, int& y_offset);
|
||||
GSTexture* GetFeedbackOutput();
|
||||
void Reset() override;
|
||||
void VSync(u32 field) override;
|
||||
GSTexture* GetOutput(int i, int& y_offset) override;
|
||||
GSTexture* GetFeedbackOutput() override;
|
||||
|
||||
void Draw();
|
||||
void Draw() override;
|
||||
void Queue(GSRingHeap::SharedPtr<GSRasterizerData>& item);
|
||||
void Sync(int reason);
|
||||
void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r);
|
||||
void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut = false);
|
||||
void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r) override;
|
||||
void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut = false) override;
|
||||
|
||||
void UsePages(const GSOffset::PageLooper& pages, const int type);
|
||||
void ReleasePages(const GSOffset::PageLooper& pages, const int type);
|
||||
|
@ -101,5 +100,5 @@ protected:
|
|||
|
||||
public:
|
||||
GSRendererSW(int threads);
|
||||
virtual ~GSRendererSW();
|
||||
~GSRendererSW() override;
|
||||
};
|
||||
|
|
|
@ -32,6 +32,11 @@ GSTextureSW::~GSTextureSW()
|
|||
_aligned_free(m_data);
|
||||
}
|
||||
|
||||
void* GSTextureSW::GetNativeHandle() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool GSTextureSW::Update(const GSVector4i& r, const void* data, int pitch, int layer)
|
||||
{
|
||||
GSMap m;
|
||||
|
|
|
@ -33,4 +33,5 @@ public:
|
|||
bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) override;
|
||||
void Unmap() 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.");
|
||||
case IDC_OSD_LOG:
|
||||
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:
|
||||
return cvtString("Enabled: GPU converts colormap-textures.\n"
|
||||
"Disabled: CPU converts colormap-textures.\n\n"
|
||||
|
@ -167,10 +165,6 @@ const char* dialog_message(int ID, bool* updateText)
|
|||
case IDC_SPARSE_TEXTURE:
|
||||
return cvtString("Allows to reduce VRAM usage on the GPU.\n\n"
|
||||
"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:
|
||||
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
|
||||
|
|
|
@ -17,6 +17,11 @@
|
|||
#include "GSwxDialog.h"
|
||||
#include "gui/AppConfig.h"
|
||||
#include "GS/GSUtil.h"
|
||||
#include "HostDisplay.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Frontend/D3D11HostDisplay.h"
|
||||
#endif
|
||||
|
||||
using namespace GSSettingsDialog;
|
||||
|
||||
|
@ -317,7 +322,7 @@ RendererTab::RendererTab(wxWindow* parent)
|
|||
void RendererTab::UpdateBlendMode(GSRendererType renderer)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (renderer == GSRendererType::DX1011_HW)
|
||||
if (renderer == GSRendererType::DX11)
|
||||
{
|
||||
m_blend_mode_d3d11.first ->Show();
|
||||
m_blend_mode_d3d11.second->Show();
|
||||
|
@ -510,30 +515,25 @@ OSDTab::OSDTab(wxWindow* parent)
|
|||
const int space = wxSizerFlags().Border().GetBorderInPixels();
|
||||
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, "Font");
|
||||
PaddedBoxSizer<wxStaticBoxSizer> font_box(wxVERTICAL, this, "Visuals");
|
||||
auto* font_grid = new wxFlexGridSizer(2, space, space);
|
||||
font_grid->AddGrowableCol(1);
|
||||
|
||||
m_ui.addSpinAndLabel(font_grid, "Size:", "osd_fontsize", 1, 100, 25, -1, monitor_check);
|
||||
|
||||
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);
|
||||
m_ui.addSliderAndLabel(font_grid, "Scale:", "OsdScale", 50, 300, 100, -1);
|
||||
|
||||
font_box->Add(font_grid, 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");
|
||||
auto* log_grid = new wxFlexGridSizer(2, space, space);
|
||||
log_grid->AddGrowableCol(1);
|
||||
|
||||
m_ui.addSpinAndLabel(log_grid, "Timeout (seconds):", "osd_log_timeout", 2, 10, 4, -1, log_check);
|
||||
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 Messages", "OsdShowMessages", -1);
|
||||
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());
|
||||
tab_box->Add(log_box.outer, wxSizerFlags().Expand());
|
||||
|
@ -554,11 +554,9 @@ DebugTab::DebugTab(wxWindow* parent)
|
|||
{
|
||||
PaddedBoxSizer<wxStaticBoxSizer> debug_box(wxVERTICAL, this, "Debug");
|
||||
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, "Print GL error", "debug_opengl");
|
||||
#ifdef _WIN32
|
||||
m_ui.addCheckBox(debug_check_box, "D3D Debug Layer", "debug_d3d");
|
||||
#endif
|
||||
m_ui.addCheckBox(debug_check_box, "Use Debug Device", "UseDebugDevice");
|
||||
m_ui.addCheckBox(debug_check_box, "Dump GS data", "dump");
|
||||
|
||||
auto* debug_save_check_box = new wxWrapSizer(wxHORIZONTAL);
|
||||
|
@ -667,43 +665,47 @@ void Dialog::OnRendererChange(wxCommandEvent&)
|
|||
|
||||
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
|
||||
// make sure you haven't made a mistake initializing everything
|
||||
ASSERT(index < static_cast<int>(theApp.m_gs_renderers.size()) || index >= 0);
|
||||
|
||||
const GSRendererType type = static_cast<GSRendererType>(
|
||||
theApp.m_gs_renderers[index].value
|
||||
);
|
||||
|
||||
return type;
|
||||
const GSRendererType type = static_cast<GSRendererType>(theApp.m_gs_renderers[index].value);
|
||||
return (type == GSRendererType::Auto) ? GSUtil::GetPreferredRenderer() : type;
|
||||
}
|
||||
|
||||
void Dialog::RendererChange()
|
||||
{
|
||||
GSRendererType renderer = GetSelectedRendererType();
|
||||
std::string current;
|
||||
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;
|
||||
const GSRendererType renderer = GetSelectedRendererType();
|
||||
const std::string current_adapter(theApp.GetConfigS("Adapter"));
|
||||
|
||||
std::vector<std::string> adapters = GSUtil::GetAdapterList(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)
|
||||
HostDisplay::AdapterAndModeList list;
|
||||
switch (renderer)
|
||||
{
|
||||
if (adapter == current)
|
||||
new_sel = m_adapter_arr_string.Count();
|
||||
m_adapter_arr_string.Add(fromUTF8(adapter));
|
||||
#ifdef _WIN32
|
||||
case GSRendererType::DX11:
|
||||
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
|
||||
m_renderer_panel->UpdateBlendMode(renderer);
|
||||
|
||||
|
@ -714,9 +716,8 @@ void Dialog::RendererChange()
|
|||
void Dialog::Load()
|
||||
{
|
||||
m_ui.Load();
|
||||
GSRendererType renderer = GSRendererType(theApp.GetConfigI("Renderer"));
|
||||
if (renderer == GSRendererType::Undefined)
|
||||
renderer = GSUtil::GetPreferredRenderer();
|
||||
|
||||
const GSRendererType renderer = GSRendererType(theApp.GetConfigI("Renderer"));
|
||||
m_renderer_select->SetSelection(get_config_index(theApp.m_gs_renderers, static_cast<int>(renderer)));
|
||||
|
||||
RendererChange();
|
||||
|
@ -735,7 +736,7 @@ void Dialog::Save()
|
|||
// only save the adapter when it makes sense to
|
||||
// prevents changing the adapter, switching to another renderer and saving
|
||||
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_renderer_panel->Save();
|
||||
|
@ -762,14 +763,14 @@ void Dialog::Update()
|
|||
else
|
||||
{
|
||||
// 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;
|
||||
m_hacks_panel->m_is_native_res = !is_hw || !is_upscale;
|
||||
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_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_hacks_panel->DoUpdate();
|
||||
|
|
|
@ -187,7 +187,6 @@ namespace GSSettingsDialog
|
|||
wxChoice* m_renderer_select;
|
||||
wxChoice* m_adapter_select;
|
||||
wxChoice* m_bifilter_select;
|
||||
wxArrayString m_adapter_arr_string;
|
||||
RendererTab* m_renderer_panel;
|
||||
HacksTab* m_hacks_panel;
|
||||
DebugTab* m_debug_panel;
|
||||
|
|
15
pcsx2/Host.h
15
pcsx2/Host.h
|
@ -28,7 +28,12 @@ struct HostKeyEvent
|
|||
{
|
||||
NoEvent = 0,
|
||||
KeyPressed = 1,
|
||||
KeyReleased = 2
|
||||
KeyReleased = 2,
|
||||
MousePressed = 3,
|
||||
MouseReleased = 4,
|
||||
MouseWheelDown = 5,
|
||||
MouseWheelUp = 6,
|
||||
MouseMove = 7,
|
||||
};
|
||||
|
||||
Type type;
|
||||
|
@ -44,6 +49,14 @@ namespace Host
|
|||
/// Reads a resource file file from the resources directory as a string.
|
||||
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.
|
||||
void ReportErrorAsync(const std::string_view& title, const std::string_view& message);
|
||||
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 <wx/datetime.h>
|
||||
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "GS.h"
|
||||
#include "Gif_Unit.h"
|
||||
#include "MTVU.h"
|
||||
#include "Elfheader.h"
|
||||
#include "PerformanceMetrics.h"
|
||||
#include "gui/Dialogs/ModalPopups.h"
|
||||
|
||||
#include "common/WindowInfo.h"
|
||||
extern WindowInfo g_gs_window_info;
|
||||
#include "Host.h"
|
||||
#include "HostDisplay.h"
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
#include "gui/Dialogs/ModalPopups.h"
|
||||
#endif
|
||||
|
||||
// Uncomment this to enable profiling of the GS RingBufferCopy function.
|
||||
//#define PCSX2_GSRING_SAMPLING_STATS
|
||||
|
@ -48,8 +52,6 @@ using namespace Threading;
|
|||
// =====================================================================================================
|
||||
|
||||
alignas(32) MTGS_BufferedData RingBuffer;
|
||||
extern bool renderswitch;
|
||||
std::atomic_bool init_gspanel = true;
|
||||
|
||||
|
||||
#ifdef RINGBUF_DEBUG_STACK
|
||||
|
@ -68,42 +70,6 @@ SysMtgsThread::SysMtgsThread()
|
|||
// 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()
|
||||
{
|
||||
m_Opened = false;
|
||||
|
@ -135,7 +101,6 @@ SysMtgsThread::~SysMtgsThread()
|
|||
|
||||
void SysMtgsThread::OnResumeReady()
|
||||
{
|
||||
PerformanceMetrics::Reset();
|
||||
m_sem_OpenDone.Reset();
|
||||
}
|
||||
|
||||
|
@ -235,19 +200,17 @@ void SysMtgsThread::OpenGS()
|
|||
if (m_Opened)
|
||||
return;
|
||||
|
||||
if (init_gspanel)
|
||||
sApp.OpenGsPanel();
|
||||
|
||||
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!");
|
||||
|
||||
GSsetVsync(EmuConfig.GS.GetVsync());
|
||||
|
||||
m_Opened = true;
|
||||
m_Opened = GSopen(EmuConfig.GS, EmuConfig.GS.Renderer, RingBuffer.Regs);
|
||||
m_sem_OpenDone.Post();
|
||||
|
||||
if (!m_Opened)
|
||||
{
|
||||
Console.Error("GS failed to open");
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
// to avoid it.
|
||||
|
||||
m_sem_event.WaitWithoutYield();
|
||||
m_sem_event.Wait();
|
||||
StateCheckInThread();
|
||||
busy.Acquire();
|
||||
|
||||
|
@ -480,14 +443,20 @@ void SysMtgsThread::ExecuteTaskInThread()
|
|||
if (m_VsyncSignalListener.exchange(false))
|
||||
m_sem_Vsync.Post();
|
||||
|
||||
PerformanceMetrics::Update();
|
||||
|
||||
// Do not StateCheckInThread() here
|
||||
// Otherwise we could pause while there's still data in the queue
|
||||
// Which could make the MTVU thread wait forever for it to empty
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_RINGTYPE_ASYNC_CALL:
|
||||
{
|
||||
AsyncCallType* const func = (AsyncCallType*)tag.pointer;
|
||||
(*func)();
|
||||
delete func;
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_RINGTYPE_FRAMESKIP:
|
||||
MTGS_LOG("(MTGS Packet Read) ringtype=Frameskip");
|
||||
_gs_ResetFrameskip();
|
||||
|
@ -586,12 +555,14 @@ void SysMtgsThread::ExecuteTaskInThread()
|
|||
|
||||
void SysMtgsThread::CloseGS()
|
||||
{
|
||||
if (!m_Opened || GSDump::isRunning)
|
||||
if (!m_Opened)
|
||||
return;
|
||||
#ifndef PCSX2_CORE
|
||||
if (GSDump::isRunning)
|
||||
return;
|
||||
#endif
|
||||
m_Opened = false;
|
||||
GSclose();
|
||||
if (init_gspanel)
|
||||
sApp.CloseGsPanel();
|
||||
}
|
||||
|
||||
void SysMtgsThread::OnSuspendInThread()
|
||||
|
@ -893,10 +864,10 @@ void SysMtgsThread::SendGameCRC(u32 crc)
|
|||
SendSimplePacket(GS_RINGTYPE_CRC, crc, 0, 0);
|
||||
}
|
||||
|
||||
void SysMtgsThread::WaitForOpen()
|
||||
bool SysMtgsThread::WaitForOpen()
|
||||
{
|
||||
if (m_Opened)
|
||||
return;
|
||||
return true;
|
||||
Resume();
|
||||
|
||||
// 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
|
||||
// GS can be very stubborned, especially in debug mode builds).
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
if (!m_sem_OpenDone.Wait(wxTimeSpan(0, 0, 2, 0)))
|
||||
{
|
||||
RethrowException();
|
||||
|
@ -917,6 +889,16 @@ void SysMtgsThread::WaitForOpen()
|
|||
}
|
||||
|
||||
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)
|
||||
|
@ -932,3 +914,89 @@ void SysMtgsThread::Freeze(FreezeAction mode, MTGS_FreezeData& data)
|
|||
WaitForOpen();
|
||||
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 "Config.h"
|
||||
#include "Host.h"
|
||||
|
||||
#include "svnrev.h"
|
||||
|
||||
#include "gui/ConsoleLogger.h"
|
||||
|
||||
#include <wx/ffile.h>
|
||||
#include <map>
|
||||
|
||||
|
@ -526,7 +525,7 @@ s32 FileMemoryCard::Save(uint slot, const u8* src, u32 adr, int size)
|
|||
{
|
||||
wxString 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();
|
||||
}
|
||||
return 1;
|
||||
|
|
|
@ -22,11 +22,13 @@
|
|||
#include "common/StringUtil.h"
|
||||
#include "Config.h"
|
||||
#include "GS.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "CDVD/CDVDaccess.h"
|
||||
#include "MemoryCardFile.h"
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
#include "gui/AppConfig.h"
|
||||
#include "GS/GS.h"
|
||||
#endif
|
||||
|
||||
namespace EmuFolders
|
||||
|
@ -245,6 +247,152 @@ void Pcsx2Config::CpuOptions::LoadSave(SettingsWrapper& 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)
|
||||
{
|
||||
SettingsWrapSection("EmuCore/GS");
|
||||
|
@ -258,7 +406,7 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
|
|||
SettingsWrapEntry(FrameSkipEnable);
|
||||
wrap.EnumEntry(CURRENT_SETTINGS_SECTION, "VsyncEnable", VsyncEnable, NULL, VsyncEnable);
|
||||
|
||||
SettingsWrapEntry(LimitScalar);
|
||||
// LimitScalar is set at runtime.
|
||||
SettingsWrapEntry(FramerateNTSC);
|
||||
SettingsWrapEntry(FrameratePAL);
|
||||
|
||||
|
@ -266,48 +414,181 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
|
|||
SettingsWrapEntry(FramesToSkip);
|
||||
|
||||
#ifdef PCSX2_CORE
|
||||
static const char* AspectRatioNames[] =
|
||||
{
|
||||
"Stretch",
|
||||
"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);
|
||||
|
||||
// These are loaded from GSWindow in wx.
|
||||
SettingsWrapEnumEx(AspectRatio, "AspectRatio", AspectRatioNames);
|
||||
SettingsWrapEnumEx(FMVAspectRatioSwitch, "FMVAspectRatioSwitch", FMVAspectRatioSwitchNames);
|
||||
SettingsWrapEntry(Zoom);
|
||||
SettingsWrapEntry(StretchY);
|
||||
SettingsWrapEntry(OffsetX);
|
||||
SettingsWrapEntry(OffsetY);
|
||||
#endif
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
if (wrap.IsLoading())
|
||||
ReloadIniSettings();
|
||||
#else
|
||||
LoadSaveIniSettings(wrap);
|
||||
#endif
|
||||
}
|
||||
|
||||
int Pcsx2Config::GSOptions::GetVsync() const
|
||||
#ifdef PCSX2_CORE
|
||||
void Pcsx2Config::GSOptions::LoadSaveIniSettings(SettingsWrapper& wrap)
|
||||
{
|
||||
if (EmuConfig.LimiterMode == LimiterModeType::Turbo || !FrameLimitEnable)
|
||||
return 0;
|
||||
SettingsWrapSection("EmuCore/GS");
|
||||
|
||||
// D3D only support a boolean state. OpenGL waits a number of vsync
|
||||
// interrupt (negative value for late vsync).
|
||||
switch (VsyncEnable)
|
||||
#define GSSettingInt(var) SettingsWrapBitfield(var)
|
||||
#define GSSettingIntEx(var, name) SettingsWrapBitfieldEx(var, name)
|
||||
#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:
|
||||
return -1;
|
||||
case VsyncMode::Off:
|
||||
return 0;
|
||||
case VsyncMode::On:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
Console.WriteLn("Vsync is OFF");
|
||||
return VsyncMode::Off;
|
||||
}
|
||||
|
||||
Console.WriteLn("Vsync is %s", GS.VsyncEnable == VsyncMode::Off ? "OFF" : (GS.VsyncEnable == VsyncMode::Adaptive ? "ADAPTIVE" : "ON"));
|
||||
return GS.VsyncEnable;
|
||||
}
|
||||
|
||||
Pcsx2Config::SPU2Options::SPU2Options()
|
||||
|
@ -348,6 +629,7 @@ static const char* const tbl_GamefixNames[] =
|
|||
"FpuMul",
|
||||
"FpuNegDiv",
|
||||
"GoemonTlb",
|
||||
"SoftwareRendererFMV",
|
||||
"SkipMPEG",
|
||||
"OPHFlag",
|
||||
"EETiming",
|
||||
|
@ -422,6 +704,9 @@ void Pcsx2Config::GamefixOptions::Set(GamefixId id, bool enabled)
|
|||
case Fix_EETiming:
|
||||
EETimingHack = enabled;
|
||||
break;
|
||||
case Fix_SoftwareRendererFMV:
|
||||
SoftwareRendererFMVHack = enabled;
|
||||
break;
|
||||
case Fix_SkipMpeg:
|
||||
SkipMPEGHack = enabled;
|
||||
break;
|
||||
|
@ -471,6 +756,8 @@ bool Pcsx2Config::GamefixOptions::Get(GamefixId id) const
|
|||
return XgKickHack;
|
||||
case Fix_EETiming:
|
||||
return EETimingHack;
|
||||
case Fix_SoftwareRendererFMV:
|
||||
return SoftwareRendererFMVHack;
|
||||
case Fix_SkipMpeg:
|
||||
return SkipMPEGHack;
|
||||
case Fix_OPHFlag:
|
||||
|
@ -505,6 +792,7 @@ void Pcsx2Config::GamefixOptions::LoadSave(SettingsWrapper& wrap)
|
|||
SettingsWrapBitBool(FpuNegDivHack);
|
||||
SettingsWrapBitBool(XgKickHack);
|
||||
SettingsWrapBitBool(EETimingHack);
|
||||
SettingsWrapBitBool(SoftwareRendererFMVHack);
|
||||
SettingsWrapBitBool(SkipMPEGHack);
|
||||
SettingsWrapBitBool(OPHFlagHack);
|
||||
SettingsWrapBitBool(DMABusyHack);
|
||||
|
@ -765,6 +1053,8 @@ void Pcsx2Config::CopyConfig(const Pcsx2Config& cfg)
|
|||
#ifdef __WXMSW__
|
||||
McdCompressNTFS = cfg.McdCompressNTFS;
|
||||
#endif
|
||||
|
||||
LimiterMode = cfg.LimiterMode;
|
||||
}
|
||||
|
||||
void EmuFolders::SetDefaults()
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "GS.h" // GSosdlog
|
||||
#include "gui/App.h" // GetRGBA
|
||||
#include "gui/ConsoleLogger.h"
|
||||
#include "Host.h"
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
|
@ -35,7 +36,7 @@ namespace inputRec
|
|||
recordingConLog(fmt::format("[REC]: {}\n", log));
|
||||
|
||||
// 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)
|
||||
|
|
|
@ -104,8 +104,10 @@ void SysCoreThread::OnStart()
|
|||
|
||||
void SysCoreThread::OnSuspendInThread()
|
||||
{
|
||||
TearDownSystems(static_cast<SystemsMask>(-1)); // All systems
|
||||
GetMTGS().Suspend();
|
||||
// We deliberately don't tear down GS here, because the state isn't saved.
|
||||
// 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()
|
||||
|
@ -183,7 +185,19 @@ void SysCoreThread::ApplySettings(const Pcsx2Config& src)
|
|||
m_resetProfilers = (src.Profiler != EmuConfig.Profiler);
|
||||
m_resetVsyncTimers = (src.GS != EmuConfig.GS);
|
||||
|
||||
const bool gs_settings_changed = !src.GS.OptionsAreEqual(EmuConfig.GS);
|
||||
|
||||
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)
|
||||
{
|
||||
PerformanceMetrics::SetCPUThreadTimer(Common::ThreadCPUTimer::GetForCallingThread());
|
||||
PerformanceMetrics::Reset();
|
||||
|
||||
GetMTGS().WaitForOpen();
|
||||
if (systemsToReinstate & System_DEV9) DEV9open();
|
||||
|
|
|
@ -526,6 +526,7 @@ public:
|
|||
void OpenGsPanel();
|
||||
void CloseGsPanel();
|
||||
void OnGsFrameClosed(wxWindowID id);
|
||||
void OnGsFrameDestroyed(wxWindowID id);
|
||||
void OnMainFrameClosed(wxWindowID id);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
|
|
@ -567,9 +567,6 @@ void AppCoreThread::ApplySettings(const Pcsx2Config& src)
|
|||
{
|
||||
_parent::ApplySettings(fixup);
|
||||
}
|
||||
|
||||
if (m_ExecMode >= ExecMode_Paused)
|
||||
GSsetVsync(EmuConfig.GS.GetVsync());
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
|
|
@ -18,9 +18,26 @@
|
|||
#include "common/Console.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "Host.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/AppConfig.h"
|
||||
|
@ -89,3 +106,104 @@ void Host::ReportErrorAsync(const std::string_view& title, const std::string_vie
|
|||
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,
|
||||
// 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.)
|
||||
extern uint eecount_on_last_vdec;
|
||||
extern bool FMVstarted;
|
||||
extern bool EnableFMV;
|
||||
extern bool renderswitch;
|
||||
|
||||
void Pcsx2App::LogicalVsync()
|
||||
{
|
||||
if( AppRpc_TryInvokeAsync( &Pcsx2App::LogicalVsync ) ) 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) )
|
||||
PADupdate(0);
|
||||
|
||||
|
@ -763,8 +728,6 @@ void Pcsx2App::OpenGsPanel()
|
|||
gsFrame->SetSize( oldsize );
|
||||
}
|
||||
|
||||
pxAssertDev( !gsopen_done, "GS must be closed prior to opening a new Gs Panel!" );
|
||||
|
||||
gsFrame->ShowFullScreen(g_Conf->GSWindow.IsFullscreen);
|
||||
wxApp::ProcessPendingEvents();
|
||||
|
||||
|
@ -784,10 +747,19 @@ void Pcsx2App::OpenGsPanel()
|
|||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Pcsx2App::CloseGsPanel()
|
||||
{
|
||||
if (AppRpc_TryInvoke(&Pcsx2App::CloseGsPanel))
|
||||
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)
|
||||
|
@ -803,6 +775,16 @@ void Pcsx2App::OnGsFrameClosed(wxWindowID id)
|
|||
// right now there's no way to resume from suspend without GUI.
|
||||
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
|
||||
// Disable recording controls that only make sense if the game is running
|
||||
sMainFrame.enableRecordingMenuItem(MenuId_Recording_FrameAdvance, false);
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
#include "MainFrame.h"
|
||||
#include "ConsoleLogger.h"
|
||||
#include "MSWstuff.h"
|
||||
#include "Host.h"
|
||||
|
||||
#include "common/Console.h"
|
||||
#include "common/IniInterface.h"
|
||||
#include "common/SafeArray.inl"
|
||||
#include "Dialogs/LogOptionsDialog.h"
|
||||
#include "DebugTools/Debug.h"
|
||||
|
||||
#include <wx/textfile.h>
|
||||
|
||||
wxDECLARE_EVENT(pxEvt_SetTitleText, wxCommandEvent);
|
||||
|
@ -1266,13 +1266,8 @@ void Pcsx2App::DisableWindowLogging() const
|
|||
|
||||
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)
|
||||
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());
|
||||
}
|
||||
|
||||
void OSDmonitor(ConsoleColors color, const std::string key, const std::string value);
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "common/EmbeddedImage.h"
|
||||
#include "gui/Resources/NoIcon.h"
|
||||
#include "GS.h"
|
||||
#include "HostDisplay.h"
|
||||
|
||||
#include "PathDefs.h"
|
||||
#include "gui/AppConfig.h"
|
||||
|
@ -199,6 +200,7 @@ void Dialogs::GSDumpDialog::CloseDump(wxCommandEvent& event)
|
|||
m_gif_packet->DeleteAllItems();
|
||||
m_debug_mode->SetValue(false);
|
||||
m_run->Enable();
|
||||
m_settings->Enable();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -219,6 +221,7 @@ void Dialogs::GSDumpDialog::RunDump(wxCommandEvent& event)
|
|||
return;
|
||||
}
|
||||
m_run->Disable();
|
||||
m_settings->Disable();
|
||||
m_debug_mode->Enable();
|
||||
m_thread->m_renderer = m_renderer_overrides->GetSelection();
|
||||
m_thread->Start();
|
||||
|
@ -278,6 +281,9 @@ void Dialogs::GSDumpDialog::ToVSync(wxCommandEvent& event)
|
|||
void Dialogs::GSDumpDialog::OpenSettings(wxCommandEvent& event)
|
||||
{
|
||||
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)
|
||||
|
@ -654,10 +660,8 @@ void Dialogs::GSDumpDialog::ProcessDumpEvent(const GSData& event, char* regs)
|
|||
case VSync:
|
||||
{
|
||||
GSvsync((*((int*)(regs + 4096)) & 0x2000) > 0 ? (u8)1 : (u8)0);
|
||||
PerformanceMetrics::Update();
|
||||
g_FrameCount++;
|
||||
Pcsx2App* app = (Pcsx2App*)wxApp::GetInstance();
|
||||
if (app)
|
||||
PerformanceMetrics::Update();
|
||||
break;
|
||||
}
|
||||
case ReadFIFO2:
|
||||
|
@ -788,8 +792,7 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
|
|||
g_FrameCount = 0;
|
||||
}
|
||||
|
||||
GSsetBaseMem((u8*)regs);
|
||||
if (GSopen2(g_gs_window_info, (renderer_override<<24)) != 0)
|
||||
if (!GSopen(g_Conf->EmuOptions.GS, static_cast<GSRendererType>(renderer_override), (u8*)regs))
|
||||
{
|
||||
OnStop();
|
||||
return;
|
||||
|
@ -801,7 +804,6 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
|
|||
GSDump::isRunning = false;
|
||||
GSvsync(1);
|
||||
GSreset();
|
||||
GSsetBaseMem((u8*)regs);
|
||||
GSfreeze(FreezeAction::Load, &fd);
|
||||
|
||||
size_t i = 0;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "App.h"
|
||||
#include "GSFrame.h"
|
||||
#include "AppAccelerators.h"
|
||||
#include "AppHost.h"
|
||||
#include "AppSaveStates.h"
|
||||
#include "Counters.h"
|
||||
#include "GS.h"
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include "MSWstuff.h"
|
||||
#include "PAD/Gamepad.h"
|
||||
#include "PerformanceMetrics.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "gui/Dialogs/ModalPopups.h"
|
||||
|
||||
|
@ -59,8 +61,6 @@
|
|||
|
||||
static const KeyAcceleratorCode FULLSCREEN_TOGGLE_ACCELERATOR_GSPANEL=KeyAcceleratorCode( WXK_RETURN ).Alt();
|
||||
|
||||
extern std::atomic_bool init_gspanel;
|
||||
|
||||
void GSPanel::InitDefaultAccelerators()
|
||||
{
|
||||
// Note: these override GlobalAccels ( Pcsx2App::InitDefaultGlobalAccelerators() )
|
||||
|
@ -211,7 +211,6 @@ GSPanel::GSPanel( wxWindow* parent )
|
|||
m_CursorShown = false;
|
||||
}
|
||||
|
||||
Bind(wxEVT_CLOSE_WINDOW, &GSPanel::OnCloseWindow, this);
|
||||
Bind(wxEVT_SIZE, &GSPanel::OnResize, this);
|
||||
Bind(wxEVT_KEY_UP, &GSPanel::OnKeyDownOrUp, this);
|
||||
Bind(wxEVT_KEY_DOWN, &GSPanel::OnKeyDownOrUp, this);
|
||||
|
@ -259,13 +258,40 @@ void GSPanel::DoShowMouse()
|
|||
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()
|
||||
{
|
||||
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)
|
||||
ret.type = WindowInfo::Type::Win32;
|
||||
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__)
|
||||
GtkWidget* child_window = GTK_WIDGET(GetHandle());
|
||||
|
||||
|
@ -320,20 +346,6 @@ std::optional<WindowInfo> GSPanel::GetWindowInfo()
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -362,18 +374,7 @@ void GSPanel::OnResize(wxSizeEvent& event)
|
|||
g_gs_window_info.surface_height = height;
|
||||
g_gs_window_info.surface_scale = scale;
|
||||
|
||||
GSResizeWindow(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.
|
||||
Host::GSWindowResized(width, height);
|
||||
}
|
||||
|
||||
void GSPanel::OnMouseEvent( wxMouseEvent& evt )
|
||||
|
@ -722,10 +723,24 @@ GSFrame::GSFrame( const wxString& title)
|
|||
|
||||
void GSFrame::OnCloseWindow(wxCloseEvent& evt)
|
||||
{
|
||||
// see GSPanel::OnCloseWindow
|
||||
init_gspanel = false;
|
||||
// if a gs dump is running, it cleans up the window once it's hidden.
|
||||
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() );
|
||||
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)
|
||||
|
@ -838,12 +853,6 @@ void GSFrame::AppStatusEvent_OnSettingsApplied()
|
|||
if (!IsFullScreen() && !IsMaximized())
|
||||
SetClientSize(g_Conf->GSWindow.WindowSize);
|
||||
Refresh();
|
||||
|
||||
if( g_Conf->GSWindow.CloseOnEsc )
|
||||
{
|
||||
if (IsShown() && !gsopen_done)
|
||||
Show( false );
|
||||
}
|
||||
}
|
||||
|
||||
GSPanel* GSFrame::GetViewport()
|
||||
|
@ -866,19 +875,9 @@ void GSFrame::OnUpdateTitle( wxTimerEvent& evt )
|
|||
cpuUsage.Write(L" | GS: %3.0f%%", PerformanceMetrics::GetGSThreadUsage());
|
||||
|
||||
if (THREAD_VU1)
|
||||
{
|
||||
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__
|
||||
// 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.
|
||||
|
@ -887,10 +886,6 @@ void GSFrame::OnUpdateTitle( wxTimerEvent& evt )
|
|||
|
||||
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;
|
||||
|
||||
if( g_Conf->EmuOptions.GS.FrameLimitEnable )
|
||||
|
@ -900,6 +895,7 @@ void GSFrame::OnUpdateTitle( wxTimerEvent& evt )
|
|||
case LimiterModeType::Nominal: limiterStr = templates.LimiterNormal; break;
|
||||
case LimiterModeType::Turbo: limiterStr = templates.LimiterTurbo; 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;
|
||||
#endif
|
||||
|
||||
std::string gsStats;
|
||||
GSgetTitleStats(gsStats);
|
||||
|
||||
title.Replace(L"${slot}", pxsFmt(L"%d", States_GetCurrentSlot()));
|
||||
title.Replace(L"${limiter}", limiterStr);
|
||||
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"${omodef}", omodef);
|
||||
title.Replace(L"${omodei}", omodei);
|
||||
title.Replace(L"${gsdx}", fromUTF8(gsDest));
|
||||
title.Replace(L"${gsdx}", StringUtil::UTF8StringToWxString(gsStats));
|
||||
title.Replace(L"${videomode}", ReportVideoMode());
|
||||
if (CoreThread.IsPaused() && !GSDump::isRunning)
|
||||
title = templates.Paused + title;
|
||||
|
|
|
@ -61,7 +61,6 @@ public:
|
|||
protected:
|
||||
void AppStatusEvent_OnSettingsApplied();
|
||||
|
||||
void OnCloseWindow( wxCloseEvent& evt );
|
||||
void OnResize(wxSizeEvent& event);
|
||||
void OnMouseEvent( wxMouseEvent& evt );
|
||||
void OnHideMouseTimeout( wxTimerEvent& evt );
|
||||
|
@ -118,6 +117,7 @@ public:
|
|||
|
||||
protected:
|
||||
void OnCloseWindow( wxCloseEvent& evt );
|
||||
void OnDestroyWindow( wxWindowDestroyEvent& evt );
|
||||
void OnMove( wxMoveEvent& evt );
|
||||
void OnResize( wxSizeEvent& evt );
|
||||
void OnFocus( wxFocusEvent& evt );
|
||||
|
|
|
@ -35,9 +35,6 @@
|
|||
#include "SPU2/spu2.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
|
||||
|
||||
|
||||
|
@ -92,15 +89,15 @@ namespace Implementations
|
|||
if (!g_Conf->EmuOptions.GS.FrameLimitEnable)
|
||||
{
|
||||
g_Conf->EmuOptions.GS.FrameLimitEnable = true;
|
||||
EmuConfig.LimiterMode = LimiterModeType::Turbo;
|
||||
g_Conf->EmuOptions.LimiterMode = LimiterModeType::Turbo;
|
||||
OSDlog(Color_StrongRed, true, "(FrameLimiter) Turbo + FrameLimit ENABLED.");
|
||||
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");
|
||||
g_Conf->EmuOptions.GS.FrameSkipEnable = true;
|
||||
|
@ -113,9 +110,9 @@ namespace Implementations
|
|||
}
|
||||
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.");
|
||||
g_Conf->EmuOptions.GS.FrameSkipEnable = true;
|
||||
|
@ -127,8 +124,6 @@ namespace Implementations
|
|||
}
|
||||
}
|
||||
|
||||
gsUpdateFrequency(g_Conf->EmuOptions);
|
||||
|
||||
pauser.AllowResume();
|
||||
}
|
||||
|
||||
|
@ -141,20 +136,18 @@ namespace Implementations
|
|||
// out a better consistency approach... -air
|
||||
|
||||
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.");
|
||||
}
|
||||
else
|
||||
{
|
||||
EmuConfig.LimiterMode = LimiterModeType::Slomo;
|
||||
g_Conf->EmuOptions.LimiterMode = LimiterModeType::Slomo;
|
||||
OSDlog(Color_StrongRed, true, "(FrameLimiter) SlowMotion ENABLED.");
|
||||
g_Conf->EmuOptions.GS.FrameLimitEnable = true;
|
||||
}
|
||||
|
||||
gsUpdateFrequency(g_Conf->EmuOptions);
|
||||
|
||||
pauser.AllowResume();
|
||||
}
|
||||
|
||||
|
@ -165,7 +158,7 @@ namespace Implementations
|
|||
OSDlog(Color_StrongRed, true, "(FrameLimiter) %s.", g_Conf->EmuOptions.GS.FrameLimitEnable ? "ENABLED" : "DISABLED");
|
||||
|
||||
// Turbo/Slowmo don't make sense when framelimiter is toggled
|
||||
EmuConfig.LimiterMode = LimiterModeType::Nominal;
|
||||
g_Conf->EmuOptions.LimiterMode = LimiterModeType::Nominal;
|
||||
|
||||
pauser.AllowResume();
|
||||
}
|
||||
|
@ -196,15 +189,22 @@ namespace Implementations
|
|||
// saved until shutdown, but it matches the behavior pre-settings-move.
|
||||
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);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
EmuConfig.GS.OffsetX = x;
|
||||
EmuConfig.GS.OffsetY = y;
|
||||
g_Conf->EmuOptions.GS.OffsetX = x;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -237,8 +237,9 @@ namespace Implementations
|
|||
{
|
||||
if (zoom <= 0)
|
||||
return;
|
||||
EmuConfig.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);
|
||||
}
|
||||
|
||||
|
@ -259,8 +260,9 @@ namespace Implementations
|
|||
{
|
||||
if (zoom < 0)
|
||||
return;
|
||||
EmuConfig.GS.Zoom = zoom;
|
||||
g_Conf->EmuOptions.GS.Zoom = zoom;
|
||||
EmuConfig.GS.Zoom = zoom;
|
||||
GSConfig.Zoom = zoom;
|
||||
|
||||
if (zoom == 0)
|
||||
OSDlog(Color_StrongBlue, true, "(GSwindow) Zoom: 0 (auto, no black bars)");
|
||||
|
@ -387,15 +389,7 @@ namespace Implementations
|
|||
{
|
||||
reentrant = true;
|
||||
ScopedCoreThreadPause paused_core;
|
||||
freezeData fP = {0, nullptr};
|
||||
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;
|
||||
GetMTGS().ToggleSoftwareRendering();
|
||||
paused_core.AllowResume();
|
||||
reentrant = false;
|
||||
}
|
||||
|
|
|
@ -36,8 +36,6 @@
|
|||
#include "Recording/InputRecordingControls.h"
|
||||
#endif
|
||||
|
||||
extern std::atomic_bool init_gspanel;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
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
|
||||
// when closing the emulator
|
||||
init_gspanel = false;
|
||||
//init_gspanel = false;
|
||||
|
||||
if (IsBeingDeleted())
|
||||
return;
|
||||
|
|
|
@ -47,8 +47,6 @@
|
|||
|
||||
using namespace Dialogs;
|
||||
|
||||
extern std::atomic_bool init_gspanel;
|
||||
|
||||
void MainEmuFrame::Menu_SysSettings_Click(wxCommandEvent& event)
|
||||
{
|
||||
AppOpenModalDialog<SysConfigDialog>(wxEmptyString, this);
|
||||
|
@ -88,29 +86,30 @@ void MainEmuFrame::Menu_PADSettings_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();
|
||||
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);
|
||||
delete[] fP.data;
|
||||
ScopedCoreThreadPause pauser(static_cast<SystemsMask>(System_USB | System_PAD));
|
||||
wxGetApp().SysApplySettings();
|
||||
}
|
||||
if (need_shutdown)
|
||||
GetMTGS().Suspend(true);
|
||||
init_gspanel = true;
|
||||
paused_core.AllowResume();
|
||||
else
|
||||
{
|
||||
wxGetApp().SysApplySettings();
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
|
|
@ -48,6 +48,10 @@ Panels::GameFixesPanel::GameFixesPanel( wxWindow* parent )
|
|||
_("Preload TLB hack to avoid tlb miss on Goemon"),
|
||||
wxEmptyString
|
||||
},
|
||||
{
|
||||
_("Switch to Software renderer for FMVs"),
|
||||
wxEmptyString
|
||||
},
|
||||
{
|
||||
_("Skip MPEG hack - Skips videos/FMVs in games to avoid game hanging/freezes."),
|
||||
wxEmptyString
|
||||
|
|
|
@ -35,13 +35,13 @@
|
|||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<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\baseclasses;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\libpng;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\glad\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>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
|
||||
|
@ -303,6 +303,9 @@
|
|||
<ClCompile Include="DEV9\Win32\DEV9WinConfig.cpp" />
|
||||
<ClCompile Include="DEV9\net.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="Gif_Logger.cpp" />
|
||||
<ClCompile Include="Gif_Unit.cpp" />
|
||||
|
@ -332,6 +335,7 @@
|
|||
<ClCompile Include="gui\wxAppWithHelpers.cpp" />
|
||||
<ClCompile Include="gui\wxSettingsInterface.cpp" />
|
||||
<ClCompile Include="Host.cpp" />
|
||||
<ClCompile Include="HostDisplay.cpp" />
|
||||
<ClCompile Include="IopGte.cpp" />
|
||||
<ClCompile Include="PINE.cpp" />
|
||||
<ClCompile Include="FW.cpp" />
|
||||
|
@ -483,7 +487,6 @@
|
|||
<ClCompile Include="GS\GSLocalMemory.cpp" />
|
||||
<ClCompile Include="GS\GSLzma.cpp" />
|
||||
<ClCompile Include="GS\GSPerfMon.cpp" />
|
||||
<ClCompile Include="GS\Renderers\Common\GSOsdManager.cpp" />
|
||||
<ClCompile Include="GS\GSPng.cpp" />
|
||||
<ClCompile Include="GS\GSRingHeap.cpp" />
|
||||
<ClCompile Include="GS\Renderers\SW\GSRasterizer.cpp" />
|
||||
|
@ -742,6 +745,9 @@
|
|||
<ClInclude Include="DEV9\smap.h" />
|
||||
<ClInclude Include="DEV9\Win32\pcap_io_win32_funcs.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="Gif_Unit.h" />
|
||||
<ClInclude Include="GS\Renderers\DX11\D3D.h" />
|
||||
|
@ -768,6 +774,7 @@
|
|||
<ClInclude Include="gui\wxAppWithHelpers.h" />
|
||||
<ClInclude Include="gui\wxSettingsInterface.h" />
|
||||
<ClInclude Include="Host.h" />
|
||||
<ClInclude Include="HostDisplay.h" />
|
||||
<ClInclude Include="IopGte.h" />
|
||||
<ClInclude Include="PINE.h" />
|
||||
<ClInclude Include="FW.h" />
|
||||
|
@ -843,7 +850,6 @@
|
|||
<ClInclude Include="GS\GSLocalMemory.h" />
|
||||
<ClInclude Include="GS\GSLzma.h" />
|
||||
<ClInclude Include="GS\GSPerfMon.h" />
|
||||
<ClInclude Include="GS\Renderers\Common\GSOsdManager.h" />
|
||||
<ClInclude Include="GS\GSPng.h" />
|
||||
<ClInclude Include="GS\GSRingHeap.h" />
|
||||
<ClInclude Include="GS\Renderers\SW\GSRasterizer.h" />
|
||||
|
@ -1107,9 +1113,6 @@
|
|||
<ProjectReference Include="$(SolutionDir)3rdparty\fmt\fmt.vcxproj">
|
||||
<Project>{449ad25e-424a-4714-babc-68706cdcc33b}</Project>
|
||||
</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">
|
||||
<Project>{bc236261-77e8-4567-8d09-45cd02965eb6}</Project>
|
||||
</ProjectReference>
|
||||
|
@ -1158,6 +1161,9 @@
|
|||
<ProjectReference Include="$(SolutionDir)3rdparty\rapidyaml\ryml.vcxproj">
|
||||
<Project>{de9653b6-17dd-356a-9ee0-28a731772587}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)3rdparty\imgui\imgui.vcxproj">
|
||||
<Project>{88fb34ec-845e-4f21-a552-f1573b9ed167}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\common\common.vcxproj">
|
||||
<Project>{4639972e-424e-4e13-8b07-ca403c481346}</Project>
|
||||
</ProjectReference>
|
||||
|
|
|
@ -1583,9 +1583,6 @@
|
|||
<ClCompile Include="GS\Renderers\Common\GSFunctionMap.cpp">
|
||||
<Filter>System\Ps2\GS\Renderers\Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GS\Renderers\Common\GSOsdManager.cpp">
|
||||
<Filter>System\Ps2\GS\Renderers\Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GS\GSCapture.cpp">
|
||||
<Filter>System\Ps2\GS\Window</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1649,6 +1646,21 @@
|
|||
<ClCompile Include="SPU2\SndOut_Cubeb.cpp">
|
||||
<Filter>System\Ps2\SPU2</Filter>
|
||||
</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">
|
||||
<Filter>Host</Filter>
|
||||
</ClCompile>
|
||||
|
@ -2689,9 +2701,6 @@
|
|||
<ClInclude Include="GS\Renderers\Common\GSFunctionMap.h">
|
||||
<Filter>System\Ps2\GS\Renderers\Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GS\Renderers\Common\GSOsdManager.h">
|
||||
<Filter>System\Ps2\GS\Renderers\Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GS\GSCapture.h">
|
||||
<Filter>System\Ps2\GS\Window</Filter>
|
||||
</ClInclude>
|
||||
|
@ -2755,6 +2764,21 @@
|
|||
<ClInclude Include="Host.h">
|
||||
<Filter>Host</Filter>
|
||||
</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>
|
||||
<ResourceCompile Include="windows\wxResources.rc">
|
||||
|
|
Loading…
Reference in New Issue