Compare commits

..

3 commits

Author SHA1 Message Date
Henrik Rydgård
a989b08648 Plumb through a parameter for creating framebuffers of different color formats 2023-02-03 13:39:51 +01:00
Henrik Rydgård
6c9963bd26 Remove a step of confusing indentation in TextureCacheCommon::LoadClut 2023-02-03 13:26:42 +01:00
Henrik Rydgård
49f830d88c Small refactor 2023-02-03 13:21:20 +01:00
1142 changed files with 52938 additions and 112329 deletions

View file

@ -139,6 +139,12 @@ jobs:
cxx: clang++ cxx: clang++
args: cd android && ./ab.sh -j2 APP_ABI=armeabi-v7a UNITTEST=1 HEADLESS=1 args: cd android && ./ab.sh -j2 APP_ABI=armeabi-v7a UNITTEST=1 HEADLESS=1
id: android-arm32 id: android-arm32
- os: ubuntu-latest
extra: android
cc: clang
cxx: clang++
args: cd android && ./ab.sh -j2 APP_ABI=x86 UNITTEST=1 HEADLESS=1
id: android-x86_32
- os: ubuntu-latest - os: ubuntu-latest
extra: android extra: android
cc: clang cc: clang
@ -149,8 +155,14 @@ jobs:
extra: android extra: android
cc: clang cc: clang
cxx: clang++ cxx: clang++
args: cd android && ./ab.sh -j2 APP_ABI=arm64-v8a OPENXR=1 args: cd android && ./ab.sh -j2 APP_ABI=arm64-v8a OPENXR=1 OPENXR_PLATFORM_QUEST=1
id: android-vr id: android-vr-quest
- os: ubuntu-latest
extra: android
cc: clang
cxx: clang++
args: cd android && ./ab.sh -j2 APP_ABI=arm64-v8a OPENXR=1 OPENXR_PLATFORM_PICO=1
id: android-vr-pico
- os: ubuntu-latest - os: ubuntu-latest
extra: android extra: android
cc: clang cc: clang
@ -219,7 +231,7 @@ jobs:
run: | run: |
sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
sudo apt-get update -y -qq sudo apt-get update -y -qq
sudo apt-get install libsdl2-dev libgl1-mesa-dev libglu1-mesa-dev libsdl2-ttf-dev libfontconfig1-dev sudo apt-get install libsdl2-dev libgl1-mesa-dev libglu1-mesa-dev
- name: Create macOS git-version.cpp for tagged release - name: Create macOS git-version.cpp for tagged release
if: startsWith(github.ref, 'refs/tags/') && runner.os == 'macOS' && matrix.extra == 'test' if: startsWith(github.ref, 'refs/tags/') && runner.os == 'macOS' && matrix.extra == 'test'
@ -229,8 +241,6 @@ jobs:
- name: Setup ccache - name: Setup ccache
uses: hendrikmuhs/ccache-action@v1.2 uses: hendrikmuhs/ccache-action@v1.2
# Disable ccache on macos for now, it's become buggy for some reason.
if: matrix.id != 'macos'
with: with:
key: ${{ matrix.id }} key: ${{ matrix.id }}
@ -333,7 +343,7 @@ jobs:
run: | run: |
sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
sudo apt-get update -y -qq sudo apt-get update -y -qq
sudo apt-get install libsdl2-dev libgl1-mesa-dev libglu1-mesa-dev libsdl2-ttf-dev libfontconfig1-dev sudo apt-get install libsdl2-dev libgl1-mesa-dev libglu1-mesa-dev
- name: Install macOS dependencies - name: Install macOS dependencies
if: runner.os == 'macOS' if: runner.os == 'macOS'

7
.gitignore vendored
View file

@ -105,7 +105,6 @@ Windows/*.ipch
# For vim # For vim
*.swp *.swp
tags tags
*.ctags
# Other VCS # Other VCS
.bzr/ .bzr/
@ -118,8 +117,6 @@ debian/ppsspp/
# Libretro build # Libretro build
*.o *.o
*.tmp
*.a
# YouCompleteMe file # YouCompleteMe file
.ycm_extra_conf.pyc .ycm_extra_conf.pyc
@ -132,7 +129,3 @@ CMakeFiles
# Clangd # Clangd
.cache/ .cache/
build
libretro/obj/local
ppsspp_retroachievements.dat

View file

@ -49,14 +49,6 @@ include:
- project: 'libretro-infrastructure/ci-templates' - project: 'libretro-infrastructure/ci-templates'
file: '/android-cmake.yml' file: '/android-cmake.yml'
# iOS
- project: 'libretro-infrastructure/ci-templates'
file: '/ios-cmake.yml'
# tvOS
- project: 'libretro-infrastructure/ci-templates'
file: '/tvos-cmake.yml'
################################## CONSOLES ################################ ################################## CONSOLES ################################
#################################### MISC ################################## #################################### MISC ##################################
@ -141,21 +133,3 @@ libretro-build-android-x86:
extends: extends:
- .libretro-android-cmake-x86 - .libretro-android-cmake-x86
- .core-defs - .core-defs
# iOS arm64
libretro-build-ios-arm64:
extends:
- .libretro-ios-cmake-arm64
- .core-defs
- .cmake-defs
variables:
CORE_ARGS: -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchains/ios.cmake -DLIBRETRO=ON
# tvOS arm64
libretro-build-tvos-arm64:
extends:
- .libretro-tvos-cmake-arm64
- .core-defs
- .cmake-defs
variables:
CORE_ARGS: -DIOS_PLATFORM=TVOS -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchains/ios.cmake -DLIBRETRO=ON

12
.gitmodules vendored
View file

@ -1,6 +1,3 @@
[submodule "libretro/libretro-common"]
path = libretro/libretro-common
url = https://github.com/libretro/libretro-common.git
[submodule "pspautotests"] [submodule "pspautotests"]
path = pspautotests path = pspautotests
url = https://github.com/hrydgard/pspautotests.git url = https://github.com/hrydgard/pspautotests.git
@ -44,12 +41,3 @@
[submodule "cpu_features"] [submodule "cpu_features"]
path = ext/cpu_features path = ext/cpu_features
url = https://github.com/google/cpu_features.git url = https://github.com/google/cpu_features.git
[submodule "ext/rcheevos"]
path = ext/rcheevos
url = https://github.com/RetroAchievements/rcheevos.git
[submodule "ext/naett"]
path = ext/naett
url = https://github.com/erkkah/naett.git
[submodule "ext/libchdr"]
path = ext/libchdr
url = https://github.com/rtissera/libchdr.git

View file

@ -67,8 +67,6 @@ if(CMAKE_SYSTEM_PROCESSOR)
set(MIPS_DEVICE ON) set(MIPS_DEVICE ON)
elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^riscv64") elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^riscv64")
set(RISCV64_DEVICE ON) set(RISCV64_DEVICE ON)
elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^loongarch64")
set(LOONGARCH64_DEVICE ON)
else() else()
message("Unknown CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") message("Unknown CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}")
endif() endif()
@ -104,18 +102,15 @@ if(ANDROID OR WIN32 OR (UNIX AND NOT ARM_NO_VULKAN))
set(VULKAN ON) set(VULKAN ON)
endif() endif()
# Default to bundled SDL2 on macOS, system SDL2 elsewhere.
if(APPLE AND NOT IOS)
set(DEFAULT_USE_SYSTEM_LIBSDL2 OFF)
else()
set(DEFAULT_USE_SYSTEM_LIBSDL2 ON)
endif()
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules)
if(NOT IOS) if(NOT IOS)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/sdl) list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/sdl)
endif() endif()
if(MACOSX AND NOT USE_SYSTEM_LIBSDL2)
set(SDL2_LIBRARY ${CMAKE_SOURCE_DIR}/SDL/macOS/SDL2.framework)
endif()
include(ccache) include(ccache)
include(GNUInstallDirs) include(GNUInstallDirs)
@ -124,8 +119,16 @@ add_definitions(-DASSETS_DIR="${CMAKE_INSTALL_FULL_DATADIR}/ppsspp/assets/")
if(OPENXR) if(OPENXR)
add_definitions(-DOPENXR) add_definitions(-DOPENXR)
add_library(openxr SHARED IMPORTED) add_library(openxr SHARED IMPORTED)
set_property(TARGET openxr PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/ext/openxr/stub/arm64-v8a/libopenxr_loader.so") if(OPENXR_PLATFORM_PICO)
message("OpenXR enabled") add_definitions(-DOPENXR_PLATFORM_PICO)
set_property(TARGET openxr PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/ext/openxr/libs/pico/arm64-v8a/libopenxr_loader.so")
message("OpenXR for Pico enabled")
endif()
if(OPENXR_PLATFORM_QUEST)
add_definitions(-DOPENXR_PLATFORM_QUEST)
set_property(TARGET openxr PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/ext/openxr/libs/quest/arm64-v8a/libopenxr_loader.so")
message("OpenXR for Quest enabled")
endif()
endif() endif()
if(GOLD) if(GOLD)
@ -141,7 +144,6 @@ option(ARMV7 "Set to ON if targeting an ARMv7 processor" ${ARMV7_DEVICE})
option(ARM "Set to ON if targeting an ARM processor" ${ARM_DEVICE}) option(ARM "Set to ON if targeting an ARM processor" ${ARM_DEVICE})
option(MIPS "Set to ON if targeting a MIPS processor" ${MIPS_DEVICE}) option(MIPS "Set to ON if targeting a MIPS processor" ${MIPS_DEVICE})
option(RISCV64 "Set to ON if targeting a RISCV64 processor" ${RISCV64_DEVICE}) option(RISCV64 "Set to ON if targeting a RISCV64 processor" ${RISCV64_DEVICE})
option(LOONGARCH64 "Set to ON if targeting a LOONGARCH64 processor" ${LOONGARCH64_DEVICE})
option(X86 "Set to ON if targeting an X86 processor" ${X86_DEVICE}) option(X86 "Set to ON if targeting an X86 processor" ${X86_DEVICE})
option(X86_64 "Set to ON if targeting an X86_64 processor" ${X86_64_DEVICE}) option(X86_64 "Set to ON if targeting an X86_64 processor" ${X86_64_DEVICE})
# :: Environments # :: Environments
@ -149,7 +151,7 @@ option(USING_EGL "Set to ON if target environment uses EGL" ${USING_EGL})
option(USING_FBDEV "Set to ON if target environment uses fbdev (eg. Pandora)" ${USING_FBDEV}) option(USING_FBDEV "Set to ON if target environment uses fbdev (eg. Pandora)" ${USING_FBDEV})
option(USING_GLES2 "Set to ON if target device uses OpenGL ES 2.0" ${USING_GLES2}) option(USING_GLES2 "Set to ON if target device uses OpenGL ES 2.0" ${USING_GLES2})
option(USING_X11_VULKAN "Set to OFF if target environment doesn't use X11 for Vulkan" ON) option(USING_X11_VULKAN "Set to OFF if target environment doesn't use X11 for Vulkan" ON)
option(USE_WAYLAND_WSI "Enable or disable Wayland WSI support for Vulkan" ON) option(USE_WAYLAND_WSI "Enable or disable Wayland WSI support for Vulkan" ${USE_WAYLAND_WSI})
option(USE_VULKAN_DISPLAY_KHR "Enable or disable full screen display of Vulkan" ${USE_VULKAN_DISPLAY_KHR}) option(USE_VULKAN_DISPLAY_KHR "Enable or disable full screen display of Vulkan" ${USE_VULKAN_DISPLAY_KHR})
# :: Frontends # :: Frontends
option(USING_QT_UI "Set to ON if you wish to use the Qt frontend wrapper" ${USING_QT_UI}) option(USING_QT_UI "Set to ON if you wish to use the Qt frontend wrapper" ${USING_QT_UI})
@ -167,7 +169,7 @@ option(USE_ARMIPS "Build with armips support in API/debugger" ON)
option(USE_SYSTEM_SNAPPY "Dynamically link against system snappy" ${USE_SYSTEM_SNAPPY}) option(USE_SYSTEM_SNAPPY "Dynamically link against system snappy" ${USE_SYSTEM_SNAPPY})
option(USE_SYSTEM_FFMPEG "Dynamically link against system FFMPEG" ${USE_SYSTEM_FFMPEG}) option(USE_SYSTEM_FFMPEG "Dynamically link against system FFMPEG" ${USE_SYSTEM_FFMPEG})
option(USE_SYSTEM_LIBZIP "Dynamically link against system libzip" ${USE_SYSTEM_LIBZIP}) option(USE_SYSTEM_LIBZIP "Dynamically link against system libzip" ${USE_SYSTEM_LIBZIP})
option(USE_SYSTEM_LIBSDL2 "Dynamically link against system SDL2" ${DEFAULT_USE_SYSTEM_LIBSDL2}) option(USE_SYSTEM_LIBSDL2 "Dynamically link against system SDL2" ON)
option(USE_SYSTEM_LIBPNG "Dynamically link against system libpng" ON) option(USE_SYSTEM_LIBPNG "Dynamically link against system libpng" ON)
option(USE_SYSTEM_ZSTD "Dynamically link against system zstd" ${USE_SYSTEM_ZSTD}) option(USE_SYSTEM_ZSTD "Dynamically link against system zstd" ${USE_SYSTEM_ZSTD})
option(USE_SYSTEM_MINIUPNPC "Dynamically link against system miniUPnPc" ${USE_SYSTEM_MINIUPNPC}) option(USE_SYSTEM_MINIUPNPC "Dynamically link against system miniUPnPc" ${USE_SYSTEM_MINIUPNPC})
@ -177,20 +179,20 @@ option(USE_UBSAN "Use undefined behaviour sanitizer" OFF)
if(UNIX AND NOT (APPLE OR ANDROID) AND VULKAN) if(UNIX AND NOT (APPLE OR ANDROID) AND VULKAN)
if(USING_X11_VULKAN) if(USING_X11_VULKAN)
message("Using X11 for Vulkan") message("Using X11 for Vulkan")
find_package(X11)
include_directories(${X11_Xlib_INCLUDE_PATH})
add_definitions(-DVK_USE_PLATFORM_XLIB_KHR) add_definitions(-DVK_USE_PLATFORM_XLIB_KHR)
else() else()
message("NOT using X11 for Vulkan") message("NOT using X11 for Vulkan")
endif() endif()
# add_definitions(-DVK_USE_PLATFORM_XCB_KHR) # add_definitions(-DVK_USE_PLATFORM_XCB_KHR)
find_package(Wayland) if(USE_WAYLAND_WSI)
if(NOT WAYLAND_FOUND) find_package(Wayland)
message(STATUS "Could not find Wayland libraries, disabling Wayland WSI support for Vulkan.") if(NOT WAYLAND_FOUND)
elseif(USE_WAYLAND_WSI) message(STATUS "Could not find Wayland libraries, disabling Wayland WSI support for Vulkan.")
include_directories(${WAYLAND_INCLUDE_DIR}) else()
add_definitions(-DVK_USE_PLATFORM_WAYLAND_KHR) include_directories(${WAYLAND_INCLUDE_DIR})
add_definitions(-DVK_USE_PLATFORM_WAYLAND_KHR)
endif()
endif() endif()
if(USE_VULKAN_DISPLAY_KHR) if(USE_VULKAN_DISPLAY_KHR)
@ -220,16 +222,6 @@ else()
set(CoreLinkType STATIC) set(CoreLinkType STATIC)
endif() endif()
if(NOT ANDROID AND NOT WIN32 AND (NOT APPLE OR IOS))
set(HTTPS_NOT_AVAILABLE ON)
endif()
# Made this flag negative because it's hopefully quite temporary and didn't
# want to have to update all build systems.
if(HTTPS_NOT_AVAILABLE)
add_definitions(-DHTTPS_NOT_AVAILABLE)
endif()
# Work around for some misfeature of the current glslang build system # Work around for some misfeature of the current glslang build system
include_directories(ext/glslang) include_directories(ext/glslang)
@ -257,31 +249,9 @@ if(USING_EGL)
set(OPENGL_LIBRARIES ${OPENGL_LIBRARIES} ${EGL_LIBRARIES}) set(OPENGL_LIBRARIES ${OPENGL_LIBRARIES} ${EGL_LIBRARIES})
endif() endif()
if(NOT LIBRETRO AND NOT IOS AND NOT MACOSX) if(NOT LIBRETRO AND NOT IOS)
find_package(SDL2) find_package(SDL2)
find_package(SDL2_ttf)
find_package(Fontconfig)
# TODO: this can be removed once CI supports newer SDL2_ttf
if (NOT SDL2_ttf_FOUND)
find_package(PkgConfig)
if(PkgConfig_FOUND)
pkg_check_modules(SDL2_ttf_PKGCONFIG IMPORTED_TARGET SDL2_ttf)
endif()
endif()
endif() endif()
if(MACOSX AND NOT IOS)
if(USE_SYSTEM_LIBSDL2)
find_package(SDL2)
find_package(SDL2_ttf)
else()
find_library(SDL2Fwk SDL2 REQUIRED PATHS SDL/macOS)
message(STATUS "found SDL2Fwk=${SDL2Fwk}")
add_definitions(-DHAVE_SYSCTLBYNAME)
endif()
endif()
include(FindThreads) include(FindThreads)
if(APPLE) if(APPLE)
@ -328,9 +298,6 @@ endif()
if(RISCV64) if(RISCV64)
message("Generating for RISCV64, ${CMAKE_BUILD_TYPE}") message("Generating for RISCV64, ${CMAKE_BUILD_TYPE}")
endif() endif()
if(LOONGARCH64)
message("Generating for LOONGARCH64, ${CMAKE_BUILD_TYPE}")
endif()
if(X86) if(X86)
message("Generating for x86, ${CMAKE_BUILD_TYPE}") message("Generating for x86, ${CMAKE_BUILD_TYPE}")
endif() endif()
@ -406,15 +373,9 @@ if(NOT MSVC)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -parallel -fopenmp") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -parallel -fopenmp")
endif() endif()
add_definitions(-fno-math-errno)
if(X86 OR X86_64) if(X86 OR X86_64)
# enable sse2 code generation # enable sse2 code generation
add_definitions(-msse2) add_definitions(-msse2)
if(NOT X86_64 AND NOT CLANG)
add_definitions(-mfpmath=sse)
# add_definitions(-mstackrealign)
endif()
endif() endif()
if(IOS) if(IOS)
@ -433,13 +394,8 @@ if(NOT MSVC)
add_definitions(-Wno-psabi) add_definitions(-Wno-psabi)
endif() endif()
add_definitions(-D_XOPEN_SOURCE=700) add_definitions(-D_XOPEN_SOURCE=700)
add_definitions(-D_XOPEN_SOURCE_EXTENDED -D__BSD_VISIBLE=1 -D_BSD_SOURCE -D_DEFAULT_SOURCE) add_definitions(-D_XOPEN_SOURCE_EXTENDED -D__BSD_VISIBLE=1)
if(CMAKE_SYSTEM_NAME MATCHES "Linux|SunOS" OR MINGW) add_definitions(-D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64)
add_definitions(-D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64)
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "NetBSD")
add_definitions(-D_NETBSD_SOURCE)
endif()
elseif(ANDROID) elseif(ANDROID)
add_definitions(-fsigned-char) add_definitions(-fsigned-char)
endif() endif()
@ -451,7 +407,6 @@ else()
endif() endif()
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -D_NDEBUG") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -D_NDEBUG")
set(CMAKE_EXE_LINKER_FLAGS /NODEFAULTLIB:"libcmt.lib")
endif() endif()
if(WIN32) if(WIN32)
@ -544,14 +499,6 @@ set(CommonRISCV64
) )
source_group(RISCV64 FILES ${CommonRISCV64}) source_group(RISCV64 FILES ${CommonRISCV64})
set(CommonLOONGARCH64
${CommonJIT}
Common/LoongArchCPUDetect.cpp
Core/MIPS/fake/FakeJit.cpp
Core/MIPS/fake/FakeJit.h
)
source_group(LOONGARCH64 FILES ${CommonLOONGARCH64})
if(WIN32) if(WIN32)
set(CommonD3D set(CommonD3D
Common/GPU/D3D9/D3D9ShaderCompiler.cpp Common/GPU/D3D9/D3D9ShaderCompiler.cpp
@ -590,7 +537,6 @@ add_library(Common STATIC
${CommonARM64} ${CommonARM64}
${CommonMIPS} ${CommonMIPS}
${CommonRISCV64} ${CommonRISCV64}
${CommonLOONGARCH64}
${CommonD3D} ${CommonD3D}
${CommonVR} ${CommonVR}
Common/Serialize/Serializer.cpp Common/Serialize/Serializer.cpp
@ -610,7 +556,6 @@ add_library(Common STATIC
Common/Data/Collections/FixedSizeQueue.h Common/Data/Collections/FixedSizeQueue.h
Common/Data/Collections/Hashmaps.h Common/Data/Collections/Hashmaps.h
Common/Data/Collections/TinySet.h Common/Data/Collections/TinySet.h
Common/Data/Collections/FastVec.h
Common/Data/Collections/ThreadSafeList.h Common/Data/Collections/ThreadSafeList.h
Common/Data/Color/RGBAUtil.cpp Common/Data/Color/RGBAUtil.cpp
Common/Data/Color/RGBAUtil.h Common/Data/Color/RGBAUtil.h
@ -634,8 +579,6 @@ add_library(Common STATIC
Common/Data/Format/JSONReader.cpp Common/Data/Format/JSONReader.cpp
Common/Data/Format/JSONWriter.h Common/Data/Format/JSONWriter.h
Common/Data/Format/JSONWriter.cpp Common/Data/Format/JSONWriter.cpp
Common/Data/Format/DDSLoad.cpp
Common/Data/Format/DDSLoad.h
Common/Data/Format/PNGLoad.cpp Common/Data/Format/PNGLoad.cpp
Common/Data/Format/PNGLoad.h Common/Data/Format/PNGLoad.h
Common/Data/Format/ZIMLoad.cpp Common/Data/Format/ZIMLoad.cpp
@ -653,14 +596,10 @@ add_library(Common STATIC
Common/Data/Random/Rng.h Common/Data/Random/Rng.h
Common/File/VFS/VFS.h Common/File/VFS/VFS.h
Common/File/VFS/VFS.cpp Common/File/VFS/VFS.cpp
Common/File/VFS/ZipFileReader.cpp Common/File/VFS/AssetReader.cpp
Common/File/VFS/ZipFileReader.h Common/File/VFS/AssetReader.h
Common/File/VFS/DirectoryReader.cpp
Common/File/VFS/DirectoryReader.h
Common/File/AndroidStorage.h Common/File/AndroidStorage.h
Common/File/AndroidStorage.cpp Common/File/AndroidStorage.cpp
Common/File/AndroidContentURI.h
Common/File/AndroidContentURI.cpp
Common/File/DiskFree.h Common/File/DiskFree.h
Common/File/DiskFree.cpp Common/File/DiskFree.cpp
Common/File/Path.h Common/File/Path.h
@ -675,8 +614,6 @@ add_library(Common STATIC
Common/File/FileDescriptor.h Common/File/FileDescriptor.h
Common/GPU/DataFormat.h Common/GPU/DataFormat.h
Common/GPU/MiscTypes.h Common/GPU/MiscTypes.h
Common/GPU/GPUBackendCommon.cpp
Common/GPU/GPUBackendCommon.h
Common/GPU/thin3d.cpp Common/GPU/thin3d.cpp
Common/GPU/thin3d.h Common/GPU/thin3d.h
Common/GPU/thin3d_create.h Common/GPU/thin3d_create.h
@ -695,11 +632,7 @@ add_library(Common STATIC
Common/GPU/OpenGL/gl3stub.h Common/GPU/OpenGL/gl3stub.h
Common/GPU/OpenGL/GLFeatures.cpp Common/GPU/OpenGL/GLFeatures.cpp
Common/GPU/OpenGL/GLFeatures.h Common/GPU/OpenGL/GLFeatures.h
Common/GPU/OpenGL/GLFrameData.cpp
Common/GPU/OpenGL/GLFrameData.h
Common/GPU/OpenGL/thin3d_gl.cpp Common/GPU/OpenGL/thin3d_gl.cpp
Common/GPU/OpenGL/GLMemory.cpp
Common/GPU/OpenGL/GLMemory.h
Common/GPU/OpenGL/GLRenderManager.cpp Common/GPU/OpenGL/GLRenderManager.cpp
Common/GPU/OpenGL/GLRenderManager.h Common/GPU/OpenGL/GLRenderManager.h
Common/GPU/OpenGL/GLQueueRunner.cpp Common/GPU/OpenGL/GLQueueRunner.cpp
@ -712,8 +645,6 @@ add_library(Common STATIC
Common/GPU/Vulkan/VulkanDebug.h Common/GPU/Vulkan/VulkanDebug.h
Common/GPU/Vulkan/VulkanContext.cpp Common/GPU/Vulkan/VulkanContext.cpp
Common/GPU/Vulkan/VulkanContext.h Common/GPU/Vulkan/VulkanContext.h
Common/GPU/Vulkan/VulkanDescSet.cpp
Common/GPU/Vulkan/VulkanDescSet.h
Common/GPU/Vulkan/VulkanFramebuffer.cpp Common/GPU/Vulkan/VulkanFramebuffer.cpp
Common/GPU/Vulkan/VulkanFramebuffer.h Common/GPU/Vulkan/VulkanFramebuffer.h
Common/GPU/Vulkan/VulkanImage.cpp Common/GPU/Vulkan/VulkanImage.cpp
@ -753,10 +684,6 @@ add_library(Common STATIC
Common/Net/HTTPClient.h Common/Net/HTTPClient.h
Common/Net/HTTPHeaders.cpp Common/Net/HTTPHeaders.cpp
Common/Net/HTTPHeaders.h Common/Net/HTTPHeaders.h
Common/Net/HTTPNaettRequest.cpp
Common/Net/HTTPNaettRequest.h
Common/Net/HTTPRequest.cpp
Common/Net/HTTPRequest.h
Common/Net/HTTPServer.cpp Common/Net/HTTPServer.cpp
Common/Net/HTTPServer.h Common/Net/HTTPServer.h
Common/Net/NetBuffer.cpp Common/Net/NetBuffer.cpp
@ -781,20 +708,12 @@ add_library(Common STATIC
Common/Render/Text/draw_text.h Common/Render/Text/draw_text.h
Common/Render/Text/draw_text_android.cpp Common/Render/Text/draw_text_android.cpp
Common/Render/Text/draw_text_android.h Common/Render/Text/draw_text_android.h
Common/Render/Text/draw_text_sdl.cpp
Common/Render/Text/draw_text_sdl.h
Common/Render/Text/draw_text_win.cpp Common/Render/Text/draw_text_win.cpp
Common/Render/Text/draw_text_win.h Common/Render/Text/draw_text_win.h
Common/Render/Text/draw_text_uwp.cpp Common/Render/Text/draw_text_uwp.cpp
Common/Render/Text/draw_text_uwp.h Common/Render/Text/draw_text_uwp.h
Common/System/Display.cpp Common/System/Display.cpp
Common/System/Display.h Common/System/Display.h
Common/System/System.h
Common/System/NativeApp.h
Common/System/Request.cpp
Common/System/Request.h
Common/System/OSD.cpp
Common/System/OSD.h
Common/Thread/Channel.h Common/Thread/Channel.h
Common/Thread/ParallelLoop.cpp Common/Thread/ParallelLoop.cpp
Common/Thread/ParallelLoop.h Common/Thread/ParallelLoop.h
@ -813,8 +732,6 @@ add_library(Common STATIC
Common/UI/UI.h Common/UI/UI.h
Common/UI/Context.cpp Common/UI/Context.cpp
Common/UI/Context.h Common/UI/Context.h
Common/UI/IconCache.cpp
Common/UI/IconCache.h
Common/UI/UIScreen.cpp Common/UI/UIScreen.cpp
Common/UI/UIScreen.h Common/UI/UIScreen.h
Common/UI/Tween.cpp Common/UI/Tween.cpp
@ -852,10 +769,8 @@ add_library(Common STATIC
Common/MemArenaDarwin.cpp Common/MemArenaDarwin.cpp
Common/MemArenaPosix.cpp Common/MemArenaPosix.cpp
Common/MemArenaWin32.cpp Common/MemArenaWin32.cpp
Common/MemArenaHorizon.cpp
Common/MemArena.h Common/MemArena.h
Common/MemoryUtil.cpp Common/MemoryUtil.cpp
Common/MemoryUtilHorizon.cpp
Common/MemoryUtil.h Common/MemoryUtil.h
Common/OSVersion.cpp Common/OSVersion.cpp
Common/OSVersion.h Common/OSVersion.h
@ -901,11 +816,7 @@ if(USE_FFMPEG)
set(PLATFORM_ARCH "android/x86") set(PLATFORM_ARCH "android/x86")
endif() endif()
elseif(IOS) elseif(IOS)
if(IOS_PLATFORM STREQUAL "TVOS") set(PLATFORM_ARCH "ios/universal")
set(PLATFORM_ARCH "tvos/arm64")
else()
set(PLATFORM_ARCH "ios/universal")
endif()
elseif(MACOSX) elseif(MACOSX)
set(PLATFORM_ARCH "macosx/universal") set(PLATFORM_ARCH "macosx/universal")
elseif(LINUX) elseif(LINUX)
@ -919,8 +830,6 @@ if(USE_FFMPEG)
set(PLATFORM_ARCH "linux/mips32") set(PLATFORM_ARCH "linux/mips32")
elseif(RISCV64) elseif(RISCV64)
set(PLATFORM_ARCH "linux/riscv64") set(PLATFORM_ARCH "linux/riscv64")
elseif(LOONGARCH64)
set(PLATFORM_ARCH "linux/loongarch64")
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(PLATFORM_ARCH "linux/x86_64") set(PLATFORM_ARCH "linux/x86_64")
elseif(X86) elseif(X86)
@ -1001,7 +910,6 @@ endif()
find_package(LIBZIP) find_package(LIBZIP)
if(LIBZIP_FOUND AND USE_SYSTEM_LIBZIP) if(LIBZIP_FOUND AND USE_SYSTEM_LIBZIP)
include_directories(${LIBZIP_INCLUDE_DIRS})
add_definitions(-DSHARED_LIBZIP) add_definitions(-DSHARED_LIBZIP)
else() else()
add_library(libzip STATIC add_library(libzip STATIC
@ -1193,32 +1101,19 @@ else()
include_directories(ext/libpng17) include_directories(ext/libpng17)
endif() endif()
add_library(basis_universal STATIC
ext/basis_universal/basisu.h
ext/basis_universal/basisu_containers.h
ext/basis_universal/basisu_containers_impl.h
ext/basis_universal/basisu_file_headers.h
ext/basis_universal/basisu_transcoder.cpp
ext/basis_universal/basisu_transcoder.h
ext/basis_universal/basisu_transcoder_internal.h
ext/basis_universal/basisu_transcoder_tables_astc.inc
ext/basis_universal/basisu_transcoder_tables_astc_0_255.inc
ext/basis_universal/basisu_transcoder_tables_atc_55.inc
ext/basis_universal/basisu_transcoder_tables_atc_56.inc
ext/basis_universal/basisu_transcoder_tables_bc7_m5_alpha.inc
ext/basis_universal/basisu_transcoder_tables_bc7_m5_color.inc
ext/basis_universal/basisu_transcoder_tables_dxt1_5.inc
ext/basis_universal/basisu_transcoder_tables_dxt1_6.inc
ext/basis_universal/basisu_transcoder_tables_pvrtc2_45.inc
ext/basis_universal/basisu_transcoder_tables_pvrtc2_alpha_33.inc
ext/basis_universal/basisu_transcoder_uastc.h
)
set(BASISU_LIBRARIES basis_universal)
set(nativeExtra) set(nativeExtra)
set(nativeExtraLibs) set(nativeExtraLibs)
if(ANDROID) if(ANDROID)
set(nativeExtra ${nativeExtra}
Common/GL/GLInterface/EGLAndroid.cpp
Common/GL/GLInterface/EGLAndroid.h
Common/GL/GLInterface/EGL.cpp
Common/GL/GLInterface/EGL.h
Common/GL/GLInterface/GLInterface.cpp
Common/GL/GLInterfaceBase.h
)
set(NativeAppSource ${NativeAppSource} set(NativeAppSource ${NativeAppSource}
android/jni/app-android.cpp android/jni/app-android.cpp
android/jni/AndroidJavaGLContext.cpp android/jni/AndroidJavaGLContext.cpp
@ -1235,7 +1130,7 @@ if(ANDROID)
set(nativeExtraLibs ${nativeExtraLibs} openxr) set(nativeExtraLibs ${nativeExtraLibs} openxr)
endif() endif()
# No target # No target
elseif(IOS AND NOT LIBRETRO) elseif(IOS)
set(nativeExtra ${nativeExtra} set(nativeExtra ${nativeExtra}
ios/main.mm ios/main.mm
ios/AppDelegate.mm ios/AppDelegate.mm
@ -1261,7 +1156,7 @@ elseif(IOS AND NOT LIBRETRO)
UI/DarwinFileSystemServices.h UI/DarwinFileSystemServices.h
Common/Battery/AppleBatteryClient.m Common/Battery/AppleBatteryClient.m
) )
set(nativeExtraLibs ${nativeExtraLibs} "-framework Foundation -framework MediaPlayer -framework AudioToolbox -framework CoreGraphics -framework QuartzCore -framework UIKit -framework GLKit -framework OpenAL -framework AVFoundation -framework CoreLocation -framework CoreVideo -framework CoreMedia -framework CoreServices" ) set(nativeExtraLibs ${nativeExtraLibs} "-framework Foundation -framework MediaPlayer -framework AudioToolbox -framework CoreGraphics -framework QuartzCore -framework UIKit -framework GLKit -framework OpenAL -framework AVFoundation -framework CoreLocation -framework CoreVideo -framework CoreMedia -framework CoreServices" )
if(EXISTS "${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks/GameController.framework") if(EXISTS "${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks/GameController.framework")
set(nativeExtraLibs ${nativeExtraLibs} "-weak_framework GameController") set(nativeExtraLibs ${nativeExtraLibs} "-weak_framework GameController")
@ -1282,14 +1177,14 @@ elseif(IOS AND NOT LIBRETRO)
set_source_files_properties(UI/DarwinFileSystemServices.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) set_source_files_properties(UI/DarwinFileSystemServices.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
set_source_files_properties(Common/Battery/AppleBatteryClient.m PROPERTIES COMPILE_FLAGS -fobjc-arc) set_source_files_properties(Common/Battery/AppleBatteryClient.m PROPERTIES COMPILE_FLAGS -fobjc-arc)
set(TargetBin PPSSPP) set(TargetBin PPSSPP)
elseif(IOS AND LIBRETRO)
set(nativeExtraLibs ${nativeExtraLibs} "-framework GLKit")
elseif(USING_QT_UI) elseif(USING_QT_UI)
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
find_package(Qt5 COMPONENTS OpenGL Gui Core Multimedia) find_package(Qt5 COMPONENTS OpenGL Gui Core Multimedia)
list(APPEND NativeAppSource list(APPEND NativeAppSource
Qt/QtMain.cpp Qt/QtMain.cpp
Qt/QtMain.h Qt/QtMain.h
Qt/QtHost.cpp
Qt/QtHost.h
Qt/mainwindow.cpp Qt/mainwindow.cpp
Qt/mainwindow.h Qt/mainwindow.h
) )
@ -1297,15 +1192,7 @@ elseif(USING_QT_UI)
if(USING_GLES2) if(USING_GLES2)
add_definitions(-DQT_OPENGL_ES -DQT_OPENGL_ES_2) add_definitions(-DQT_OPENGL_ES -DQT_OPENGL_ES_2)
endif() endif()
if(APPLE)
list(APPEND NativeAppSource
UI/DarwinFileSystemServices.mm
UI/DarwinFileSystemServices.h
Common/Battery/AppleBatteryClient.m)
set_source_files_properties(Common/Battery/AppleBatteryClient.m PROPERTIES COMPILE_FLAGS -fobjc-arc)
set_source_files_properties(UI/DarwinFileSystemServices.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
set(nativeExtraLibs ${nativeExtraLibs} ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY})
endif()
include_directories(Qt) include_directories(Qt)
include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(nativeExtraLibs ${nativeExtraLibs} Qt5::OpenGL Qt5::Gui Qt5::Core Qt5::Multimedia) set(nativeExtraLibs ${nativeExtraLibs} Qt5::OpenGL Qt5::Gui Qt5::Core Qt5::Multimedia)
@ -1329,13 +1216,8 @@ elseif(WIN32)
else() else()
link_directories(dx9sdk/Lib/x86) link_directories(dx9sdk/Lib/x86)
endif() endif()
elseif(LIBRETRO) elseif(TARGET SDL2::SDL2)
else() set(TargetBin PPSSPPSDL)
if(GOLD)
set(TargetBin PPSSPPGold)
else()
set(TargetBin PPSSPPSDL)
endif()
# Require SDL # Require SDL
add_definitions(-DSDL) add_definitions(-DSDL)
set(nativeExtra ${nativeExtra} set(nativeExtra ${nativeExtra}
@ -1349,55 +1231,17 @@ else()
SDL/SDLVulkanGraphicsContext.cpp SDL/SDLVulkanGraphicsContext.cpp
) )
endif() endif()
if(SDL2_ttf_FOUND OR set(nativeExtraLibs ${nativeExtraLibs} SDL2::SDL2)
(SDL2_ttf_PKGCONFIG_FOUND AND
SDL2_ttf_PKGCONFIG_VERSION VERSION_GREATER_EQUAL "2.0.18"))
add_definitions(-DUSE_SDL2_TTF)
if(FONTCONFIG_FOUND)
add_definitions(-DUSE_SDL2_TTF_FONTCONFIG)
set(nativeExtraLibs ${nativeExtraLibs} Fontconfig::Fontconfig)
endif()
elseif(SDL2_ttf_PKGCONFIG_FOUND)
message(WARNING "Found SDL2_ttf <2.0.18 - this is too old, falling back to atlas")
endif()
if(SDL2_ttf_FOUND)
set(nativeExtraLibs ${nativeExtraLibs} SDL2_ttf::SDL2_ttf)
elseif(SDL2_ttf_PKGCONFIG_FOUND)
set(nativeExtraLibs ${nativeExtraLibs} PkgConfig::SDL2_ttf_PKGCONFIG)
endif()
if(APPLE) if(APPLE)
set(nativeExtra ${nativeExtra} set(nativeExtra ${nativeExtra} SDL/SDLMain.h SDL/SDLMain.mm SDL/SDLCocoaMetalLayer.h SDL/SDLCocoaMetalLayer.mm UI/DarwinFileSystemServices.mm UI/DarwinFileSystemServices.h Common/Battery/AppleBatteryClient.m)
SDL/SDLMain.h
SDL/SDLMain.mm
SDL/SDLCocoaMetalLayer.h
SDL/SDLCocoaMetalLayer.mm
SDL/CocoaBarItems.mm
SDL/CocoaBarItems.h
SDL/PPSSPPAboutViewController.m
SDL/PPSSPPAboutViewController.h
UI/DarwinFileSystemServices.mm
UI/DarwinFileSystemServices.h
Common/Battery/AppleBatteryClient.m
UI/PSPNSApplicationDelegate.mm
UI/PSPNSApplicationDelegate.h)
set_source_files_properties(UI/DarwinFileSystemServices.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) set_source_files_properties(UI/DarwinFileSystemServices.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
set_source_files_properties(UI/PSPNSApplicationDelegate.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
set_source_files_properties(SDL/CocoaBarItems.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
set_source_files_properties(SDL/PPSSPPAboutViewController.m PROPERTIES COMPILE_FLAGS -fobjc-arc)
set_source_files_properties(Common/Battery/AppleBatteryClient.m PROPERTIES COMPILE_FLAGS -fobjc-arc) set_source_files_properties(Common/Battery/AppleBatteryClient.m PROPERTIES COMPILE_FLAGS -fobjc-arc)
set(nativeExtraLibs ${nativeExtraLibs} ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY}) set(nativeExtraLibs ${nativeExtraLibs} ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY})
if(USE_SYSTEM_LIBSDL2)
set(nativeExtraLibs ${nativeExtraLibs} SDL2::SDL2)
else()
set(nativeExtraLibs ${nativeExtraLibs} ${SDL2Fwk})
endif()
elseif(USING_EGL) elseif(USING_EGL)
set(nativeExtraLibs ${nativeExtraLibs} pthread SDL2::SDL2) set(nativeExtraLibs ${nativeExtraLibs} pthread)
else()
set(nativeExtraLibs ${nativeExtraLibs} SDL2::SDL2)
endif() endif()
elseif(NOT LIBRETRO)
message(FATAL_ERROR "Could not find SDL2. Failing.")
endif() endif()
if(WIN32) if(WIN32)
@ -1411,13 +1255,6 @@ if(WIN32)
target_link_libraries(Common winmm d3d9 dsound) target_link_libraries(Common winmm d3d9 dsound)
endif() endif()
if(NOT LIBRETRO)
list(APPEND NativeAppSource
UI/AudioCommon.h
UI/AudioCommon.cpp
)
endif()
list(APPEND NativeAppSource list(APPEND NativeAppSource
android/jni/TestRunner.cpp android/jni/TestRunner.cpp
UI/DiscordIntegration.cpp UI/DiscordIntegration.cpp
@ -1426,8 +1263,6 @@ list(APPEND NativeAppSource
UI/BackgroundAudio.cpp UI/BackgroundAudio.cpp
UI/ChatScreen.h UI/ChatScreen.h
UI/ChatScreen.cpp UI/ChatScreen.cpp
UI/DebugOverlay.cpp
UI/DebugOverlay.h
UI/DevScreens.cpp UI/DevScreens.cpp
UI/DevScreens.h UI/DevScreens.h
UI/DisplayLayoutScreen.cpp UI/DisplayLayoutScreen.cpp
@ -1442,8 +1277,6 @@ list(APPEND NativeAppSource
UI/MiscScreens.cpp UI/MiscScreens.cpp
UI/PauseScreen.h UI/PauseScreen.h
UI/PauseScreen.cpp UI/PauseScreen.cpp
UI/TabbedDialogScreen.h
UI/TabbedDialogScreen.cpp
UI/GameScreen.h UI/GameScreen.h
UI/GameScreen.cpp UI/GameScreen.cpp
UI/GameSettingsScreen.h UI/GameSettingsScreen.h
@ -1480,12 +1313,10 @@ list(APPEND NativeAppSource
UI/MemStickScreen.cpp UI/MemStickScreen.cpp
UI/ProfilerDraw.h UI/ProfilerDraw.h
UI/ProfilerDraw.cpp UI/ProfilerDraw.cpp
UI/CustomButtonMappingScreen.h UI/ComboKeyMappingScreen.h
UI/CustomButtonMappingScreen.cpp UI/ComboKeyMappingScreen.cpp
UI/Theme.h UI/Theme.h
UI/Theme.cpp UI/Theme.cpp
UI/RetroAchievementScreens.cpp
UI/RetroAchievementScreens.h
) )
if(ANDROID) if(ANDROID)
@ -1519,11 +1350,11 @@ if(LINUX AND NOT ANDROID)
endif() endif()
set(ATOMIC_LIB) set(ATOMIC_LIB)
if(ANDROID OR (LINUX AND ARM_DEVICE) OR (LINUX AND RISCV64) OR (LINUX AND LOONGARCH64)) if(ANDROID OR (LINUX AND ARM_DEVICE))
set(ATOMIC_LIB atomic) set(ATOMIC_LIB atomic)
endif() endif()
target_link_libraries(native ${LIBZIP_LIBRARY} ${PNG_LIBRARIES} ${BASISU_LIBRARIES} ${ZLIB_LIBRARY} vma gason udis86 ${RT_LIB} ${nativeExtraLibs} ${ATOMIC_LIB} Common) target_link_libraries(native ${LIBZIP_LIBRARY} ${PNG_LIBRARIES} ${ZLIB_LIBRARY} vma gason udis86 ${RT_LIB} ${nativeExtraLibs} ${ATOMIC_LIB} Common)
if(TARGET Ext::GLEW) if(TARGET Ext::GLEW)
target_link_libraries(native Ext::GLEW) target_link_libraries(native Ext::GLEW)
endif() endif()
@ -1576,8 +1407,6 @@ set(CoreExtra)
set(CoreExtraLibs) set(CoreExtraLibs)
set(CoreExtra ${CoreExtra} set(CoreExtra ${CoreExtra}
Core/MIPS/IR/IRAnalysis.cpp
Core/MIPS/IR/IRAnalysis.h
Core/MIPS/IR/IRCompALU.cpp Core/MIPS/IR/IRCompALU.cpp
Core/MIPS/IR/IRCompBranch.cpp Core/MIPS/IR/IRCompBranch.cpp
Core/MIPS/IR/IRCompFPU.cpp Core/MIPS/IR/IRCompFPU.cpp
@ -1591,8 +1420,6 @@ set(CoreExtra ${CoreExtra}
Core/MIPS/IR/IRInterpreter.h Core/MIPS/IR/IRInterpreter.h
Core/MIPS/IR/IRJit.cpp Core/MIPS/IR/IRJit.cpp
Core/MIPS/IR/IRJit.h Core/MIPS/IR/IRJit.h
Core/MIPS/IR/IRNativeCommon.cpp
Core/MIPS/IR/IRNativeCommon.h
Core/MIPS/IR/IRPassSimplify.cpp Core/MIPS/IR/IRPassSimplify.cpp
Core/MIPS/IR/IRPassSimplify.h Core/MIPS/IR/IRPassSimplify.h
Core/MIPS/IR/IRRegCache.cpp Core/MIPS/IR/IRRegCache.cpp
@ -1632,17 +1459,6 @@ list(APPEND CoreExtra
Core/MIPS/ARM64/Arm64RegCache.h Core/MIPS/ARM64/Arm64RegCache.h
Core/MIPS/ARM64/Arm64RegCacheFPU.cpp Core/MIPS/ARM64/Arm64RegCacheFPU.cpp
Core/MIPS/ARM64/Arm64RegCacheFPU.h Core/MIPS/ARM64/Arm64RegCacheFPU.h
Core/MIPS/ARM64/Arm64IRAsm.cpp
Core/MIPS/ARM64/Arm64IRCompALU.cpp
Core/MIPS/ARM64/Arm64IRCompBranch.cpp
Core/MIPS/ARM64/Arm64IRCompFPU.cpp
Core/MIPS/ARM64/Arm64IRCompLoadStore.cpp
Core/MIPS/ARM64/Arm64IRCompSystem.cpp
Core/MIPS/ARM64/Arm64IRCompVec.cpp
Core/MIPS/ARM64/Arm64IRJit.cpp
Core/MIPS/ARM64/Arm64IRJit.h
Core/MIPS/ARM64/Arm64IRRegCache.cpp
Core/MIPS/ARM64/Arm64IRRegCache.h
GPU/Common/VertexDecoderArm64.cpp GPU/Common/VertexDecoderArm64.cpp
Core/Util/DisArm64.cpp Core/Util/DisArm64.cpp
) )
@ -1663,17 +1479,6 @@ list(APPEND CoreExtra
Core/MIPS/x86/RegCache.h Core/MIPS/x86/RegCache.h
Core/MIPS/x86/RegCacheFPU.cpp Core/MIPS/x86/RegCacheFPU.cpp
Core/MIPS/x86/RegCacheFPU.h Core/MIPS/x86/RegCacheFPU.h
Core/MIPS/x86/X64IRAsm.cpp
Core/MIPS/x86/X64IRCompALU.cpp
Core/MIPS/x86/X64IRCompBranch.cpp
Core/MIPS/x86/X64IRCompFPU.cpp
Core/MIPS/x86/X64IRCompLoadStore.cpp
Core/MIPS/x86/X64IRCompSystem.cpp
Core/MIPS/x86/X64IRCompVec.cpp
Core/MIPS/x86/X64IRJit.cpp
Core/MIPS/x86/X64IRJit.h
Core/MIPS/x86/X64IRRegCache.cpp
Core/MIPS/x86/X64IRRegCache.h
GPU/Common/VertexDecoderX86.cpp GPU/Common/VertexDecoderX86.cpp
GPU/Software/DrawPixelX86.cpp GPU/Software/DrawPixelX86.cpp
GPU/Software/SamplerX86.cpp GPU/Software/SamplerX86.cpp
@ -1684,21 +1489,6 @@ list(APPEND CoreExtra
Core/MIPS/MIPS/MipsJit.h Core/MIPS/MIPS/MipsJit.h
) )
list(APPEND CoreExtra
Core/MIPS/RiscV/RiscVAsm.cpp
Core/MIPS/RiscV/RiscVCompALU.cpp
Core/MIPS/RiscV/RiscVCompBranch.cpp
Core/MIPS/RiscV/RiscVCompFPU.cpp
Core/MIPS/RiscV/RiscVCompLoadStore.cpp
Core/MIPS/RiscV/RiscVCompSystem.cpp
Core/MIPS/RiscV/RiscVCompVec.cpp
Core/MIPS/RiscV/RiscVJit.cpp
Core/MIPS/RiscV/RiscVJit.h
Core/MIPS/RiscV/RiscVRegCache.cpp
Core/MIPS/RiscV/RiscVRegCache.h
GPU/Common/VertexDecoderRiscV.cpp
)
if(NOT MOBILE_DEVICE) if(NOT MOBILE_DEVICE)
set(CoreExtra ${CoreExtra} set(CoreExtra ${CoreExtra}
Core/AVIDump.cpp Core/AVIDump.cpp
@ -1709,7 +1499,7 @@ if(NOT MOBILE_DEVICE)
endif() endif()
set(GPU_GLES set(GPU_GLES
GPU/GLES/StencilBufferGLES.cpp GPU/GLES/DepthBufferGLES.cpp
GPU/GLES/GPU_GLES.cpp GPU/GLES/GPU_GLES.cpp
GPU/GLES/GPU_GLES.h GPU/GLES/GPU_GLES.h
GPU/GLES/FragmentTestCacheGLES.cpp GPU/GLES/FragmentTestCacheGLES.cpp
@ -1790,7 +1580,6 @@ set(GPU_SOURCES
${GPU_NEON} ${GPU_NEON}
GPU/Common/Draw2D.cpp GPU/Common/Draw2D.cpp
GPU/Common/Draw2D.h GPU/Common/Draw2D.h
GPU/Common/DepthBufferCommon.cpp
GPU/Common/TextureShaderCommon.cpp GPU/Common/TextureShaderCommon.cpp
GPU/Common/TextureShaderCommon.h GPU/Common/TextureShaderCommon.h
GPU/Common/DepalettizeShaderCommon.cpp GPU/Common/DepalettizeShaderCommon.cpp
@ -1839,10 +1628,6 @@ set(GPU_SOURCES
GPU/Common/TextureScalerCommon.h GPU/Common/TextureScalerCommon.h
GPU/Common/PostShader.cpp GPU/Common/PostShader.cpp
GPU/Common/PostShader.h GPU/Common/PostShader.h
GPU/Common/TextureReplacer.cpp
GPU/Common/TextureReplacer.h
GPU/Common/ReplacedTexture.cpp
GPU/Common/ReplacedTexture.h
GPU/Debugger/Breakpoints.cpp GPU/Debugger/Breakpoints.cpp
GPU/Debugger/Breakpoints.h GPU/Debugger/Breakpoints.h
GPU/Debugger/Debugger.cpp GPU/Debugger/Debugger.cpp
@ -1865,8 +1650,6 @@ set(GPU_SOURCES
GPU/GPU.h GPU/GPU.h
GPU/GPUCommon.cpp GPU/GPUCommon.cpp
GPU/GPUCommon.h GPU/GPUCommon.h
GPU/GPUCommonHW.cpp
GPU/GPUCommonHW.h
GPU/GPUState.cpp GPU/GPUState.cpp
GPU/GPUState.h GPU/GPUState.h
GPU/Math3D.cpp GPU/Math3D.cpp
@ -1902,8 +1685,6 @@ add_library(${CoreLibName} ${CoreLinkType}
${CommonJIT} ${CommonJIT}
Core/Config.cpp Core/Config.cpp
Core/Config.h Core/Config.h
Core/ConfigSettings.cpp
Core/ConfigSettings.h
Core/ConfigValues.h Core/ConfigValues.h
Core/ControlMapper.cpp Core/ControlMapper.cpp
Core/ControlMapper.h Core/ControlMapper.h
@ -1916,8 +1697,6 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/CoreTiming.h Core/CoreTiming.h
Core/CwCheat.cpp Core/CwCheat.cpp
Core/CwCheat.h Core/CwCheat.h
Core/FrameTiming.cpp
Core/FrameTiming.h
Core/HDRemaster.cpp Core/HDRemaster.cpp
Core/HDRemaster.h Core/HDRemaster.h
Core/Instance.cpp Core/Instance.cpp
@ -1926,8 +1705,6 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/KeyMap.h Core/KeyMap.h
Core/KeyMapDefaults.cpp Core/KeyMapDefaults.cpp
Core/KeyMapDefaults.h Core/KeyMapDefaults.h
Core/RetroAchievements.h
Core/RetroAchievements.cpp
Core/ThreadEventQueue.h Core/ThreadEventQueue.h
Core/TiltEventProcessor.h Core/TiltEventProcessor.h
Core/TiltEventProcessor.cpp Core/TiltEventProcessor.cpp
@ -1954,8 +1731,6 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/Debugger/WebSocket/GameBroadcaster.h Core/Debugger/WebSocket/GameBroadcaster.h
Core/Debugger/WebSocket/GameSubscriber.cpp Core/Debugger/WebSocket/GameSubscriber.cpp
Core/Debugger/WebSocket/GameSubscriber.h Core/Debugger/WebSocket/GameSubscriber.h
Core/Debugger/WebSocket/ClientConfigSubscriber.cpp
Core/Debugger/WebSocket/ClientConfigSubscriber.h
Core/Debugger/WebSocket/GPUBufferSubscriber.cpp Core/Debugger/WebSocket/GPUBufferSubscriber.cpp
Core/Debugger/WebSocket/GPUBufferSubscriber.h Core/Debugger/WebSocket/GPUBufferSubscriber.h
Core/Debugger/WebSocket/GPURecordSubscriber.cpp Core/Debugger/WebSocket/GPURecordSubscriber.cpp
@ -2201,6 +1976,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/HW/SasReverb.h Core/HW/SasReverb.h
Core/HW/StereoResampler.cpp Core/HW/StereoResampler.cpp
Core/HW/StereoResampler.h Core/HW/StereoResampler.h
Core/Host.cpp
Core/Host.h
Core/Loaders.cpp Core/Loaders.cpp
Core/Loaders.h Core/Loaders.h
Core/FileLoaders/CachingFileLoader.cpp Core/FileLoaders/CachingFileLoader.cpp
@ -2237,8 +2014,6 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/MIPS/MIPSTables.h Core/MIPS/MIPSTables.h
Core/MIPS/MIPSVFPUUtils.cpp Core/MIPS/MIPSVFPUUtils.cpp
Core/MIPS/MIPSVFPUUtils.h Core/MIPS/MIPSVFPUUtils.h
Core/MIPS/MIPSVFPUFallbacks.cpp
Core/MIPS/MIPSVFPUFallbacks.h
Core/MIPS/MIPSAsm.cpp Core/MIPS/MIPSAsm.cpp
Core/MIPS/MIPSAsm.h Core/MIPS/MIPSAsm.h
Core/MemFault.cpp Core/MemFault.cpp
@ -2259,14 +2034,14 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/Screenshot.h Core/Screenshot.h
Core/System.cpp Core/System.cpp
Core/System.h Core/System.h
Core/TextureReplacer.cpp
Core/TextureReplacer.h
Core/ThreadPools.cpp Core/ThreadPools.cpp
Core/ThreadPools.h Core/ThreadPools.h
Core/Util/AudioFormat.cpp Core/Util/AudioFormat.cpp
Core/Util/AudioFormat.h Core/Util/AudioFormat.h
Core/Util/GameManager.cpp Core/Util/GameManager.cpp
Core/Util/GameManager.h Core/Util/GameManager.h
Core/Util/GameDB.cpp
Core/Util/GameDB.h
Core/Util/PortManager.cpp Core/Util/PortManager.cpp
Core/Util/PortManager.h Core/Util/PortManager.h
Core/Util/BlockAllocator.cpp Core/Util/BlockAllocator.cpp
@ -2325,18 +2100,9 @@ else()
include_directories(ext/zstd/lib) include_directories(ext/zstd/lib)
endif() endif()
include_directories(ext/libchdr/include) target_link_libraries(${CoreLibName} Common native kirk cityhash sfmt19937 xbrz xxhash ${GlslangLibs}
target_link_libraries(${CoreLibName} Common native chdr kirk cityhash sfmt19937 xbrz xxhash rcheevos ${GlslangLibs}
${CoreExtraLibs} ${OPENGL_LIBRARIES} ${X11_LIBRARIES} ${CMAKE_DL_LIBS}) ${CoreExtraLibs} ${OPENGL_LIBRARIES} ${X11_LIBRARIES} ${CMAKE_DL_LIBS})
if(NOT HTTPS_NOT_AVAILABLE)
target_link_libraries(${CoreLibName} naett)
if(WIN32)
target_link_libraries(${CoreLibName} winhttp)
endif()
endif()
target_compile_features(${CoreLibName} PUBLIC cxx_std_17) target_compile_features(${CoreLibName} PUBLIC cxx_std_17)
if(FFmpeg_FOUND) if(FFmpeg_FOUND)
@ -2483,12 +2249,10 @@ set(WindowsFiles
Windows/Debugger/Debugger_MemoryDlg.h Windows/Debugger/Debugger_MemoryDlg.h
Windows/Debugger/Debugger_Lists.cpp Windows/Debugger/Debugger_Lists.cpp
Windows/Debugger/Debugger_Lists.h Windows/Debugger/Debugger_Lists.h
Windows/Debugger/Debugger_SymbolMap.h
Windows/Debugger/Debugger_VFPUDlg.cpp Windows/Debugger/Debugger_VFPUDlg.cpp
Windows/Debugger/Debugger_VFPUDlg.h Windows/Debugger/Debugger_VFPUDlg.h
Windows/Debugger/WatchItemWindow.cpp Windows/Debugger/SimpleELF.h
Windows/Debugger/WatchItemWindow.h
Windows/Debugger/EditSymbolsWindow.cpp
Windows/Debugger/EditSymbolsWindow.h
Windows/GEDebugger/CtrlDisplayListView.cpp Windows/GEDebugger/CtrlDisplayListView.cpp
Windows/GEDebugger/SimpleGLWindow.cpp Windows/GEDebugger/SimpleGLWindow.cpp
Windows/GEDebugger/TabState.cpp Windows/GEDebugger/TabState.cpp
@ -2532,13 +2296,6 @@ set(WindowsFiles
Windows/W32Util/ShellUtil.h Windows/W32Util/ShellUtil.h
Windows/W32Util/TabControl.cpp Windows/W32Util/TabControl.cpp
Windows/W32Util/TabControl.h Windows/W32Util/TabControl.h
Windows/W32Util/IatHook.h
Windows/W32Util/ContextMenu.h
Windows/W32Util/ContextMenu.cpp
Windows/W32Util/DarkMode.h
Windows/W32Util/DarkMode.cpp
Windows/W32Util/UAHMenuBar.h
Windows/W32Util/UAHMenuBar.cpp
Windows/WindowsHost.cpp Windows/WindowsHost.cpp
Windows/WindowsHost.h Windows/WindowsHost.h
Windows/MainWindow.cpp Windows/MainWindow.cpp
@ -2562,7 +2319,7 @@ set(WindowsFiles
list(APPEND LinkCommon ${CoreLibName} ${CMAKE_THREAD_LIBS_INIT}) list(APPEND LinkCommon ${CoreLibName} ${CMAKE_THREAD_LIBS_INIT})
if(WIN32) if(WIN32)
list(APPEND LinkCommon kernel32 user32 gdi32 shell32 comctl32 dsound xinput d3d9 winmm dinput8 ole32 winspool ksuser mf uxtheme mfplat mfreadwrite mfuuid shlwapi) list(APPEND LinkCommon kernel32 user32 gdi32 shell32 comctl32 dsound xinput d3d9 winmm dinput8 ole32 winspool ksuser mf mfplat mfreadwrite mfuuid shlwapi)
#setup_target_project(${TargetBin} Windows) #setup_target_project(${TargetBin} Windows)
list(APPEND NativeAppSource ${WindowsFiles}) list(APPEND NativeAppSource ${WindowsFiles})
endif() endif()
@ -2581,7 +2338,6 @@ set(NativeAssets
assets/lang assets/lang
assets/shaders assets/shaders
assets/themes assets/themes
assets/vfpu
assets/Roboto-Condensed.ttf assets/Roboto-Condensed.ttf
assets/7z.png assets/7z.png
assets/compat.ini assets/compat.ini
@ -2605,8 +2361,8 @@ set(NativeAssets
if(HEADLESS) if(HEADLESS)
set(HeadlessSource set(HeadlessSource
headless/Headless.cpp headless/Headless.cpp
headless/HeadlessHost.cpp headless/StubHost.cpp
headless/HeadlessHost.h headless/StubHost.h
headless/Compare.cpp headless/Compare.cpp
headless/Compare.h headless/Compare.h
headless/SDLHeadlessHost.cpp headless/SDLHeadlessHost.cpp
@ -2644,7 +2400,6 @@ if(UNITTEST)
unittest/TestIRPassSimplify.cpp unittest/TestIRPassSimplify.cpp
unittest/TestX64Emitter.cpp unittest/TestX64Emitter.cpp
unittest/TestVertexJit.cpp unittest/TestVertexJit.cpp
unittest/TestVFS.cpp
unittest/TestRiscVEmitter.cpp unittest/TestRiscVEmitter.cpp
unittest/TestSoftwareGPUJit.cpp unittest/TestSoftwareGPUJit.cpp
unittest/TestThreadManager.cpp unittest/TestThreadManager.cpp
@ -2678,18 +2433,11 @@ endif()
if(TargetBin) if(TargetBin)
if(APPLE) if(APPLE)
if(NOT IOS) if(NOT IOS)
if(GOLD) set(ICON_PATH_ABS ${CMAKE_CURRENT_SOURCE_DIR}/icons/ppsspp.icns)
set(ICON_PATH_ABS ${CMAKE_CURRENT_SOURCE_DIR}/icons/ppsspp_gold.icns) set(MACOSX_BUNDLE_ICON_FILE ppsspp.icns)
set(MACOSX_BUNDLE_ICON_FILE ppsspp_gold.icns)
set(MACOSX_BUNDLE_BUNDLE_NAME "PPSSPP Gold")
set(MACOSX_BUNDLE_GUI_IDENTIFIER org.ppsspp.ppssppgold)
else()
set(ICON_PATH_ABS ${CMAKE_CURRENT_SOURCE_DIR}/icons/ppsspp.icns)
set(MACOSX_BUNDLE_ICON_FILE ppsspp.icns)
set(MACOSX_BUNDLE_BUNDLE_NAME "PPSSPP")
set(MACOSX_BUNDLE_GUI_IDENTIFIER org.ppsspp.ppsspp)
endif()
set_source_files_properties(${ICON_PATH_ABS} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") set_source_files_properties(${ICON_PATH_ABS} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
set(MACOSX_BUNDLE_BUNDLE_NAME PPSSPP)
set(MACOSX_BUNDLE_GUI_IDENTIFIER org.ppsspp.ppsspp)
endif() endif()
# TODO: there must a native way to copy these. # TODO: there must a native way to copy these.
@ -2700,7 +2448,6 @@ if(TargetBin)
file(GLOB_RECURSE SHADER_FILES assets/shaders/*) file(GLOB_RECURSE SHADER_FILES assets/shaders/*)
file(GLOB_RECURSE THEME_FILE assets/themes/*) file(GLOB_RECURSE THEME_FILE assets/themes/*)
file(GLOB_RECURSE DEBUGGER_FILES assets/debugger/*) file(GLOB_RECURSE DEBUGGER_FILES assets/debugger/*)
file(GLOB_RECURSE VFPU_FILES assets/vfpu/*)
if(NOT IOS) if(NOT IOS)
set_source_files_properties(${BigFontAssets} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/assets") set_source_files_properties(${BigFontAssets} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/assets")
@ -2710,7 +2457,6 @@ if(TargetBin)
set_source_files_properties(${SHADER_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/assets/shaders") set_source_files_properties(${SHADER_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/assets/shaders")
set_source_files_properties(${THEME_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/assets/themes") set_source_files_properties(${THEME_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/assets/themes")
set_source_files_properties(${DEBUGGER_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/assets/debugger") set_source_files_properties(${DEBUGGER_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/assets/debugger")
set_source_files_properties(${VFPU_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/assets/vfpu")
endif() endif()
if(IOS) if(IOS)
@ -2718,11 +2464,11 @@ if(TargetBin)
file(INSTALL "${CMAKE_SOURCE_DIR}/ext/vulkan/iOS/Frameworks/libMoltenVK.dylib" DESTINATION "${CMAKE_BINARY_DIR}/PPSSPP.app/Frameworks/") file(INSTALL "${CMAKE_SOURCE_DIR}/ext/vulkan/iOS/Frameworks/libMoltenVK.dylib" DESTINATION "${CMAKE_BINARY_DIR}/PPSSPP.app/Frameworks/")
else() else()
add_executable(${TargetBin} MACOSX_BUNDLE ${ICON_PATH_ABS} ${NativeAssets} ${BigFontAssets} ${SHADER_FILES} ${THEME_FILE} ${DEBUGGER_FILES} ${FLASH0_FILES} ${LANG_FILES} ${NativeAppSource}) add_executable(${TargetBin} MACOSX_BUNDLE ${ICON_PATH_ABS} ${NativeAssets} ${BigFontAssets} ${SHADER_FILES} ${THEME_FILE} ${DEBUGGER_FILES} ${FLASH0_FILES} ${LANG_FILES} ${NativeAppSource})
file(INSTALL "${CMAKE_SOURCE_DIR}/ext/vulkan/macOS/Frameworks/libMoltenVK.dylib" DESTINATION "${CMAKE_BINARY_DIR}/${TargetBin}.app/Contents/Frameworks/") file(INSTALL "${CMAKE_SOURCE_DIR}/ext/vulkan/macOS/Frameworks/libMoltenVK.dylib" DESTINATION "${CMAKE_BINARY_DIR}/PPSSPPSDL.app/Contents/Frameworks/")
if(USING_QT_UI) if(TARGET SDL2::SDL2 AND NOT USING_QT_UI)
add_custom_command(TARGET ${TargetBin} POST_BUILD COMMAND /bin/bash "${CMAKE_SOURCE_DIR}/SDL/macbundle.sh" "${CMAKE_BINARY_DIR}/PPSSPPSDL.app")
elseif(USING_QT_UI)
add_custom_command(TARGET ${TargetBin} POST_BUILD COMMAND /bin/bash "${CMAKE_SOURCE_DIR}/Qt/macbundle.sh" "${CMAKE_BINARY_DIR}/PPSSPPQt.app") add_custom_command(TARGET ${TargetBin} POST_BUILD COMMAND /bin/bash "${CMAKE_SOURCE_DIR}/Qt/macbundle.sh" "${CMAKE_BINARY_DIR}/PPSSPPQt.app")
elseif(NOT USE_SYSTEM_LIBSDL2)
add_custom_command(TARGET ${TargetBin} POST_BUILD COMMAND /bin/bash "${CMAKE_SOURCE_DIR}/SDL/macbundle.sh" "${CMAKE_BINARY_DIR}/${TargetBin}.app" "${TargetBin}")
endif() endif()
endif() endif()
elseif(WIN32) elseif(WIN32)
@ -2746,7 +2492,7 @@ if(NOT ANDROID)
file(INSTALL assets/flash0 DESTINATION assets) file(INSTALL assets/flash0 DESTINATION assets)
endif() endif()
# packaging and code signing # packaging and code signing
if(IOS AND NOT LIBRETRO) if(IOS)
set(DEPLOYMENT_TARGET 11.0) set(DEPLOYMENT_TARGET 11.0)
file(GLOB IOSAssets ios/assets/*.png) file(GLOB IOSAssets ios/assets/*.png)
list(REMOVE_ITEM IOSAssets ${CMAKE_CURRENT_SOURCE_DIR}/ios/assets/Default-568h@2x.png) list(REMOVE_ITEM IOSAssets ${CMAKE_CURRENT_SOURCE_DIR}/ios/assets/Default-568h@2x.png)
@ -2787,18 +2533,6 @@ if(IOS AND NOT LIBRETRO)
) )
endif() endif()
if(MACOSX AND NOT IOS)
if(GOLD)
set_target_properties(${TargetBin} PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/macOS/InfoGold.plist"
)
else()
set_target_properties(${TargetBin} PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/macOS/Info.plist"
)
endif()
endif()
if(UNIX AND NOT ANDROID AND NOT APPLE) if(UNIX AND NOT ANDROID AND NOT APPLE)
configure_file( configure_file(
"${CMAKE_SOURCE_DIR}/ppsspp.desktop.in" "${CMAKE_SOURCE_DIR}/ppsspp.desktop.in"

View file

@ -315,14 +315,6 @@ const u8* ARM64XEmitter::AlignCodePage()
return m_code; return m_code;
} }
const u8 *ARM64XEmitter::NopAlignCode16() {
int bytes = ((-(intptr_t)m_code) & 15);
for (int i = 0; i < bytes / 4; i++) {
Write32(0xD503201F); // official nop instruction
}
return m_code;
}
void ARM64XEmitter::FlushIcache() void ARM64XEmitter::FlushIcache()
{ {
FlushIcacheSection(m_lastCacheFlushEnd, m_code); FlushIcacheSection(m_lastCacheFlushEnd, m_code);
@ -514,7 +506,7 @@ void ARM64XEmitter::EncodeTestBranchInst(u32 op, ARM64Reg Rt, u8 bits, const voi
distance >>= 2; distance >>= 2;
_assert_msg_(distance >= -0x2000 && distance <= 0x1FFF, "%s: Received too large distance: %llx", __FUNCTION__, distance); _assert_msg_(distance >= -0x1FFF && distance < 0x1FFF, "%s: Received too large distance: %llx", __FUNCTION__, distance);
Rt = DecodeReg(Rt); Rt = DecodeReg(Rt);
Write32((b64Bit << 31) | (0x36 << 24) | (op << 24) | \ Write32((b64Bit << 31) | (0x36 << 24) | (op << 24) | \
@ -2128,13 +2120,6 @@ void ARM64FloatEmitter::EmitCopy(bool Q, u32 op, u32 imm5, u32 imm4, ARM64Reg Rd
(1 << 10) | (Rn << 5) | Rd); (1 << 10) | (Rn << 5) | Rd);
} }
void ARM64FloatEmitter::EmitScalarPairwise(bool U, u32 size, u32 opcode, ARM64Reg Rd, ARM64Reg Rn) {
Rd = DecodeReg(Rd);
Rn = DecodeReg(Rn);
Write32((1 << 30) | (U << 29) | (0b111100011 << 20) | (size << 22) | (opcode << 12) | (1 << 11) | (Rn << 5) | Rd);
}
void ARM64FloatEmitter::Emit2RegMisc(bool Q, bool U, u32 size, u32 opcode, ARM64Reg Rd, ARM64Reg Rn) void ARM64FloatEmitter::Emit2RegMisc(bool Q, bool U, u32 size, u32 opcode, ARM64Reg Rd, ARM64Reg Rn)
{ {
_assert_msg_(!IsSingle(Rd), "%s doesn't support singles!", __FUNCTION__); _assert_msg_(!IsSingle(Rd), "%s doesn't support singles!", __FUNCTION__);
@ -2234,45 +2219,6 @@ void ARM64FloatEmitter::FCVTU(ARM64Reg Rd, ARM64Reg Rn, RoundingMode round) {
EmitConvertScalarToInt(Rd, Rn, round, true); EmitConvertScalarToInt(Rd, Rn, round, true);
} }
void ARM64FloatEmitter::FCVTZS(ARM64Reg Rd, ARM64Reg Rn, int scale) {
if (IsScalar(Rd)) {
int imm = (IsDouble(Rn) ? 64 : 32) * 2 - scale;
Rd = DecodeReg(Rd);
Rn = DecodeReg(Rn);
Write32((1 << 30) | (0 << 29) | (0x1F << 24) | (imm << 16) | (0x1F << 11) | (1 << 10) | (Rn << 5) | Rd);
} else {
bool sf = Is64Bit(Rd);
u32 type = 0;
if (IsDouble(Rd))
type = 1;
int rmode = 3;
int opcode = 0;
Write32((sf << 31) | (0 << 29) | (0x1E << 24) | (type << 22) | (rmode << 19) | (opcode << 16) | (scale << 10) | (Rn << 5) | Rd);
}
}
void ARM64FloatEmitter::FCVTZU(ARM64Reg Rd, ARM64Reg Rn, int scale) {
if (IsScalar(Rd)) {
int imm = (IsDouble(Rn) ? 64 : 32) * 2 - scale;
Rd = DecodeReg(Rd);
Rn = DecodeReg(Rn);
Write32((1 << 30) | (1 << 29) | (0x1F << 24) | (imm << 16) | (0x1F << 11) | (1 << 10) | (Rn << 5) | Rd);
} else {
bool sf = Is64Bit(Rd);
u32 type = 0;
if (IsDouble(Rd))
type = 1;
int rmode = 3;
int opcode = 1;
Write32((sf << 31) | (0 << 29) | (0x1E << 24) | (type << 22) | (rmode << 19) | (opcode << 16) | (scale << 10) | (Rn << 5) | Rd);
}
}
void ARM64FloatEmitter::EmitConversion2(bool sf, bool S, bool direction, u32 type, u32 rmode, u32 opcode, int scale, ARM64Reg Rd, ARM64Reg Rn) void ARM64FloatEmitter::EmitConversion2(bool sf, bool S, bool direction, u32 type, u32 rmode, u32 opcode, int scale, ARM64Reg Rd, ARM64Reg Rn)
{ {
Rd = DecodeReg(Rd); Rd = DecodeReg(Rd);
@ -2307,17 +2253,6 @@ void ARM64FloatEmitter::EmitCondSelect(bool M, bool S, CCFlags cond, ARM64Reg Rd
(cond << 12) | (3 << 10) | (Rn << 5) | Rd); (cond << 12) | (3 << 10) | (Rn << 5) | Rd);
} }
void ARM64FloatEmitter::EmitCondCompare(bool M, bool S, CCFlags cond, int op, u8 nzcv, ARM64Reg Rn, ARM64Reg Rm) {
_assert_msg_(!IsQuad(Rn), "%s doesn't support vector!", __FUNCTION__);
bool is_double = IsDouble(Rn);
Rn = DecodeReg(Rn);
Rm = DecodeReg(Rm);
Write32((M << 31) | (S << 29) | (0xF1 << 21) | (is_double << 22) | (Rm << 16) | \
(cond << 12) | (1 << 10) | (Rn << 5) | (op << 4) | nzcv);
}
void ARM64FloatEmitter::EmitPermute(u32 size, u32 op, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) void ARM64FloatEmitter::EmitPermute(u32 size, u32 op, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
{ {
_assert_msg_(!IsSingle(Rd), "%s doesn't support singles!", __FUNCTION__); _assert_msg_(!IsSingle(Rd), "%s doesn't support singles!", __FUNCTION__);
@ -2963,22 +2898,6 @@ void ARM64FloatEmitter::FSQRT(ARM64Reg Rd, ARM64Reg Rn)
EmitScalar1Source(0, 0, IsDouble(Rd), 3, Rd, Rn); EmitScalar1Source(0, 0, IsDouble(Rd), 3, Rd, Rn);
} }
// Scalar - pairwise
void ARM64FloatEmitter::FADDP(ARM64Reg Rd, ARM64Reg Rn) {
EmitScalarPairwise(1, IsDouble(Rd), 0b01101, Rd, Rn);
}
void ARM64FloatEmitter::FMAXP(ARM64Reg Rd, ARM64Reg Rn) {
EmitScalarPairwise(1, IsDouble(Rd), 0b01111, Rd, Rn);
}
void ARM64FloatEmitter::FMINP(ARM64Reg Rd, ARM64Reg Rn) {
EmitScalarPairwise(1, IsDouble(Rd) ? 3 : 2, 0b01111, Rd, Rn);
}
void ARM64FloatEmitter::FMAXNMP(ARM64Reg Rd, ARM64Reg Rn) {
EmitScalarPairwise(1, IsDouble(Rd), 0b01100, Rd, Rn);
}
void ARM64FloatEmitter::FMINNMP(ARM64Reg Rd, ARM64Reg Rn) {
EmitScalarPairwise(1, IsDouble(Rd) ? 3 : 2, 0b01100, Rd, Rn);
}
// Scalar - 2 Source // Scalar - 2 Source
void ARM64FloatEmitter::FADD(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) void ARM64FloatEmitter::FADD(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
@ -3061,12 +2980,6 @@ void ARM64FloatEmitter::BSL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
{ {
EmitThreeSame(1, 1, 3, Rd, Rn, Rm); EmitThreeSame(1, 1, 3, Rd, Rn, Rm);
} }
void ARM64FloatEmitter::BIT(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) {
EmitThreeSame(1, 2, 3, Rd, Rn, Rm);
}
void ARM64FloatEmitter::BIF(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) {
EmitThreeSame(1, 3, 3, Rd, Rn, Rm);
}
void ARM64FloatEmitter::DUP(u8 size, ARM64Reg Rd, ARM64Reg Rn, u8 index) void ARM64FloatEmitter::DUP(u8 size, ARM64Reg Rd, ARM64Reg Rn, u8 index)
{ {
u32 imm5 = 0; u32 imm5 = 0;
@ -3102,9 +3015,6 @@ void ARM64FloatEmitter::FADD(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
{ {
EmitThreeSame(0, size >> 6, 0x1A, Rd, Rn, Rm); EmitThreeSame(0, size >> 6, 0x1A, Rd, Rn, Rm);
} }
void ARM64FloatEmitter::FADDP(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) {
EmitThreeSame(1, size >> 6, 0x1A, Rd, Rn, Rm);
}
void ARM64FloatEmitter::FMAX(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) void ARM64FloatEmitter::FMAX(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
{ {
EmitThreeSame(0, size >> 6, 0x1E, Rd, Rn, Rm); EmitThreeSame(0, size >> 6, 0x1E, Rd, Rn, Rm);
@ -3137,14 +3047,6 @@ void ARM64FloatEmitter::FCVTZU(u8 size, ARM64Reg Rd, ARM64Reg Rn)
{ {
Emit2RegMisc(IsQuad(Rd), 1, 2 | (size >> 6), 0x1B, Rd, Rn); Emit2RegMisc(IsQuad(Rd), 1, 2 | (size >> 6), 0x1B, Rd, Rn);
} }
void ARM64FloatEmitter::FCVTZS(u8 size, ARM64Reg Rd, ARM64Reg Rn, int scale) {
int imm = size * 2 - scale;
EmitShiftImm(IsQuad(Rd), false, imm >> 3, imm & 7, 0x1F, Rd, Rn);
}
void ARM64FloatEmitter::FCVTZU(u8 size, ARM64Reg Rd, ARM64Reg Rn, int scale) {
int imm = size * 2 - scale;
EmitShiftImm(IsQuad(Rd), true, imm >> 3, imm & 7, 0x1F, Rd, Rn);
}
void ARM64FloatEmitter::FDIV(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) void ARM64FloatEmitter::FDIV(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
{ {
EmitThreeSame(1, size >> 6, 0x1F, Rd, Rn, Rm); EmitThreeSame(1, size >> 6, 0x1F, Rd, Rn, Rm);
@ -3248,61 +3150,6 @@ void ARM64FloatEmitter::XTN2(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn)
Emit2RegMisc(true, 0, dest_size >> 4, 0x12, Rd, Rn); Emit2RegMisc(true, 0, dest_size >> 4, 0x12, Rd, Rn);
} }
void ARM64FloatEmitter::CMEQ(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) {
_assert_msg_(!IsQuad(Rd) || size != 64, "%s cannot be used for scalar double", __FUNCTION__);
EmitThreeSame(true, size >> 4, 0b10001, Rd, Rn, Rm);
}
void ARM64FloatEmitter::CMGE(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) {
_assert_msg_(!IsQuad(Rd) || size != 64, "%s cannot be used for scalar double", __FUNCTION__);
EmitThreeSame(false, size >> 4, 0b00111, Rd, Rn, Rm);
}
void ARM64FloatEmitter::CMGT(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) {
_assert_msg_(!IsQuad(Rd) || size != 64, "%s cannot be used for scalar double", __FUNCTION__);
EmitThreeSame(false, size >> 4, 0b00110, Rd, Rn, Rm);
}
void ARM64FloatEmitter::CMHI(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) {
_assert_msg_(!IsQuad(Rd) || size != 64, "%s cannot be used for scalar double", __FUNCTION__);
EmitThreeSame(true, size >> 4, 0b00110, Rd, Rn, Rm);
}
void ARM64FloatEmitter::CMHS(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) {
_assert_msg_(!IsQuad(Rd) || size != 64, "%s cannot be used for scalar double", __FUNCTION__);
EmitThreeSame(true, size >> 4, 0b00111, Rd, Rn, Rm);
}
void ARM64FloatEmitter::CMTST(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) {
_assert_msg_(!IsQuad(Rd) || size != 64, "%s cannot be used for scalar double", __FUNCTION__);
EmitThreeSame(false, size >> 4, 0b10001, Rd, Rn, Rm);
}
void ARM64FloatEmitter::CMEQ(u8 size, ARM64Reg Rd, ARM64Reg Rn) {
_assert_msg_(!IsQuad(Rd) || size != 64, "%s cannot be used for scalar double", __FUNCTION__);
Emit2RegMisc(IsQuad(Rd), false, size >> 4, 0b01001, Rd, Rn);
}
void ARM64FloatEmitter::CMGE(u8 size, ARM64Reg Rd, ARM64Reg Rn) {
_assert_msg_(!IsQuad(Rd) || size != 64, "%s cannot be used for scalar double", __FUNCTION__);
Emit2RegMisc(IsQuad(Rd), true, size >> 4, 0b01000, Rd, Rn);
}
void ARM64FloatEmitter::CMGT(u8 size, ARM64Reg Rd, ARM64Reg Rn) {
_assert_msg_(!IsQuad(Rd) || size != 64, "%s cannot be used for scalar double", __FUNCTION__);
Emit2RegMisc(IsQuad(Rd), false, size >> 4, 0b01000, Rd, Rn);
}
void ARM64FloatEmitter::CMLE(u8 size, ARM64Reg Rd, ARM64Reg Rn) {
_assert_msg_(!IsQuad(Rd) || size != 64, "%s cannot be used for scalar double", __FUNCTION__);
Emit2RegMisc(IsQuad(Rd), true, size >> 4, 0b01001, Rd, Rn);
}
void ARM64FloatEmitter::CMLT(u8 size, ARM64Reg Rd, ARM64Reg Rn) {
_assert_msg_(!IsQuad(Rd) || size != 64, "%s cannot be used for scalar double", __FUNCTION__);
Emit2RegMisc(IsQuad(Rd), false, size >> 4, 0b01010, Rd, Rn);
}
// Move // Move
void ARM64FloatEmitter::DUP(u8 size, ARM64Reg Rd, ARM64Reg Rn) void ARM64FloatEmitter::DUP(u8 size, ARM64Reg Rd, ARM64Reg Rn)
{ {
@ -3435,95 +3282,6 @@ void ARM64FloatEmitter::SMOV(u8 size, ARM64Reg Rd, ARM64Reg Rn, u8 index)
EmitCopy(b64Bit, 0, imm5, 5, Rd, Rn); EmitCopy(b64Bit, 0, imm5, 5, Rd, Rn);
} }
void ARM64FloatEmitter::EncodeModImm(bool Q, u8 op, u8 cmode, u8 o2, ARM64Reg Rd, u8 abcdefgh) {
Rd = DecodeReg(Rd);
u8 abc = abcdefgh >> 5;
u8 defgh = abcdefgh & 0x1F;
Write32((Q << 30) | (op << 29) | (0xF << 24) | (abc << 16) | (cmode << 12) | (o2 << 11) | (1 << 10) | (defgh << 5) | Rd);
}
void ARM64FloatEmitter::FMOV(u8 size, ARM64Reg Rd, u8 imm8) {
_assert_msg_(!IsSingle(Rd), "%s doesn't support singles", __FUNCTION__);
_assert_msg_(size == 32 || size == 64, "%s: unsupported size", __FUNCTION__);
_assert_msg_(IsQuad(Rd) || size == 32, "Use non-SIMD FMOV to load one double imm8");
EncodeModImm(IsQuad(Rd), size >> 6, 0b1111, 0, Rd, imm8);
}
void ARM64FloatEmitter::MOVI(u8 size, ARM64Reg Rd, u8 imm8, u8 shift, bool MSL) {
_assert_msg_(!IsSingle(Rd), "%s doesn't support singles", __FUNCTION__);
_assert_msg_(size == 8 || size == 16 || size == 32 || size == 64, "%s: unsupported size %d", __FUNCTION__, size);
_assert_msg_((shift & 7) == 0 && shift < size, "%s: unsupported shift %d", __FUNCTION__, shift);
_assert_msg_(!MSL || (size == 32 && shift > 0 && shift <= 16), "MOVI MSL shift requires size 32, shift must be 8 or 16");
_assert_msg_(size != 64 || shift == 0, "MOVI 64-bit imm cannot be shifted");
u8 cmode = 0;
if (size == 8)
cmode = 0b1110;
else if (size == 16)
cmode = 0b1000 | (shift >> 2);
else if (MSL)
cmode = 0b1100 | (shift >> 3);
else if (size == 32)
cmode = (shift >> 2);
else if (size == 64)
cmode = 0b1110;
else
_assert_msg_(false, "%s: unhandled case", __FUNCTION__);
EncodeModImm(IsQuad(Rd), size >> 6, cmode, 0, Rd, imm8);
}
void ARM64FloatEmitter::MVNI(u8 size, ARM64Reg Rd, u8 imm8, u8 shift, bool MSL) {
_assert_msg_(!IsSingle(Rd), "%s doesn't support singles", __FUNCTION__);
_assert_msg_(size == 16 || size == 32, "%s: unsupported size %d", __FUNCTION__, size);
_assert_msg_((shift & 7) == 0 && shift < size, "%s: unsupported shift %d", __FUNCTION__, shift);
_assert_msg_(!MSL || (size == 32 && shift > 0 && shift <= 16), "MVNI MSL shift requires size 32, shift must be 8 or 16");
u8 cmode = 0;
if (size == 16)
cmode = 0b1000 | (shift >> 2);
else if (MSL)
cmode = 0b1100 | (shift >> 3);
else if (size == 32)
cmode = (shift >> 2);
else
_assert_msg_(false, "%s: unhandled case", __FUNCTION__);
EncodeModImm(IsQuad(Rd), 1, cmode, 0, Rd, imm8);
}
void ARM64FloatEmitter::ORR(u8 size, ARM64Reg Rd, u8 imm8, u8 shift) {
_assert_msg_(!IsSingle(Rd), "%s doesn't support singles", __FUNCTION__);
_assert_msg_(size == 16 || size == 32, "%s: unsupported size %d", __FUNCTION__, size);
_assert_msg_((shift & 7) == 0 && shift < size, "%s: unsupported shift %d", __FUNCTION__, shift);
u8 cmode = 0;
if (size == 16)
cmode = 0b1001 | (shift >> 2);
else if (size == 32)
cmode = 0b0001 | (shift >> 2);
else
_assert_msg_(false, "%s: unhandled case", __FUNCTION__);
EncodeModImm(IsQuad(Rd), 0, cmode, 0, Rd, imm8);
}
void ARM64FloatEmitter::BIC(u8 size, ARM64Reg Rd, u8 imm8, u8 shift) {
_assert_msg_(!IsSingle(Rd), "%s doesn't support singles", __FUNCTION__);
_assert_msg_(size == 16 || size == 32, "%s: unsupported size %d", __FUNCTION__, size);
_assert_msg_((shift & 7) == 0 && shift < size, "%s: unsupported shift %d", __FUNCTION__, shift);
u8 cmode = 0;
if (size == 16)
cmode = 0b1001 | (shift >> 2);
else if (size == 32)
cmode = 0b0001 | (shift >> 2);
else
_assert_msg_(false, "%s: unhandled case", __FUNCTION__);
EncodeModImm(IsQuad(Rd), 1, cmode, 0, Rd, imm8);
}
// One source // One source
void ARM64FloatEmitter::FCVT(u8 size_to, u8 size_from, ARM64Reg Rd, ARM64Reg Rn) void ARM64FloatEmitter::FCVT(u8 size_to, u8 size_from, ARM64Reg Rd, ARM64Reg Rn)
{ {
@ -3586,38 +3344,22 @@ void ARM64FloatEmitter::UCVTF(ARM64Reg Rd, ARM64Reg Rn)
void ARM64FloatEmitter::SCVTF(ARM64Reg Rd, ARM64Reg Rn, int scale) void ARM64FloatEmitter::SCVTF(ARM64Reg Rd, ARM64Reg Rn, int scale)
{ {
if (IsScalar(Rn)) { bool sf = Is64Bit(Rn);
int imm = (IsDouble(Rn) ? 64 : 32) * 2 - scale; u32 type = 0;
Rd = DecodeReg(Rd); if (IsDouble(Rd))
Rn = DecodeReg(Rn); type = 1;
Write32((1 << 30) | (0 << 29) | (0x1F << 24) | (imm << 16) | (0x1C << 11) | (1 << 10) | (Rn << 5) | Rd); EmitConversion2(sf, 0, false, type, 0, 2, 64 - scale, Rd, Rn);
} else {
bool sf = Is64Bit(Rn);
u32 type = 0;
if (IsDouble(Rd))
type = 1;
EmitConversion2(sf, 0, false, type, 0, 2, 64 - scale, Rd, Rn);
}
} }
void ARM64FloatEmitter::UCVTF(ARM64Reg Rd, ARM64Reg Rn, int scale) void ARM64FloatEmitter::UCVTF(ARM64Reg Rd, ARM64Reg Rn, int scale)
{ {
if (IsScalar(Rn)) { bool sf = Is64Bit(Rn);
int imm = (IsDouble(Rn) ? 64 : 32) * 2 - scale; u32 type = 0;
Rd = DecodeReg(Rd); if (IsDouble(Rd))
Rn = DecodeReg(Rn); type = 1;
Write32((1 << 30) | (1 << 29) | (0x1F << 24) | (imm << 16) | (0x1C << 11) | (1 << 10) | (Rn << 5) | Rd); EmitConversion2(sf, 0, false, type, 0, 3, 64 - scale, Rd, Rn);
} else {
bool sf = Is64Bit(Rn);
u32 type = 0;
if (IsDouble(Rd))
type = 1;
EmitConversion2(sf, 0, false, type, 0, 3, 64 - scale, Rd, Rn);
}
} }
void ARM64FloatEmitter::FCMP(ARM64Reg Rn, ARM64Reg Rm) void ARM64FloatEmitter::FCMP(ARM64Reg Rn, ARM64Reg Rm)
@ -3674,14 +3416,6 @@ void ARM64FloatEmitter::FCSEL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, CCFlags con
EmitCondSelect(0, 0, cond, Rd, Rn, Rm); EmitCondSelect(0, 0, cond, Rd, Rn, Rm);
} }
void ARM64FloatEmitter::FCCMP(ARM64Reg Rn, ARM64Reg Rm, u8 nzcv, CCFlags cond) {
EmitCondCompare(0, 0, cond, 0, nzcv, Rn, Rm);
}
void ARM64FloatEmitter::FCCMPE(ARM64Reg Rn, ARM64Reg Rm, u8 nzcv, CCFlags cond) {
EmitCondCompare(0, 0, cond, 1, nzcv, Rn, Rm);
}
// Permute // Permute
void ARM64FloatEmitter::UZP1(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) void ARM64FloatEmitter::UZP1(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
{ {
@ -3708,20 +3442,6 @@ void ARM64FloatEmitter::ZIP2(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
EmitPermute(size, 7, Rd, Rn, Rm); EmitPermute(size, 7, Rd, Rn, Rm);
} }
void ARM64FloatEmitter::EXT(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, int index) {
_assert_msg_(!IsSingle(Rd), "%s doesn't support singles!", __FUNCTION__);
bool quad = IsQuad(Rd);
_assert_msg_(index >= 0 && index < 16 && (quad || index < 8), "%s start index out of bounds", __FUNCTION__);
_assert_msg_(IsQuad(Rd) == IsQuad(Rn) && IsQuad(Rd) == IsQuad(Rm), "%s operands not same size", __FUNCTION__);
Rd = DecodeReg(Rd);
Rn = DecodeReg(Rn);
Rm = DecodeReg(Rm);
Write32((quad << 30) | (0x17 << 25) | (Rm << 16) | (index << 11) | (Rn << 5) | Rd);
}
// Shift by immediate // Shift by immediate
void ARM64FloatEmitter::SSHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift) void ARM64FloatEmitter::SSHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift)
{ {
@ -3747,12 +3467,6 @@ void ARM64FloatEmitter::USHLL2(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift)
{ {
USHLL(src_size, Rd, Rn, shift, true); USHLL(src_size, Rd, Rn, shift, true);
} }
void ARM64FloatEmitter::SHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn) {
SHLL(src_size, Rd, Rn, false);
}
void ARM64FloatEmitter::SHLL2(u8 src_size, ARM64Reg Rd, ARM64Reg Rn) {
SHLL(src_size, Rd, Rn, true);
}
void ARM64FloatEmitter::SXTL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn) void ARM64FloatEmitter::SXTL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn)
{ {
SXTL(src_size, Rd, Rn, false); SXTL(src_size, Rd, Rn, false);
@ -3792,11 +3506,6 @@ void ARM64FloatEmitter::USHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift,
EmitShiftImm(upper, 1, imm >> 3, imm & 7, 0x14, Rd, Rn); EmitShiftImm(upper, 1, imm >> 3, imm & 7, 0x14, Rd, Rn);
} }
void ARM64FloatEmitter::SHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, bool upper) {
_assert_msg_(src_size <= 32, "%s shift amount cannot be 64", __FUNCTION__);
Emit2RegMisc(upper, 1, src_size >> 4, 0b10011, Rd, Rn);
}
void ARM64FloatEmitter::SHRN(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift, bool upper) void ARM64FloatEmitter::SHRN(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift, bool upper)
{ {
_assert_msg_(shift > 0, "%s shift amount must be greater than zero!", __FUNCTION__); _assert_msg_(shift > 0, "%s shift amount must be greater than zero!", __FUNCTION__);
@ -4187,131 +3896,20 @@ void ARM64FloatEmitter::MOVI2F(ARM64Reg Rd, float value, ARM64Reg scratch, bool
} }
// TODO: Quite a few values could be generated easily using the MOVI instruction and friends. // TODO: Quite a few values could be generated easily using the MOVI instruction and friends.
void ARM64FloatEmitter::MOVI2FDUP(ARM64Reg Rd, float value, ARM64Reg scratch, bool negate) { void ARM64FloatEmitter::MOVI2FDUP(ARM64Reg Rd, float value, ARM64Reg scratch) {
_assert_msg_(!IsSingle(Rd), "%s doesn't support singles", __FUNCTION__); // TODO: Make it work with more element sizes
// TODO: Optimize - there are shorter solution for many values
ARM64Reg s = (ARM64Reg)(S0 + DecodeReg(Rd));
int ival; int ival;
memcpy(&ival, &value, 4); memcpy(&ival, &value, 4);
uint8_t imm8;
if (ival == 0) { // Make sure to not catch negative zero here if (ival == 0) { // Make sure to not catch negative zero here
// Prefer MOVI 0, which may have no latency on some CPUs. EOR(Rd, Rd, Rd);
MOVI(32, Rd, 0);
if (negate)
FNEG(32, Rd, Rd);
} else if (negate && FPImm8FromFloat(-value, &imm8)) {
FMOV(32, Rd, imm8);
} else if (FPImm8FromFloat(value, &imm8)) {
FMOV(32, Rd, imm8);
if (negate) {
FNEG(32, Rd, Rd);
}
} else if (TryAnyMOVI(32, Rd, ival)) {
if (negate) {
FNEG(32, Rd, Rd);
}
} else if (TryAnyMOVI(32, Rd, ival ^ 0x80000000)) {
if (!negate) {
FNEG(32, Rd, Rd);
}
} else { } else {
_assert_msg_(scratch != INVALID_REG, "Failed to find a way to generate FP immediate %f without scratch", value); MOVI2F(s, value, scratch);
if (negate) { DUP(32, Rd, Rd, 0);
ival ^= 0x80000000;
}
m_emit->MOVI2R(scratch, ival);
DUP(32, Rd, scratch);
} }
} }
bool ARM64FloatEmitter::TryMOVI(u8 size, ARM64Reg Rd, uint64_t elementValue) {
if (size == 8) {
// Can always do 8.
MOVI(size, Rd, elementValue & 0xFF);
return true;
} else if (size == 16) {
if ((elementValue & 0xFF00) == 0) {
MOVI(size, Rd, elementValue & 0xFF, 0);
return true;
} else if ((elementValue & 0x00FF) == 0) {
MOVI(size, Rd, (elementValue >> 8) & 0xFF, 8);
return true;
} else if ((elementValue & 0xFF00) == 0xFF00) {
MVNI(size, Rd, ~elementValue & 0xFF, 0);
return true;
} else if ((elementValue & 0x00FF) == 0x00FF) {
MVNI(size, Rd, (~elementValue >> 8) & 0xFF, 8);
return true;
}
return false;
} else if (size == 32) {
for (int shift = 0; shift < 32; shift += 8) {
uint32_t mask = 0xFFFFFFFF &~ (0xFF << shift);
if ((elementValue & mask) == 0) {
MOVI(size, Rd, (elementValue >> shift) & 0xFF, shift);
return true;
} else if ((elementValue & mask) == mask) {
MVNI(size, Rd, (~elementValue >> shift) & 0xFF, shift);
return true;
}
}
// Maybe an MSL shift will work?
for (int shift = 8; shift <= 16; shift += 8) {
uint32_t mask = 0xFFFFFFFF & ~(0xFF << shift);
uint32_t ones = (1 << shift) - 1;
uint32_t notOnes = 0xFFFFFF00 << shift;
if ((elementValue & mask) == ones) {
MOVI(size, Rd, (elementValue >> shift) & 0xFF, shift, true);
return true;
} else if ((elementValue & mask) == notOnes) {
MVNI(size, Rd, (elementValue >> shift) & 0xFF, shift, true);
return true;
}
}
return false;
} else if (size == 64) {
uint8_t imm8 = 0;
for (int i = 0; i < 8; ++i) {
uint8_t byte = (elementValue >> (i * 8)) & 0xFF;
if (byte != 0 && byte != 0xFF)
return false;
if (byte == 0xFF)
imm8 |= 1 << i;
}
// Didn't run into any partial bytes, so size 64 is doable.
MOVI(size, Rd, imm8);
return true;
}
return false;
}
bool ARM64FloatEmitter::TryAnyMOVI(u8 size, ARM64Reg Rd, uint64_t elementValue) {
// Try the original size first in case that's more optimal.
if (TryMOVI(size, Rd, elementValue))
return true;
uint64_t value = elementValue;
if (size != 64) {
uint64_t masked = elementValue & ((1 << size) - 1);
for (int i = size; i < 64; ++i) {
value |= masked << i;
}
}
for (int attempt = 8; attempt <= 64; attempt += attempt) {
// Original size was already attempted above.
if (attempt != size) {
if (TryMOVI(attempt, Rd, value))
return true;
}
}
return false;
}
void ARM64XEmitter::SUBSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch) { void ARM64XEmitter::SUBSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch) {
u32 val; u32 val;
bool shift; bool shift;

View file

@ -94,7 +94,7 @@ enum ARM64Reg
// R19-R28. R29 (FP), R30 (LR) are always saved and FP updated appropriately. // R19-R28. R29 (FP), R30 (LR) are always saved and FP updated appropriately.
const u32 ALL_CALLEE_SAVED = 0x1FF80000; const u32 ALL_CALLEE_SAVED = 0x1FF80000;
const u32 ALL_CALLEE_SAVED_FP = 0x0000FF00; // q8-q15 const u32 ALL_CALLEE_SAVED_FP = 0x0000FF00; // d8-d15
inline bool Is64Bit(ARM64Reg reg) { return (reg & 0x20) != 0; } inline bool Is64Bit(ARM64Reg reg) { return (reg & 0x20) != 0; }
inline bool IsSingle(ARM64Reg reg) { return (reg & 0xC0) == 0x40; } inline bool IsSingle(ARM64Reg reg) { return (reg & 0xC0) == 0x40; }
@ -290,23 +290,6 @@ public:
} }
m_shifttype = ST_LSL; m_shifttype = ST_LSL;
} }
ArithOption(ARM64Reg Rd, bool index, bool signExtend) {
if (index)
m_shift = 4;
else
m_shift = 0;
m_destReg = Rd;
m_type = TYPE_EXTENDEDREG;
if (Is64Bit(Rd)) {
m_width = WIDTH_64BIT;
m_extend = EXTEND_UXTX;
} else {
m_width = WIDTH_32BIT;
m_extend = signExtend ? EXTEND_SXTW : EXTEND_UXTW;
}
m_shifttype = ST_LSL;
}
ArithOption(ARM64Reg Rd, ShiftType shift_type, u32 shift) ArithOption(ARM64Reg Rd, ShiftType shift_type, u32 shift)
{ {
m_destReg = Rd; m_destReg = Rd;
@ -418,7 +401,6 @@ public:
void ReserveCodeSpace(u32 bytes); void ReserveCodeSpace(u32 bytes);
const u8* AlignCode16(); const u8* AlignCode16();
const u8* AlignCodePage(); const u8* AlignCodePage();
const u8 *NopAlignCode16();
void FlushIcache(); void FlushIcache();
void FlushIcacheSection(const u8* start, const u8* end); void FlushIcacheSection(const u8* start, const u8* end);
u8* GetWritableCodePtr(); u8* GetWritableCodePtr();
@ -820,13 +802,6 @@ public:
void FSQRT(ARM64Reg Rd, ARM64Reg Rn); void FSQRT(ARM64Reg Rd, ARM64Reg Rn);
void FMOV(ARM64Reg Rd, ARM64Reg Rn, bool top = false); // Also generalized move between GPR/FP void FMOV(ARM64Reg Rd, ARM64Reg Rn, bool top = false); // Also generalized move between GPR/FP
// Scalar - pairwise
void FADDP(ARM64Reg Rd, ARM64Reg Rn);
void FMAXP(ARM64Reg Rd, ARM64Reg Rn);
void FMINP(ARM64Reg Rd, ARM64Reg Rn);
void FMAXNMP(ARM64Reg Rd, ARM64Reg Rn);
void FMINNMP(ARM64Reg Rd, ARM64Reg Rn);
// Scalar - 2 Source // Scalar - 2 Source
void FADD(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void FADD(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void FMUL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void FMUL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
@ -851,12 +826,9 @@ public:
void AND(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void AND(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void EOR(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void EOR(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void BSL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void BSL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void BIT(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void BIF(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void DUP(u8 size, ARM64Reg Rd, ARM64Reg Rn, u8 index); void DUP(u8 size, ARM64Reg Rd, ARM64Reg Rn, u8 index);
void FABS(u8 size, ARM64Reg Rd, ARM64Reg Rn); void FABS(u8 size, ARM64Reg Rd, ARM64Reg Rn);
void FADD(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void FADD(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void FADDP(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void FMAX(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void FMAX(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void FMLA(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void FMLA(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void FMLS(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void FMLS(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
@ -866,8 +838,6 @@ public:
void FCVTN(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn); void FCVTN(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn);
void FCVTZS(u8 size, ARM64Reg Rd, ARM64Reg Rn); void FCVTZS(u8 size, ARM64Reg Rd, ARM64Reg Rn);
void FCVTZU(u8 size, ARM64Reg Rd, ARM64Reg Rn); void FCVTZU(u8 size, ARM64Reg Rd, ARM64Reg Rn);
void FCVTZS(u8 size, ARM64Reg Rd, ARM64Reg Rn, int scale);
void FCVTZU(u8 size, ARM64Reg Rd, ARM64Reg Rn, int scale);
void FDIV(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void FDIV(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void FMUL(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void FMUL(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void FNEG(u8 size, ARM64Reg Rd, ARM64Reg Rn); void FNEG(u8 size, ARM64Reg Rd, ARM64Reg Rn);
@ -898,18 +868,6 @@ public:
void XTN(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn); void XTN(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn);
void XTN2(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn); void XTN2(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn);
void CMEQ(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void CMGE(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void CMGT(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void CMHI(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void CMHS(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void CMTST(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void CMEQ(u8 size, ARM64Reg Rd, ARM64Reg Rn);
void CMGE(u8 size, ARM64Reg Rd, ARM64Reg Rn);
void CMGT(u8 size, ARM64Reg Rd, ARM64Reg Rn);
void CMLE(u8 size, ARM64Reg Rd, ARM64Reg Rn);
void CMLT(u8 size, ARM64Reg Rd, ARM64Reg Rn);
// Move // Move
void DUP(u8 size, ARM64Reg Rd, ARM64Reg Rn); void DUP(u8 size, ARM64Reg Rd, ARM64Reg Rn);
void INS(u8 size, ARM64Reg Rd, u8 index, ARM64Reg Rn); void INS(u8 size, ARM64Reg Rd, u8 index, ARM64Reg Rn);
@ -917,18 +875,6 @@ public:
void UMOV(u8 size, ARM64Reg Rd, ARM64Reg Rn, u8 index); void UMOV(u8 size, ARM64Reg Rd, ARM64Reg Rn, u8 index);
void SMOV(u8 size, ARM64Reg Rd, ARM64Reg Rn, u8 index); void SMOV(u8 size, ARM64Reg Rd, ARM64Reg Rn, u8 index);
// Vector immediates
void FMOV(u8 size, ARM64Reg Rd, u8 imm8);
// MSL means bits shifted in are 1s. For size=64, each bit of imm8 is expanded to 8 actual bits.
void MOVI(u8 size, ARM64Reg Rd, u8 imm8, u8 shift = 0, bool MSL = false);
void MVNI(u8 size, ARM64Reg Rd, u8 imm8, u8 shift = 0, bool MSL = false);
void ORR(u8 size, ARM64Reg Rd, u8 imm8, u8 shift = 0);
void BIC(u8 size, ARM64Reg Rd, u8 imm8, u8 shift = 0);
bool TryMOVI(u8 size, ARM64Reg Rd, uint64_t value);
// Allow using a different size. Unclear if there's a penalty.
bool TryAnyMOVI(u8 size, ARM64Reg Rd, uint64_t value);
// One source // One source
void FCVT(u8 size_to, u8 size_from, ARM64Reg Rd, ARM64Reg Rn); void FCVT(u8 size_to, u8 size_from, ARM64Reg Rd, ARM64Reg Rn);
@ -937,8 +883,6 @@ public:
// and one that outputs to a scalar fp register. // and one that outputs to a scalar fp register.
void FCVTS(ARM64Reg Rd, ARM64Reg Rn, RoundingMode round); void FCVTS(ARM64Reg Rd, ARM64Reg Rn, RoundingMode round);
void FCVTU(ARM64Reg Rd, ARM64Reg Rn, RoundingMode round); void FCVTU(ARM64Reg Rd, ARM64Reg Rn, RoundingMode round);
void FCVTZS(ARM64Reg Rd, ARM64Reg Rn, int scale);
void FCVTZU(ARM64Reg Rd, ARM64Reg Rn, int scale);
// Scalar convert int to float. No rounding mode specifier necessary. // Scalar convert int to float. No rounding mode specifier necessary.
void SCVTF(ARM64Reg Rd, ARM64Reg Rn); void SCVTF(ARM64Reg Rd, ARM64Reg Rn);
@ -965,10 +909,6 @@ public:
// Conditional select // Conditional select
void FCSEL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, CCFlags cond); void FCSEL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, CCFlags cond);
// Conditional compare
void FCCMP(ARM64Reg Rn, ARM64Reg Rm, u8 nzcv, CCFlags cond);
void FCCMPE(ARM64Reg Rn, ARM64Reg Rm, u8 nzcv, CCFlags cond);
// Permute // Permute
void UZP1(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void UZP1(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void TRN1(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void TRN1(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
@ -976,17 +916,12 @@ public:
void UZP2(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void UZP2(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void TRN2(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void TRN2(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void ZIP2(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void ZIP2(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
// Related to permute, extract vector from pair (always by byte arrangement.)
void EXT(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, int index);
// Shift by immediate // Shift by immediate
void SSHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift); void SSHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift);
void SSHLL2(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift); void SSHLL2(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift);
void USHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift); void USHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift);
void USHLL2(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift); void USHLL2(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift);
// Shift == src_size for these.
void SHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn);
void SHLL2(u8 src_size, ARM64Reg Rd, ARM64Reg Rn);
void SHRN(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift); void SHRN(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift);
void SHRN2(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift); void SHRN2(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift);
void SXTL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn); void SXTL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn);
@ -1003,7 +938,7 @@ public:
void FMLA(u8 esize, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, u8 index); void FMLA(u8 esize, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, u8 index);
void MOVI2F(ARM64Reg Rd, float value, ARM64Reg scratch = INVALID_REG, bool negate = false); void MOVI2F(ARM64Reg Rd, float value, ARM64Reg scratch = INVALID_REG, bool negate = false);
void MOVI2FDUP(ARM64Reg Rd, float value, ARM64Reg scratch = INVALID_REG, bool negate = false); void MOVI2FDUP(ARM64Reg Rd, float value, ARM64Reg scratch = INVALID_REG);
// ABI related // ABI related
void ABI_PushRegisters(uint32_t gpr_registers, uint32_t fp_registers); void ABI_PushRegisters(uint32_t gpr_registers, uint32_t fp_registers);
@ -1018,7 +953,6 @@ private:
void EmitScalar2Source(bool M, bool S, u32 type, u32 opcode, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void EmitScalar2Source(bool M, bool S, u32 type, u32 opcode, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void EmitThreeSame(bool U, u32 size, u32 opcode, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void EmitThreeSame(bool U, u32 size, u32 opcode, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void EmitCopy(bool Q, u32 op, u32 imm5, u32 imm4, ARM64Reg Rd, ARM64Reg Rn); void EmitCopy(bool Q, u32 op, u32 imm5, u32 imm4, ARM64Reg Rd, ARM64Reg Rn);
void EmitScalarPairwise(bool U, u32 size, u32 opcode, ARM64Reg Rd, ARM64Reg Rn);
void Emit2RegMisc(bool Q, bool U, u32 size, u32 opcode, ARM64Reg Rd, ARM64Reg Rn); void Emit2RegMisc(bool Q, bool U, u32 size, u32 opcode, ARM64Reg Rd, ARM64Reg Rn);
void EmitLoadStoreSingleStructure(bool L, bool R, u32 opcode, bool S, u32 size, ARM64Reg Rt, ARM64Reg Rn); void EmitLoadStoreSingleStructure(bool L, bool R, u32 opcode, bool S, u32 size, ARM64Reg Rt, ARM64Reg Rn);
void EmitLoadStoreSingleStructure(bool L, bool R, u32 opcode, bool S, u32 size, ARM64Reg Rt, ARM64Reg Rn, ARM64Reg Rm); void EmitLoadStoreSingleStructure(bool L, bool R, u32 opcode, bool S, u32 size, ARM64Reg Rt, ARM64Reg Rn, ARM64Reg Rm);
@ -1027,7 +961,6 @@ private:
void EmitConversion2(bool sf, bool S, bool direction, u32 type, u32 rmode, u32 opcode, int scale, ARM64Reg Rd, ARM64Reg Rn); void EmitConversion2(bool sf, bool S, bool direction, u32 type, u32 rmode, u32 opcode, int scale, ARM64Reg Rd, ARM64Reg Rn);
void EmitCompare(bool M, bool S, u32 op, u32 opcode2, ARM64Reg Rn, ARM64Reg Rm); void EmitCompare(bool M, bool S, u32 op, u32 opcode2, ARM64Reg Rn, ARM64Reg Rm);
void EmitCondSelect(bool M, bool S, CCFlags cond, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void EmitCondSelect(bool M, bool S, CCFlags cond, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void EmitCondCompare(bool M, bool S, CCFlags cond, int op, u8 nzcv, ARM64Reg Rn, ARM64Reg Rm);
void EmitPermute(u32 size, u32 op, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm); void EmitPermute(u32 size, u32 op, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
void EmitScalarImm(bool M, bool S, u32 type, u32 imm5, ARM64Reg Rd, u32 imm8); void EmitScalarImm(bool M, bool S, u32 type, u32 imm5, ARM64Reg Rd, u32 imm8);
void EmitShiftImm(bool Q, bool U, u32 immh, u32 immb, u32 opcode, ARM64Reg Rd, ARM64Reg Rn); void EmitShiftImm(bool Q, bool U, u32 immh, u32 immb, u32 opcode, ARM64Reg Rd, ARM64Reg Rn);
@ -1041,11 +974,9 @@ private:
void EmitScalar3Source(bool isDouble, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, ARM64Reg Ra, int opcode); void EmitScalar3Source(bool isDouble, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, ARM64Reg Ra, int opcode);
void EncodeLoadStorePair(u32 size, bool load, IndexType type, ARM64Reg Rt, ARM64Reg Rt2, ARM64Reg Rn, s32 imm); void EncodeLoadStorePair(u32 size, bool load, IndexType type, ARM64Reg Rt, ARM64Reg Rt2, ARM64Reg Rn, s32 imm);
void EncodeLoadStoreRegisterOffset(u32 size, bool load, ARM64Reg Rt, ARM64Reg Rn, ArithOption Rm); void EncodeLoadStoreRegisterOffset(u32 size, bool load, ARM64Reg Rt, ARM64Reg Rn, ArithOption Rm);
void EncodeModImm(bool Q, u8 op, u8 cmode, u8 o2, ARM64Reg Rd, u8 abcdefgh);
void SSHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift, bool upper); void SSHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift, bool upper);
void USHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift, bool upper); void USHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift, bool upper);
void SHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, bool upper);
void SHRN(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift, bool upper); void SHRN(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift, bool upper);
void SXTL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, bool upper); void SXTL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, bool upper);
void UXTL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, bool upper); void UXTL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, bool upper);

View file

@ -29,18 +29,17 @@
#if PPSSPP_ARCH(ARM) #if PPSSPP_ARCH(ARM)
#include "ext/cpu_features/include/cpuinfo_arm.h" #include "ext/cpu_features/include/cpuinfo_arm.h"
#if defined(CPU_FEATURES_OS_LINUX) #if defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
#define USE_CPU_FEATURES 1 #define USE_CPU_FEATURES 1
#endif #endif
#elif PPSSPP_ARCH(ARM64) #elif PPSSPP_ARCH(ARM64) && defined(__aarch64__)
#include "ext/cpu_features/include/cpuinfo_aarch64.h" #include "ext/cpu_features/include/cpuinfo_aarch64.h"
#if defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID) || defined(CPU_FEATURES_OS_WINDOWS) #if defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
#define USE_CPU_FEATURES 1 #define USE_CPU_FEATURES 1
#endif #endif
#endif #endif
#include <cstring>
#include <ctype.h> #include <ctype.h>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
@ -55,7 +54,7 @@
std::string GetCPUBrandString(); std::string GetCPUBrandString();
#else #else
// No CPUID on ARM, so we'll have to read the registry // No CPUID on ARM, so we'll have to read the registry
#include "Common/CommonWindows.h" #include <windows.h>
std::string GetCPUBrandString() { std::string GetCPUBrandString() {
std::string cpu_string; std::string cpu_string;

View file

@ -613,14 +613,6 @@ const u8 *ARMXEmitter::AlignCode16()
return code; return code;
} }
const u8 *ARMXEmitter::NopAlignCode16() {
int bytes = ((-(intptr_t)code) & 15);
for (int i = 0; i < bytes / 4; i++) {
Write32(0xE320F000); // one of many possible nops
}
return code;
}
const u8 *ARMXEmitter::AlignCodePage() const u8 *ARMXEmitter::AlignCodePage()
{ {
ReserveCodeSpace((-(intptr_t)code) & 4095); ReserveCodeSpace((-(intptr_t)code) & 4095);

View file

@ -446,8 +446,6 @@ public:
void ReserveCodeSpace(u32 bytes); void ReserveCodeSpace(u32 bytes);
const u8 *AlignCode16(); const u8 *AlignCode16();
const u8 *AlignCodePage(); const u8 *AlignCodePage();
const u8 *NopAlignCode16();
void FlushIcache(); void FlushIcache();
void FlushIcacheSection(u8 *start, u8 *end); void FlushIcacheSection(u8 *start, u8 *end);
u8 *GetWritableCodePtr(); u8 *GetWritableCodePtr();

View file

@ -42,7 +42,7 @@ void Buffer::Append(const Buffer &other) {
void Buffer::AppendValue(int value) { void Buffer::AppendValue(int value) {
char buf[16]; char buf[16];
// This is slow. // This is slow.
snprintf(buf, sizeof(buf), "%i", value); sprintf(buf, "%i", value);
Append(buf); Append(buf);
} }

View file

@ -17,7 +17,7 @@
// Reference : https://stackoverflow.com/questions/6121792/how-to-check-if-a-cpu-supports-the-sse3-instruction-set // Reference : https://stackoverflow.com/questions/6121792/how-to-check-if-a-cpu-supports-the-sse3-instruction-set
#include "ppsspp_config.h" #include "ppsspp_config.h"
#if (PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)) && !defined(__EMSCRIPTEN__) #if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
#include "ext/cpu_features/include/cpuinfo_x86.h" #include "ext/cpu_features/include/cpuinfo_x86.h"

View file

@ -109,27 +109,8 @@ struct CPUInfo {
bool RiscV_D; bool RiscV_D;
bool RiscV_C; bool RiscV_C;
bool RiscV_V; bool RiscV_V;
bool RiscV_B;
bool RiscV_Zicsr; bool RiscV_Zicsr;
bool RiscV_Zba;
bool RiscV_Zbb;
bool RiscV_Zbc;
bool RiscV_Zbs;
// LoongArch specific extension flags.
bool LOONGARCH_CPUCFG;
bool LOONGARCH_LAM;
bool LOONGARCH_UAL;
bool LOONGARCH_FPU;
bool LOONGARCH_LSX;
bool LOONGARCH_LASX;
bool LOONGARCH_CRC32;
bool LOONGARCH_COMPLEX;
bool LOONGARCH_CRYPTO;
bool LOONGARCH_LVZ;
bool LOONGARCH_LBT_X86;
bool LOONGARCH_LBT_ARM;
bool LOONGARCH_LBT_MIPS;
bool LOONGARCH_PTW;
// Quirks // Quirks
struct { struct {

View file

@ -10,11 +10,6 @@
#include "Common/Log.h" #include "Common/Log.h"
#include "Common/MemoryUtil.h" #include "Common/MemoryUtil.h"
#if PPSSPP_PLATFORM(SWITCH)
#include <cstdio>
#include <switch.h>
#endif // PPSSPP_PLATFORM(SWITCH)
// Everything that needs to generate code should inherit from this. // Everything that needs to generate code should inherit from this.
// You get memory management for free, plus, you can use all emitter functions without // You get memory management for free, plus, you can use all emitter functions without
// having to prefix them with gen-> or something similar. // having to prefix them with gen-> or something similar.
@ -32,7 +27,7 @@ public:
virtual const u8 *GetCodePtr() const = 0; virtual const u8 *GetCodePtr() const = 0;
u8 *GetBasePtr() const { u8 *GetBasePtr() {
return region; return region;
} }
@ -70,20 +65,9 @@ public:
// Call this before you generate any code. // Call this before you generate any code.
void AllocCodeSpace(int size) { void AllocCodeSpace(int size) {
region_size = size; region_size = size;
#if PPSSPP_PLATFORM(SWITCH)
Result rc = jitCreate(&jitController, size);
if(R_FAILED(rc)) {
printf("Failed to create Jitbuffer of size 0x%x err: 0x%x\n", size, rc);
}
printf("[NXJIT]: Initialized RX: %p RW: %p\n", jitController.rx_addr, jitController.rw_addr);
region = (u8 *)jitController.rx_addr;
writableRegion = (u8 *)jitController.rw_addr;
#else // PPSSPP_PLATFORM(SWITCH)
// The protection will be set to RW if PlatformIsWXExclusive. // The protection will be set to RW if PlatformIsWXExclusive.
region = (u8 *)AllocateExecutableMemory(region_size); region = (u8 *)AllocateExecutableMemory(region_size);
writableRegion = region; writableRegion = region;
#endif // !PPSSPP_PLATFORM(SWITCH)
T::SetCodePointer(region, writableRegion); T::SetCodePointer(region, writableRegion);
} }
@ -151,13 +135,8 @@ public:
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job. // Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
void FreeCodeSpace() { void FreeCodeSpace() {
#if !PPSSPP_PLATFORM(SWITCH)
ProtectMemoryPages(region, region_size, MEM_PROT_READ | MEM_PROT_WRITE); ProtectMemoryPages(region, region_size, MEM_PROT_READ | MEM_PROT_WRITE);
FreeExecutableMemory(region, region_size); FreeMemoryPages(region, region_size);
#else // !PPSSPP_PLATFORM(SWITCH)
jitClose(&jitController);
printf("[NXJIT]: Jit closed\n");
#endif // PPSSPP_PLATFORM(SWITCH)
region = nullptr; region = nullptr;
writableRegion = nullptr; writableRegion = nullptr;
region_size = 0; region_size = 0;
@ -197,7 +176,5 @@ private:
const uint8_t *writeStart_ = nullptr; const uint8_t *writeStart_ = nullptr;
uint8_t *writableRegion = nullptr; uint8_t *writableRegion = nullptr;
size_t writeEstimated_ = 0; size_t writeEstimated_ = 0;
#if PPSSPP_PLATFORM(SWITCH)
Jit jitController;
#endif // PPSSPP_PLATFORM(SWITCH)
}; };

View file

@ -143,7 +143,6 @@
<RuntimeTypeInfo>false</RuntimeTypeInfo> <RuntimeTypeInfo>false</RuntimeTypeInfo>
<MinimalRebuild>false</MinimalRebuild> <MinimalRebuild>false</MinimalRebuild>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>
@ -171,7 +170,6 @@
<RuntimeTypeInfo>false</RuntimeTypeInfo> <RuntimeTypeInfo>false</RuntimeTypeInfo>
<MinimalRebuild>false</MinimalRebuild> <MinimalRebuild>false</MinimalRebuild>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>
@ -199,7 +197,6 @@
<RuntimeTypeInfo>false</RuntimeTypeInfo> <RuntimeTypeInfo>false</RuntimeTypeInfo>
<MinimalRebuild>false</MinimalRebuild> <MinimalRebuild>false</MinimalRebuild>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>
@ -228,7 +225,6 @@
<RuntimeTypeInfo>false</RuntimeTypeInfo> <RuntimeTypeInfo>false</RuntimeTypeInfo>
<MinimalRebuild>false</MinimalRebuild> <MinimalRebuild>false</MinimalRebuild>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>
@ -260,7 +256,6 @@
<RuntimeTypeInfo>false</RuntimeTypeInfo> <RuntimeTypeInfo>false</RuntimeTypeInfo>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary> <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>
@ -296,7 +291,6 @@
<RuntimeLibrary>MultiThreaded</RuntimeLibrary> <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StringPooling>true</StringPooling> <StringPooling>true</StringPooling>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>
@ -332,7 +326,6 @@
<RuntimeLibrary>MultiThreaded</RuntimeLibrary> <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StringPooling>true</StringPooling> <StringPooling>true</StringPooling>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>
@ -368,7 +361,6 @@
<RuntimeLibrary>MultiThreaded</RuntimeLibrary> <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StringPooling>true</StringPooling> <StringPooling>true</StringPooling>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>
@ -385,13 +377,6 @@
</Lib> </Lib>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\ext\basis_universal\basisu.h" />
<ClInclude Include="..\ext\basis_universal\basisu_containers.h" />
<ClInclude Include="..\ext\basis_universal\basisu_containers_impl.h" />
<ClInclude Include="..\ext\basis_universal\basisu_file_headers.h" />
<ClInclude Include="..\ext\basis_universal\basisu_transcoder.h" />
<ClInclude Include="..\ext\basis_universal\basisu_transcoder_internal.h" />
<ClInclude Include="..\ext\basis_universal\basisu_transcoder_uastc.h" />
<ClInclude Include="..\ext\libpng17\png.h" /> <ClInclude Include="..\ext\libpng17\png.h" />
<ClInclude Include="..\ext\libpng17\pngconf.h" /> <ClInclude Include="..\ext\libpng17\pngconf.h" />
<ClInclude Include="..\ext\libpng17\pngdebug.h" /> <ClInclude Include="..\ext\libpng17\pngdebug.h" />
@ -399,7 +384,6 @@
<ClInclude Include="..\ext\libpng17\pnglibconf.h" /> <ClInclude Include="..\ext\libpng17\pnglibconf.h" />
<ClInclude Include="..\ext\libpng17\pngpriv.h" /> <ClInclude Include="..\ext\libpng17\pngpriv.h" />
<ClInclude Include="..\ext\libpng17\pngstruct.h" /> <ClInclude Include="..\ext\libpng17\pngstruct.h" />
<ClInclude Include="..\ext\naett\naett.h" />
<ClInclude Include="..\ext\vma\vk_mem_alloc.h" /> <ClInclude Include="..\ext\vma\vk_mem_alloc.h" />
<ClInclude Include="ABI.h" /> <ClInclude Include="ABI.h" />
<ClInclude Include="Arm64Emitter.h" /> <ClInclude Include="Arm64Emitter.h" />
@ -421,7 +405,6 @@
<ClInclude Include="Data\Encoding\Shiftjis.h" /> <ClInclude Include="Data\Encoding\Shiftjis.h" />
<ClInclude Include="Data\Encoding\Utf16.h" /> <ClInclude Include="Data\Encoding\Utf16.h" />
<ClInclude Include="Data\Encoding\Utf8.h" /> <ClInclude Include="Data\Encoding\Utf8.h" />
<ClInclude Include="Data\Format\DDSLoad.h" />
<ClInclude Include="Data\Format\IniFile.h" /> <ClInclude Include="Data\Format\IniFile.h" />
<ClInclude Include="Data\Format\JSONReader.h" /> <ClInclude Include="Data\Format\JSONReader.h" />
<ClInclude Include="Data\Format\JSONWriter.h" /> <ClInclude Include="Data\Format\JSONWriter.h" />
@ -435,7 +418,6 @@
<ClInclude Include="Data\Text\Parsers.h" /> <ClInclude Include="Data\Text\Parsers.h" />
<ClInclude Include="Data\Text\WrapText.h" /> <ClInclude Include="Data\Text\WrapText.h" />
<ClInclude Include="FakeEmitter.h" /> <ClInclude Include="FakeEmitter.h" />
<ClInclude Include="File\AndroidContentURI.h" />
<ClInclude Include="File\AndroidStorage.h" /> <ClInclude Include="File\AndroidStorage.h" />
<ClInclude Include="File\DirListing.h" /> <ClInclude Include="File\DirListing.h" />
<ClInclude Include="File\DiskFree.h" /> <ClInclude Include="File\DiskFree.h" />
@ -443,21 +425,17 @@
<ClInclude Include="File\FileUtil.h" /> <ClInclude Include="File\FileUtil.h" />
<ClInclude Include="File\Path.h" /> <ClInclude Include="File\Path.h" />
<ClInclude Include="File\PathBrowser.h" /> <ClInclude Include="File\PathBrowser.h" />
<ClInclude Include="File\VFS\DirectoryReader.h" />
<ClInclude Include="File\VFS\VFS.h" /> <ClInclude Include="File\VFS\VFS.h" />
<ClInclude Include="File\VFS\ZipFileReader.h" /> <ClInclude Include="File\VFS\AssetReader.h" />
<ClInclude Include="GPU\D3D11\D3D11Loader.h" /> <ClInclude Include="GPU\D3D11\D3D11Loader.h" />
<ClInclude Include="GPU\D3D9\D3DCompilerLoader.h" /> <ClInclude Include="GPU\D3D9\D3DCompilerLoader.h" />
<ClInclude Include="GPU\D3D9\D3D9ShaderCompiler.h" /> <ClInclude Include="GPU\D3D9\D3D9ShaderCompiler.h" />
<ClInclude Include="GPU\D3D9\D3D9StateCache.h" /> <ClInclude Include="GPU\D3D9\D3D9StateCache.h" />
<ClInclude Include="GPU\DataFormat.h" /> <ClInclude Include="GPU\DataFormat.h" />
<ClInclude Include="GPU\GPUBackendCommon.h" />
<ClInclude Include="GPU\MiscTypes.h" /> <ClInclude Include="GPU\MiscTypes.h" />
<ClInclude Include="GPU\OpenGL\DataFormatGL.h" /> <ClInclude Include="GPU\OpenGL\DataFormatGL.h" />
<ClInclude Include="GPU\OpenGL\gl3stub.h" /> <ClInclude Include="GPU\OpenGL\gl3stub.h" />
<ClInclude Include="GPU\OpenGL\GLFeatures.h" /> <ClInclude Include="GPU\OpenGL\GLFeatures.h" />
<ClInclude Include="GPU\OpenGL\GLFrameData.h" />
<ClInclude Include="GPU\OpenGL\GLMemory.h" />
<ClInclude Include="GPU\OpenGL\GLQueueRunner.h" /> <ClInclude Include="GPU\OpenGL\GLQueueRunner.h" />
<ClInclude Include="GPU\OpenGL\GLRenderManager.h" /> <ClInclude Include="GPU\OpenGL\GLRenderManager.h" />
<ClInclude Include="GPU\OpenGL\GLSLProgram.h" /> <ClInclude Include="GPU\OpenGL\GLSLProgram.h" />
@ -472,7 +450,6 @@
<ClInclude Include="GPU\Vulkan\VulkanBarrier.h" /> <ClInclude Include="GPU\Vulkan\VulkanBarrier.h" />
<ClInclude Include="GPU\Vulkan\VulkanContext.h" /> <ClInclude Include="GPU\Vulkan\VulkanContext.h" />
<ClInclude Include="GPU\Vulkan\VulkanDebug.h" /> <ClInclude Include="GPU\Vulkan\VulkanDebug.h" />
<ClInclude Include="GPU\Vulkan\VulkanDescSet.h" />
<ClInclude Include="GPU\Vulkan\VulkanFramebuffer.h" /> <ClInclude Include="GPU\Vulkan\VulkanFramebuffer.h" />
<ClInclude Include="GPU\Vulkan\VulkanFrameData.h" /> <ClInclude Include="GPU\Vulkan\VulkanFrameData.h" />
<ClInclude Include="GPU\Vulkan\VulkanImage.h" /> <ClInclude Include="GPU\Vulkan\VulkanImage.h" />
@ -492,12 +469,10 @@
<ClInclude Include="Math\lin\vec3.h" /> <ClInclude Include="Math\lin\vec3.h" />
<ClInclude Include="Math\math_util.h" /> <ClInclude Include="Math\math_util.h" />
<ClInclude Include="Math\Statistics.h" /> <ClInclude Include="Math\Statistics.h" />
<ClInclude Include="Net\HTTPNaettRequest.h" />
<ClInclude Include="Net\NetBuffer.h" /> <ClInclude Include="Net\NetBuffer.h" />
<ClInclude Include="Net\HTTPClient.h" /> <ClInclude Include="Net\HTTPClient.h" />
<ClInclude Include="Net\HTTPHeaders.h" /> <ClInclude Include="Net\HTTPHeaders.h" />
<ClInclude Include="Net\HTTPServer.h" /> <ClInclude Include="Net\HTTPServer.h" />
<ClInclude Include="Net\HTTPRequest.h" />
<ClInclude Include="Net\Resolve.h" /> <ClInclude Include="Net\Resolve.h" />
<ClInclude Include="Net\Sinks.h" /> <ClInclude Include="Net\Sinks.h" />
<ClInclude Include="Net\URL.h" /> <ClInclude Include="Net\URL.h" />
@ -509,7 +484,6 @@
<ClInclude Include="Render\Text\draw_text.h" /> <ClInclude Include="Render\Text\draw_text.h" />
<ClInclude Include="Render\Text\draw_text_android.h" /> <ClInclude Include="Render\Text\draw_text_android.h" />
<ClInclude Include="Render\Text\draw_text_qt.h" /> <ClInclude Include="Render\Text\draw_text_qt.h" />
<ClInclude Include="Render\Text\draw_text_sdl.h" />
<ClInclude Include="Render\Text\draw_text_uwp.h" /> <ClInclude Include="Render\Text\draw_text_uwp.h" />
<ClInclude Include="Render\Text\draw_text_win.h" /> <ClInclude Include="Render\Text\draw_text_win.h" />
<ClInclude Include="LogReporting.h" /> <ClInclude Include="LogReporting.h" />
@ -532,6 +506,27 @@
<ClInclude Include="Crypto\sha256.h" /> <ClInclude Include="Crypto\sha256.h" />
<ClInclude Include="DbgNew.h" /> <ClInclude Include="DbgNew.h" />
<ClInclude Include="ExceptionHandlerSetup.h" /> <ClInclude Include="ExceptionHandlerSetup.h" />
<ClInclude Include="GL\GLInterfaceBase.h" />
<ClInclude Include="GL\GLInterface\EGL.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="GL\GLInterface\EGLAndroid.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="GraphicsContext.h" /> <ClInclude Include="GraphicsContext.h" />
<ClInclude Include="Log.h" /> <ClInclude Include="Log.h" />
<ClInclude Include="LogManager.h" /> <ClInclude Include="LogManager.h" />
@ -545,8 +540,6 @@
<ClInclude Include="Swap.h" /> <ClInclude Include="Swap.h" />
<ClInclude Include="SysError.h" /> <ClInclude Include="SysError.h" />
<ClInclude Include="System\Display.h" /> <ClInclude Include="System\Display.h" />
<ClInclude Include="System\OSD.h" />
<ClInclude Include="System\Request.h" />
<ClInclude Include="System\NativeApp.h" /> <ClInclude Include="System\NativeApp.h" />
<ClInclude Include="System\System.h" /> <ClInclude Include="System\System.h" />
<ClInclude Include="Thread\Barrier.h" /> <ClInclude Include="Thread\Barrier.h" />
@ -561,7 +554,6 @@
<ClInclude Include="TimeUtil.h" /> <ClInclude Include="TimeUtil.h" />
<ClInclude Include="UI\AsyncImageFileView.h" /> <ClInclude Include="UI\AsyncImageFileView.h" />
<ClInclude Include="UI\Context.h" /> <ClInclude Include="UI\Context.h" />
<ClInclude Include="UI\IconCache.h" />
<ClInclude Include="UI\PopupScreens.h" /> <ClInclude Include="UI\PopupScreens.h" />
<ClInclude Include="UI\Root.h" /> <ClInclude Include="UI\Root.h" />
<ClInclude Include="UI\Screen.h" /> <ClInclude Include="UI\Screen.h" />
@ -582,7 +574,6 @@
<ClInclude Include="x64Emitter.h" /> <ClInclude Include="x64Emitter.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\ext\basis_universal\basisu_transcoder.cpp" />
<ClCompile Include="..\ext\libpng17\png.c"> <ClCompile Include="..\ext\libpng17\png.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
@ -831,7 +822,6 @@
<ForcedIncludeFiles Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ForcedIncludeFiles Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
</ForcedIncludeFiles> </ForcedIncludeFiles>
</ClCompile> </ClCompile>
<ClCompile Include="..\ext\naett\naett.c" />
<ClCompile Include="..\ext\vma\vk_mem_alloc.cpp" /> <ClCompile Include="..\ext\vma\vk_mem_alloc.cpp" />
<ClCompile Include="ABI.cpp" /> <ClCompile Include="ABI.cpp" />
<ClCompile Include="Arm64Emitter.cpp" /> <ClCompile Include="Arm64Emitter.cpp" />
@ -847,13 +837,11 @@
</ClCompile> </ClCompile>
<ClCompile Include="ArmEmitter.cpp" /> <ClCompile Include="ArmEmitter.cpp" />
<ClCompile Include="Buffer.cpp" /> <ClCompile Include="Buffer.cpp" />
<ClCompile Include="Data\Collections\FastVec.h" />
<ClCompile Include="Data\Color\RGBAUtil.cpp" /> <ClCompile Include="Data\Color\RGBAUtil.cpp" />
<ClCompile Include="Data\Convert\SmallDataConvert.cpp" /> <ClCompile Include="Data\Convert\SmallDataConvert.cpp" />
<ClCompile Include="Data\Encoding\Base64.cpp" /> <ClCompile Include="Data\Encoding\Base64.cpp" />
<ClCompile Include="Data\Encoding\Compression.cpp" /> <ClCompile Include="Data\Encoding\Compression.cpp" />
<ClCompile Include="Data\Encoding\Utf8.cpp" /> <ClCompile Include="Data\Encoding\Utf8.cpp" />
<ClCompile Include="Data\Format\DDSLoad.cpp" />
<ClCompile Include="Data\Format\IniFile.cpp" /> <ClCompile Include="Data\Format\IniFile.cpp" />
<ClCompile Include="Data\Format\JSONReader.cpp" /> <ClCompile Include="Data\Format\JSONReader.cpp" />
<ClCompile Include="Data\Format\JSONWriter.cpp" /> <ClCompile Include="Data\Format\JSONWriter.cpp" />
@ -865,7 +853,6 @@
<ClCompile Include="Data\Text\I18n.cpp" /> <ClCompile Include="Data\Text\I18n.cpp" />
<ClCompile Include="Data\Text\Parsers.cpp" /> <ClCompile Include="Data\Text\Parsers.cpp" />
<ClCompile Include="Data\Text\WrapText.cpp" /> <ClCompile Include="Data\Text\WrapText.cpp" />
<ClCompile Include="File\AndroidContentURI.cpp" />
<ClCompile Include="File\AndroidStorage.cpp" /> <ClCompile Include="File\AndroidStorage.cpp" />
<ClCompile Include="File\DirListing.cpp" /> <ClCompile Include="File\DirListing.cpp" />
<ClCompile Include="File\DiskFree.cpp" /> <ClCompile Include="File\DiskFree.cpp" />
@ -873,21 +860,17 @@
<ClCompile Include="File\FileUtil.cpp" /> <ClCompile Include="File\FileUtil.cpp" />
<ClCompile Include="File\Path.cpp" /> <ClCompile Include="File\Path.cpp" />
<ClCompile Include="File\PathBrowser.cpp" /> <ClCompile Include="File\PathBrowser.cpp" />
<ClCompile Include="File\VFS\DirectoryReader.cpp" />
<ClCompile Include="File\VFS\VFS.cpp" /> <ClCompile Include="File\VFS\VFS.cpp" />
<ClCompile Include="File\VFS\ZipFileReader.cpp" /> <ClCompile Include="File\VFS\AssetReader.cpp" />
<ClCompile Include="GPU\D3D11\D3D11Loader.cpp" /> <ClCompile Include="GPU\D3D11\D3D11Loader.cpp" />
<ClCompile Include="GPU\D3D11\thin3d_d3d11.cpp" /> <ClCompile Include="GPU\D3D11\thin3d_d3d11.cpp" />
<ClCompile Include="GPU\D3D9\D3DCompilerLoader.cpp" /> <ClCompile Include="GPU\D3D9\D3DCompilerLoader.cpp" />
<ClCompile Include="GPU\D3D9\D3D9ShaderCompiler.cpp" /> <ClCompile Include="GPU\D3D9\D3D9ShaderCompiler.cpp" />
<ClCompile Include="GPU\D3D9\D3D9StateCache.cpp" /> <ClCompile Include="GPU\D3D9\D3D9StateCache.cpp" />
<ClCompile Include="GPU\D3D9\thin3d_d3d9.cpp" /> <ClCompile Include="GPU\D3D9\thin3d_d3d9.cpp" />
<ClCompile Include="GPU\GPUBackendCommon.cpp" />
<ClCompile Include="GPU\OpenGL\DataFormatGL.cpp" /> <ClCompile Include="GPU\OpenGL\DataFormatGL.cpp" />
<ClCompile Include="GPU\OpenGL\gl3stub.c" /> <ClCompile Include="GPU\OpenGL\gl3stub.c" />
<ClCompile Include="GPU\OpenGL\GLFeatures.cpp" /> <ClCompile Include="GPU\OpenGL\GLFeatures.cpp" />
<ClCompile Include="GPU\OpenGL\GLFrameData.cpp" />
<ClCompile Include="GPU\OpenGL\GLMemory.cpp" />
<ClCompile Include="GPU\OpenGL\GLQueueRunner.cpp" /> <ClCompile Include="GPU\OpenGL\GLQueueRunner.cpp" />
<ClCompile Include="GPU\OpenGL\GLRenderManager.cpp" /> <ClCompile Include="GPU\OpenGL\GLRenderManager.cpp" />
<ClCompile Include="GPU\OpenGL\GLSLProgram.cpp" /> <ClCompile Include="GPU\OpenGL\GLSLProgram.cpp" />
@ -901,7 +884,6 @@
<ClCompile Include="GPU\Vulkan\VulkanBarrier.cpp" /> <ClCompile Include="GPU\Vulkan\VulkanBarrier.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanContext.cpp" /> <ClCompile Include="GPU\Vulkan\VulkanContext.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanDebug.cpp" /> <ClCompile Include="GPU\Vulkan\VulkanDebug.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanDescSet.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanFramebuffer.cpp" /> <ClCompile Include="GPU\Vulkan\VulkanFramebuffer.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanFrameData.cpp" /> <ClCompile Include="GPU\Vulkan\VulkanFrameData.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanImage.cpp" /> <ClCompile Include="GPU\Vulkan\VulkanImage.cpp" />
@ -920,12 +902,10 @@
<ClCompile Include="Math\lin\vec3.cpp" /> <ClCompile Include="Math\lin\vec3.cpp" />
<ClCompile Include="Math\math_util.cpp" /> <ClCompile Include="Math\math_util.cpp" />
<ClCompile Include="Math\Statistics.cpp" /> <ClCompile Include="Math\Statistics.cpp" />
<ClCompile Include="Net\HTTPNaettRequest.cpp" />
<ClCompile Include="Net\NetBuffer.cpp" /> <ClCompile Include="Net\NetBuffer.cpp" />
<ClCompile Include="Net\HTTPClient.cpp" /> <ClCompile Include="Net\HTTPClient.cpp" />
<ClCompile Include="Net\HTTPHeaders.cpp" /> <ClCompile Include="Net\HTTPHeaders.cpp" />
<ClCompile Include="Net\HTTPServer.cpp" /> <ClCompile Include="Net\HTTPServer.cpp" />
<ClCompile Include="Net\HTTPRequest.cpp" />
<ClCompile Include="Net\Resolve.cpp" /> <ClCompile Include="Net\Resolve.cpp" />
<ClCompile Include="Net\Sinks.cpp" /> <ClCompile Include="Net\Sinks.cpp" />
<ClCompile Include="Net\URL.cpp" /> <ClCompile Include="Net\URL.cpp" />
@ -937,13 +917,11 @@
<ClCompile Include="Render\Text\draw_text.cpp" /> <ClCompile Include="Render\Text\draw_text.cpp" />
<ClCompile Include="Render\Text\draw_text_android.cpp" /> <ClCompile Include="Render\Text\draw_text_android.cpp" />
<ClCompile Include="Render\Text\draw_text_qt.cpp" /> <ClCompile Include="Render\Text\draw_text_qt.cpp" />
<ClCompile Include="Render\Text\draw_text_sdl.cpp" />
<ClCompile Include="Render\Text\draw_text_uwp.cpp" /> <ClCompile Include="Render\Text\draw_text_uwp.cpp" />
<ClCompile Include="Render\Text\draw_text_win.cpp" /> <ClCompile Include="Render\Text\draw_text_win.cpp" />
<ClCompile Include="LogReporting.cpp" /> <ClCompile Include="LogReporting.cpp" />
<ClCompile Include="RiscVCPUDetect.cpp" /> <ClCompile Include="RiscVCPUDetect.cpp" />
<ClCompile Include="RiscVEmitter.cpp" /> <ClCompile Include="RiscVEmitter.cpp" />
<ClCompile Include="LoongArchCPUDetect.cpp" />
<ClCompile Include="Serialize\Serializer.cpp" /> <ClCompile Include="Serialize\Serializer.cpp" />
<ClCompile Include="Data\Convert\ColorConv.cpp" /> <ClCompile Include="Data\Convert\ColorConv.cpp" />
<ClCompile Include="ConsoleListener.cpp" /> <ClCompile Include="ConsoleListener.cpp" />
@ -972,6 +950,36 @@
<ClCompile Include="Crypto\sha1.cpp" /> <ClCompile Include="Crypto\sha1.cpp" />
<ClCompile Include="Crypto\sha256.cpp" /> <ClCompile Include="Crypto\sha256.cpp" />
<ClCompile Include="ExceptionHandlerSetup.cpp" /> <ClCompile Include="ExceptionHandlerSetup.cpp" />
<ClCompile Include="GL\GLInterface\EGL.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GL\GLInterface\EGLAndroid.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GL\GLInterface\GLInterface.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="LogManager.cpp" /> <ClCompile Include="LogManager.cpp" />
<ClCompile Include="MemArenaAndroid.cpp" /> <ClCompile Include="MemArenaAndroid.cpp" />
<ClCompile Include="MemArenaPosix.cpp" /> <ClCompile Include="MemArenaPosix.cpp" />
@ -983,8 +991,6 @@
<ClCompile Include="OSVersion.cpp" /> <ClCompile Include="OSVersion.cpp" />
<ClCompile Include="StringUtils.cpp" /> <ClCompile Include="StringUtils.cpp" />
<ClCompile Include="System\Display.cpp" /> <ClCompile Include="System\Display.cpp" />
<ClCompile Include="System\OSD.cpp" />
<ClCompile Include="System\Request.cpp" />
<ClCompile Include="Thread\ParallelLoop.cpp" /> <ClCompile Include="Thread\ParallelLoop.cpp" />
<ClCompile Include="Thread\ThreadManager.cpp" /> <ClCompile Include="Thread\ThreadManager.cpp" />
<ClCompile Include="Thread\ThreadUtil.cpp" /> <ClCompile Include="Thread\ThreadUtil.cpp" />
@ -992,7 +998,6 @@
<ClCompile Include="TimeUtil.cpp" /> <ClCompile Include="TimeUtil.cpp" />
<ClCompile Include="UI\AsyncImageFileView.cpp" /> <ClCompile Include="UI\AsyncImageFileView.cpp" />
<ClCompile Include="UI\Context.cpp" /> <ClCompile Include="UI\Context.cpp" />
<ClCompile Include="UI\IconCache.cpp" />
<ClCompile Include="UI\PopupScreens.cpp" /> <ClCompile Include="UI\PopupScreens.cpp" />
<ClCompile Include="UI\Root.cpp" /> <ClCompile Include="UI\Root.cpp" />
<ClCompile Include="UI\Screen.cpp" /> <ClCompile Include="UI\Screen.cpp" />
@ -1023,18 +1028,6 @@
<Project>{f761046e-6c38-4428-a5f1-38391a37bb34}</Project> <Project>{f761046e-6c38-4428-a5f1-38391a37bb34}</Project>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="..\ext\basis_universal\basisu_transcoder_tables_astc.inc" />
<None Include="..\ext\basis_universal\basisu_transcoder_tables_astc_0_255.inc" />
<None Include="..\ext\basis_universal\basisu_transcoder_tables_atc_55.inc" />
<None Include="..\ext\basis_universal\basisu_transcoder_tables_atc_56.inc" />
<None Include="..\ext\basis_universal\basisu_transcoder_tables_bc7_m5_alpha.inc" />
<None Include="..\ext\basis_universal\basisu_transcoder_tables_bc7_m5_color.inc" />
<None Include="..\ext\basis_universal\basisu_transcoder_tables_dxt1_5.inc" />
<None Include="..\ext\basis_universal\basisu_transcoder_tables_dxt1_6.inc" />
<None Include="..\ext\basis_universal\basisu_transcoder_tables_pvrtc2_45.inc" />
<None Include="..\ext\basis_universal\basisu_transcoder_tables_pvrtc2_alpha_33.inc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>

View file

@ -32,6 +32,15 @@
<ClInclude Include="ArmCommon.h" /> <ClInclude Include="ArmCommon.h" />
<ClInclude Include="BitSet.h" /> <ClInclude Include="BitSet.h" />
<ClInclude Include="CodeBlock.h" /> <ClInclude Include="CodeBlock.h" />
<ClInclude Include="GL\GLInterface\EGL.h">
<Filter>GL\GLInterface</Filter>
</ClInclude>
<ClInclude Include="GL\GLInterface\EGLAndroid.h">
<Filter>GL\GLInterface</Filter>
</ClInclude>
<ClInclude Include="GL\GLInterfaceBase.h">
<Filter>GL</Filter>
</ClInclude>
<ClInclude Include="GraphicsContext.h" /> <ClInclude Include="GraphicsContext.h" />
<ClInclude Include="DbgNew.h" /> <ClInclude Include="DbgNew.h" />
<ClInclude Include="OSVersion.h" /> <ClInclude Include="OSVersion.h" />
@ -167,6 +176,9 @@
<ClInclude Include="File\VFS\VFS.h"> <ClInclude Include="File\VFS\VFS.h">
<Filter>File\VFS</Filter> <Filter>File\VFS</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="File\VFS\AssetReader.h">
<Filter>File\VFS</Filter>
</ClInclude>
<ClInclude Include="Data\Format\IniFile.h"> <ClInclude Include="Data\Format\IniFile.h">
<Filter>Data\Format</Filter> <Filter>Data\Format</Filter>
</ClInclude> </ClInclude>
@ -440,6 +452,9 @@
<ClInclude Include="Render\ManagedTexture.h"> <ClInclude Include="Render\ManagedTexture.h">
<Filter>Render</Filter> <Filter>Render</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="GPU\MiscTypes.h">
<Filter>GPU</Filter>
</ClInclude>
<ClInclude Include="GPU\Vulkan\VulkanFramebuffer.h"> <ClInclude Include="GPU\Vulkan\VulkanFramebuffer.h">
<Filter>GPU\Vulkan</Filter> <Filter>GPU\Vulkan</Filter>
</ClInclude> </ClInclude>
@ -449,75 +464,6 @@
<ClInclude Include="UI\PopupScreens.h"> <ClInclude Include="UI\PopupScreens.h">
<Filter>UI</Filter> <Filter>UI</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="GPU\OpenGL\GLFrameData.h">
<Filter>GPU\OpenGL</Filter>
</ClInclude>
<ClInclude Include="File\VFS\DirectoryReader.h">
<Filter>File\VFS</Filter>
</ClInclude>
<ClInclude Include="File\VFS\ZipFileReader.h">
<Filter>File\VFS</Filter>
</ClInclude>
<ClInclude Include="Data\Format\DDSLoad.h">
<Filter>Data\Format</Filter>
</ClInclude>
<ClInclude Include="..\ext\basis_universal\basisu.h">
<Filter>ext\basis_universal</Filter>
</ClInclude>
<ClInclude Include="..\ext\basis_universal\basisu_containers.h">
<Filter>ext\basis_universal</Filter>
</ClInclude>
<ClInclude Include="..\ext\basis_universal\basisu_containers_impl.h">
<Filter>ext\basis_universal</Filter>
</ClInclude>
<ClInclude Include="..\ext\basis_universal\basisu_file_headers.h">
<Filter>ext\basis_universal</Filter>
</ClInclude>
<ClInclude Include="..\ext\basis_universal\basisu_transcoder.h">
<Filter>ext\basis_universal</Filter>
</ClInclude>
<ClInclude Include="..\ext\basis_universal\basisu_transcoder_internal.h">
<Filter>ext\basis_universal</Filter>
</ClInclude>
<ClInclude Include="..\ext\basis_universal\basisu_transcoder_uastc.h">
<Filter>ext\basis_universal</Filter>
</ClInclude>
<ClInclude Include="System\Request.h">
<Filter>System</Filter>
</ClInclude>
<ClInclude Include="File\AndroidContentURI.h">
<Filter>File</Filter>
</ClInclude>
<ClInclude Include="GPU\OpenGL\GLMemory.h">
<Filter>GPU\OpenGL</Filter>
</ClInclude>
<ClInclude Include="GPU\GPUBackendCommon.h">
<Filter>GPU</Filter>
</ClInclude>
<ClInclude Include="GPU\MiscTypes.h">
<Filter>GPU</Filter>
</ClInclude>
<ClInclude Include="UI\IconCache.h">
<Filter>UI</Filter>
</ClInclude>
<ClInclude Include="System\OSD.h">
<Filter>System</Filter>
</ClInclude>
<ClInclude Include="Net\HTTPRequest.h">
<Filter>Net</Filter>
</ClInclude>
<ClInclude Include="..\ext\naett\naett.h">
<Filter>ext\naett</Filter>
</ClInclude>
<ClInclude Include="Net\HTTPNaettRequest.h">
<Filter>Net</Filter>
</ClInclude>
<ClInclude Include="Render\Text\draw_text_sdl.h">
<Filter>Render\Text</Filter>
</ClInclude>
<ClInclude Include="GPU\Vulkan\VulkanDescSet.h">
<Filter>GPU\Vulkan</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="ABI.cpp" /> <ClCompile Include="ABI.cpp" />
@ -544,6 +490,15 @@
</ClCompile> </ClCompile>
<ClCompile Include="MipsEmitter.cpp" /> <ClCompile Include="MipsEmitter.cpp" />
<ClCompile Include="Arm64Emitter.cpp" /> <ClCompile Include="Arm64Emitter.cpp" />
<ClCompile Include="GL\GLInterface\EGL.cpp">
<Filter>GL\GLInterface</Filter>
</ClCompile>
<ClCompile Include="GL\GLInterface\EGLAndroid.cpp">
<Filter>GL\GLInterface</Filter>
</ClCompile>
<ClCompile Include="GL\GLInterface\GLInterface.cpp">
<Filter>GL\GLInterface</Filter>
</ClCompile>
<ClCompile Include="MemArenaPosix.cpp" /> <ClCompile Include="MemArenaPosix.cpp" />
<ClCompile Include="MemArenaWin32.cpp" /> <ClCompile Include="MemArenaWin32.cpp" />
<ClCompile Include="MemArenaAndroid.cpp" /> <ClCompile Include="MemArenaAndroid.cpp" />
@ -670,6 +625,9 @@
<ClCompile Include="File\VFS\VFS.cpp"> <ClCompile Include="File\VFS\VFS.cpp">
<Filter>File\VFS</Filter> <Filter>File\VFS</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="File\VFS\AssetReader.cpp">
<Filter>File\VFS</Filter>
</ClCompile>
<ClCompile Include="Data\Format\IniFile.cpp"> <ClCompile Include="Data\Format\IniFile.cpp">
<Filter>Data\Format</Filter> <Filter>Data\Format</Filter>
</ClCompile> </ClCompile>
@ -878,7 +836,6 @@
<Filter>GPU\Vulkan</Filter> <Filter>GPU\Vulkan</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="RiscVEmitter.cpp" /> <ClCompile Include="RiscVEmitter.cpp" />
<ClCompile Include="LoongArchCPUDetect.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanFrameData.cpp"> <ClCompile Include="GPU\Vulkan\VulkanFrameData.cpp">
<Filter>GPU\Vulkan</Filter> <Filter>GPU\Vulkan</Filter>
</ClCompile> </ClCompile>
@ -921,62 +878,17 @@
<ClCompile Include="UI\PopupScreens.cpp"> <ClCompile Include="UI\PopupScreens.cpp">
<Filter>UI</Filter> <Filter>UI</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="GPU\OpenGL\GLFrameData.cpp">
<Filter>GPU\OpenGL</Filter>
</ClCompile>
<ClCompile Include="File\VFS\DirectoryReader.cpp">
<Filter>File\VFS</Filter>
</ClCompile>
<ClCompile Include="File\VFS\ZipFileReader.cpp">
<Filter>File\VFS</Filter>
</ClCompile>
<ClCompile Include="Data\Format\DDSLoad.cpp">
<Filter>Data\Format</Filter>
</ClCompile>
<ClCompile Include="..\ext\basis_universal\basisu_transcoder.cpp">
<Filter>ext\basis_universal</Filter>
</ClCompile>
<ClCompile Include="System\Request.cpp">
<Filter>System</Filter>
</ClCompile>
<ClCompile Include="File\AndroidContentURI.cpp">
<Filter>File</Filter>
</ClCompile>
<ClCompile Include="GPU\OpenGL\GLMemory.cpp">
<Filter>GPU\OpenGL</Filter>
</ClCompile>
<ClCompile Include="Data\Collections\FastVec.h">
<Filter>Data\Collections</Filter>
</ClCompile>
<ClCompile Include="GPU\GPUBackendCommon.cpp">
<Filter>GPU</Filter>
</ClCompile>
<ClCompile Include="UI\IconCache.cpp">
<Filter>UI</Filter>
</ClCompile>
<ClCompile Include="System\OSD.cpp">
<Filter>System</Filter>
</ClCompile>
<ClCompile Include="Net\HTTPRequest.cpp">
<Filter>Net</Filter>
</ClCompile>
<ClCompile Include="..\ext\naett\naett.c">
<Filter>ext\naett</Filter>
</ClCompile>
<ClCompile Include="Net\HTTPNaettRequest.cpp">
<Filter>Net</Filter>
</ClCompile>
<ClCompile Include="Render\Text\draw_text_sdl.cpp">
<Filter>Render\Text</Filter>
</ClCompile>
<ClCompile Include="GPU\Vulkan\VulkanDescSet.cpp">
<Filter>GPU\Vulkan</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Filter Include="Crypto"> <Filter Include="Crypto">
<UniqueIdentifier>{1b593f03-7b28-4707-9228-4981796f5589}</UniqueIdentifier> <UniqueIdentifier>{1b593f03-7b28-4707-9228-4981796f5589}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="GL">
<UniqueIdentifier>{2f2ca112-9e26-499e-9cb9-38a78b4ac09d}</UniqueIdentifier>
</Filter>
<Filter Include="GL\GLInterface">
<UniqueIdentifier>{2c723cf4-75b6-406a-90c0-ebb7a13ba476}</UniqueIdentifier>
</Filter>
<Filter Include="Serialize"> <Filter Include="Serialize">
<UniqueIdentifier>{7be79ad5-3520-46a1-a370-dce2a943978c}</UniqueIdentifier> <UniqueIdentifier>{7be79ad5-3520-46a1-a370-dce2a943978c}</UniqueIdentifier>
</Filter> </Filter>
@ -1073,51 +985,10 @@
<Filter Include="VR"> <Filter Include="VR">
<UniqueIdentifier>{9d1c29fd-8ac7-4475-8ea6-c8c759b695fe}</UniqueIdentifier> <UniqueIdentifier>{9d1c29fd-8ac7-4475-8ea6-c8c759b695fe}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="ext\basis_universal">
<UniqueIdentifier>{d6d5f6e0-1c72-496b-af11-6d52d5123033}</UniqueIdentifier>
</Filter>
<Filter Include="ext\naett">
<UniqueIdentifier>{34f45db9-5c08-49cb-b349-b9e760ce3213}</UniqueIdentifier>
</Filter>
<Filter Include="ext\libchdr">
<UniqueIdentifier>{b681797d-7747-487f-b448-5ef5b2d2805b}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Text Include="..\ext\libpng17\CMakeLists.txt"> <Text Include="..\ext\libpng17\CMakeLists.txt">
<Filter>ext\libpng17</Filter> <Filter>ext\libpng17</Filter>
</Text> </Text>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="..\ext\basis_universal\basisu_transcoder_tables_astc.inc">
<Filter>ext\basis_universal</Filter>
</None>
<None Include="..\ext\basis_universal\basisu_transcoder_tables_astc_0_255.inc">
<Filter>ext\basis_universal</Filter>
</None>
<None Include="..\ext\basis_universal\basisu_transcoder_tables_atc_55.inc">
<Filter>ext\basis_universal</Filter>
</None>
<None Include="..\ext\basis_universal\basisu_transcoder_tables_atc_56.inc">
<Filter>ext\basis_universal</Filter>
</None>
<None Include="..\ext\basis_universal\basisu_transcoder_tables_bc7_m5_alpha.inc">
<Filter>ext\basis_universal</Filter>
</None>
<None Include="..\ext\basis_universal\basisu_transcoder_tables_bc7_m5_color.inc">
<Filter>ext\basis_universal</Filter>
</None>
<None Include="..\ext\basis_universal\basisu_transcoder_tables_dxt1_5.inc">
<Filter>ext\basis_universal</Filter>
</None>
<None Include="..\ext\basis_universal\basisu_transcoder_tables_dxt1_6.inc">
<Filter>ext\basis_universal</Filter>
</None>
<None Include="..\ext\basis_universal\basisu_transcoder_tables_pvrtc2_45.inc">
<Filter>ext\basis_universal</Filter>
</None>
<None Include="..\ext\basis_universal\basisu_transcoder_tables_pvrtc2_alpha_33.inc">
<Filter>ext\basis_universal</Filter>
</None>
</ItemGroup>
</Project> </Project>

View file

@ -30,17 +30,12 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#if (PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)) && !defined(__EMSCRIPTEN__) #if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
#define Crash() {asm ("int $3");} #define Crash() {asm ("int $3");}
#elif PPSSPP_PLATFORM(SWITCH)
// TODO: Implement Crash() for Switch, lets not use breakpoint for the time being
#define Crash() {*((volatile u32 *)0x0) = 0xDEADC0DE;}
#elif PPSSPP_ARCH(ARM) #elif PPSSPP_ARCH(ARM)
#define Crash() {asm ("bkpt #0");} #define Crash() {asm ("bkpt #0");}
#elif PPSSPP_ARCH(ARM64) #elif PPSSPP_ARCH(ARM64)
#define Crash() {asm ("brk #0");} #define Crash() {asm ("brk #0");}
#elif PPSSPP_ARCH(RISCV64)
#define Crash() {asm ("ebreak");}
#else #else
#include <signal.h> #include <signal.h>
#define Crash() {kill(getpid(), SIGINT);} #define Crash() {kill(getpid(), SIGINT);}

View file

@ -36,39 +36,6 @@ typedef signed __int64 s64;
#else #else
#ifdef __SWITCH__
// Some HID conflicts
#define KEY_UP PKEY_UP
#define KEY_DOWN PKEY_DOWN
// Other conflicts
#define Event _Event
#define Framebuffer _Framebuffer
#define Waitable _Waitable
#define ThreadContext _ThreadContext
#include <switch.h>
// Cleanup
#undef KEY_UP
#undef KEY_DOWN
#undef Event
#undef Framebuffer
#undef Waitable
#undef ThreadContext
// Conflicting types with libnx
#ifndef _u64
#define u64 _u64
#endif // _u64
#ifndef s64
#define s64 _s64
#endif // _s64
typedef unsigned char u_char;
typedef unsigned short u_short;
typedef unsigned int u_int;
typedef unsigned long u_long;
#endif // __SWITCH__
typedef unsigned char u8; typedef unsigned char u8;
typedef unsigned short u16; typedef unsigned short u16;
typedef unsigned int u32; typedef unsigned int u32;

View file

@ -119,10 +119,7 @@ void ConsoleListener::Init(bool AutoOpen, int Width, int Height, const char *Tit
title_ = ConvertUTF8ToWString(Title); title_ = ConvertUTF8ToWString(Title);
if (AutoOpen) if (AutoOpen)
{
Open(); Open();
bHidden = false;
}
#endif #endif
} }
@ -358,7 +355,7 @@ void ConsoleListener::LogWriterThread()
for (char *Text = logLocal, *End = logLocal + logLocalSize; Text < End; ) for (char *Text = logLocal, *End = logLocal + logLocalSize; Text < End; )
{ {
LogLevel Level = LogLevel::LINFO; LogTypes::LOG_LEVELS Level = LogTypes::LINFO;
char *next = (char *) memchr(Text + 1, '\033', End - Text); char *next = (char *) memchr(Text + 1, '\033', End - Text);
size_t Len = next - Text; size_t Len = next - Text;
@ -367,7 +364,7 @@ void ConsoleListener::LogWriterThread()
if (Text[0] == '\033' && Text + 1 < End) if (Text[0] == '\033' && Text + 1 < End)
{ {
Level = (LogLevel)(Text[1] - '0'); Level = (LogTypes::LOG_LEVELS) (Text[1] - '0');
Len -= 2; Len -= 2;
Text += 2; Text += 2;
} }
@ -384,7 +381,7 @@ void ConsoleListener::LogWriterThread()
delete [] logLocal; delete [] logLocal;
} }
void ConsoleListener::SendToThread(LogLevel Level, const char *Text) void ConsoleListener::SendToThread(LogTypes::LOG_LEVELS Level, const char *Text)
{ {
// Oops, we're already quitting. Just do nothing. // Oops, we're already quitting. Just do nothing.
if (logPendingWritePos == (u32) -1) if (logPendingWritePos == (u32) -1)
@ -462,7 +459,7 @@ void ConsoleListener::SendToThread(LogLevel Level, const char *Text)
SetEvent(hTriggerEvent); SetEvent(hTriggerEvent);
} }
void ConsoleListener::WriteToConsole(LogLevel Level, const char *Text, size_t Len) void ConsoleListener::WriteToConsole(LogTypes::LOG_LEVELS Level, const char *Text, size_t Len)
{ {
_dbg_assert_msg_(IsOpen(), "Don't call this before opening the console."); _dbg_assert_msg_(IsOpen(), "Don't call this before opening the console.");
@ -479,20 +476,21 @@ void ConsoleListener::WriteToConsole(LogLevel Level, const char *Text, size_t Le
WORD Color; WORD Color;
static wchar_t tempBuf[2048]; static wchar_t tempBuf[2048];
switch (Level) { switch (Level)
case LogLevel::LNOTICE: // light green {
case NOTICE_LEVEL: // light green
Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY; Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
break; break;
case LogLevel::LERROR: // light red case ERROR_LEVEL: // light red
Color = FOREGROUND_RED | FOREGROUND_INTENSITY; Color = FOREGROUND_RED | FOREGROUND_INTENSITY;
break; break;
case LogLevel::LWARNING: // light yellow case WARNING_LEVEL: // light yellow
Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
break; break;
case LogLevel::LINFO: // cyan case INFO_LEVEL: // cyan
Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
break; break;
case LogLevel::LDEBUG: // gray case DEBUG_LEVEL: // gray
Color = FOREGROUND_INTENSITY; Color = FOREGROUND_INTENSITY;
break; break;
default: // off-white default: // off-white
@ -592,7 +590,7 @@ void ConsoleListener::PixelSpace(int Left, int Top, int Width, int Height, bool
COORD Coo = GetCoordinates(OldCursor, LBufWidth); COORD Coo = GetCoordinates(OldCursor, LBufWidth);
SetConsoleCursorPosition(hConsole, Coo); SetConsoleCursorPosition(hConsole, Coo);
// if (SLog.length() > 0) Log(LogLevel::LNOTICE, SLog.c_str()); // if (SLog.length() > 0) Log(LogTypes::LNOTICE, SLog.c_str());
// Resize the window too // Resize the window too
if (Resize) MoveWindow(GetConsoleWindow(), Left,Top, (Width + 100),Height, true); if (Resize) MoveWindow(GetConsoleWindow(), Left,Top, (Width + 100),Height, true);
@ -614,16 +612,18 @@ void ConsoleListener::Log(const LogMessage &msg) {
char ColorAttr[16] = ""; char ColorAttr[16] = "";
char ResetAttr[16] = ""; char ResetAttr[16] = "";
if (bUseColor) { if (bUseColor)
{
strcpy(ResetAttr, "\033[0m"); strcpy(ResetAttr, "\033[0m");
switch (msg.level) { switch (msg.level)
case LogLevel::LNOTICE: // light green {
case NOTICE_LEVEL: // light green
strcpy(ColorAttr, "\033[92m"); strcpy(ColorAttr, "\033[92m");
break; break;
case LogLevel::LERROR: // light red case ERROR_LEVEL: // light red
strcpy(ColorAttr, "\033[91m"); strcpy(ColorAttr, "\033[91m");
break; break;
case LogLevel::LWARNING: // light yellow case WARNING_LEVEL: // light yellow
strcpy(ColorAttr, "\033[93m"); strcpy(ColorAttr, "\033[93m");
break; break;
default: default:
@ -653,3 +653,5 @@ void ConsoleListener::ClearScreen(bool Cursor)
if (Cursor) SetConsoleCursorPosition(hConsole, coordScreen); if (Cursor) SetConsoleCursorPosition(hConsole, coordScreen);
#endif #endif
} }

View file

@ -54,8 +54,8 @@ private:
static unsigned int WINAPI RunThread(void *lpParam); static unsigned int WINAPI RunThread(void *lpParam);
void LogWriterThread(); void LogWriterThread();
void SendToThread(LogLevel Level, const char *Text); void SendToThread(LogTypes::LOG_LEVELS Level, const char *Text);
void WriteToConsole(LogLevel Level, const char *Text, size_t Len); void WriteToConsole(LogTypes::LOG_LEVELS Level, const char *Text, size_t Len);
static int refCount; static int refCount;
static HANDLE hThread; static HANDLE hThread;

View file

@ -62,7 +62,7 @@
/* /*
* MD5 context setup * MD5 context setup
*/ */
void ppsspp_md5_starts( md5_context *ctx ) void md5_starts( md5_context *ctx )
{ {
ctx->total[0] = 0; ctx->total[0] = 0;
ctx->total[1] = 0; ctx->total[1] = 0;
@ -73,7 +73,7 @@ void ppsspp_md5_starts( md5_context *ctx )
ctx->state[3] = 0x10325476; ctx->state[3] = 0x10325476;
} }
static void ppsspp_md5_process( md5_context *ctx, unsigned char data[64] ) static void md5_process( md5_context *ctx, unsigned char data[64] )
{ {
unsigned long X[16], A, B, C, D; unsigned long X[16], A, B, C, D;
@ -199,7 +199,7 @@ static void ppsspp_md5_process( md5_context *ctx, unsigned char data[64] )
/* /*
* MD5 process buffer * MD5 process buffer
*/ */
void ppsspp_md5_update( md5_context *ctx, unsigned char *input, int ilen ) void md5_update( md5_context *ctx, unsigned char *input, int ilen )
{ {
int fill; int fill;
unsigned long left; unsigned long left;
@ -220,7 +220,7 @@ void ppsspp_md5_update( md5_context *ctx, unsigned char *input, int ilen )
{ {
memcpy( (void *) (ctx->buffer + left), memcpy( (void *) (ctx->buffer + left),
(void *) input, fill ); (void *) input, fill );
ppsspp_md5_process( ctx, ctx->buffer ); md5_process( ctx, ctx->buffer );
input += fill; input += fill;
ilen -= fill; ilen -= fill;
left = 0; left = 0;
@ -228,7 +228,7 @@ void ppsspp_md5_update( md5_context *ctx, unsigned char *input, int ilen )
while( ilen >= 64 ) while( ilen >= 64 )
{ {
ppsspp_md5_process( ctx, input ); md5_process( ctx, input );
input += 64; input += 64;
ilen -= 64; ilen -= 64;
} }
@ -251,7 +251,7 @@ static const unsigned char md5_padding[64] =
/* /*
* MD5 final digest * MD5 final digest
*/ */
void ppsspp_md5_finish( md5_context *ctx, unsigned char output[16] ) void md5_finish( md5_context *ctx, unsigned char output[16] )
{ {
unsigned long last, padn; unsigned long last, padn;
unsigned long high, low; unsigned long high, low;
@ -267,8 +267,8 @@ void ppsspp_md5_finish( md5_context *ctx, unsigned char output[16] )
last = ctx->total[0] & 0x3F; last = ctx->total[0] & 0x3F;
padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
ppsspp_md5_update( ctx, (unsigned char *) md5_padding, padn ); md5_update( ctx, (unsigned char *) md5_padding, padn );
ppsspp_md5_update( ctx, msglen, 8 ); md5_update( ctx, msglen, 8 );
PUT_ULONG_LE( ctx->state[0], output, 0 ); PUT_ULONG_LE( ctx->state[0], output, 0 );
PUT_ULONG_LE( ctx->state[1], output, 4 ); PUT_ULONG_LE( ctx->state[1], output, 4 );
@ -279,13 +279,13 @@ void ppsspp_md5_finish( md5_context *ctx, unsigned char output[16] )
/* /*
* output = MD5( input buffer ) * output = MD5( input buffer )
*/ */
void ppsspp_md5( unsigned char *input, int ilen, unsigned char output[16] ) void md5( unsigned char *input, int ilen, unsigned char output[16] )
{ {
md5_context ctx; md5_context ctx;
ppsspp_md5_starts( &ctx ); md5_starts( &ctx );
ppsspp_md5_update( &ctx, input, ilen ); md5_update( &ctx, input, ilen );
ppsspp_md5_finish( &ctx, output ); md5_finish( &ctx, output );
memset( &ctx, 0, sizeof( md5_context ) ); memset( &ctx, 0, sizeof( md5_context ) );
} }
@ -293,14 +293,14 @@ void ppsspp_md5( unsigned char *input, int ilen, unsigned char output[16] )
/* /*
* MD5 HMAC context setup * MD5 HMAC context setup
*/ */
void ppsspp_md5_hmac_starts( md5_context *ctx, unsigned char *key, int keylen ) void md5_hmac_starts( md5_context *ctx, unsigned char *key, int keylen )
{ {
int i; int i;
unsigned char sum[16]; unsigned char sum[16];
if( keylen > 64 ) if( keylen > 64 )
{ {
ppsspp_md5( key, keylen, sum ); md5( key, keylen, sum );
keylen = 16; keylen = 16;
key = sum; key = sum;
} }
@ -314,8 +314,8 @@ void ppsspp_md5_hmac_starts( md5_context *ctx, unsigned char *key, int keylen )
ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] ); ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] );
} }
ppsspp_md5_starts( ctx ); md5_starts( ctx );
ppsspp_md5_update( ctx, ctx->ipad, 64 ); md5_update( ctx, ctx->ipad, 64 );
memset( sum, 0, sizeof( sum ) ); memset( sum, 0, sizeof( sum ) );
} }
@ -323,23 +323,23 @@ void ppsspp_md5_hmac_starts( md5_context *ctx, unsigned char *key, int keylen )
/* /*
* MD5 HMAC process buffer * MD5 HMAC process buffer
*/ */
void ppsspp_md5_hmac_update( md5_context *ctx, unsigned char *input, int ilen ) void md5_hmac_update( md5_context *ctx, unsigned char *input, int ilen )
{ {
ppsspp_md5_update( ctx, input, ilen ); md5_update( ctx, input, ilen );
} }
/* /*
* MD5 HMAC final digest * MD5 HMAC final digest
*/ */
void ppsspp_md5_hmac_finish( md5_context *ctx, unsigned char output[16] ) void md5_hmac_finish( md5_context *ctx, unsigned char output[16] )
{ {
unsigned char tmpbuf[16]; unsigned char tmpbuf[16];
ppsspp_md5_finish( ctx, tmpbuf ); md5_finish( ctx, tmpbuf );
ppsspp_md5_starts( ctx ); md5_starts( ctx );
ppsspp_md5_update( ctx, ctx->opad, 64 ); md5_update( ctx, ctx->opad, 64 );
ppsspp_md5_update( ctx, tmpbuf, 16 ); md5_update( ctx, tmpbuf, 16 );
ppsspp_md5_finish( ctx, output ); md5_finish( ctx, output );
memset( tmpbuf, 0, sizeof( tmpbuf ) ); memset( tmpbuf, 0, sizeof( tmpbuf ) );
} }
@ -347,14 +347,14 @@ void ppsspp_md5_hmac_finish( md5_context *ctx, unsigned char output[16] )
/* /*
* output = HMAC-MD5( hmac key, input buffer ) * output = HMAC-MD5( hmac key, input buffer )
*/ */
void ppsspp_md5_hmac( unsigned char *key, int keylen, unsigned char *input, int ilen, void md5_hmac( unsigned char *key, int keylen, unsigned char *input, int ilen,
unsigned char output[16] ) unsigned char output[16] )
{ {
md5_context ctx; md5_context ctx;
ppsspp_md5_hmac_starts( &ctx, key, keylen ); md5_hmac_starts( &ctx, key, keylen );
ppsspp_md5_hmac_update( &ctx, input, ilen ); md5_hmac_update( &ctx, input, ilen );
ppsspp_md5_hmac_finish( &ctx, output ); md5_hmac_finish( &ctx, output );
memset( &ctx, 0, sizeof( md5_context ) ); memset( &ctx, 0, sizeof( md5_context ) );
} }
@ -464,7 +464,7 @@ static const unsigned char md5_hmac_test_sum[7][16] =
/* /*
* Checkup routine * Checkup routine
*/ */
int ppsspp_md5_self_test( int verbose ) int md5_self_test( int verbose )
{ {
int i, buflen; int i, buflen;
unsigned char buf[1024]; unsigned char buf[1024];

View file

@ -46,7 +46,7 @@ extern "C" {
* *
* \param ctx context to be initialized * \param ctx context to be initialized
*/ */
void ppsspp_md5_starts( md5_context *ctx ); void md5_starts( md5_context *ctx );
/** /**
* \brief MD5 process buffer * \brief MD5 process buffer
@ -55,7 +55,7 @@ void ppsspp_md5_starts( md5_context *ctx );
* \param input buffer holding the data * \param input buffer holding the data
* \param ilen length of the input data * \param ilen length of the input data
*/ */
void ppsspp_md5_update( md5_context *ctx, unsigned char *input, int ilen ); void md5_update( md5_context *ctx, unsigned char *input, int ilen );
/** /**
* \brief MD5 final digest * \brief MD5 final digest
@ -63,7 +63,7 @@ void ppsspp_md5_update( md5_context *ctx, unsigned char *input, int ilen );
* \param ctx MD5 context * \param ctx MD5 context
* \param output MD5 checksum result * \param output MD5 checksum result
*/ */
void ppsspp_md5_finish( md5_context *ctx, unsigned char output[16] ); void md5_finish( md5_context *ctx, unsigned char output[16] );
/** /**
* \brief Output = MD5( input buffer ) * \brief Output = MD5( input buffer )
@ -72,7 +72,7 @@ void ppsspp_md5_finish( md5_context *ctx, unsigned char output[16] );
* \param ilen length of the input data * \param ilen length of the input data
* \param output MD5 checksum result * \param output MD5 checksum result
*/ */
void ppsspp_md5( unsigned char *input, int ilen, unsigned char output[16] ); void md5( unsigned char *input, int ilen, unsigned char output[16] );
/** /**
* \brief Output = MD5( file contents ) * \brief Output = MD5( file contents )
@ -83,7 +83,7 @@ void ppsspp_md5( unsigned char *input, int ilen, unsigned char output[16] );
* \return 0 if successful, 1 if fopen failed, * \return 0 if successful, 1 if fopen failed,
* or 2 if fread failed * or 2 if fread failed
*/ */
int ppsspp_md5_file( char *path, unsigned char output[16] ); int md5_file( char *path, unsigned char output[16] );
/** /**
* \brief MD5 HMAC context setup * \brief MD5 HMAC context setup
@ -92,7 +92,7 @@ int ppsspp_md5_file( char *path, unsigned char output[16] );
* \param key HMAC secret key * \param key HMAC secret key
* \param keylen length of the HMAC key * \param keylen length of the HMAC key
*/ */
void ppsspp_md5_hmac_starts( md5_context *ctx, unsigned char *key, int keylen ); void md5_hmac_starts( md5_context *ctx, unsigned char *key, int keylen );
/** /**
* \brief MD5 HMAC process buffer * \brief MD5 HMAC process buffer
@ -101,7 +101,7 @@ void ppsspp_md5_hmac_starts( md5_context *ctx, unsigned char *key, int keylen );
* \param input buffer holding the data * \param input buffer holding the data
* \param ilen length of the input data * \param ilen length of the input data
*/ */
void ppsspp_md5_hmac_update( md5_context *ctx, unsigned char *input, int ilen ); void md5_hmac_update( md5_context *ctx, unsigned char *input, int ilen );
/** /**
* \brief MD5 HMAC final digest * \brief MD5 HMAC final digest
@ -109,7 +109,7 @@ void ppsspp_md5_hmac_update( md5_context *ctx, unsigned char *input, int ilen );
* \param ctx HMAC context * \param ctx HMAC context
* \param output MD5 HMAC checksum result * \param output MD5 HMAC checksum result
*/ */
void ppsspp_md5_hmac_finish( md5_context *ctx, unsigned char output[16] ); void md5_hmac_finish( md5_context *ctx, unsigned char output[16] );
/** /**
* \brief Output = HMAC-MD5( hmac key, input buffer ) * \brief Output = HMAC-MD5( hmac key, input buffer )
@ -120,7 +120,7 @@ void ppsspp_md5_hmac_finish( md5_context *ctx, unsigned char output[16] );
* \param ilen length of the input data * \param ilen length of the input data
* \param output HMAC-MD5 result * \param output HMAC-MD5 result
*/ */
void ppsspp_md5_hmac( unsigned char *key, int keylen, void md5_hmac( unsigned char *key, int keylen,
unsigned char *input, int ilen, unsigned char *input, int ilen,
unsigned char output[16] ); unsigned char output[16] );
@ -129,7 +129,7 @@ void ppsspp_md5_hmac( unsigned char *key, int keylen,
* *
* \return 0 if successful, or 1 if the test failed * \return 0 if successful, or 1 if the test failed
*/ */
int ppsspp_md5_self_test( int verbose ); int md5_self_test( int verbose );
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -1,225 +0,0 @@
#pragma once
// Yet another replacement for std::vector, this time for use in graphics queues.
// Its major difference is that you can append uninitialized structures and initialize them after.
// This is not allows by std::vector but is very useful for our sometimes oversized unions.
// Also, copies during resize are done by memcpy, not by any move constructor or similar.
#include <cstdlib>
#include <cstring>
#ifdef _DEBUG
#include "Common/Log.h"
#endif
template<class T>
class FastVec {
public:
FastVec() {}
FastVec(size_t initialCapacity) {
capacity_ = initialCapacity;
data_ = (T *)malloc(initialCapacity * sizeof(T));
}
~FastVec() { if (data_) free(data_); }
T &push_uninitialized() {
if (size_ < capacity_) {
size_++;
return data_[size_ - 1];
} else {
ExtendByOne();
return data_[size_ - 1];
}
}
void push_back(const T &t) {
T &dest = push_uninitialized();
dest = t;
}
// Move constructor
FastVec(FastVec &&other) {
data_ = other.data_;
size_ = other.size_;
capacity_ = other.capacity_;
other.data_ = nullptr;
other.size_ = 0;
other.capacity_ = 0;
}
FastVec &operator=(FastVec &&other) {
if (this != &other) {
delete[] data_;
data_ = other.data_;
size_ = other.size_;
capacity_ = other.capacity_;
other.data_ = nullptr;
other.size_ = 0;
other.capacity_ = 0;
}
return *this;
}
// No copy constructor.
FastVec(const FastVec &other) = delete;
FastVec &operator=(const FastVec &other) = delete;
size_t size() const { return size_; }
size_t capacity() const { return capacity_; }
void clear() { size_ = 0; }
bool empty() const { return size_ == 0; }
const T *data() { return data_; }
T *begin() { return data_; }
T *end() { return data_ + size_; }
const T *begin() const { return data_; }
const T *end() const { return data_ + size_; }
// Out of bounds (past size() - 1) is undefined behavior.
T &operator[] (const size_t index) { return data_[index]; }
const T &operator[] (const size_t index) const { return data_[index]; }
T &at(const size_t index) { return data_[index]; }
const T &at(const size_t index) const { return data_[index]; }
// These two are invalid if empty().
const T &back() const { return (*this)[size() - 1]; }
const T &front() const { return (*this)[0]; }
// Limited functionality for inserts and similar, add as needed.
T &insert(T *iter) {
int pos = iter - data_;
ExtendByOne();
if (pos + 1 < (int)size_) {
memmove(data_ + pos + 1, data_ + pos, (size_ - pos) * sizeof(T));
}
return data_[pos];
}
void insert(T *destIter, const T *beginIter, const T *endIter) {
int pos = destIter - data_;
if (beginIter == endIter)
return;
size_t newItems = endIter - beginIter;
IncreaseCapacityTo(size_ + newItems);
memmove(data_ + pos + newItems, data_ + pos, (size_ - pos) * sizeof(T));
memcpy(data_ + pos, beginIter, newItems * sizeof(T));
size_ += newItems;
}
void resize(size_t size) {
if (size < size_) {
size_ = size;
} else {
// TODO
}
}
void reserve(size_t newCapacity) {
IncreaseCapacityTo(newCapacity);
}
void extend(const T *newData, size_t count) {
IncreaseCapacityTo(size_ + count);
memcpy(data_ + size_, newData, count * sizeof(T));
size_ += count;
}
T *extend_uninitialized(size_t count) {
size_t sz = size_;
if (size_ + count <= capacity_) {
size_ += count;
return &data_[sz];
} else {
size_t newCapacity = size_ + count * 2; // Leave some extra room when growing in all cases
if (newCapacity < capacity_ * 2) {
// Standard amortized O(1).
newCapacity = capacity_ * 2;
}
IncreaseCapacityTo(newCapacity);
size_ += count;
return &data_[sz];
}
}
void LockCapacity() {
#ifdef _DEBUG
capacityLocked_ = true;
#endif
}
private:
void IncreaseCapacityTo(size_t newCapacity) {
#ifdef _DEBUG
_dbg_assert_(!capacityLocked_);
#endif
if (newCapacity <= capacity_)
return;
T *oldData = data_;
data_ = (T *)malloc(sizeof(T) * newCapacity);
if (capacity_ != 0) {
memcpy(data_, oldData, sizeof(T) * size_);
free(oldData);
}
capacity_ = newCapacity;
}
void ExtendByOne() {
size_t newCapacity = capacity_ * 2;
if (newCapacity < 16) {
newCapacity = 16;
}
IncreaseCapacityTo(newCapacity);
size_++;
}
size_t size_ = 0;
size_t capacity_ = 0;
T *data_ = nullptr;
#ifdef _DEBUG
bool capacityLocked_ = false;
#endif
};
// Simple cyclical vector.
template <class T, size_t size>
class HistoryBuffer {
public:
T &Add(size_t index) {
#ifdef _DEBUG
_dbg_assert_((int64_t)index >= 0);
#endif
if (index > maxIndex_)
maxIndex_ = index;
T &entry = data_[index % size];
entry = T{};
return entry;
}
const T &Back(size_t index) const {
#ifdef _DEBUG
_dbg_assert_(index < maxIndex_ && index < size);
#endif
return data_[(maxIndex_ - index) % size];
}
// Out of bounds (past size() - 1) is undefined behavior.
T &operator[] (const size_t index) {
#ifdef _DEBUG
_dbg_assert_(index <= maxIndex_);
#endif
return data_[index % size];
}
const T &operator[] (const size_t index) const {
#ifdef _DEBUG
_dbg_assert_(index <= maxIndex_);
#endif
return data_[index % size];
}
size_t MaxIndex() const {
return maxIndex_;
}
private:
T data_[size]{};
size_t maxIndex_ = 0;
};

View file

@ -30,11 +30,15 @@ template <class T, int N>
class FixedSizeQueue { class FixedSizeQueue {
public: public:
FixedSizeQueue() { FixedSizeQueue() {
// Allocate aligned memory, just because.
//int sizeInBytes = N * sizeof(T);
//storage_ = (T *)AllocateMemoryPages(sizeInBytes);
storage_ = new T[N]; storage_ = new T[N];
clear(); clear();
} }
~FixedSizeQueue() { ~FixedSizeQueue() {
// FreeMemoryPages((void *)storage_, N * sizeof(T));
delete [] storage_; delete [] storage_;
} }
@ -222,3 +226,4 @@ private:
volatile int curReadBlock; volatile int curReadBlock;
volatile int curWriteBlock; volatile int curWriteBlock;
}; };

View file

@ -29,7 +29,7 @@ enum class BucketState : uint8_t {
// we always use very small values, so it's probably better to have them in the same // we always use very small values, so it's probably better to have them in the same
// cache-line as the corresponding key. // cache-line as the corresponding key.
// Enforces that value are pointers to make sure that combined storage makes sense. // Enforces that value are pointers to make sure that combined storage makes sense.
template <class Key, class Value> template <class Key, class Value, Value NullValue>
class DenseHashMap { class DenseHashMap {
public: public:
DenseHashMap(int initialCapacity) : capacity_(initialCapacity) { DenseHashMap(int initialCapacity) : capacity_(initialCapacity) {
@ -37,47 +37,26 @@ public:
state.resize(initialCapacity); state.resize(initialCapacity);
} }
// Returns true if the entry was found, and writes the entry to *value. // Returns nullptr if no entry was found.
// Returns false and does not write to value if no entry was found. Value Get(const Key &key) {
// Note that nulls can be stored.
bool Get(const Key &key, Value *value) const {
uint32_t mask = capacity_ - 1; uint32_t mask = capacity_ - 1;
uint32_t pos = HashKey(key) & mask; uint32_t pos = HashKey(key) & mask;
// No? Let's go into search mode. Linear probing. // No? Let's go into search mode. Linear probing.
uint32_t p = pos; uint32_t p = pos;
while (true) { while (true) {
if (state[p] == BucketState::TAKEN && KeyEquals(key, map[p].key)) { if (state[p] == BucketState::TAKEN && KeyEquals(key, map[p].key))
*value = map[p].value; return map[p].value;
return true; else if (state[p] == BucketState::FREE)
} else if (state[p] == BucketState::FREE) { return NullValue;
return false;
}
p = (p + 1) & mask; // If the state is REMOVED, we just keep on walking. p = (p + 1) & mask; // If the state is REMOVED, we just keep on walking.
if (p == pos) { if (p == pos) {
// We looped around the whole map.
_assert_msg_(false, "DenseHashMap: Hit full on Get()"); _assert_msg_(false, "DenseHashMap: Hit full on Get()");
} }
} }
return false; return NullValue;
} }
// Only works if Value can be nullptr // Returns false if we already had the key! Which is a bit different.
Value GetOrNull(const Key &key) const {
Value value;
if (Get(key, &value)) {
return value;
} else {
return (Value)nullptr;
}
}
bool ContainsKey(const Key &key) const {
// Slightly wasteful, though compiler might optimize it.
Value value;
return Get(key, &value);
}
// Asserts if we already had the key!
bool Insert(const Key &key, Value value) { bool Insert(const Key &key, Value value) {
// Check load factor, resize if necessary. We never shrink. // Check load factor, resize if necessary. We never shrink.
if (count_ > capacity_ / 2) { if (count_ > capacity_ / 2) {
@ -135,7 +114,6 @@ public:
return false; return false;
} }
// This will never crash if you call it without locking - but, the value might not be right.
size_t size() const { size_t size() const {
return count_; return count_;
} }
@ -212,7 +190,7 @@ private:
// Like the above, uses linear probing for cache-friendliness. // Like the above, uses linear probing for cache-friendliness.
// Does not perform hashing at all so expects well-distributed keys. // Does not perform hashing at all so expects well-distributed keys.
template <class Value> template <class Value, Value NullValue>
class PrehashMap { class PrehashMap {
public: public:
PrehashMap(int initialCapacity) : capacity_(initialCapacity) { PrehashMap(int initialCapacity) : capacity_(initialCapacity) {
@ -221,24 +199,22 @@ public:
} }
// Returns nullptr if no entry was found. // Returns nullptr if no entry was found.
bool Get(uint32_t hash, Value *value) { Value Get(uint32_t hash) {
uint32_t mask = capacity_ - 1; uint32_t mask = capacity_ - 1;
uint32_t pos = hash & mask; uint32_t pos = hash & mask;
// No? Let's go into search mode. Linear probing. // No? Let's go into search mode. Linear probing.
uint32_t p = pos; uint32_t p = pos;
while (true) { while (true) {
if (state[p] == BucketState::TAKEN && hash == map[p].hash) { if (state[p] == BucketState::TAKEN && hash == map[p].hash)
*value = map[p].value; return map[p].value;
return true; else if (state[p] == BucketState::FREE)
} else if (state[p] == BucketState::FREE) { return NullValue;
return false;
}
p = (p + 1) & mask; // If the state is REMOVED, we just keep on walking. p = (p + 1) & mask; // If the state is REMOVED, we just keep on walking.
if (p == pos) { if (p == pos) {
_assert_msg_(false, "PrehashMap: Hit full on Get()"); _assert_msg_(false, "PrehashMap: Hit full on Get()");
} }
} }
return false; return NullValue;
} }
// Returns false if we already had the key! Which is a bit different. // Returns false if we already had the key! Which is a bit different.

View file

@ -134,68 +134,3 @@ private:
T fastLookup_[MaxFastSize]; T fastLookup_[MaxFastSize];
std::vector<T> *slowLookup_ = nullptr; std::vector<T> *slowLookup_ = nullptr;
}; };
template <class T, int MaxSize>
struct FixedTinyVec {
~FixedTinyVec() {}
// WARNING: Can fail if you exceed MaxSize!
inline bool push_back(const T &t) {
if (count_ < MaxSize) {
data_[count_++] = t;
return true;
} else {
return false;
}
}
// WARNING: Can fail if you exceed MaxSize!
inline T *add_back() {
if (count_ < MaxSize) {
return &data_[count_++];
}
return nullptr;
}
// Invalid if empty().
void pop_back() { count_--; }
// Unlike TinySet, we can trivially support begin/end as pointers.
T *begin() { return data_; }
T *end() { return data_ + count_; }
const T *begin() const { return data_; }
const T *end() const { return data_ + count_; }
size_t capacity() const { return MaxSize; }
void clear() { count_ = 0; }
bool empty() const { return count_ == 0; }
size_t size() const { return count_; }
bool contains(T t) const {
for (int i = 0; i < count_; i++) {
if (data_[i] == t)
return true;
}
return false;
}
// Out of bounds (past size() - 1) is undefined behavior.
T &operator[] (const size_t index) { return data_[index]; }
const T &operator[] (const size_t index) const { return data_[index]; }
// These two are invalid if empty().
const T &back() const { return (*this)[size() - 1]; }
const T &front() const { return (*this)[0]; }
bool operator == (const FixedTinyVec<T, MaxSize> &other) const {
if (count_ != other.count_)
return false;
for (int i = 0; i < count_; i++) {
if (!(data_[i] == other.data_[i])) {
return false;
}
}
return true;
}
private:
int count_ = 0; // first in the struct just so it's more visible in the VS debugger.
T data_[MaxSize];
};

View file

@ -617,7 +617,6 @@ void ConvertRGB565ToBGR565(u16 *dst, const u16 *src, u32 numPixels) {
u32 i = 0; u32 i = 0;
#endif #endif
// TODO: Add a 64-bit loop too.
const u32 *src32 = (const u32 *)src; const u32 *src32 = (const u32 *)src;
u32 *dst32 = (u32 *)dst; u32 *dst32 = (u32 *)dst;
for (; i < numPixels / 2; i++) { for (; i < numPixels / 2; i++) {

View file

@ -219,15 +219,13 @@ int u8_strlen(const char *s)
} }
/* reads the next utf-8 sequence out of a string, updating an index */ /* reads the next utf-8 sequence out of a string, updating an index */
uint32_t u8_nextchar(const char *s, int *index) { uint32_t u8_nextchar(const char *s, int *i) {
uint32_t ch = 0; uint32_t ch = 0;
int sz = 0; int sz = 0;
int i = *index;
do { do {
ch = (ch << 6) + (unsigned char)s[i++]; ch = (ch << 6) + (unsigned char)s[(*i)++];
sz++; sz++;
} while (s[i] && ((s[i]) & 0xC0) == 0x80); } while (s[*i] && ((s[*i]) & 0xC0) == 0x80);
*index = i;
return ch - offsetsFromUTF8[sz - 1]; return ch - offsetsFromUTF8[sz - 1];
} }
@ -428,17 +426,6 @@ int u8_is_locale_utf8(const char *locale)
return 0; return 0;
} }
bool AnyEmojiInString(const char *s, size_t byteCount) {
int i = 0;
while (i < byteCount) {
uint32_t c = u8_nextchar(s, &i);
if (CodepointIsProbablyEmoji(c)) {
return true;
}
}
return false;
}
int UTF8StringNonASCIICount(const char *utf8string) { int UTF8StringNonASCIICount(const char *utf8string) {
UTF8 utf(utf8string); UTF8 utf(utf8string);
int count = 0; int count = 0;
@ -571,12 +558,6 @@ std::u16string ConvertUTF8ToUCS2(const std::string &source) {
return dst; return dst;
} }
std::string CodepointToUTF8(uint32_t codePoint) {
char temp[16]{};
UTF8::encode(temp, codePoint);
return std::string(temp);
}
#ifndef _WIN32 #ifndef _WIN32
// Replacements for the Win32 wstring functions. Not to be used from emulation code! // Replacements for the Win32 wstring functions. Not to be used from emulation code!

View file

@ -26,15 +26,6 @@ int u8_strlen(const char *s);
void u8_inc(const char *s, int *i); void u8_inc(const char *s, int *i);
void u8_dec(const char *s, int *i); void u8_dec(const char *s, int *i);
inline bool CodepointIsProbablyEmoji(uint32_t c) {
// Original check was some ranges grabbed from https://stackoverflow.com/a/62898106.
// But let's just go with checking if outside the BMP, it's not a big deal if we accidentally
// switch to color when not needed if someone uses a weird glyph.
return c > 0xFFFF;
}
bool AnyEmojiInString(const char *s, size_t byteCount);
class UTF8 { class UTF8 {
public: public:
static const uint32_t INVALID = (uint32_t)-1; static const uint32_t INVALID = (uint32_t)-1;
@ -98,8 +89,6 @@ bool UTF8StringHasNonASCII(const char *utf8string);
// Removes overlong encodings and similar. // Removes overlong encodings and similar.
std::string SanitizeUTF8(const std::string &utf8string); std::string SanitizeUTF8(const std::string &utf8string);
std::string CodepointToUTF8(uint32_t codePoint);
// UTF8 to Win32 UTF-16 // UTF8 to Win32 UTF-16
// Should be used when calling Win32 api calls // Should be used when calling Win32 api calls

View file

@ -1,5 +0,0 @@
#include "Common/Data/Format/DDSLoad.h"
bool DetectDDSParams(const DDSHeader *header, DDSLoadInfo *info) {
return false;
}

View file

@ -1,83 +0,0 @@
#pragma once
#include <cstdint>
// DDSPixelFormat.dwFlags bits
enum {
DDPF_ALPHAPIXELS = 1, // Texture contains alpha data; dwRGBAlphaBitMask contains valid data.
DDPF_ALPHA = 2, // Used in some older DDS files for alpha channel only uncompressed data(dwRGBBitCount contains the alpha channel bitcount; dwABitMask contains valid data)
DDPF_FOURCC = 4, // Texture contains compressed RGB data; dwFourCC contains valid data. 0x4
DDPF_RGB = 8, // Texture contains uncompressed RGB data; dwRGBBitCount and the RGB masks(dwRBitMask, dwGBitMask, dwBBitMask) contain valid data. 0x40
DDPF_YUV = 16, //Used in some older DDS files for YUV uncompressed data(dwRGBBitCount contains the YUV bit count; dwRBitMask contains the Y mask, dwGBitMask contains the U mask, dwBBitMask contains the V mask)
DDPF_LUMINANCE = 32, // Used in some older DDS files for single channel color uncompressed data (dwRGBBitCount contains the luminance channel bit count; dwRBitMask contains the channel mask). Can be combined with DDPF_ALPHAPIXELS for a two channel DDS file.
};
// dwCaps members
enum {
DDSCAPS_COMPLEX = 8,
DDSCAPS_MIPMAP = 0x400000,
DDSCAPS_TEXTURE = 0x1000, // Required
};
// Not using any D3D headers here, this is cross platform and minimal.
// Boiled down from the public documentation.
struct DDSPixelFormat {
uint32_t dwSize; // must be 32
uint32_t dwFlags;
uint32_t dwFourCC;
uint32_t dwRGBBitCount;
uint32_t dwRBitMask;
uint32_t dwGBitMask;
uint32_t dwBBitMask;
uint32_t dwABitMask;
};
struct DDSHeader {
uint32_t dwMagic; // Magic is not technically part of the header struct but convenient to have here when reading the files.
uint32_t dwSize; // must be 124
uint32_t dwFlags;
uint32_t dwHeight;
uint32_t dwWidth;
uint32_t dwPitchOrLinearSize; // The pitch or number of bytes per scan line in an uncompressed texture; the total number of bytes in the top level texture for a compressed texture
uint32_t dwDepth; // we'll always use 1 here
uint32_t dwMipMapCount;
uint32_t dwReserved1[11];
DDSPixelFormat ddspf;
uint32_t dwCaps;
uint32_t dwCaps2; // nothing we care about
uint32_t dwCaps3; // unused
uint32_t dwCaps4; // unused
uint32_t dwReserved2;
};
// DDS header extension to handle resource arrays, DXGI pixel formats that don't map to the legacy Microsoft DirectDraw pixel format structures, and additional metadata.
struct DDSHeaderDXT10 {
uint32_t dxgiFormat;
uint32_t resourceDimension; // 1d = 2, 2d = 3, 3d = 4. very intuitive
uint32_t miscFlag;
uint32_t arraySize; // we only support 1 here
uint32_t miscFlags2; // sets alpha interpretation, let's not bother
};
// Simple DDS parser, suitable for texture replacement packs.
// Doesn't actually load, only does some logic to fill out DDSLoadInfo so the caller can then
// do the actual load with a simple series of memcpys or whatever is appropriate.
struct DDSLoadInfo {
uint32_t bytesToCopy;
};
bool DetectDDSParams(const DDSHeader *header, DDSLoadInfo *info);
// We use the Basis library for the actual reading, but before we do, we pre-scan using this, similarly to the png trick.
struct KTXHeader {
uint8_t identifier[12];
uint32_t vkFormat;
uint32_t typeSize;
uint32_t pixelWidth;
uint32_t pixelHeight;
uint32_t pixelDepth;
uint32_t layerCount;
uint32_t faceCount;
uint32_t levelCount;
uint32_t supercompressionScheme;
};

View file

@ -5,9 +5,6 @@
#include <cstdlib> #include <cstdlib>
#include <cstdio> #include <cstdio>
#include <inttypes.h>
// Hm, what's this for?
#ifndef _MSC_VER #ifndef _MSC_VER
#include <strings.h> #include <strings.h>
#endif #endif
@ -20,17 +17,17 @@
#include <vector> #include <vector>
#include "Common/Data/Format/IniFile.h" #include "Common/Data/Format/IniFile.h"
#include "Common/Data/Text/Parsers.h"
#include "Common/File/VFS/VFS.h" #include "Common/File/VFS/VFS.h"
#include "Common/File/FileUtil.h" #include "Common/File/FileUtil.h"
#include "Common/Log.h" #include "Common/Data/Text/Parsers.h"
#include "Common/Math/math_util.h"
#ifdef _WIN32
#include "Common/Data/Encoding/Utf8.h"
#endif
#include "Common/StringUtils.h" #include "Common/StringUtils.h"
// This unescapes # signs. static bool ParseLineKey(const std::string &line, size_t &pos, std::string *keyOut) {
// NOTE: These parse functions can make better use of the string_view - the pos argument should not be needed, for example.
static bool ParseLineKey(std::string_view line, size_t &pos, std::string *keyOut) {
std::string key = ""; std::string key = "";
while (pos < line.size()) { while (pos < line.size()) {
@ -45,8 +42,7 @@ static bool ParseLineKey(std::string_view line, size_t &pos, std::string *keyOut
} }
// Escaped. // Escaped.
key += line.substr(pos, next - pos - 1); key += line.substr(pos, next - pos - 1) + "#";
key.push_back('#');
pos = next + 1; pos = next + 1;
} else if (line[next] == '=') { } else if (line[next] == '=') {
// Hurray, done. // Hurray, done.
@ -62,11 +58,11 @@ static bool ParseLineKey(std::string_view line, size_t &pos, std::string *keyOut
return true; return true;
} }
static bool ParseLineValue(std::string_view line, size_t &pos, std::string *valueOut) { static bool ParseLineValue(const std::string &line, size_t &pos, std::string *valueOut) {
std::string value = ""; std::string value = "";
std::string_view strippedLine = StripSpaces(line.substr(pos)); std::string strippedLine = StripSpaces(line.substr(pos));
if (strippedLine.size() >= 2 && strippedLine[0] == '"' && strippedLine[strippedLine.size() - 1] == '"') { if (strippedLine[0] == '"' && strippedLine[strippedLine.size()-1] == '"') {
// Don't remove comment if is surrounded by " " // Don't remove comment if is surrounded by " "
value += line.substr(pos); value += line.substr(pos);
pos = line.npos; // Won't enter the while below pos = line.npos; // Won't enter the while below
@ -86,8 +82,7 @@ static bool ParseLineValue(std::string_view line, size_t &pos, std::string *valu
break; break;
} else { } else {
// Escaped. // Escaped.
value += line.substr(pos, next - pos - 1); value += line.substr(pos, next - pos - 1) + "#";
value.push_back('#');
pos = next + 1; pos = next + 1;
} }
} }
@ -99,7 +94,7 @@ static bool ParseLineValue(std::string_view line, size_t &pos, std::string *valu
return true; return true;
} }
static bool ParseLineComment(std::string_view line, size_t &pos, std::string *commentOut) { static bool ParseLineComment(const std::string& line, size_t &pos, std::string *commentOut) {
// Don't bother with anything if we don't need the comment data. // Don't bother with anything if we don't need the comment data.
if (commentOut) { if (commentOut) {
// Include any whitespace/formatting in the comment. // Include any whitespace/formatting in the comment.
@ -120,7 +115,8 @@ static bool ParseLineComment(std::string_view line, size_t &pos, std::string *co
return true; return true;
} }
static bool ParseLine(std::string_view line, std::string* keyOut, std::string* valueOut, std::string* commentOut) // Ugh, this is ugly.
static bool ParseLine(const std::string& line, std::string* keyOut, std::string* valueOut, std::string* commentOut)
{ {
// Rules: // Rules:
// 1. A line starting with ; is commented out. // 1. A line starting with ; is commented out.
@ -144,7 +140,7 @@ static bool ParseLine(std::string_view line, std::string* keyOut, std::string* v
return true; return true;
} }
static std::string EscapeHash(std::string_view value) { static std::string EscapeComments(const std::string &value) {
std::string result = ""; std::string result = "";
for (size_t pos = 0; pos < value.size(); ) { for (size_t pos = 0; pos < value.size(); ) {
@ -153,8 +149,7 @@ static std::string EscapeHash(std::string_view value) {
result += value.substr(pos); result += value.substr(pos);
pos = value.npos; pos = value.npos;
} else { } else {
result += value.substr(pos, next - pos); result += value.substr(pos, next - pos) + "\\#";
result += "\\#";
pos = next + 1; pos = next + 1;
} }
} }
@ -162,56 +157,34 @@ static std::string EscapeHash(std::string_view value) {
return result; return result;
} }
void ParsedIniLine::ParseFrom(std::string_view line) {
line = StripSpaces(line);
if (line.empty()) {
key.clear();
value.clear();
comment.clear();
} else if (line[0] == '#') {
key.clear();
value.clear();
comment = line;
} else {
ParseLine(line, &key, &value, &comment);
}
}
void ParsedIniLine::Reconstruct(std::string *output) const {
if (!key.empty()) {
*output = EscapeHash(key) + " = " + EscapeHash(value) + comment;
} else {
*output = comment;
}
}
void Section::Clear() { void Section::Clear() {
lines_.clear(); lines.clear();
} }
bool Section::GetKeys(std::vector<std::string> &keys) const { std::string* Section::GetLine(const char* key, std::string* valueOut, std::string* commentOut)
keys.clear(); {
for (auto liter = lines_.begin(); liter != lines_.end(); ++liter) { for (std::vector<std::string>::iterator iter = lines.begin(); iter != lines.end(); ++iter)
if (!liter->Key().empty()) {
keys.push_back(std::string(liter->Key())); std::string& line = *iter;
} std::string lineKey;
return true; ParseLine(line, &lineKey, valueOut, commentOut);
} if (!strcasecmp(lineKey.c_str(), key))
ParsedIniLine *Section::GetLine(const char *key) {
for (auto &line : lines_) {
if (equalsNoCase(line.Key(), key))
return &line; return &line;
} }
return nullptr; return 0;
} }
const ParsedIniLine *Section::GetLine(const char* key) const { const std::string* Section::GetLine(const char* key, std::string* valueOut, std::string* commentOut) const
for (auto &line : lines_) { {
if (equalsNoCase(line.Key(), key)) for (std::vector<std::string>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter)
{
const std::string& line = *iter;
std::string lineKey;
ParseLine(line, &lineKey, valueOut, commentOut);
if (!strcasecmp(lineKey.c_str(), key))
return &line; return &line;
} }
return nullptr; return 0;
} }
void Section::Set(const char* key, uint32_t newValue) { void Section::Set(const char* key, uint32_t newValue) {
@ -219,11 +192,10 @@ void Section::Set(const char* key, uint32_t newValue) {
} }
void Section::Set(const char* key, uint64_t newValue) { void Section::Set(const char* key, uint64_t newValue) {
Set(key, StringFromFormat("0x%016" PRIx64, newValue).c_str()); Set(key, StringFromFormat("0x%016lx", newValue).c_str());
} }
void Section::Set(const char* key, float newValue) { void Section::Set(const char* key, float newValue) {
_dbg_assert_(!my_isnanorinf(newValue));
Set(key, StringFromFormat("%f", newValue).c_str()); Set(key, StringFromFormat("%f", newValue).c_str());
} }
@ -235,13 +207,19 @@ void Section::Set(const char* key, int newValue) {
Set(key, StringFromInt(newValue).c_str()); Set(key, StringFromInt(newValue).c_str());
} }
void Section::Set(const char* key, const char* newValue) { void Section::Set(const char* key, const char* newValue)
ParsedIniLine *line = GetLine(key); {
if (line) { std::string value, commented;
line->SetValue(newValue); std::string* line = GetLine(key, &value, &commented);
} else { if (line)
{
// Change the value - keep the key and comment
*line = StripSpaces(key) + " = " + EscapeComments(newValue) + commented;
}
else
{
// The key did not already exist in this section - let's add it. // The key did not already exist in this section - let's add it.
lines_.emplace_back(ParsedIniLine(key, newValue)); lines.emplace_back(std::string(key) + " = " + EscapeComments(newValue));
} }
} }
@ -253,15 +231,16 @@ void Section::Set(const char* key, const std::string& newValue, const std::strin
Delete(key); Delete(key);
} }
bool Section::Get(const char* key, std::string* value, const char* defaultValue) const { bool Section::Get(const char* key, std::string* value, const char* defaultValue) const
const ParsedIniLine *line = GetLine(key); {
if (!line) { const std::string* line = GetLine(key, value, 0);
if (defaultValue) { if (!line)
{
if (defaultValue)
{
*value = defaultValue; *value = defaultValue;
} }
return false; return false;
} else {
*value = line->Value();
} }
return true; return true;
} }
@ -306,7 +285,7 @@ void Section::Set(const char* key, const std::vector<std::string>& newValues)
} }
void Section::AddComment(const std::string &comment) { void Section::AddComment(const std::string &comment) {
lines_.emplace_back(ParsedIniLine::CommentOnly("# " + comment)); lines.emplace_back("# " + comment);
} }
bool Section::Get(const char* key, std::vector<std::string>& values) const bool Section::Get(const char* key, std::vector<std::string>& values) const
@ -341,7 +320,7 @@ bool Section::Get(const char* key, int* value, int defaultValue) const
{ {
std::string temp; std::string temp;
bool retval = Get(key, &temp, 0); bool retval = Get(key, &temp, 0);
if (retval && TryParse(temp, value)) if (retval && TryParse(temp.c_str(), value))
return true; return true;
*value = defaultValue; *value = defaultValue;
return false; return false;
@ -371,7 +350,7 @@ bool Section::Get(const char* key, bool* value, bool defaultValue) const
{ {
std::string temp; std::string temp;
bool retval = Get(key, &temp, 0); bool retval = Get(key, &temp, 0);
if (retval && TryParse(temp, value)) if (retval && TryParse(temp.c_str(), value))
return true; return true;
*value = defaultValue; *value = defaultValue;
return false; return false;
@ -381,7 +360,7 @@ bool Section::Get(const char* key, float* value, float defaultValue) const
{ {
std::string temp; std::string temp;
bool retval = Get(key, &temp, 0); bool retval = Get(key, &temp, 0);
if (retval && TryParse(temp, value)) if (retval && TryParse(temp.c_str(), value))
return true; return true;
*value = defaultValue; *value = defaultValue;
return false; return false;
@ -391,35 +370,46 @@ bool Section::Get(const char* key, double* value, double defaultValue) const
{ {
std::string temp; std::string temp;
bool retval = Get(key, &temp, 0); bool retval = Get(key, &temp, 0);
if (retval && TryParse(temp, value)) if (retval && TryParse(temp.c_str(), value))
return true; return true;
*value = defaultValue; *value = defaultValue;
return false; return false;
} }
bool Section::Exists(const char *key) const { bool Section::Exists(const char *key) const
for (auto &line : lines_) { {
if (equalsNoCase(key, line.Key())) for (std::vector<std::string>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter)
{
std::string lineKey;
ParseLine(*iter, &lineKey, NULL, NULL);
if (!strcasecmp(lineKey.c_str(), key))
return true; return true;
} }
return false; return false;
} }
std::map<std::string, std::string> Section::ToMap() const { std::map<std::string, std::string> Section::ToMap() const
{
std::map<std::string, std::string> outMap; std::map<std::string, std::string> outMap;
for (auto &line : lines_) { for (std::vector<std::string>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter)
if (!line.Key().empty()) { {
outMap[std::string(line.Key())] = line.Value(); std::string lineKey, lineValue;
if (ParseLine(*iter, &lineKey, &lineValue, NULL)) {
outMap[lineKey] = lineValue;
} }
} }
return outMap; return outMap;
} }
bool Section::Delete(const char *key) {
ParsedIniLine *line = GetLine(key); bool Section::Delete(const char *key)
for (auto liter = lines_.begin(); liter != lines_.end(); ++liter) { {
if (line == &*liter) { std::string* line = GetLine(key, 0, 0);
lines_.erase(liter); for (std::vector<std::string>::iterator liter = lines.begin(); liter != lines.end(); ++liter)
{
if (line == &*liter)
{
lines.erase(liter);
return true; return true;
} }
} }
@ -428,36 +418,42 @@ bool Section::Delete(const char *key) {
// IniFile // IniFile
const Section* IniFile::GetSection(const char* sectionName) const { const Section* IniFile::GetSection(const char* sectionName) const
for (const auto &iter : sections) {
for (std::vector<Section>::const_iterator iter = sections.begin(); iter != sections.end(); ++iter)
if (!strcasecmp(iter->name().c_str(), sectionName)) if (!strcasecmp(iter->name().c_str(), sectionName))
return iter.get(); return (&(*iter));
return nullptr; return 0;
} }
Section* IniFile::GetSection(const char* sectionName) { Section* IniFile::GetSection(const char* sectionName)
for (const auto &iter : sections) {
for (std::vector<Section>::iterator iter = sections.begin(); iter != sections.end(); ++iter)
if (!strcasecmp(iter->name().c_str(), sectionName)) if (!strcasecmp(iter->name().c_str(), sectionName))
return iter.get(); return (&(*iter));
return nullptr; return 0;
} }
Section* IniFile::GetOrCreateSection(const char* sectionName) { Section* IniFile::GetOrCreateSection(const char* sectionName)
{
Section* section = GetSection(sectionName); Section* section = GetSection(sectionName);
if (!section) { if (!section)
sections.push_back(std::unique_ptr<Section>(new Section(sectionName))); {
section = sections.back().get(); sections.push_back(Section(sectionName));
section = &sections[sections.size() - 1];
} }
return section; return section;
} }
bool IniFile::DeleteSection(const char* sectionName) { bool IniFile::DeleteSection(const char* sectionName)
{
Section* s = GetSection(sectionName); Section* s = GetSection(sectionName);
if (!s) if (!s)
return false; return false;
for (std::vector<Section>::iterator iter = sections.begin(); iter != sections.end(); ++iter)
for (auto iter = sections.begin(); iter != sections.end(); ++iter) { {
if (iter->get() == s) { if (&(*iter) == s)
{
sections.erase(iter); sections.erase(iter);
return true; return true;
} }
@ -465,21 +461,35 @@ bool IniFile::DeleteSection(const char* sectionName) {
return false; return false;
} }
bool IniFile::Exists(const char* sectionName, const char* key) const { bool IniFile::Exists(const char* sectionName, const char* key) const
{
const Section* section = GetSection(sectionName); const Section* section = GetSection(sectionName);
if (!section) if (!section)
return false; return false;
return section->Exists(key); return section->Exists(key);
} }
bool IniFile::DeleteKey(const char* sectionName, const char* key) { void IniFile::SetLines(const char* sectionName, const std::vector<std::string> &lines)
{
Section* section = GetOrCreateSection(sectionName);
section->lines.clear();
for (std::vector<std::string>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter)
{
section->lines.push_back(*iter);
}
}
bool IniFile::DeleteKey(const char* sectionName, const char* key)
{
Section* section = GetSection(sectionName); Section* section = GetSection(sectionName);
if (!section) if (!section)
return false; return false;
ParsedIniLine *line = section->GetLine(key); std::string* line = section->GetLine(key, 0, 0);
for (auto liter = section->lines_.begin(); liter != section->lines_.end(); ++liter) { for (std::vector<std::string>::iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter)
if (line == &(*liter)) { {
section->lines_.erase(liter); if (line == &(*liter))
{
section->lines.erase(liter);
return true; return true;
} }
} }
@ -487,13 +497,55 @@ bool IniFile::DeleteKey(const char* sectionName, const char* key) {
} }
// Return a list of all keys in a section // Return a list of all keys in a section
bool IniFile::GetKeys(const char* sectionName, std::vector<std::string>& keys) const { bool IniFile::GetKeys(const char* sectionName, std::vector<std::string>& keys) const
const Section *section = GetSection(sectionName); {
const Section* section = GetSection(sectionName);
if (!section) if (!section)
return false; return false;
return section->GetKeys(keys); keys.clear();
for (std::vector<std::string>::const_iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter)
{
std::string key;
ParseLine(*liter, &key, 0, 0);
if (!key.empty())
keys.push_back(key);
}
return true;
} }
// Return a list of all lines in a section
bool IniFile::GetLines(const char* sectionName, std::vector<std::string>& lines, const bool remove_comments) const
{
const Section* section = GetSection(sectionName);
if (!section)
return false;
lines.clear();
for (std::vector<std::string>::const_iterator iter = section->lines.begin(); iter != section->lines.end(); ++iter)
{
std::string line = StripSpaces(*iter);
if (remove_comments)
{
int commentPos = (int)line.find('#');
if (commentPos == 0)
{
continue;
}
if (commentPos != (int)std::string::npos)
{
line = StripSpaces(line.substr(0, commentPos));
}
}
lines.push_back(line);
}
return true;
}
void IniFile::SortSections() void IniFile::SortSections()
{ {
std::sort(sections.begin(), sections.end()); std::sort(sections.begin(), sections.end());
@ -502,7 +554,7 @@ void IniFile::SortSections()
bool IniFile::Load(const Path &path) bool IniFile::Load(const Path &path)
{ {
sections.clear(); sections.clear();
sections.push_back(std::unique_ptr<Section>(new Section(""))); sections.push_back(Section(""));
// first section consists of the comments before the first real section // first section consists of the comments before the first real section
// Open file // Open file
@ -515,9 +567,9 @@ bool IniFile::Load(const Path &path)
return success; return success;
} }
bool IniFile::LoadFromVFS(VFSInterface &vfs, const std::string &filename) { bool IniFile::LoadFromVFS(const std::string &filename) {
size_t size; size_t size;
uint8_t *data = vfs.ReadFile(filename.c_str(), &size); uint8_t *data = VFSReadFile(filename.c_str(), &size);
if (!data) if (!data)
return false; return false;
std::string str((const char*)data, size); std::string str((const char*)data, size);
@ -530,10 +582,10 @@ bool IniFile::LoadFromVFS(VFSInterface &vfs, const std::string &filename) {
bool IniFile::Load(std::istream &in) { bool IniFile::Load(std::istream &in) {
// Maximum number of letters in a line // Maximum number of letters in a line
static const int MAX_BYTES = 1024*32; static const int MAX_BYTES = 1024*32;
char *templine = new char[MAX_BYTES]; // avoid using up massive stack space
while (!(in.eof() || in.fail())) while (!(in.eof() || in.fail()))
{ {
char templine[MAX_BYTES];
in.getline(templine, MAX_BYTES); in.getline(templine, MAX_BYTES);
std::string line = templine; std::string line = templine;
@ -558,23 +610,20 @@ bool IniFile::Load(std::istream &in) {
if (sectionNameEnd != std::string::npos) { if (sectionNameEnd != std::string::npos) {
// New section! // New section!
std::string sub = line.substr(1, sectionNameEnd - 1); std::string sub = line.substr(1, sectionNameEnd - 1);
sections.push_back(std::unique_ptr<Section>(new Section(sub))); sections.push_back(Section(sub));
if (sectionNameEnd + 1 < line.size()) { if (sectionNameEnd + 1 < line.size()) {
sections.back()->comment = line.substr(sectionNameEnd + 1); sections[sections.size() - 1].comment = line.substr(sectionNameEnd + 1);
} }
} else { } else {
if (sections.empty()) { if (sections.empty()) {
sections.push_back(std::unique_ptr<Section>(new Section(""))); sections.push_back(Section(""));
} }
ParsedIniLine parsedLine; sections[sections.size() - 1].lines.push_back(line);
parsedLine.ParseFrom(line);
sections.back()->lines_.push_back(parsedLine);
} }
} }
} }
delete[] templine;
return true; return true;
} }
@ -589,14 +638,13 @@ bool IniFile::Save(const Path &filename)
// TODO: Do we still need this? It's annoying. // TODO: Do we still need this? It's annoying.
fprintf(file, "\xEF\xBB\xBF"); fprintf(file, "\xEF\xBB\xBF");
for (const auto &section : sections) { for (const Section &section : sections) {
if (!section->name().empty() && (!section->lines_.empty() || !section->comment.empty())) { if (!section.name().empty() && (!section.lines.empty() || !section.comment.empty())) {
fprintf(file, "[%s]%s\n", section->name().c_str(), section->comment.c_str()); fprintf(file, "[%s]%s\n", section.name().c_str(), section.comment.c_str());
} }
for (const auto &line : section->lines_) {
std::string buffer; for (const std::string &s : section.lines) {
line.Reconstruct(&buffer); fprintf(file, "%s\n", s.c_str());
fprintf(file, "%s\n", buffer.c_str());
} }
} }

View file

@ -5,50 +5,12 @@
#pragma once #pragma once
#include <istream> #include <istream>
#include <memory>
#include <map> #include <map>
#include <string> #include <string>
#include <string_view>
#include <vector> #include <vector>
#include <cstdint>
#include "Common/File/Path.h" #include "Common/File/Path.h"
class VFSInterface;
class ParsedIniLine {
public:
ParsedIniLine() {}
ParsedIniLine(std::string_view key, std::string_view value) {
this->key = key;
this->value = value;
}
ParsedIniLine(std::string_view key, std::string_view value, std::string_view comment) {
this->key = key;
this->value = value;
this->comment = comment;
}
static ParsedIniLine CommentOnly(std::string_view comment) {
return ParsedIniLine(std::string_view(), std::string_view(), comment);
}
// Comments only come from "ParseFrom".
void ParseFrom(std::string_view line);
void Reconstruct(std::string *output) const;
// Having these as views allows a more efficient internal representation, like one joint string.
std::string_view Key() const { return key; }
std::string_view Value() const { return value; }
std::string_view Comment() const { return comment; }
void SetValue(std::string_view newValue) { value = newValue; }
private:
std::string key;
std::string value;
std::string comment;
};
class Section { class Section {
friend class IniFile; friend class IniFile;
@ -63,8 +25,8 @@ public:
std::map<std::string, std::string> ToMap() const; std::map<std::string, std::string> ToMap() const;
ParsedIniLine *GetLine(const char *key); std::string *GetLine(const char* key, std::string* valueOut, std::string* commentOut);
const ParsedIniLine *GetLine(const char *key) const; const std::string *GetLine(const char* key, std::string* valueOut, std::string* commentOut) const;
void Set(const char* key, const char* newValue); void Set(const char* key, const char* newValue);
void Set(const char* key, const std::string& newValue, const std::string& defaultValue); void Set(const char* key, const std::string& newValue, const std::string& defaultValue);
@ -105,9 +67,6 @@ public:
bool Get(const char* key, double* value, double defaultValue = false) const; bool Get(const char* key, double* value, double defaultValue = false) const;
bool Get(const char* key, std::vector<std::string>& values) const; bool Get(const char* key, std::vector<std::string>& values) const;
// Return a list of all keys in this section
bool GetKeys(std::vector<std::string> &keys) const;
bool operator < (const Section& other) const { bool operator < (const Section& other) const {
return name_ < other.name_; return name_ < other.name_;
} }
@ -117,7 +76,7 @@ public:
} }
protected: protected:
std::vector<ParsedIniLine> lines_; std::vector<std::string> lines;
std::string name_; std::string name_;
std::string comment; std::string comment;
}; };
@ -125,10 +84,12 @@ protected:
class IniFile { class IniFile {
public: public:
bool Load(const Path &path); bool Load(const Path &path);
bool Load(const std::string &filename) { return Load(Path(filename)); }
bool Load(std::istream &istream); bool Load(std::istream &istream);
bool LoadFromVFS(VFSInterface &vfs, const std::string &filename); bool LoadFromVFS(const std::string &filename);
bool Save(const Path &path); bool Save(const Path &path);
bool Save(const std::string &filename) { return Save(Path(filename)); }
// Returns true if key exists in section // Returns true if key exists in section
bool Exists(const char* sectionName, const char* key) const; bool Exists(const char* sectionName, const char* key) const;
@ -173,19 +134,21 @@ public:
bool GetKeys(const char* sectionName, std::vector<std::string>& keys) const; bool GetKeys(const char* sectionName, std::vector<std::string>& keys) const;
void SetLines(const char* sectionName, const std::vector<std::string> &lines);
bool GetLines(const char* sectionName, std::vector<std::string>& lines, const bool remove_comments = true) const;
bool DeleteKey(const char* sectionName, const char* key); bool DeleteKey(const char* sectionName, const char* key);
bool DeleteSection(const char* sectionName); bool DeleteSection(const char* sectionName);
void SortSections(); void SortSections();
std::vector<Section> &Sections() { return sections; }
std::vector<std::unique_ptr<Section>> &Sections() { return sections; }
bool HasSection(const char *section) { return GetSection(section) != 0; } bool HasSection(const char *section) { return GetSection(section) != 0; }
Section* GetOrCreateSection(const char* section); Section* GetOrCreateSection(const char* section);
private: private:
std::vector<std::unique_ptr<Section>> sections; std::vector<Section> sections;
const Section* GetSection(const char* section) const; const Section* GetSection(const char* section) const;
Section* GetSection(const char* section); Section* GetSection(const char* section);

View file

@ -8,7 +8,7 @@ namespace json {
JsonReader::JsonReader(const std::string &filename) { JsonReader::JsonReader(const std::string &filename) {
size_t buf_size; size_t buf_size;
buffer_ = (char *)g_VFS.ReadFile(filename.c_str(), &buf_size); buffer_ = (char *)VFSReadFile(filename.c_str(), &buf_size);
if (buffer_) { if (buffer_) {
parse(); parse();
} else { } else {

View file

@ -1,5 +1,3 @@
#pragma once
#include <cstring> #include <cstring>
#include <string> #include <string>
#include <vector> #include <vector>

View file

@ -9,8 +9,6 @@
// //
// Zero dependencies apart from stdlib (if you remove the vhjson usage.) // Zero dependencies apart from stdlib (if you remove the vhjson usage.)
#pragma once
#include <string> #include <string>
#include <vector> #include <vector>
#include <sstream> #include <sstream>

View file

@ -59,14 +59,3 @@ int pngLoadPtr(const unsigned char *input_ptr, size_t input_len, int *pwidth, in
png_image_finish_read(&png, NULL, *image_data_ptr, stride, NULL); png_image_finish_read(&png, NULL, *image_data_ptr, stride, NULL);
return 1; return 1;
} }
bool PNGHeaderPeek::IsValidPNGHeader() const {
if (magic != 0x474e5089 || ihdrTag != 0x52444849) {
return false;
}
// Reject crazy sized images, too.
if (Width() > 32768 && Height() > 32768) {
return false;
}
return true;
}

View file

@ -1,8 +1,5 @@
#pragma once #ifndef _PNG_LOAD_H
#define _PNG_LOAD_H
#include <cstdint>
#include "Common/BitSet.h"
// *image_data_ptr should be deleted with free() // *image_data_ptr should be deleted with free()
// return value of 1 == success. // return value of 1 == success.
@ -12,22 +9,4 @@ int pngLoad(const char *file, int *pwidth,
int pngLoadPtr(const unsigned char *input_ptr, size_t input_len, int *pwidth, int pngLoadPtr(const unsigned char *input_ptr, size_t input_len, int *pwidth,
int *pheight, unsigned char **image_data_ptr); int *pheight, unsigned char **image_data_ptr);
// PNG peeker - just read the start of a PNG straight into this struct, in order to #endif // _PNG_LOAD_H
// look at basic parameters like width and height. Note that while PNG is a chunk-based
// format, the IHDR chunk is REQUIRED to be the first one, so this will work.
// Does not handle Apple's weirdo extension CgBI. http://iphonedevwiki.net/index.php/CgBI_file_format
// That should not be an issue.
struct PNGHeaderPeek {
uint32_t magic;
uint32_t ignore0;
uint32_t ignore1;
uint32_t ihdrTag;
uint32_t be_width; // big endian
uint32_t be_height;
uint8_t bitDepth; // bits per channel, can be 1, 2, 4, 8, 16
uint8_t colorType; // really, pixel format. 0 = grayscale, 2 = rgb, 3 = palette index, 4 = gray+alpha, 6 = rgba
bool IsValidPNGHeader() const;
int Width() const { return swap32(be_width); }
int Height() const { return swap32(be_height); }
};

View file

@ -126,7 +126,7 @@ int LoadZIMPtr(const uint8_t *zim, size_t datasize, int *width, int *height, int
int LoadZIM(const char *filename, int *width, int *height, int *format, uint8_t **image) { int LoadZIM(const char *filename, int *width, int *height, int *format, uint8_t **image) {
size_t size; size_t size;
uint8_t *buffer = g_VFS.ReadFile(filename, &size); uint8_t *buffer = VFSReadFile(filename, &size);
if (!buffer) { if (!buffer) {
ERROR_LOG(IO, "Couldn't read data for '%s'", filename); ERROR_LOG(IO, "Couldn't read data for '%s'", filename);
return 0; return 0;

View file

@ -1,88 +1,38 @@
#include <cstring>
#include "Common/Data/Text/I18n.h" #include "Common/Data/Text/I18n.h"
#include "Common/Data/Format/IniFile.h" #include "Common/Data/Format/IniFile.h"
#include "Common/File/VFS/VFS.h" #include "Common/File/VFS/VFS.h"
#include "Common/Log.h"
#include "Common/StringUtils.h" #include "Common/StringUtils.h"
static const char * const g_categoryNames[(size_t)I18NCat::CATEGORY_COUNT] = { I18NRepo i18nrepo;
"Audio",
"Controls",
"CwCheats",
"DesktopUI",
"Developer",
"Dialog",
"Error",
"Game",
"Graphics",
"InstallZip",
"KeyMapping",
"MainMenu",
"MainSettings",
"MappableControls",
"Networking",
"Pause",
"PostShaders",
"PSPCredits",
"MemStick",
"RemoteISO",
"Reporting",
"Savedata",
"Screen",
"Search",
"Store",
"SysInfo",
"System",
"TextureShaders",
"Themes",
"UI Elements",
"Upgrade",
"VR",
"Achievements",
"PSPSettings",
};
I18NRepo g_i18nrepo; I18NRepo::~I18NRepo() {
Clear();
}
std::string I18NRepo::LanguageID() { std::string I18NRepo::LanguageID() {
return languageID_; return languageID_;
} }
I18NRepo::I18NRepo() {
Clear();
}
void I18NRepo::Clear() { void I18NRepo::Clear() {
std::lock_guard<std::mutex> guard(catsLock_); std::lock_guard<std::mutex> guard(catsLock_);
for (auto &iter : cats_) { for (auto iter = cats_.begin(); iter != cats_.end(); ++iter) {
// Initialize with empty categories, so that early lookups don't crash. iter->second.reset();
iter = std::shared_ptr<I18NCategory>(new I18NCategory());
} }
} cats_.clear();
I18NCategory::I18NCategory(const Section &section) {
std::map<std::string, std::string> sectionMap = section.ToMap();
SetMap(sectionMap);
}
void I18NCategory::Clear() {
map_.clear();
missedKeyLog_.clear();
} }
const char *I18NCategory::T(const char *key, const char *def) { const char *I18NCategory::T(const char *key, const char *def) {
if (!key) { if (!key) {
return "ERROR"; return "ERROR";
} }
// Replace the \n's with \\n's so that key values with newlines will be found correctly. // Replace the \n's with \\n's so that key values with newlines will be found correctly.
std::string modifiedKey = key; std::string modifiedKey = key;
modifiedKey = ReplaceAll(modifiedKey, "\n", "\\n"); modifiedKey = ReplaceAll(modifiedKey, "\n", "\\n");
auto iter = map_.find(modifiedKey); auto iter = map_.find(modifiedKey);
if (iter != map_.end()) { if (iter != map_.end()) {
// INFO_LOG(SYSTEM, "translation key found in %s: %s", name_.c_str(), key);
return iter->second.text.c_str(); return iter->second.text.c_str();
} else { } else {
std::lock_guard<std::mutex> guard(missedKeyLock_); std::lock_guard<std::mutex> guard(missedKeyLock_);
@ -90,6 +40,7 @@ const char *I18NCategory::T(const char *key, const char *def) {
missedKeyLog_[key] = def; missedKeyLog_[key] = def;
else else
missedKeyLog_[key] = modifiedKey; missedKeyLog_[key] = modifiedKey;
// INFO_LOG(SYSTEM, "Missed translation key in %s: %s", name_.c_str(), key);
return def ? def : key; return def ? def : key;
} }
} }
@ -99,16 +50,21 @@ void I18NCategory::SetMap(const std::map<std::string, std::string> &m) {
if (map_.find(iter->first) == map_.end()) { if (map_.find(iter->first) == map_.end()) {
std::string text = ReplaceAll(iter->second, "\\n", "\n"); std::string text = ReplaceAll(iter->second, "\\n", "\n");
map_[iter->first] = I18NEntry(text); map_[iter->first] = I18NEntry(text);
// INFO_LOG(SYSTEM, "Language entry: %s -> %s", iter->first.c_str(), text.c_str());
} }
} }
} }
std::shared_ptr<I18NCategory> I18NRepo::GetCategory(I18NCat category) { std::shared_ptr<I18NCategory> I18NRepo::GetCategory(const char *category) {
std::lock_guard<std::mutex> guard(catsLock_); std::lock_guard<std::mutex> guard(catsLock_);
if (category != I18NCat::NONE) auto iter = cats_.find(category);
return cats_[(size_t)category]; if (iter != cats_.end()) {
else return iter->second;
return nullptr; } else {
I18NCategory *c = new I18NCategory(this, category);
cats_[category].reset(c);
return cats_[category];
}
} }
Path I18NRepo::GetIniPath(const std::string &languageID) const { Path I18NRepo::GetIniPath(const std::string &languageID) const {
@ -117,7 +73,7 @@ Path I18NRepo::GetIniPath(const std::string &languageID) const {
bool I18NRepo::IniExists(const std::string &languageID) const { bool I18NRepo::IniExists(const std::string &languageID) const {
File::FileInfo info; File::FileInfo info;
if (!g_VFS.GetFileInfo(GetIniPath(languageID).ToString().c_str(), &info)) if (!VFSGetFileInfo(GetIniPath(languageID).ToString().c_str(), &info))
return false; return false;
if (!info.exists) if (!info.exists)
return false; return false;
@ -135,19 +91,17 @@ bool I18NRepo::LoadIni(const std::string &languageID, const Path &overridePath)
iniPath = GetIniPath(languageID); iniPath = GetIniPath(languageID);
} }
if (!ini.LoadFromVFS(g_VFS, iniPath.ToString())) if (!ini.LoadFromVFS(iniPath.ToString()))
return false; return false;
Clear(); Clear();
const std::vector<std::unique_ptr<Section>> &sections = ini.Sections(); const std::vector<Section> &sections = ini.Sections();
std::lock_guard<std::mutex> guard(catsLock_); std::lock_guard<std::mutex> guard(catsLock_);
for (auto &section : sections) { for (auto iter = sections.begin(); iter != sections.end(); ++iter) {
for (size_t i = 0; i < (size_t)I18NCat::CATEGORY_COUNT; i++) { if (iter->name() != "") {
if (!strcmp(section->name().c_str(), g_categoryNames[i])) { cats_[iter->name()].reset(LoadSection(&(*iter), iter->name().c_str()));
cats_[i].reset(new I18NCategory(*section.get()));
}
} }
} }
@ -155,21 +109,53 @@ bool I18NRepo::LoadIni(const std::string &languageID, const Path &overridePath)
return true; return true;
} }
void I18NRepo::LogMissingKeys() const { std::map<std::string, std::vector<std::string>> I18NRepo::GetMissingKeys() const {
std::map<std::string, std::vector<std::string>> ret;
std::lock_guard<std::mutex> guard(catsLock_); std::lock_guard<std::mutex> guard(catsLock_);
for (size_t i = 0; i < (size_t)I18NCat::CATEGORY_COUNT; i++) { for (auto &cat : cats_) {
auto &cat = cats_[i]; for (auto &key : cat.second->Missed()) {
for (auto &key : cat->Missed()) { ret[cat.first].push_back(key.first);
INFO_LOG(SYSTEM, "Missing translation [%s]: %s (%s)", g_categoryNames[i], key.first.c_str(), key.second.c_str());
} }
} }
return ret;
} }
std::shared_ptr<I18NCategory> GetI18NCategory(I18NCat category) { I18NCategory *I18NRepo::LoadSection(const Section *section, const char *name) {
if (category == I18NCat::NONE) { I18NCategory *cat = new I18NCategory(this, name);
return std::shared_ptr<I18NCategory>(); std::map<std::string, std::string> sectionMap = section->ToMap();
} cat->SetMap(sectionMap);
std::shared_ptr<I18NCategory> cat = g_i18nrepo.GetCategory(category);
_dbg_assert_(cat);
return cat; return cat;
} }
// This is a very light touched save variant - it won't overwrite
// anything, only create new entries.
void I18NRepo::SaveIni(const std::string &languageID) {
IniFile ini;
ini.Load(GetIniPath(languageID));
std::lock_guard<std::mutex> guard(catsLock_);
for (auto iter = cats_.begin(); iter != cats_.end(); ++iter) {
std::string categoryName = iter->first;
Section *section = ini.GetOrCreateSection(categoryName.c_str());
SaveSection(ini, section, iter->second);
}
ini.Save(GetIniPath(languageID));
}
void I18NRepo::SaveSection(IniFile &ini, Section *section, std::shared_ptr<I18NCategory> cat) {
const std::map<std::string, std::string> &missed = cat->Missed();
for (auto iter = missed.begin(); iter != missed.end(); ++iter) {
if (!section->Exists(iter->first.c_str())) {
std::string text = ReplaceAll(iter->second, "\n", "\\n");
section->Set(iter->first, text);
}
}
const std::map<std::string, I18NEntry> &entries = cat->GetMap();
for (auto iter = entries.begin(); iter != entries.end(); ++iter) {
std::string text = ReplaceAll(iter->second.text, "\n", "\\n");
section->Set(iter->first, text);
}
cat->ClearMissed();
}

View file

@ -9,10 +9,10 @@
// As usual, everything is UTF-8. Nothing else allowed. // As usual, everything is UTF-8. Nothing else allowed.
#include <map> #include <map>
#include <memory>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <vector> #include <vector>
#include <memory>
#include "Common/Common.h" #include "Common/Common.h"
#include "Common/File/Path.h" #include "Common/File/Path.h"
@ -23,45 +23,6 @@ class I18NRepo;
class IniFile; class IniFile;
class Section; class Section;
enum class I18NCat : uint8_t {
AUDIO = 0,
CONTROLS,
CWCHEATS,
DESKTOPUI,
DEVELOPER,
DIALOG,
ERRORS, // Can't name it ERROR, clashes with many defines.
GAME,
GRAPHICS,
INSTALLZIP,
KEYMAPPING,
MAINMENU,
MAINSETTINGS,
MAPPABLECONTROLS,
NETWORKING,
PAUSE,
POSTSHADERS,
PSPCREDITS,
MEMSTICK,
REMOTEISO,
REPORTING,
SAVEDATA,
SCREEN,
SEARCH,
STORE,
SYSINFO,
SYSTEM,
TEXTURESHADERS,
THEMES,
UI_ELEMENTS,
UPGRADE,
VR,
ACHIEVEMENTS,
PSPSETTINGS,
CATEGORY_COUNT,
NONE = CATEGORY_COUNT,
};
struct I18NEntry { struct I18NEntry {
I18NEntry(const std::string &t) : text(t), readFlag(false) {} I18NEntry(const std::string &t) : text(t), readFlag(false) {}
I18NEntry() : readFlag(false) {} I18NEntry() : readFlag(false) {}
@ -78,10 +39,9 @@ struct I18NCandidate {
class I18NCategory { class I18NCategory {
public: public:
I18NCategory() {} // NOTE: Name must be a global constant string - it is not copied.
explicit I18NCategory(const Section &section); I18NCategory(const char *name) : name_(name) {}
const char *T(const char *key, const char *def = 0);
const char *T(const char *key, const char *def = nullptr);
const char *T(const std::string &key) { const char *T(const std::string &key) {
return T(key.c_str(), nullptr); return T(key.c_str(), nullptr);
} }
@ -93,55 +53,76 @@ public:
const std::map<std::string, I18NEntry> &GetMap() { return map_; } const std::map<std::string, I18NEntry> &GetMap() { return map_; }
void ClearMissed() { missedKeyLog_.clear(); } void ClearMissed() { missedKeyLog_.clear(); }
void Clear(); const char *GetName() const { return name_.c_str(); }
private: private:
I18NCategory(I18NRepo *repo, const char *name) {} I18NCategory(I18NRepo *repo, const char *name) : name_(name) {}
void SetMap(const std::map<std::string, std::string> &m); void SetMap(const std::map<std::string, std::string> &m);
std::string name_;
std::map<std::string, I18NEntry> map_; std::map<std::string, I18NEntry> map_;
mutable std::mutex missedKeyLock_; mutable std::mutex missedKeyLock_;
std::map<std::string, std::string> missedKeyLog_; std::map<std::string, std::string> missedKeyLog_;
// Noone else can create these. // Noone else can create these.
friend class I18NRepo; friend class I18NRepo;
DISALLOW_COPY_AND_ASSIGN(I18NCategory);
}; };
class I18NRepo { class I18NRepo {
public: public:
I18NRepo(); I18NRepo() {}
~I18NRepo();
bool IniExists(const std::string &languageID) const; bool IniExists(const std::string &languageID) const;
bool LoadIni(const std::string &languageID, const Path &overridePath = Path()); // NOT the filename! bool LoadIni(const std::string &languageID, const Path &overridePath = Path()); // NOT the filename!
void SaveIni(const std::string &languageID);
std::string LanguageID(); std::string LanguageID();
std::shared_ptr<I18NCategory> GetCategory(I18NCat category); std::shared_ptr<I18NCategory> GetCategory(const char *categoryName);
bool HasCategory(const char *categoryName) const {
// Translate the string, by looking up "key" in the file, and falling back to either def or key, in that order, if the lookup fails. std::lock_guard<std::mutex> guard(catsLock_);
// def can (and usually is) set to nullptr. return cats_.find(categoryName) != cats_.end();
const char *T(I18NCat category, const char *key, const char *def = nullptr) {
if (category == I18NCat::NONE)
return def ? def : key;
return cats_[(size_t)category]->T(key, def);
} }
const char *T(const char *category, const char *key, const char *def = 0);
void LogMissingKeys() const; std::map<std::string, std::vector<std::string>> GetMissingKeys() const;
private: private:
Path GetIniPath(const std::string &languageID) const; Path GetIniPath(const std::string &languageID) const;
void Clear(); void Clear();
I18NCategory *LoadSection(const Section *section, const char *name);
void SaveSection(IniFile &ini, Section *section, std::shared_ptr<I18NCategory> cat);
mutable std::mutex catsLock_; mutable std::mutex catsLock_;
std::shared_ptr<I18NCategory> cats_[(size_t)I18NCat::CATEGORY_COUNT]; std::map<std::string, std::shared_ptr<I18NCategory>> cats_;
std::string languageID_; std::string languageID_;
DISALLOW_COPY_AND_ASSIGN(I18NRepo);
}; };
extern I18NRepo g_i18nrepo; extern I18NRepo i18nrepo;
// These are simply talking to the one global instance of I18NRepo. // These are simply talking to the one global instance of I18NRepo.
std::shared_ptr<I18NCategory> GetI18NCategory(I18NCat cat); inline std::shared_ptr<I18NCategory> GetI18NCategory(const char *categoryName) {
if (!categoryName)
inline const char *T(I18NCat category, const char *key, const char *def = nullptr) { return nullptr;
return g_i18nrepo.T(category, key, def); return i18nrepo.GetCategory(categoryName);
} }
inline bool I18NCategoryLoaded(const char *categoryName) {
return i18nrepo.HasCategory(categoryName);
}
inline const char *T(const char *category, const char *key, const char *def = 0) {
return i18nrepo.T(category, key, def);
}
inline std::map<std::string, std::vector<std::string>> GetI18NMissingKeys() {
return i18nrepo.GetMissingKeys();
}

View file

@ -19,7 +19,7 @@ void NiceSizeFormat(uint64_t size, char *out, size_t bufSize) {
if (s == 0) if (s == 0)
snprintf(out, bufSize, "%d B", (int)size); snprintf(out, bufSize, "%d B", (int)size);
else else
snprintf(out, bufSize, "%3.2f %s", f, sizes[s]); snprintf(out, bufSize, "%3.1f %s", f, sizes[s]);
} }
std::string NiceSizeFormat(uint64_t size) { std::string NiceSizeFormat(uint64_t size) {
@ -43,7 +43,7 @@ bool Version::ParseVersionString(std::string str) {
std::string Version::ToString() const { std::string Version::ToString() const {
char temp[128]; char temp[128];
snprintf(temp, sizeof(temp), "%i.%i.%i", major, minor, sub); sprintf(temp, "%i.%i.%i", major, minor, sub);
return std::string(temp); return std::string(temp);
} }

View file

@ -177,18 +177,20 @@ void WordWrapper::AppendWord(int endIndex, int lastChar, bool addNewline) {
} }
void WordWrapper::Wrap() { void WordWrapper::Wrap() {
out_.clear();
// First, let's check if it fits as-is. // First, let's check if it fits as-is.
size_t len = strlen(str_); size_t len = strlen(str_);
// We know it'll be approximately this size. It's fine if the guess is a little off.
out_.reserve(len + len / 16);
if (MeasureWidth(str_, len) <= maxW_) { if (MeasureWidth(str_, len) <= maxW_) {
// If it fits, we don't need to go through each character. // If it fits, we don't need to go through each character.
out_ = str_; out_ = str_;
return; return;
} }
out_.clear();
// We know it'll be approximately this size. It's fine if the guess is a little off.
out_.reserve(len + len / 16);
if (flags_ & FLAG_ELLIPSIZE_TEXT) { if (flags_ & FLAG_ELLIPSIZE_TEXT) {
ellipsisWidth_ = MeasureWidth("...", 3); ellipsisWidth_ = MeasureWidth("...", 3);
} }

View file

@ -18,14 +18,12 @@
#include "ppsspp_config.h" #include "ppsspp_config.h"
#if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64) #if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64)
#define REAL_CPUDETECT_AVAIL 1 #define REAL_CPUDETECT_AVAIL 1
#elif (PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)) && !defined(__EMSCRIPTEN__) #elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
#define REAL_CPUDETECT_AVAIL 1 #define REAL_CPUDETECT_AVAIL 1
#elif PPSSPP_ARCH(MIPS) || PPSSPP_ARCH(MIPS64) #elif PPSSPP_ARCH(MIPS) || PPSSPP_ARCH(MIPS64)
#define REAL_CPUDETECT_AVAIL 1 #define REAL_CPUDETECT_AVAIL 1
#elif PPSSPP_ARCH(RISCV64) #elif PPSSPP_ARCH(RISCV64)
#define REAL_CPUDETECT_AVAIL 1 #define REAL_CPUDETECT_AVAIL 1
#elif PPSSPP_ARCH(LOONGARCH64)
#define REAL_CPUDETECT_AVAIL 1
#endif #endif
#ifndef REAL_CPUDETECT_AVAIL #ifndef REAL_CPUDETECT_AVAIL

View file

@ -1,207 +0,0 @@
#include "Common/File/AndroidContentURI.h"
bool AndroidContentURI::Parse(std::string_view path) {
const char *prefix = "content://";
if (!startsWith(path, prefix)) {
return false;
}
std::string_view components = path.substr(strlen(prefix));
std::vector<std::string_view> parts;
SplitString(components, '/', parts);
if (parts.size() == 3) {
// Single file URI.
provider = parts[0];
if (parts[1] == "tree") {
// Single directory URI.
// Not sure when we encounter these?
// file empty signals this type.
root = UriDecode(parts[2]);
return true;
} else if (parts[1] == "document") {
// root empty signals this type.
file = UriDecode(parts[2]);
return true;
} else {
// What's this?
return false;
}
} else if (parts.size() == 5) {
// Tree URI
provider = parts[0];
if (parts[1] != "tree") {
return false;
}
root = UriDecode(parts[2]);
if (parts[3] != "document") {
return false;
}
file = UriDecode(parts[4]);
// Sanity check file path.
return startsWith(file, root);
} else {
// Invalid Content URI
return false;
}
}
AndroidContentURI AndroidContentURI::WithRootFilePath(const std::string &filePath) {
if (root.empty()) {
ERROR_LOG(SYSTEM, "WithRootFilePath cannot be used with single file URIs.");
return *this;
}
AndroidContentURI uri = *this;
uri.file = uri.root;
if (!filePath.empty()) {
uri.file += "/" + filePath;
}
return uri;
}
AndroidContentURI AndroidContentURI::WithComponent(std::string_view filePath) {
AndroidContentURI uri = *this;
if (uri.file.empty()) {
// Not sure what to do.
return uri;
}
if (uri.file.back() == ':') {
// Special case handling for Document URIs: Treat the ':' as a directory separator too (but preserved in the filename).
uri.file.append(filePath);
} else {
uri.file.push_back('/');
uri.file.append(filePath);
}
return uri;
}
AndroidContentURI AndroidContentURI::WithExtraExtension(std::string_view extension) {
AndroidContentURI uri = *this;
uri.file.append(extension);
return uri;
}
AndroidContentURI AndroidContentURI::WithReplacedExtension(const std::string &oldExtension, const std::string &newExtension) const {
_dbg_assert_(!oldExtension.empty() && oldExtension[0] == '.');
_dbg_assert_(!newExtension.empty() && newExtension[0] == '.');
AndroidContentURI uri = *this;
if (endsWithNoCase(file, oldExtension)) {
uri.file = file.substr(0, file.size() - oldExtension.size()) + newExtension;
}
return uri;
}
AndroidContentURI AndroidContentURI::WithReplacedExtension(const std::string &newExtension) const {
_dbg_assert_(!newExtension.empty() && newExtension[0] == '.');
AndroidContentURI uri = *this;
if (file.empty()) {
return uri;
}
std::string extension = GetFileExtension();
uri.file = file.substr(0, file.size() - extension.size()) + newExtension;
return uri;
}
bool AndroidContentURI::CanNavigateUp() const {
if (IsTreeURI()) {
return file.size() > root.size();
} else {
return file.find(':') != std::string::npos && file.back() != ':';
}
}
// Only goes downwards in hierarchies. No ".." will ever be generated.
bool AndroidContentURI::ComputePathTo(const AndroidContentURI &other, std::string &path) const {
size_t offset = FilePath().size() + 1;
std::string otherFilePath = other.FilePath();
if (offset >= otherFilePath.size()) {
ERROR_LOG(SYSTEM, "Bad call to PathTo. '%s' -> '%s'", FilePath().c_str(), other.FilePath().c_str());
return false;
}
path = other.FilePath().substr(FilePath().size() + 1);
return true;
}
std::string AndroidContentURI::GetFileExtension() const {
size_t pos = file.rfind(".");
if (pos == std::string::npos) {
return "";
}
size_t slash_pos = file.rfind("/");
if (slash_pos != std::string::npos && slash_pos > pos) {
// Don't want to detect "df/file" from "/as.df/file"
return "";
}
std::string ext = file.substr(pos);
for (size_t i = 0; i < ext.size(); i++) {
ext[i] = tolower(ext[i]);
}
return ext;
}
std::string AndroidContentURI::GetLastPart() const {
if (file.empty()) {
// Can't do anything anyway.
return std::string();
}
if (!CanNavigateUp()) {
size_t colon = file.rfind(':');
if (colon == std::string::npos) {
return std::string();
}
if (file.back() == ':') {
return file;
}
return file.substr(colon + 1);
}
size_t slash = file.rfind('/');
if (slash == std::string::npos) {
// ok, look for the final colon. If it's the last char, we would have been caught above in !CanNavigateUp.
size_t colon = file.rfind(':');
if (colon == std::string::npos) {
return std::string();
}
return file.substr(colon + 1);
}
std::string part = file.substr(slash + 1);
return part;
}
bool AndroidContentURI::NavigateUp() {
if (!CanNavigateUp()) {
return false;
}
size_t slash = file.rfind('/');
if (slash == std::string::npos) {
// ok, look for the final colon.
size_t colon = file.rfind(':');
if (colon == std::string::npos) {
return false;
}
file = file.substr(0, colon + 1); // Note: we include the colon in these paths.
return true;
}
file = file.substr(0, slash);
return true;
}
std::string AndroidContentURI::ToString() const {
if (file.empty()) {
// Tree URI
return StringFromFormat("content://%s/tree/%s", provider.c_str(), UriEncode(root).c_str());
} else if (root.empty()) {
// Single file URI
return StringFromFormat("content://%s/document/%s", provider.c_str(), UriEncode(file).c_str());
} else {
// File URI from Tree
return StringFromFormat("content://%s/tree/%s/document/%s", provider.c_str(), UriEncode(root).c_str(), UriEncode(file).c_str());
}
}

View file

@ -1,73 +0,0 @@
#pragma once
#include <string>
#include "Common/StringUtils.h"
#include "Common/Net/URL.h"
#include "Common/Log.h"
// Utility to deal with Android storage URIs of the forms:
// content://com.android.externalstorage.documents/tree/primary%3APSP%20ISO
// content://com.android.externalstorage.documents/tree/primary%3APSP%20ISO/document/primary%3APSP%20ISO
// This file compiles on all platforms, to reduce the need for ifdefs.
// I am not 100% sure it's OK to rely on the internal format of file content URIs.
// On the other hand, I'm sure tons of apps would break if these changed, so I think we can
// consider them pretty stable. Additionally, the official Document library just manipulates the URIs
// in similar ways...
class AndroidContentURI {
private:
std::string provider;
std::string root;
std::string file;
public:
AndroidContentURI() {}
explicit AndroidContentURI(std::string_view path) {
Parse(path);
}
bool Parse(std::string_view path);
AndroidContentURI WithRootFilePath(const std::string &filePath);
AndroidContentURI WithComponent(std::string_view filePath);
AndroidContentURI WithExtraExtension(std::string_view extension); // The ext string contains the dot.
AndroidContentURI WithReplacedExtension(const std::string &oldExtension, const std::string &newExtension) const;
AndroidContentURI WithReplacedExtension(const std::string &newExtension) const;
bool CanNavigateUp() const;
// Only goes downwards in hierarchies. No ".." will ever be generated.
bool ComputePathTo(const AndroidContentURI &other, std::string &path) const;
std::string GetFileExtension() const;
std::string GetLastPart() const;
bool NavigateUp();
bool TreeContains(const AndroidContentURI &fileURI) {
if (root.empty()) {
return false;
}
return startsWith(fileURI.file, root);
}
std::string ToString() const;
// Never store the output of this, only show it to the user.
std::string ToVisualString() const {
return file;
}
const std::string &FilePath() const {
return file;
}
const std::string &RootPath() const {
return root.empty() ? file : root;
}
bool IsTreeURI() const {
return !root.empty();
}
};

View file

@ -1,5 +1,3 @@
#include <inttypes.h>
#include "Common/File/AndroidStorage.h" #include "Common/File/AndroidStorage.h"
#include "Common/StringUtils.h" #include "Common/StringUtils.h"
#include "Common/Log.h" #include "Common/Log.h"
@ -61,16 +59,16 @@ void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj) {
_dbg_assert_(computeRecursiveDirectorySize); _dbg_assert_(computeRecursiveDirectorySize);
} }
bool Android_IsContentUri(std::string_view filename) { bool Android_IsContentUri(const std::string &filename) {
return startsWith(filename, "content://"); return startsWith(filename, "content://");
} }
int Android_OpenContentUriFd(std::string_view filename, Android_OpenContentUriMode mode) { int Android_OpenContentUriFd(const std::string &filename, Android_OpenContentUriMode mode) {
if (!g_nativeActivity) { if (!g_nativeActivity) {
return -1; return -1;
} }
std::string fname(filename); std::string fname = filename;
// PPSSPP adds an ending slash to directories before looking them up. // PPSSPP adds an ending slash to directories before looking them up.
// TODO: Fix that in the caller (or don't call this for directories). // TODO: Fix that in the caller (or don't call this for directories).
if (fname.back() == '/') if (fname.back() == '/')

View file

@ -2,7 +2,6 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <string_view>
#include "Common/File/DirListing.h" #include "Common/File/DirListing.h"
@ -40,8 +39,8 @@ extern std::string g_externalDir;
void Android_StorageSetNativeActivity(jobject nativeActivity); void Android_StorageSetNativeActivity(jobject nativeActivity);
bool Android_IsContentUri(std::string_view uri); bool Android_IsContentUri(const std::string &uri);
int Android_OpenContentUriFd(std::string_view uri, const Android_OpenContentUriMode mode); int Android_OpenContentUriFd(const std::string &uri, const Android_OpenContentUriMode mode);
StorageError Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName); StorageError Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName);
StorageError Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName); StorageError Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName);
StorageError Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri); StorageError Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri);
@ -64,8 +63,8 @@ void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj);
// Stub out the Android Storage wrappers, so that we can avoid ifdefs everywhere. // Stub out the Android Storage wrappers, so that we can avoid ifdefs everywhere.
inline bool Android_IsContentUri(std::string_view uri) { return false; } inline bool Android_IsContentUri(const std::string &uri) { return false; }
inline int Android_OpenContentUriFd(std::string_view uri, const Android_OpenContentUriMode mode) { return -1; } inline int Android_OpenContentUriFd(const std::string &uri, const Android_OpenContentUriMode mode) { return -1; }
inline StorageError Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName) { return StorageError::UNKNOWN; } inline StorageError Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName) { return StorageError::UNKNOWN; }
inline StorageError Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName) { return StorageError::UNKNOWN; } inline StorageError Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName) { return StorageError::UNKNOWN; }
inline StorageError Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri) { return StorageError::UNKNOWN; } inline StorageError Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri) { return StorageError::UNKNOWN; }

View file

@ -6,7 +6,6 @@
#include <direct.h> #include <direct.h>
#if PPSSPP_PLATFORM(UWP) #if PPSSPP_PLATFORM(UWP)
#include <fileapifromapp.h> #include <fileapifromapp.h>
#include <UWP/UWPHelpers/StorageManager.h>
#endif #endif
#else #else
#include <strings.h> #include <strings.h>
@ -184,7 +183,7 @@ bool GetFilesInDir(const Path &directory, std::vector<FileInfo> *files, const ch
std::string tmp; std::string tmp;
while (*filter) { while (*filter) {
if (*filter == ':') { if (*filter == ':') {
filters.insert(tmp); filters.insert(std::move(tmp));
tmp.clear(); tmp.clear();
} else { } else {
tmp.push_back(*filter); tmp.push_back(*filter);
@ -192,7 +191,7 @@ bool GetFilesInDir(const Path &directory, std::vector<FileInfo> *files, const ch
filter++; filter++;
} }
if (!tmp.empty()) if (!tmp.empty())
filters.insert(tmp); filters.insert(std::move(tmp));
} }
#if PPSSPP_PLATFORM(WINDOWS) #if PPSSPP_PLATFORM(WINDOWS)
@ -221,13 +220,6 @@ bool GetFilesInDir(const Path &directory, std::vector<FileInfo> *files, const ch
HANDLE hFind = FindFirstFileEx((directory.ToWString() + L"\\*").c_str(), FindExInfoStandard, &ffd, FindExSearchNameMatch, NULL, 0); HANDLE hFind = FindFirstFileEx((directory.ToWString() + L"\\*").c_str(), FindExInfoStandard, &ffd, FindExSearchNameMatch, NULL, 0);
#endif #endif
if (hFind == INVALID_HANDLE_VALUE) { if (hFind == INVALID_HANDLE_VALUE) {
#if PPSSPP_PLATFORM(UWP)
// This step just to avoid empty results by adding fake folders
// it will help also to navigate back between selected folder
// we must ignore this function for any request other than UI navigation
if (GetFakeFolders(directory, files, filter, filters))
return true;
#endif
return false; return false;
} }
do { do {

View file

@ -2,8 +2,10 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <cstdio> #include <cstdio>
#include <cstdint>
#include <inttypes.h>
#include "Common/File/Path.h" #include "Common/File/Path.h"

View file

@ -23,19 +23,9 @@
#include "Common/File/AndroidStorage.h" #include "Common/File/AndroidStorage.h"
#include "Common/Data/Encoding/Utf8.h" #include "Common/Data/Encoding/Utf8.h"
#if PPSSPP_PLATFORM(UWP)
#include "UWP/UWPHelpers/StorageManager.h"
#endif
bool free_disk_space(const Path &path, int64_t &space) { bool free_disk_space(const Path &path, int64_t &space) {
#ifdef _WIN32 #ifdef _WIN32
ULARGE_INTEGER free; ULARGE_INTEGER free;
#if PPSSPP_PLATFORM(UWP)
if (GetDriveFreeSpace(path, space)) {
return true;
}
else
#endif
if (GetDiskFreeSpaceExW(path.ToWString().c_str(), &free, nullptr, nullptr)) { if (GetDiskFreeSpaceExW(path.ToWString().c_str(), &free, nullptr, nullptr)) {
space = free.QuadPart; space = free.QuadPart;
return true; return true;

View file

@ -25,6 +25,7 @@
#include "ppsspp_config.h" #include "ppsspp_config.h"
#include "android/jni/app-android.h" #include "android/jni/app-android.h"
#include "android/jni/AndroidContentURI.h"
#ifdef __MINGW32__ #ifdef __MINGW32__
#include <unistd.h> #include <unistd.h>
@ -32,14 +33,12 @@
#define _POSIX_THREAD_SAFE_FUNCTIONS 200112L #define _POSIX_THREAD_SAFE_FUNCTIONS 200112L
#endif #endif
#endif #endif
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
#include <memory> #include <memory>
#include "Common/Log.h" #include "Common/Log.h"
#include "Common/LogReporting.h" #include "Common/LogReporting.h"
#include "Common/File/AndroidContentURI.h"
#include "Common/File/FileUtil.h" #include "Common/File/FileUtil.h"
#include "Common/StringUtils.h" #include "Common/StringUtils.h"
#include "Common/SysError.h" #include "Common/SysError.h"
@ -54,7 +53,6 @@
#include <direct.h> // getcwd #include <direct.h> // getcwd
#if PPSSPP_PLATFORM(UWP) #if PPSSPP_PLATFORM(UWP)
#include <fileapifromapp.h> #include <fileapifromapp.h>
#include "UWP/UWPHelpers/StorageManager.h"
#endif #endif
#else #else
#include <sys/param.h> #include <sys/param.h>
@ -137,20 +135,14 @@ FILE *OpenCFile(const Path &path, const char *mode) {
} }
// TODO: Support append modes and stuff... For now let's go with the most common one. // TODO: Support append modes and stuff... For now let's go with the most common one.
Android_OpenContentUriMode openMode = Android_OpenContentUriMode::READ_WRITE_TRUNCATE; int descriptor = Android_OpenContentUriFd(path.ToString(), Android_OpenContentUriMode::READ_WRITE_TRUNCATE);
const char *fmode = "wb";
if (!strcmp(mode, "at") || !strcmp(mode, "a")) {
openMode = Android_OpenContentUriMode::READ_WRITE;
fmode = "ab";
}
int descriptor = Android_OpenContentUriFd(path.ToString(), openMode);
if (descriptor < 0) { if (descriptor < 0) {
INFO_LOG(COMMON, "Opening '%s' for write failed", path.ToString().c_str()); INFO_LOG(COMMON, "Opening '%s' for write failed", path.ToString().c_str());
return nullptr; return nullptr;
} }
FILE *f = fdopen(descriptor, fmode); FILE *f = fdopen(descriptor, "wb");
if (f && (!strcmp(mode, "at") || !strcmp(mode, "a"))) { if (f && (!strcmp(mode, "at") || !strcmp(mode, "a"))) {
// Append mode - not sure we got a "true" append mode, so seek to the end. // Append mode.
fseek(f, 0, SEEK_END); fseek(f, 0, SEEK_END);
} }
return f; return f;
@ -165,18 +157,7 @@ FILE *OpenCFile(const Path &path, const char *mode) {
} }
#if defined(_WIN32) && defined(UNICODE) #if defined(_WIN32) && defined(UNICODE)
#if PPSSPP_PLATFORM(UWP) && !defined(__LIBRETRO__)
// We shouldn't use _wfopen here,
// this function is not allowed to read outside Local and Installation folders
// FileSystem (broadFileSystemAccess) doesn't apply on _wfopen
// if we have custom memory stick location _wfopen will return null
// 'GetFileStreamFromApp' will convert 'mode' to [access, share, creationDisposition]
// then it will call 'CreateFile2FromAppW' -> convert HANDLE to FILE*
FILE* file = GetFileStreamFromApp(path.ToString(), mode);
return file;
#else
return _wfopen(path.ToWString().c_str(), ConvertUTF8ToWString(mode).c_str()); return _wfopen(path.ToWString().c_str(), ConvertUTF8ToWString(mode).c_str());
#endif
#else #else
return fopen(path.c_str(), mode); return fopen(path.c_str(), mode);
#endif #endif
@ -592,7 +573,7 @@ bool CreateFullPath(const Path &path) {
return false; return false;
} }
std::vector<std::string_view> parts; std::vector<std::string> parts;
SplitString(diff, '/', parts); SplitString(diff, '/', parts);
// Probably not necessary sanity check, ported from the old code. // Probably not necessary sanity check, ported from the old code.
@ -602,7 +583,7 @@ bool CreateFullPath(const Path &path) {
} }
Path curPath = root; Path curPath = root;
for (auto part : parts) { for (auto &part : parts) {
curPath /= part; curPath /= part;
if (!File::Exists(curPath)) { if (!File::Exists(curPath)) {
File::CreateDir(curPath); File::CreateDir(curPath);
@ -677,15 +658,10 @@ bool Rename(const Path &srcFilename, const Path &destFilename) {
INFO_LOG(COMMON, "Rename: %s --> %s", srcFilename.c_str(), destFilename.c_str()); INFO_LOG(COMMON, "Rename: %s --> %s", srcFilename.c_str(), destFilename.c_str());
#if defined(_WIN32) && defined(UNICODE) #if defined(_WIN32) && defined(UNICODE)
#if PPSSPP_PLATFORM(UWP)
if (MoveFileFromAppW(srcFilename.ToWString().c_str(), destFilename.ToWString().c_str()))
return true;
#else
std::wstring srcw = srcFilename.ToWString(); std::wstring srcw = srcFilename.ToWString();
std::wstring destw = destFilename.ToWString(); std::wstring destw = destFilename.ToWString();
if (_wrename(srcw.c_str(), destw.c_str()) == 0) if (_wrename(srcw.c_str(), destw.c_str()) == 0)
return true; return true;
#endif
#else #else
if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
return true; return true;
@ -969,7 +945,7 @@ bool OpenFileInEditor(const Path &fileName) {
#if PPSSPP_PLATFORM(WINDOWS) #if PPSSPP_PLATFORM(WINDOWS)
#if PPSSPP_PLATFORM(UWP) #if PPSSPP_PLATFORM(UWP)
OpenFile(fileName.ToString()); // Do nothing.
#else #else
ShellExecuteW(nullptr, L"open", fileName.ToWString().c_str(), nullptr, nullptr, SW_SHOW); ShellExecuteW(nullptr, L"open", fileName.ToWString().c_str(), nullptr, nullptr, SW_SHOW);
#endif #endif
@ -1181,7 +1157,6 @@ uint8_t *ReadLocalFile(const Path &filename, size_t *size) {
return nullptr; return nullptr;
} }
fseek(file, 0, SEEK_SET); fseek(file, 0, SEEK_SET);
// NOTE: If you find ~10 memory leaks from here, with very varying sizes, it might be the VFPU LUTs.
uint8_t *contents = new uint8_t[f_size + 1]; uint8_t *contents = new uint8_t[f_size + 1];
if (fread(contents, 1, f_size, file) != f_size) { if (fread(contents, 1, f_size, file) != f_size) {
delete[] contents; delete[] contents;

View file

@ -4,17 +4,13 @@
#include <cstring> #include <cstring>
#include "Common/File/Path.h" #include "Common/File/Path.h"
#include "Common/File/AndroidContentURI.h"
#include "Common/File/FileUtil.h" #include "Common/File/FileUtil.h"
#include "Common/StringUtils.h" #include "Common/StringUtils.h"
#include "Common/Log.h" #include "Common/Log.h"
#include "Common/Data/Encoding/Utf8.h" #include "Common/Data/Encoding/Utf8.h"
#include "android/jni/app-android.h" #include "android/jni/app-android.h"
#include "android/jni/AndroidContentURI.h"
#if PPSSPP_PLATFORM(UWP) && !defined(__LIBRETRO__)
#include "UWP/UWPHelpers/StorageManager.h"
#endif
#if HOST_IS_CASE_SENSITIVE #if HOST_IS_CASE_SENSITIVE
#include <dirent.h> #include <dirent.h>
@ -22,7 +18,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#endif #endif
Path::Path(std::string_view str) { Path::Path(const std::string &str) {
Init(str); Init(str);
} }
@ -33,7 +29,7 @@ Path::Path(const std::wstring &str) {
} }
#endif #endif
void Path::Init(std::string_view str) { void Path::Init(const std::string &str) {
if (str.empty()) { if (str.empty()) {
type_ = PathType::UNDEFINED; type_ = PathType::UNDEFINED;
path_.clear(); path_.clear();
@ -81,7 +77,7 @@ void Path::Init(std::string_view str) {
// We always use forward slashes internally, we convert to backslash only when // We always use forward slashes internally, we convert to backslash only when
// converted to a wstring. // converted to a wstring.
Path Path::operator /(std::string_view subdir) const { Path Path::operator /(const std::string &subdir) const {
if (type_ == PathType::CONTENT_URI) { if (type_ == PathType::CONTENT_URI) {
AndroidContentURI uri(path_); AndroidContentURI uri(path_);
return Path(uri.WithComponent(subdir).ToString()); return Path(uri.WithComponent(subdir).ToString());
@ -104,18 +100,18 @@ Path Path::operator /(std::string_view subdir) const {
return Path(fullPath); return Path(fullPath);
} }
void Path::operator /=(std::string_view subdir) { void Path::operator /=(const std::string &subdir) {
*this = *this / subdir; *this = *this / subdir;
} }
Path Path::WithExtraExtension(std::string_view ext) const { Path Path::WithExtraExtension(const std::string &ext) const {
if (type_ == PathType::CONTENT_URI) { if (type_ == PathType::CONTENT_URI) {
AndroidContentURI uri(path_); AndroidContentURI uri(path_);
return Path(uri.WithExtraExtension(ext).ToString()); return Path(uri.WithExtraExtension(ext).ToString());
} }
_dbg_assert_(!ext.empty() && ext[0] == '.'); _dbg_assert_(!ext.empty() && ext[0] == '.');
return Path(path_ + std::string(ext)); return Path(path_ + ext);
} }
Path Path::WithReplacedExtension(const std::string &oldExtension, const std::string &newExtension) const { Path Path::WithReplacedExtension(const std::string &oldExtension, const std::string &newExtension) const {
@ -135,11 +131,6 @@ Path Path::WithReplacedExtension(const std::string &oldExtension, const std::str
} }
Path Path::WithReplacedExtension(const std::string &newExtension) const { Path Path::WithReplacedExtension(const std::string &newExtension) const {
if (type_ == PathType::CONTENT_URI) {
AndroidContentURI uri(path_);
return Path(uri.WithReplacedExtension(newExtension).ToString());
}
_dbg_assert_(!newExtension.empty() && newExtension[0] == '.'); _dbg_assert_(!newExtension.empty() && newExtension[0] == '.');
if (path_.empty()) { if (path_.empty()) {
return Path(*this); return Path(*this);
@ -161,7 +152,7 @@ std::string Path::GetFilename() const {
return path_; return path_;
} }
std::string GetExtFromString(std::string_view str) { static std::string GetExtFromString(const std::string &str) {
size_t pos = str.rfind("."); size_t pos = str.rfind(".");
if (pos == std::string::npos) { if (pos == std::string::npos) {
return ""; return "";
@ -171,7 +162,7 @@ std::string GetExtFromString(std::string_view str) {
// Don't want to detect "df/file" from "/as.df/file" // Don't want to detect "df/file" from "/as.df/file"
return ""; return "";
} }
std::string ext(str.substr(pos)); std::string ext = str.substr(pos);
for (size_t i = 0; i < ext.size(); i++) { for (size_t i = 0; i < ext.size(); i++) {
ext[i] = tolower(ext[i]); ext[i] = tolower(ext[i]);
} }
@ -181,7 +172,7 @@ std::string GetExtFromString(std::string_view str) {
std::string Path::GetFileExtension() const { std::string Path::GetFileExtension() const {
if (type_ == PathType::CONTENT_URI) { if (type_ == PathType::CONTENT_URI) {
AndroidContentURI uri(path_); AndroidContentURI uri(path_);
return uri.GetFileExtension(); return GetExtFromString(uri.FilePath());
} }
return GetExtFromString(path_); return GetExtFromString(path_);
} }
@ -262,49 +253,14 @@ std::wstring Path::ToWString() const {
} }
return w; return w;
} }
std::string Path::ToCString() const {
std::string w = path_;
for (size_t i = 0; i < w.size(); i++) {
if (w[i] == '/') {
w[i] = '\\';
}
}
return w;
}
#endif #endif
std::string Path::ToVisualString(const char *relativeRoot) const { std::string Path::ToVisualString() const {
if (type_ == PathType::CONTENT_URI) { if (type_ == PathType::CONTENT_URI) {
return AndroidContentURI(path_).ToVisualString(); return AndroidContentURI(path_).ToVisualString();
#if PPSSPP_PLATFORM(WINDOWS) #if PPSSPP_PLATFORM(WINDOWS)
} else if (type_ == PathType::NATIVE) { } else if (type_ == PathType::NATIVE) {
#if PPSSPP_PLATFORM(UWP) && !defined(__LIBRETRO__) return ReplaceAll(path_, "/", "\\");
return GetPreviewPath(path_);
#else
// It can be useful to show the path as relative to the memstick
if (relativeRoot) {
std::string root = ReplaceAll(relativeRoot, "/", "\\");
std::string path = ReplaceAll(path_, "/", "\\");
if (startsWithNoCase(path, root)) {
return path.substr(root.size());
} else {
return path;
}
} else {
return ReplaceAll(path_, "/", "\\");
}
#endif
#else
if (relativeRoot) {
std::string root = relativeRoot;
if (startsWithNoCase(path_, root)) {
return path_.substr(root.size());
} else {
return path_;
}
} else {
return path_;
}
#endif #endif
} else { } else {
return path_; return path_;

View file

@ -3,7 +3,6 @@
#include "ppsspp_config.h" #include "ppsspp_config.h"
#include <string> #include <string>
#include <string_view>
#if defined(__APPLE__) #if defined(__APPLE__)
@ -37,11 +36,11 @@ enum class PathType {
class Path { class Path {
private: private:
void Init(std::string_view str); void Init(const std::string &str);
public: public:
Path() : type_(PathType::UNDEFINED) {} Path() : type_(PathType::UNDEFINED) {}
explicit Path(std::string_view str); explicit Path(const std::string &str);
#if PPSSPP_PLATFORM(WINDOWS) #if PPSSPP_PLATFORM(WINDOWS)
explicit Path(const std::wstring &str); explicit Path(const std::wstring &str);
@ -72,34 +71,28 @@ public:
bool IsAbsolute() const; bool IsAbsolute() const;
// Returns a path extended with a subdirectory. // Returns a path extended with a subdirectory.
Path operator /(std::string_view subdir) const; Path operator /(const std::string &subdir) const;
// Navigates down into a subdir. // Navigates down into a subdir.
void operator /=(std::string_view subdir); void operator /=(const std::string &subdir);
// File extension manipulation. // File extension manipulation.
Path WithExtraExtension(std::string_view ext) const; Path WithExtraExtension(const std::string &ext) const;
Path WithReplacedExtension(const std::string &oldExtension, const std::string &newExtension) const; Path WithReplacedExtension(const std::string &oldExtension, const std::string &newExtension) const;
Path WithReplacedExtension(const std::string &newExtension) const; Path WithReplacedExtension(const std::string &newExtension) const;
// Removes the last component.
std::string GetFilename() const; // Really, GetLastComponent. Could be a file or directory. Includes the extension. std::string GetFilename() const; // Really, GetLastComponent. Could be a file or directory. Includes the extension.
std::string GetFileExtension() const; // Always lowercase return. Includes the dot. std::string GetFileExtension() const; // Always lowercase return. Includes the dot.
// Removes the last component.
std::string GetDirectory() const; std::string GetDirectory() const;
const std::string &ToString() const; const std::string &ToString() const;
#if PPSSPP_PLATFORM(WINDOWS) #if PPSSPP_PLATFORM(WINDOWS)
std::wstring ToWString() const; std::wstring ToWString() const;
std::string ToCString() const; // Flips the slashes back to Windows standard, but string still UTF-8.
#else
std::string ToCString() const {
return ToString();
}
#endif #endif
// Pass in a relative root to turn the path into a relative path - if it is one! std::string ToVisualString() const;
std::string ToVisualString(const char *relativeRoot = nullptr) const;
bool CanNavigateUp() const; bool CanNavigateUp() const;
Path NavigateUp() const; Path NavigateUp() const;
@ -139,8 +132,6 @@ private:
PathType type_; PathType type_;
}; };
// Utility function for parsing out file extensions.
std::string GetExtFromString(std::string_view str);
// Utility function for fixing the case of paths. Only present on Unix-like systems. // Utility function for fixing the case of paths. Only present on Unix-like systems.

View file

@ -17,6 +17,7 @@
#if PPSSPP_PLATFORM(ANDROID) #if PPSSPP_PLATFORM(ANDROID)
#include "android/jni/app-android.h" #include "android/jni/app-android.h"
#include "android/jni/AndroidContentURI.h"
#endif #endif
bool LoadRemoteFileList(const Path &url, const std::string &userAgent, bool *cancel, std::vector<File::FileInfo> &files) { bool LoadRemoteFileList(const Path &url, const std::string &userAgent, bool *cancel, std::vector<File::FileInfo> &files) {
@ -38,7 +39,7 @@ bool LoadRemoteFileList(const Path &url, const std::string &userAgent, bool *can
http::RequestParams req(baseURL.Resource(), "text/plain, text/html; q=0.9, */*; q=0.8"); http::RequestParams req(baseURL.Resource(), "text/plain, text/html; q=0.9, */*; q=0.8");
if (http.Resolve(baseURL.Host().c_str(), baseURL.Port())) { if (http.Resolve(baseURL.Host().c_str(), baseURL.Port())) {
if (http.Connect(2, 20.0, cancel)) { if (http.Connect(2, 20.0, cancel)) {
net::RequestProgress progress(cancel); http::RequestProgress progress(cancel);
code = http.GET(req, &result, responseHeaders, &progress); code = http.GET(req, &result, responseHeaders, &progress);
http.Disconnect(); http.Disconnect();
} }
@ -78,7 +79,7 @@ bool LoadRemoteFileList(const Path &url, const std::string &userAgent, bool *can
return false; return false;
} }
for (auto &item : items) { for (std::string item : items) {
// Apply some workarounds. // Apply some workarounds.
if (item.empty()) if (item.empty())
continue; continue;
@ -210,7 +211,7 @@ std::string PathBrowser::GetFriendlyPath() const {
bool PathBrowser::GetListing(std::vector<File::FileInfo> &fileInfo, const char *filter, bool *cancel) { bool PathBrowser::GetListing(std::vector<File::FileInfo> &fileInfo, const char *filter, bool *cancel) {
std::unique_lock<std::mutex> guard(pendingLock_); std::unique_lock<std::mutex> guard(pendingLock_);
while (!IsListingReady() && (!cancel || !*cancel)) { while (!IsListingReady() && (!cancel || !*cancel)) {
// In case cancel changes, just sleep. TODO: Replace with condition variable. // In case cancel changes, just sleep.
guard.unlock(); guard.unlock();
sleep_ms(50); sleep_ms(50);
guard.lock(); guard.lock();
@ -221,6 +222,14 @@ bool PathBrowser::GetListing(std::vector<File::FileInfo> &fileInfo, const char *
} }
bool PathBrowser::CanNavigateUp() { bool PathBrowser::CanNavigateUp() {
/* Leaving this commented out, not sure if there's a use in UWP for navigating up from the user data folder.
#if PPSSPP_PLATFORM(UWP)
// Can't navigate up from memstick folder :(
if (path_ == GetSysDirectory(DIRECTORY_MEMSTICK_ROOT)) {
return false;
}
#endif
*/
return path_.CanNavigateUp(); return path_.CanNavigateUp();
} }

View file

@ -0,0 +1,204 @@
#include <algorithm>
#include <ctype.h>
#include <set>
#include <stdio.h>
#ifdef __ANDROID__
#include <zip.h>
#endif
#include "Common/Common.h"
#include "Common/Log.h"
#include "Common/File/VFS/AssetReader.h"
#ifdef __ANDROID__
uint8_t *ReadFromZip(zip *archive, const char* filename, size_t *size) {
// Figure out the file size first.
struct zip_stat zstat;
zip_file *file = zip_fopen(archive, filename, ZIP_FL_NOCASE|ZIP_FL_UNCHANGED);
if (!file) {
ERROR_LOG(IO, "Error opening %s from ZIP", filename);
return 0;
}
zip_stat(archive, filename, ZIP_FL_NOCASE|ZIP_FL_UNCHANGED, &zstat);
uint8_t *contents = new uint8_t[zstat.size + 1];
zip_fread(file, contents, zstat.size);
zip_fclose(file);
contents[zstat.size] = 0;
*size = zstat.size;
return contents;
}
#endif
#ifdef __ANDROID__
ZipAssetReader::ZipAssetReader(const char *zip_file, const char *in_zip_path) {
zip_file_ = zip_open(zip_file, 0, NULL);
strcpy(in_zip_path_, in_zip_path);
if (!zip_file_) {
ERROR_LOG(IO, "Failed to open %s as a zip file", zip_file);
}
std::vector<File::FileInfo> info;
GetFileListing("assets", &info, 0);
for (size_t i = 0; i < info.size(); i++) {
if (info[i].isDirectory) {
DEBUG_LOG(IO, "Directory: %s", info[i].name.c_str());
} else {
DEBUG_LOG(IO, "File: %s", info[i].name.c_str());
}
}
}
ZipAssetReader::~ZipAssetReader() {
std::lock_guard<std::mutex> guard(lock_);
zip_close(zip_file_);
}
uint8_t *ZipAssetReader::ReadAsset(const char *path, size_t *size) {
char temp_path[1024];
strcpy(temp_path, in_zip_path_);
strcat(temp_path, path);
std::lock_guard<std::mutex> guard(lock_);
return ReadFromZip(zip_file_, temp_path, size);
}
bool ZipAssetReader::GetFileListing(const char *orig_path, std::vector<File::FileInfo> *listing, const char *filter = 0) {
char path[1024];
strcpy(path, in_zip_path_);
strcat(path, orig_path);
std::set<std::string> filters;
std::string tmp;
if (filter) {
while (*filter) {
if (*filter == ':') {
filters.insert("." + tmp);
tmp.clear();
} else {
tmp.push_back(*filter);
}
filter++;
}
}
if (tmp.size())
filters.insert("." + tmp);
// We just loop through the whole ZIP file and deduce what files are in this directory, and what subdirectories there are.
std::set<std::string> files;
std::set<std::string> directories;
GetZipListings(path, files, directories);
for (auto diter = directories.begin(); diter != directories.end(); ++diter) {
File::FileInfo info;
info.name = *diter;
// Remove the "inzip" part of the fullname.
info.fullName = Path(std::string(path).substr(strlen(in_zip_path_))) / *diter;
info.exists = true;
info.isWritable = false;
info.isDirectory = true;
listing->push_back(info);
}
for (auto fiter = files.begin(); fiter != files.end(); ++fiter) {
std::string fpath = path;
File::FileInfo info;
info.name = *fiter;
info.fullName = Path(std::string(path).substr(strlen(in_zip_path_))) / *fiter;
info.exists = true;
info.isWritable = false;
info.isDirectory = false;
std::string ext = info.fullName.GetFileExtension();
if (filter) {
if (filters.find(ext) == filters.end()) {
continue;
}
}
listing->push_back(info);
}
std::sort(listing->begin(), listing->end());
return true;
}
void ZipAssetReader::GetZipListings(const char *path, std::set<std::string> &files, std::set<std::string> &directories) {
size_t pathlen = strlen(path);
if (path[pathlen - 1] == '/')
pathlen--;
std::lock_guard<std::mutex> guard(lock_);
int numFiles = zip_get_num_files(zip_file_);
for (int i = 0; i < numFiles; i++) {
const char* name = zip_get_name(zip_file_, i, 0);
if (!name)
continue;
if (!memcmp(name, path, pathlen)) {
// The prefix is right. Let's see if this is a file or path.
const char *slashPos = strchr(name + pathlen + 1, '/');
if (slashPos != 0) {
// A directory.
std::string dirName = std::string(name + pathlen + 1, slashPos - (name + pathlen + 1));
directories.insert(dirName);
} else if (name[pathlen] == '/') {
const char *fn = name + pathlen + 1;
files.insert(std::string(fn));
} // else, it was a file with the same prefix as the path. like langregion.ini next to lang/.
}
}
}
bool ZipAssetReader::GetFileInfo(const char *path, File::FileInfo *info) {
struct zip_stat zstat;
char temp_path[1024];
strcpy(temp_path, in_zip_path_);
strcat(temp_path, path);
if (0 != zip_stat(zip_file_, temp_path, ZIP_FL_NOCASE|ZIP_FL_UNCHANGED, &zstat)) {
// ZIP files do not have real directories, so we'll end up here if we
// try to stat one. For now that's fine.
info->exists = false;
info->size = 0;
return false;
}
info->fullName = Path(path);
info->exists = true; // TODO
info->isWritable = false;
info->isDirectory = false; // TODO
info->size = zstat.size;
return true;
}
#endif
DirectoryAssetReader::DirectoryAssetReader(const Path &path) {
path_ = path;
}
uint8_t *DirectoryAssetReader::ReadAsset(const char *path, size_t *size) {
Path new_path = Path(path).StartsWith(path_) ? Path(path) : path_ / path;
return File::ReadLocalFile(new_path, size);
}
bool DirectoryAssetReader::GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter = nullptr) {
Path new_path = Path(path).StartsWith(path_) ? Path(path) : path_ / path;
File::FileInfo info;
if (!File::GetFileInfo(new_path, &info))
return false;
if (info.isDirectory) {
File::GetFilesInDir(new_path, listing, filter);
return true;
}
return false;
}
bool DirectoryAssetReader::GetFileInfo(const char *path, File::FileInfo *info) {
Path new_path = Path(path).StartsWith(path_) ? Path(path) : path_ / path;
return File::GetFileInfo(new_path, info);
}

View file

@ -0,0 +1,65 @@
// TODO: Move much of this code to vfs.cpp
#pragma once
#ifdef __ANDROID__
#include <zip.h>
#endif
#include <mutex>
#include <set>
#include <string.h>
#include <string>
#include "Common/File/VFS/VFS.h"
#include "Common/File/FileUtil.h"
#include "Common/File/Path.h"
class AssetReader {
public:
virtual ~AssetReader() {}
// use delete[]
virtual uint8_t *ReadAsset(const char *path, size_t *size) = 0;
// Filter support is optional but nice to have
virtual bool GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter = 0) = 0;
virtual bool GetFileInfo(const char *path, File::FileInfo *info) = 0;
virtual std::string toString() const = 0;
};
#ifdef __ANDROID__
uint8_t *ReadFromZip(zip *archive, const char* filename, size_t *size);
class ZipAssetReader : public AssetReader {
public:
ZipAssetReader(const char *zip_file, const char *in_zip_path);
~ZipAssetReader();
// use delete[]
uint8_t *ReadAsset(const char *path, size_t *size) override;
bool GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter) override;
bool GetFileInfo(const char *path, File::FileInfo *info) override;
std::string toString() const override {
return in_zip_path_;
}
private:
void GetZipListings(const char *path, std::set<std::string> &files, std::set<std::string> &directories);
zip *zip_file_;
std::mutex lock_;
char in_zip_path_[256];
};
#endif
class DirectoryAssetReader : public AssetReader {
public:
explicit DirectoryAssetReader(const Path &path);
// use delete[]
uint8_t *ReadAsset(const char *path, size_t *size) override;
bool GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter) override;
bool GetFileInfo(const char *path, File::FileInfo *info) override;
std::string toString() const override {
return path_.ToString();
}
private:
Path path_;
};

View file

@ -1,99 +0,0 @@
#include <cstdio>
#include "Common/Common.h"
#include "Common/Log.h"
#include "Common/File/VFS/DirectoryReader.h"
DirectoryReader::DirectoryReader(const Path &path) {
path_ = path;
}
uint8_t *DirectoryReader::ReadFile(const char *path, size_t *size) {
Path new_path = Path(path).StartsWith(path_) ? Path(path) : path_ / path;
return File::ReadLocalFile(new_path, size);
}
bool DirectoryReader::GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter = nullptr) {
Path new_path = Path(path).StartsWith(path_) ? Path(path) : path_ / path;
File::FileInfo info;
if (!File::GetFileInfo(new_path, &info))
return false;
if (info.isDirectory) {
File::GetFilesInDir(new_path, listing, filter);
return true;
}
return false;
}
bool DirectoryReader::GetFileInfo(const char *path, File::FileInfo *info) {
Path new_path = Path(path).StartsWith(path_) ? Path(path) : path_ / path;
return File::GetFileInfo(new_path, info);
}
class DirectoryReaderFileReference : public VFSFileReference {
public:
Path path;
};
class DirectoryReaderOpenFile : public VFSOpenFile {
public:
~DirectoryReaderOpenFile() {
_dbg_assert_(file == nullptr);
}
FILE *file = nullptr;
};
VFSFileReference *DirectoryReader::GetFile(const char *path) {
Path filePath = path_ / path;
if (!File::Exists(filePath)) {
return nullptr;
}
DirectoryReaderFileReference *reference = new DirectoryReaderFileReference();
reference->path = filePath;
return reference;
}
bool DirectoryReader::GetFileInfo(VFSFileReference *vfsReference, File::FileInfo *fileInfo) {
DirectoryReaderFileReference *reference = (DirectoryReaderFileReference *)vfsReference;
return File::GetFileInfo(reference->path, fileInfo);
}
void DirectoryReader::ReleaseFile(VFSFileReference *vfsReference) {
DirectoryReaderFileReference *reference = (DirectoryReaderFileReference *)vfsReference;
delete reference;
}
VFSOpenFile *DirectoryReader::OpenFileForRead(VFSFileReference *vfsReference, size_t *size) {
DirectoryReaderFileReference *reference = (DirectoryReaderFileReference *)vfsReference;
FILE *file = File::OpenCFile(reference->path, "rb");
if (!file) {
return nullptr;
}
fseek(file, 0, SEEK_END);
*size = ftell(file);
fseek(file, 0, SEEK_SET);
DirectoryReaderOpenFile *openFile = new DirectoryReaderOpenFile();
openFile->file = file;
return openFile;
}
void DirectoryReader::Rewind(VFSOpenFile *vfsOpenFile) {
DirectoryReaderOpenFile *openFile = (DirectoryReaderOpenFile *)vfsOpenFile;
fseek(openFile->file, 0, SEEK_SET);
}
size_t DirectoryReader::Read(VFSOpenFile *vfsOpenFile, void *buffer, size_t length) {
DirectoryReaderOpenFile *openFile = (DirectoryReaderOpenFile *)vfsOpenFile;
return fread(buffer, 1, length, openFile->file);
}
void DirectoryReader::CloseFile(VFSOpenFile *vfsOpenFile) {
DirectoryReaderOpenFile *openFile = (DirectoryReaderOpenFile *)vfsOpenFile;
_dbg_assert_(openFile->file != nullptr);
fclose(openFile->file);
openFile->file = nullptr;
delete openFile;
}

View file

@ -1,30 +0,0 @@
#pragma once
#include "Common/File/VFS/VFS.h"
#include "Common/File/FileUtil.h"
#include "Common/File/Path.h"
class DirectoryReader : public VFSBackend {
public:
explicit DirectoryReader(const Path &path);
// use delete[] on the returned value.
uint8_t *ReadFile(const char *path, size_t *size) override;
VFSFileReference *GetFile(const char *path) override;
bool GetFileInfo(VFSFileReference *vfsReference, File::FileInfo *fileInfo) override;
void ReleaseFile(VFSFileReference *vfsReference) override;
VFSOpenFile *OpenFileForRead(VFSFileReference *vfsReference, size_t *size) override;
void Rewind(VFSOpenFile *vfsOpenFile) override;
size_t Read(VFSOpenFile *vfsOpenFile, void *buffer, size_t length) override;
void CloseFile(VFSOpenFile *vfsOpenFile) override;
bool GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter) override;
bool GetFileInfo(const char *path, File::FileInfo *info) override;
std::string toString() const override {
return path_.ToString();
}
private:
Path path_;
};

View file

@ -1,34 +1,35 @@
#include <cstring>
#include "Common/Log.h" #include "Common/Log.h"
#include "Common/File/VFS/VFS.h" #include "Common/File/VFS/VFS.h"
#include "Common/File/FileUtil.h" #include "Common/File/VFS/AssetReader.h"
#include "Common/File/AndroidStorage.h" #include "Common/File/AndroidStorage.h"
#include "Common/StringUtils.h"
VFS g_VFS; struct VFSEntry {
const char *prefix;
AssetReader *reader;
};
void VFS::Register(const char *prefix, VFSBackend *reader) { static VFSEntry entries[16];
if (reader) { static int num_entries = 0;
entries_.push_back(VFSEntry{ prefix, reader });
DEBUG_LOG(IO, "Registered VFS for prefix %s: %s", prefix, reader->toString().c_str()); void VFSRegister(const char *prefix, AssetReader *reader) {
} else { entries[num_entries].prefix = prefix;
ERROR_LOG(IO, "Trying to register null VFS backend for prefix %s", prefix); entries[num_entries].reader = reader;
} DEBUG_LOG(IO, "Registered VFS for prefix %s: %s", prefix, reader->toString().c_str());
num_entries++;
} }
void VFS::Clear() { void VFSShutdown() {
for (auto &entry : entries_) { for (int i = 0; i < num_entries; i++) {
delete entry.reader; delete entries[i].reader;
} }
entries_.clear(); num_entries = 0;
} }
// TODO: Use Path more. // TODO: Use Path more.
static bool IsLocalAbsolutePath(const char *path) { static bool IsLocalAbsolutePath(const char *path) {
bool isUnixLocal = path[0] == '/'; bool isUnixLocal = path[0] == '/';
#ifdef _WIN32 #ifdef _WIN32
bool isWindowsLocal = (isalpha(path[0]) && path[1] == ':') || startsWith(path, "\\\\") || startsWith(path, "//"); bool isWindowsLocal = isalpha(path[0]) && path[1] == ':';
#else #else
bool isWindowsLocal = false; bool isWindowsLocal = false;
#endif #endif
@ -37,7 +38,7 @@ static bool IsLocalAbsolutePath(const char *path) {
} }
// The returned data should be free'd with delete[]. // The returned data should be free'd with delete[].
uint8_t *VFS::ReadFile(const char *filename, size_t *size) { uint8_t *VFSReadFile(const char *filename, size_t *size) {
if (IsLocalAbsolutePath(filename)) { if (IsLocalAbsolutePath(filename)) {
// Local path, not VFS. // Local path, not VFS.
// INFO_LOG(IO, "Not a VFS path: %s . Reading local file.", filename); // INFO_LOG(IO, "Not a VFS path: %s . Reading local file.", filename);
@ -46,13 +47,13 @@ uint8_t *VFS::ReadFile(const char *filename, size_t *size) {
int fn_len = (int)strlen(filename); int fn_len = (int)strlen(filename);
bool fileSystemFound = false; bool fileSystemFound = false;
for (const auto &entry : entries_) { for (int i = 0; i < num_entries; i++) {
int prefix_len = (int)strlen(entry.prefix); int prefix_len = (int)strlen(entries[i].prefix);
if (prefix_len >= fn_len) continue; if (prefix_len >= fn_len) continue;
if (0 == memcmp(filename, entry.prefix, prefix_len)) { if (0 == memcmp(filename, entries[i].prefix, prefix_len)) {
fileSystemFound = true; fileSystemFound = true;
// INFO_LOG(IO, "Prefix match: %s (%s) -> %s", entries[i].prefix, filename, filename + prefix_len); // INFO_LOG(IO, "Prefix match: %s (%s) -> %s", entries[i].prefix, filename, filename + prefix_len);
uint8_t *data = entry.reader->ReadFile(filename + prefix_len, size); uint8_t *data = entries[i].reader->ReadAsset(filename + prefix_len, size);
if (data) if (data)
return data; return data;
else else
@ -66,7 +67,7 @@ uint8_t *VFS::ReadFile(const char *filename, size_t *size) {
return 0; return 0;
} }
bool VFS::GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter) { bool VFSGetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter) {
if (IsLocalAbsolutePath(path)) { if (IsLocalAbsolutePath(path)) {
// Local path, not VFS. // Local path, not VFS.
// INFO_LOG(IO, "Not a VFS path: %s . Reading local directory.", path); // INFO_LOG(IO, "Not a VFS path: %s . Reading local directory.", path);
@ -76,12 +77,12 @@ bool VFS::GetFileListing(const char *path, std::vector<File::FileInfo> *listing,
int fn_len = (int)strlen(path); int fn_len = (int)strlen(path);
bool fileSystemFound = false; bool fileSystemFound = false;
for (const auto &entry : entries_) { for (int i = 0; i < num_entries; i++) {
int prefix_len = (int)strlen(entry.prefix); int prefix_len = (int)strlen(entries[i].prefix);
if (prefix_len >= fn_len) continue; if (prefix_len >= fn_len) continue;
if (0 == memcmp(path, entry.prefix, prefix_len)) { if (0 == memcmp(path, entries[i].prefix, prefix_len)) {
fileSystemFound = true; fileSystemFound = true;
if (entry.reader->GetFileListing(path + prefix_len, listing, filter)) { if (entries[i].reader->GetFileListing(path + prefix_len, listing, filter)) {
return true; return true;
} }
} }
@ -93,7 +94,7 @@ bool VFS::GetFileListing(const char *path, std::vector<File::FileInfo> *listing,
return false; return false;
} }
bool VFS::GetFileInfo(const char *path, File::FileInfo *info) { bool VFSGetFileInfo(const char *path, File::FileInfo *info) {
if (IsLocalAbsolutePath(path)) { if (IsLocalAbsolutePath(path)) {
// Local path, not VFS. // Local path, not VFS.
// INFO_LOG(IO, "Not a VFS path: %s . Getting local file info.", path); // INFO_LOG(IO, "Not a VFS path: %s . Getting local file info.", path);
@ -102,19 +103,19 @@ bool VFS::GetFileInfo(const char *path, File::FileInfo *info) {
bool fileSystemFound = false; bool fileSystemFound = false;
int fn_len = (int)strlen(path); int fn_len = (int)strlen(path);
for (const auto &entry : entries_) { for (int i = 0; i < num_entries; i++) {
int prefix_len = (int)strlen(entry.prefix); int prefix_len = (int)strlen(entries[i].prefix);
if (prefix_len >= fn_len) continue; if (prefix_len >= fn_len) continue;
if (0 == memcmp(path, entry.prefix, prefix_len)) { if (0 == memcmp(path, entries[i].prefix, prefix_len)) {
fileSystemFound = true; fileSystemFound = true;
if (entry.reader->GetFileInfo(path + prefix_len, info)) if (entries[i].reader->GetFileInfo(path + prefix_len, info))
return true; return true;
else else
continue; continue;
} }
} }
if (!fileSystemFound) { if (!fileSystemFound) {
ERROR_LOG(IO, "Missing filesystem for '%s'", path); ERROR_LOG(IO, "Missing filesystem for %s", path);
} // Otherwise, the file was just missing. No need to log. } // Otherwise, the file was just missing. No need to log.
return false; return false;
} }

View file

@ -1,83 +1,21 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <cstdint>
#include "Common/File/DirListing.h" #include "Common/File/DirListing.h"
// Basic read-only virtual file system. Used to manage assets on Android, where we have to // Basic virtual file system. Used to manage assets on Android, where we have to
// read them manually out of the APK zipfile, while being able to run on other // read them manually out of the APK zipfile, while being able to run on other
// platforms as well with the appropriate directory set-up. // platforms as well with the appropriate directory set-up.
// Note that this is kinda similar in concept to Core/MetaFileSystem.h, but that one class AssetReader;
// is specifically for operations done by the emulated PSP, while this is for operations
// on the system level, like loading assets, and maybe texture packs. Also, as mentioned,
// this one is read-only, so a bit smaller and simpler.
// VFSBackend instances can be used on their own, without the VFS, to serve as an abstraction of void VFSRegister(const char *prefix, AssetReader *reader);
// a single directory or ZIP file. void VFSShutdown();
// The VFSFileReference level of abstraction is there to hold things like zip file indices, // Use delete [] to release the returned memory.
// for fast re-open etc. // Always allocates an extra zero byte at the end, so that it
// can be used for text like shader sources.
class VFSFileReference { uint8_t *VFSReadFile(const char *filename, size_t *size);
public: bool VFSGetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter = 0);
virtual ~VFSFileReference() {} bool VFSGetFileInfo(const char *filename, File::FileInfo *fileInfo);
};
class VFSOpenFile {
public:
virtual ~VFSOpenFile() {}
};
// Common interface parts between VFSBackend and VFS.
// Sometimes you don't need the VFS multiplexing and only have a VFSBackend *, sometimes you do need it,
// and it would be cool to be able to use the same interface, like when loading INI files.
class VFSInterface {
public:
virtual ~VFSInterface() {}
virtual uint8_t *ReadFile(const char *path, size_t *size) = 0;
// If listing already contains files, it'll be cleared.
virtual bool GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter = nullptr) = 0;
};
class VFSBackend : public VFSInterface {
public:
virtual VFSFileReference *GetFile(const char *path) = 0;
virtual bool GetFileInfo(VFSFileReference *vfsReference, File::FileInfo *fileInfo) = 0;
virtual void ReleaseFile(VFSFileReference *vfsReference) = 0;
// Must write the size of the file to *size. Both backends can do this efficiently here,
// avoiding a call to GetFileInfo.
virtual VFSOpenFile *OpenFileForRead(VFSFileReference *vfsReference, size_t *size) = 0;
virtual void Rewind(VFSOpenFile *vfsOpenFile) = 0;
virtual size_t Read(VFSOpenFile *vfsOpenFile, void *buffer, size_t length) = 0;
virtual void CloseFile(VFSOpenFile *vfsOpenFile) = 0;
// Filter support is optional but nice to have
virtual bool GetFileInfo(const char *path, File::FileInfo *info) = 0;
virtual std::string toString() const = 0;
};
class VFS : public VFSInterface {
public:
~VFS() { Clear(); }
void Register(const char *prefix, VFSBackend *reader);
void Clear();
// Use delete [] to release the returned memory.
// Always allocates an extra zero byte at the end, so that it
// can be used for text like shader sources.
uint8_t *ReadFile(const char *filename, size_t *size) override;
bool GetFileInfo(const char *filename, File::FileInfo *fileInfo);
bool GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter = nullptr) override;
private:
struct VFSEntry {
const char *prefix;
VFSBackend *reader;
};
std::vector<VFSEntry> entries_;
};
extern VFS g_VFS;

View file

@ -1,310 +0,0 @@
#include <algorithm>
#include <ctype.h>
#include <set>
#include <cstdio>
#include <cstring>
#ifdef SHARED_LIBZIP
#include <zip.h>
#else
#include "ext/libzip/zip.h"
#endif
#include "Common/Common.h"
#include "Common/Log.h"
#include "Common/File/VFS/ZipFileReader.h"
#include "Common/StringUtils.h"
ZipFileReader *ZipFileReader::Create(const Path &zipFile, const char *inZipPath, bool logErrors) {
int error = 0;
zip *zip_file;
if (zipFile.Type() == PathType::CONTENT_URI) {
int fd = File::OpenFD(zipFile, File::OPEN_READ);
if (!fd) {
if (logErrors) {
ERROR_LOG(IO, "Failed to open FD for '%s' as zip file", zipFile.c_str());
}
return nullptr;
}
zip_file = zip_fdopen(fd, 0, &error);
} else {
zip_file = zip_open(zipFile.c_str(), 0, &error);
}
if (!zip_file) {
if (logErrors) {
ERROR_LOG(IO, "Failed to open %s as a zip file", zipFile.c_str());
}
return nullptr;
}
// The inZipPath is supposed to be a folder, and internally in this class, we suffix
// folder paths with '/', matching how the zip library works.
std::string path = inZipPath;
if (!path.empty() && path.back() != '/') {
path.push_back('/');
}
return new ZipFileReader(zip_file, path);
}
ZipFileReader::~ZipFileReader() {
std::lock_guard<std::mutex> guard(lock_);
zip_close(zip_file_);
}
uint8_t *ZipFileReader::ReadFile(const char *path, size_t *size) {
std::string temp_path = inZipPath_ + path;
std::lock_guard<std::mutex> guard(lock_);
// Figure out the file size first.
struct zip_stat zstat;
zip_stat(zip_file_, temp_path.c_str(), ZIP_FL_NOCASE | ZIP_FL_UNCHANGED, &zstat);
zip_file *file = zip_fopen(zip_file_, temp_path.c_str(), ZIP_FL_NOCASE | ZIP_FL_UNCHANGED);
if (!file) {
ERROR_LOG(IO, "Error opening %s from ZIP", temp_path.c_str());
return 0;
}
uint8_t *contents = new uint8_t[zstat.size + 1];
zip_fread(file, contents, zstat.size);
zip_fclose(file);
contents[zstat.size] = 0;
*size = zstat.size;
return contents;
}
bool ZipFileReader::GetFileListing(const char *orig_path, std::vector<File::FileInfo> *listing, const char *filter = 0) {
std::string path = std::string(inZipPath_) + orig_path;
if (!path.empty() && path.back() != '/') {
path.push_back('/');
}
std::set<std::string> filters;
std::string tmp;
if (filter) {
while (*filter) {
if (*filter == ':') {
filters.insert("." + tmp);
tmp.clear();
} else {
tmp.push_back(*filter);
}
filter++;
}
}
if (tmp.size())
filters.insert("." + tmp);
// We just loop through the whole ZIP file and deduce what files are in this directory, and what subdirectories there are.
std::set<std::string> files;
std::set<std::string> directories;
bool success = GetZipListings(path, files, directories);
if (!success) {
// This means that no file prefix matched the path.
return false;
}
listing->clear();
INFO_LOG(SYSTEM, "Listing %s", orig_path);
for (auto diter = directories.begin(); diter != directories.end(); ++diter) {
File::FileInfo info;
info.name = *diter;
// Remove the "inzip" part of the fullname.
std::string relativePath = std::string(path).substr(inZipPath_.size());
info.fullName = Path(relativePath + *diter);
info.exists = true;
info.isWritable = false;
info.isDirectory = true;
// INFO_LOG(SYSTEM, "Found file: %s (%s)", info.name.c_str(), info.fullName.c_str());
listing->push_back(info);
}
for (auto fiter = files.begin(); fiter != files.end(); ++fiter) {
std::string fpath = path;
File::FileInfo info;
info.name = *fiter;
std::string relativePath = std::string(path).substr(inZipPath_.size());
info.fullName = Path(relativePath + *fiter);
info.exists = true;
info.isWritable = false;
info.isDirectory = false;
std::string ext = info.fullName.GetFileExtension();
if (filter) {
if (filters.find(ext) == filters.end()) {
continue;
}
}
// INFO_LOG(SYSTEM, "Found dir: %s (%s)", info.name.c_str(), info.fullName.c_str());
listing->push_back(info);
}
std::sort(listing->begin(), listing->end());
return true;
}
// path here is from the root, so inZipPath needs to already be added.
bool ZipFileReader::GetZipListings(const std::string &path, std::set<std::string> &files, std::set<std::string> &directories) {
_dbg_assert_(path.empty() || path.back() == '/');
std::lock_guard<std::mutex> guard(lock_);
int numFiles = zip_get_num_files(zip_file_);
bool anyPrefixMatched = false;
for (int i = 0; i < numFiles; i++) {
const char* name = zip_get_name(zip_file_, i, 0);
if (!name)
continue; // shouldn't happen, I think
if (startsWith(name, path)) {
if (strlen(name) == path.size()) {
// Don't want to return the same folder.
continue;
}
const char *slashPos = strchr(name + path.size(), '/');
if (slashPos != 0) {
anyPrefixMatched = true;
// A directory. Let's pick off the only part we care about.
size_t offset = path.size();
std::string dirName = std::string(name + offset, slashPos - (name + offset));
// We might get a lot of these if the tree is deep. The std::set deduplicates.
directories.insert(dirName);
} else {
anyPrefixMatched = true;
// It's a file.
const char *fn = name + path.size();
files.insert(std::string(fn));
}
}
}
return anyPrefixMatched;
}
bool ZipFileReader::GetFileInfo(const char *path, File::FileInfo *info) {
struct zip_stat zstat;
std::string temp_path = inZipPath_ + path;
// Clear some things to start.
info->isDirectory = false;
info->isWritable = false;
info->size = 0;
{
std::lock_guard<std::mutex> guard(lock_);
if (0 != zip_stat(zip_file_, temp_path.c_str(), ZIP_FL_NOCASE | ZIP_FL_UNCHANGED, &zstat)) {
// ZIP files do not have real directories, so we'll end up here if we
// try to stat one. For now that's fine.
info->exists = false;
return false;
}
}
// Zips usually don't contain directory entries, but they may.
if ((zstat.valid & ZIP_STAT_NAME) != 0 && zstat.name) {
info->isDirectory = zstat.name[strlen(zstat.name) - 1] == '/';
}
if ((zstat.valid & ZIP_STAT_SIZE) != 0) {
info->size = zstat.size;
}
info->fullName = Path(path);
info->exists = true;
return true;
}
class ZipFileReaderFileReference : public VFSFileReference {
public:
int zi;
};
class ZipFileReaderOpenFile : public VFSOpenFile {
public:
~ZipFileReaderOpenFile() {
// Needs to be closed properly and unlocked.
_dbg_assert_(zf == nullptr);
}
ZipFileReaderFileReference *reference;
zip_file_t *zf = nullptr;
};
VFSFileReference *ZipFileReader::GetFile(const char *path) {
std::lock_guard<std::mutex> guard(lock_);
int zi = zip_name_locate(zip_file_, path, ZIP_FL_NOCASE);
if (zi < 0) {
// Not found.
return nullptr;
}
ZipFileReaderFileReference *ref = new ZipFileReaderFileReference();
ref->zi = zi;
return ref;
}
bool ZipFileReader::GetFileInfo(VFSFileReference *vfsReference, File::FileInfo *fileInfo) {
ZipFileReaderFileReference *reference = (ZipFileReaderFileReference *)vfsReference;
// If you crash here, you called this while having the lock held by having the file open.
// Don't do that, check the info before you open the file.
std::lock_guard<std::mutex> guard(lock_);
zip_stat_t zstat;
if (zip_stat_index(zip_file_, reference->zi, 0, &zstat) != 0)
return false;
*fileInfo = File::FileInfo{};
fileInfo->size = 0;
if (zstat.valid & ZIP_STAT_SIZE)
fileInfo->size = zstat.size;
return zstat.size;
}
void ZipFileReader::ReleaseFile(VFSFileReference *vfsReference) {
ZipFileReaderFileReference *reference = (ZipFileReaderFileReference *)vfsReference;
// Don't do anything other than deleting it.
delete reference;
}
VFSOpenFile *ZipFileReader::OpenFileForRead(VFSFileReference *vfsReference, size_t *size) {
ZipFileReaderFileReference *reference = (ZipFileReaderFileReference *)vfsReference;
ZipFileReaderOpenFile *openFile = new ZipFileReaderOpenFile();
openFile->reference = reference;
*size = 0;
// We only allow one file to be open for read concurrently. It's possible that this can be improved,
// especially if we only access by index like this.
lock_.lock();
zip_stat_t zstat;
if (zip_stat_index(zip_file_, reference->zi, 0, &zstat) != 0) {
lock_.unlock();
delete openFile;
return nullptr;
}
openFile->zf = zip_fopen_index(zip_file_, reference->zi, 0);
if (!openFile->zf) {
WARN_LOG(G3D, "File with index %d not found in zip", reference->zi);
lock_.unlock();
return nullptr;
}
*size = zstat.size;
// Intentionally leaving the mutex locked, will be closed in CloseFile.
return openFile;
}
void ZipFileReader::Rewind(VFSOpenFile *vfsOpenFile) {
ZipFileReaderOpenFile *openFile = (ZipFileReaderOpenFile *)vfsOpenFile;
// Close and re-open.
zip_fclose(openFile->zf);
openFile->zf = zip_fopen_index(zip_file_, openFile->reference->zi, 0);
}
size_t ZipFileReader::Read(VFSOpenFile *vfsOpenFile, void *buffer, size_t length) {
ZipFileReaderOpenFile *file = (ZipFileReaderOpenFile *)vfsOpenFile;
return zip_fread(file->zf, buffer, length);
}
void ZipFileReader::CloseFile(VFSOpenFile *vfsOpenFile) {
ZipFileReaderOpenFile *file = (ZipFileReaderOpenFile *)vfsOpenFile;
_dbg_assert_(file->zf != nullptr);
zip_fclose(file->zf);
file->zf = nullptr;
lock_.unlock();
delete file;
}

View file

@ -1,50 +0,0 @@
#pragma once
#ifdef SHARED_LIBZIP
#include <zip.h>
#else
#include "ext/libzip/zip.h"
#endif
#include <mutex>
#include <set>
#include <string>
#include "Common/File/VFS/VFS.h"
#include "Common/File/FileUtil.h"
#include "Common/File/Path.h"
class ZipFileReader : public VFSBackend {
public:
static ZipFileReader *Create(const Path &zipFile, const char *inZipPath, bool logErrors = true);
~ZipFileReader();
bool IsValid() const { return zip_file_ != nullptr; }
// use delete[] on the returned value.
uint8_t *ReadFile(const char *path, size_t *size) override;
VFSFileReference *GetFile(const char *path) override;
bool GetFileInfo(VFSFileReference *vfsReference, File::FileInfo *fileInfo) override;
void ReleaseFile(VFSFileReference *vfsReference) override;
VFSOpenFile *OpenFileForRead(VFSFileReference *vfsReference, size_t *size) override;
void Rewind(VFSOpenFile *vfsOpenFile) override;
size_t Read(VFSOpenFile *vfsOpenFile, void *buffer, size_t length) override;
void CloseFile(VFSOpenFile *vfsOpenFile) override;
bool GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter) override;
bool GetFileInfo(const char *path, File::FileInfo *info) override;
std::string toString() const override {
return inZipPath_;
}
private:
ZipFileReader(zip *zip_file, const std::string &inZipPath) : zip_file_(zip_file), inZipPath_(inZipPath) {}
// Path has to be either an empty string, or a string ending with a /.
bool GetZipListings(const std::string &path, std::set<std::string> &files, std::set<std::string> &directories);
zip *zip_file_ = nullptr;
std::mutex lock_;
std::string inZipPath_;
};

View file

@ -0,0 +1,402 @@
// Copyright 2012 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <array>
#include <cstdlib>
#include "Common/Log.h"
#include "Common/GL/GLInterface/EGL.h"
// Show the current FPS
void cInterfaceEGL::Swap() {
eglSwapBuffers(egl_dpy, egl_surf);
}
void cInterfaceEGL::SwapInterval(int Interval) {
eglSwapInterval(egl_dpy, Interval);
}
void* cInterfaceEGL::GetFuncAddress(const std::string& name) {
return (void*)eglGetProcAddress(name.c_str());
}
void cInterfaceEGL::DetectMode() {
EGLint num_configs;
bool supportsGL = false, supportsGLES2 = false, supportsGLES3 = false;
static const int renderable_types[3] = {
EGL_OPENGL_BIT,
(1 << 6), /* EGL_OPENGL_ES3_BIT_KHR */
EGL_OPENGL_ES2_BIT,
};
static const char *renderable_names[3] = {
"OpenGL", "OpenGL ES 3", "OpenGL ES 2"
};
for (int i = 0; i < 3; i++) {
int renderable_type = renderable_types[i];
const char *renderable_name = renderable_names[i];
// attributes for a visual in RGBA format with at least
// 8 bits per color
int attribs[] = {
EGL_RENDERABLE_TYPE, renderable_type,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 16,
EGL_STENCIL_SIZE, 8,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_TRANSPARENT_TYPE, EGL_NONE,
EGL_SAMPLES, 0,
EGL_NONE
};
// Get how many configs there are
if (!eglChooseConfig( egl_dpy, attribs, nullptr, 0, &num_configs)) {
EGL_ILOG("DetectMode: couldn't get an EGL visual config with renderable_type=%s", renderable_name);
continue;
}
EGL_ILOG("DetectMode: got an EGL visual config with renderable_type=%s", renderable_name);
EGLConfig* config = new EGLConfig[num_configs];
// Get all the configurations
if (!eglChooseConfig(egl_dpy, attribs, config, num_configs, &num_configs)) {
EGL_ILOG("DetectMode: couldn't choose an EGL visual config\n");
delete[] config;
continue;
}
for (int i = 0; i < num_configs; ++i) {
EGLint attribVal;
bool ret;
ret = eglGetConfigAttrib(egl_dpy, config[i], EGL_RENDERABLE_TYPE, &attribVal);
if (ret) {
if ((attribVal & EGL_OPENGL_BIT) && s_opengl_mode != GLInterfaceMode::MODE_DETECT_ES)
supportsGL = true;
if (attribVal & (1 << 6)) /* EGL_OPENGL_ES3_BIT_KHR */
supportsGLES3 = true; // Apparently, this cannot be completely trusted so we implement a fallback to ES 2.0 below.
if (attribVal & EGL_OPENGL_ES2_BIT)
supportsGLES2 = true;
}
}
delete[] config;
}
if (supportsGL)
s_opengl_mode = GLInterfaceMode::MODE_OPENGL;
else if (supportsGLES3)
s_opengl_mode = GLInterfaceMode::MODE_OPENGLES3;
else if (supportsGLES2)
s_opengl_mode = GLInterfaceMode::MODE_OPENGLES2;
if (s_opengl_mode == GLInterfaceMode::MODE_DETECT) // Errored before we found a mode
s_opengl_mode = GLInterfaceMode::MODE_OPENGL; // Fall back to OpenGL
}
static void LogEGLConfig(EGLDisplay egl_dpy, EGLConfig config) {
EGLint red = 0, green = 0, blue = 0, alpha = 0, depth = 0, stencil = 0, format = -1, type;
struct {
EGLint value;
const char *name;
} vals[] = {
{ EGL_RED_SIZE, "EGL_RED_SIZE" },
{ EGL_GREEN_SIZE, "EGL_GREEN_SIZE" },
{ EGL_BLUE_SIZE, "EGL_BLUE_SIZE" },
{ EGL_ALPHA_SIZE, "EGL_ALPHA_SIZE" },
{ EGL_DEPTH_SIZE, "EGL_DEPTH_SIZE" },
{ EGL_STENCIL_SIZE, "EGL_STENCIL_SIZE" },
{ EGL_NATIVE_VISUAL_ID, "EGL_NATIVE_VISUAL_ID" },
{ EGL_NATIVE_VISUAL_TYPE, "EGL_NATIVE_VISUAL_TYPE" },
{ EGL_MAX_SWAP_INTERVAL, "EGL_MAX_SWAP_INTERVAL" },
{ EGL_MIN_SWAP_INTERVAL, "EGL_MIN_SWAP_INTERVAL" },
{ EGL_MIN_SWAP_INTERVAL, "EGL_MIN_SWAP_INTERVAL" },
{ EGL_NATIVE_RENDERABLE, "EGL_NATIVE_RENDERABLE" },
{ EGL_COLOR_BUFFER_TYPE, "EGL_COLOR_BUFFER_TYPE" },
{ EGL_BUFFER_SIZE, "EGL_BUFFER_SIZE" },
{ EGL_CONFIG_ID, "EGL_CONFIG_ID" },
{ EGL_SAMPLES, "EGL_SAMPLES" },
};
for (int i = 0; i < (int)(sizeof(vals)/sizeof(vals[0])); i++) {
EGLint value;
eglGetConfigAttrib(egl_dpy, config, vals[i].value, &value);
EGL_ILOG(" %s = %d", vals[i].name, value);
}
}
const char *cInterfaceEGL::EGLGetErrorString(EGLint error) {
switch (error) {
case EGL_SUCCESS: return "EGL_SUCCESS";
case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
default:
return "(UNKNOWN)";
}
}
bool cInterfaceEGL::ChooseAndCreate(void *window_handle, bool core, bool use565) {
int attribs32[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // Keep this first!
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 16,
EGL_STENCIL_SIZE, 8,
EGL_TRANSPARENT_TYPE, EGL_NONE,
EGL_NONE, 0
};
int attribs16[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // Keep this first!
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
EGL_ALPHA_SIZE, 0,
EGL_DEPTH_SIZE, 16,
EGL_STENCIL_SIZE, 8,
EGL_TRANSPARENT_TYPE, EGL_NONE,
EGL_NONE, 0
};
int attribsFallback32[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // Keep this first!
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 16,
EGL_NONE, 0
};
int attribsFallback16[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // Keep this first!
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
EGL_ALPHA_SIZE, 0,
EGL_DEPTH_SIZE, 16,
EGL_NONE, 0
};
int *attribs = attribs32;
int *attribsFallback = attribsFallback32;
if (use565) {
attribs = attribs16;
attribsFallback = attribsFallback16;
}
EGLint ctx_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE, 0,
EGL_NONE, 0,
EGL_NONE, 0,
EGL_NONE, 0,
};
switch (s_opengl_mode) {
case MODE_OPENGL:
EGL_ILOG("Setting RENDERABLE_TYPE to EGL_OPENGL_BIT");
attribs[1] = EGL_OPENGL_BIT;
// 1 will be major version, and 3 the minor version.
ctx_attribs[2] = 0x30FB; /* EGL_CONTEXT_MINOR_VERSION_KHR */
// Let's always use a core profile here.
ctx_attribs[4] = 0x30FD; /* EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR */
ctx_attribs[5] = 1; /* EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR */
break;
case MODE_OPENGLES2:
EGL_ILOG("Setting RENDERABLE_TYPE to EGL_OPENGL_ES2_BIT");
attribs[1] = EGL_OPENGL_ES2_BIT;
ctx_attribs[1] = 2;
break;
case MODE_OPENGLES3:
EGL_ILOG("Setting RENDERABLE_TYPE to EGL_OPENGL_ES3_BIT_KHR");
attribs[1] = (1 << 6); /* EGL_OPENGL_ES3_BIT_KHR */
ctx_attribs[1] = 3;
break;
default:
EGL_ELOG("Unknown OpenGL mode set\n");
return false;
break;
}
EGL_ILOG("Calling eglChooseConfig to get number of configs (use16bit=%d)...", (int)use565);
EGLConfig *configs;
EGLint num_configs = 0;
if (!eglChooseConfig(egl_dpy, attribs, NULL, 0, &num_configs) || num_configs == 0) {
EGL_ILOG("Error: couldn't get a number of configs. Trying with fallback config (no stencil, not specifying transparent:none)\n");
attribsFallback[1] = attribs[1];
attribs = attribsFallback;
if (!eglChooseConfig(egl_dpy, attribs, NULL, 0, &num_configs) || num_configs == 0) {
eglTerminate(egl_dpy);
return false;
}
}
EGL_ILOG("Got %d configs. Now choosing...", num_configs);
configs = new EGLConfig[num_configs];
if (!eglChooseConfig(egl_dpy, attribs, configs, num_configs, &num_configs)) {
EGL_ELOG("Error: couldn't get an EGL visual config (num_configs=%d)! Terminating EGL.\n", num_configs);
eglTerminate(egl_dpy);
return false;
}
int chosenConfig = -1;
// Find our ideal config in the list. If it's there, use it, otherwise pick whatever the device wanted (#0)
int wantedAlpha = 8;
// Requiring alpha seems to be a problem on older devices. Let's see if this helps...
if (attribs[1] == EGL_OPENGL_ES2_BIT)
wantedAlpha = 0;
for (int i = 0; i < num_configs; i++) {
EGL_ILOG("Config %d:", i);
LogEGLConfig(egl_dpy, configs[i]);
int red, green, blue, alpha, depth, stencil;
eglGetConfigAttrib(egl_dpy, configs[i], EGL_RED_SIZE, &red);
eglGetConfigAttrib(egl_dpy, configs[i], EGL_GREEN_SIZE, &green);
eglGetConfigAttrib(egl_dpy, configs[i], EGL_BLUE_SIZE, &blue);
eglGetConfigAttrib(egl_dpy, configs[i], EGL_ALPHA_SIZE, &alpha);
eglGetConfigAttrib(egl_dpy, configs[i], EGL_DEPTH_SIZE, &depth);
eglGetConfigAttrib(egl_dpy, configs[i], EGL_STENCIL_SIZE, &stencil);
if (chosenConfig == -1 && red == 8 && green == 8 && blue == 8 && alpha == wantedAlpha && depth == 24 && stencil == 8) {
chosenConfig = i;
}
}
if (chosenConfig == -1)
chosenConfig = 0;
EGL_ILOG("eglChooseConfig successful: num_configs=%d, choosing config %d", num_configs, chosenConfig);
if (s_opengl_mode == MODE_OPENGL) {
EGL_ILOG("eglBindAPI(OPENGL)");
eglBindAPI(EGL_OPENGL_API);
} else {
EGL_ILOG("eglBindAPI(OPENGL_ES)");
eglBindAPI(EGL_OPENGL_ES_API);
}
EGLNativeWindowType host_window = (EGLNativeWindowType)window_handle;
EGLNativeWindowType native_window = InitializePlatform(host_window, configs[chosenConfig]);
const char *s = eglQueryString(egl_dpy, EGL_VERSION);
EGL_ILOG("EGL_VERSION = %s\n", s);
s = eglQueryString(egl_dpy, EGL_VENDOR);
EGL_ILOG("EGL_VENDOR = %s\n", s);
s = eglQueryString(egl_dpy, EGL_EXTENSIONS);
EGL_ILOG("EGL_EXTENSIONS = %s\n", s);
s = eglQueryString(egl_dpy, EGL_CLIENT_APIS);
EGL_ILOG("EGL_CLIENT_APIS = %s\n", s);
if (s_opengl_mode == MODE_OPENGL) {
EGL_ILOG("Finding a good GL version");
egl_ctx = nullptr;
for (int minor = 6; minor >= 0 && !egl_ctx; --minor) {
ctx_attribs[1] = 4;
ctx_attribs[3] = minor;
egl_ctx = eglCreateContext(egl_dpy, configs[chosenConfig], EGL_NO_CONTEXT, ctx_attribs);
}
if (!egl_ctx) {
ctx_attribs[1] = 3;
ctx_attribs[3] = 3;
egl_ctx = eglCreateContext(egl_dpy, configs[chosenConfig], EGL_NO_CONTEXT, ctx_attribs);
}
} else {
egl_ctx = eglCreateContext(egl_dpy, configs[chosenConfig], EGL_NO_CONTEXT, ctx_attribs);
}
if (!egl_ctx) {
EGL_ILOG("Error: eglCreateContext failed: %s\n", EGLGetErrorString(eglGetError()));
delete[] configs;
return false;
}
EGL_ILOG("Successfully created EGL context.\n");
egl_surf = eglCreateWindowSurface(egl_dpy, configs[chosenConfig], native_window, nullptr);
if (!egl_surf) {
EGL_ILOG("Error: eglCreateWindowSurface failed: native_window=%p error=%s ctx_attribs[1]==%d\n", native_window, EGLGetErrorString(eglGetError()), ctx_attribs[1]);
eglDestroyContext(egl_dpy, egl_ctx);
delete[] configs;
return false;
}
EGL_ILOG("Successfully created EGL window surface (window=%p).\n", native_window);
delete[] configs;
return true;
}
// Create rendering window.
bool cInterfaceEGL::Create(void *window_handle, bool core, bool use565) {
EGLint egl_major, egl_minor;
egl_dpy = OpenDisplay();
if (!egl_dpy) {
EGL_ILOG("Error: eglGetDisplay() failed\n");
return false;
}
if (!eglInitialize(egl_dpy, &egl_major, &egl_minor)) {
EGL_ILOG("Error: eglInitialize() failed\n");
return false;
}
EGL_ILOG("eglInitialize() succeeded (use565=%d)\n", (int)use565);
if (s_opengl_mode == MODE_DETECT || s_opengl_mode == MODE_DETECT_ES)
DetectMode();
if (!ChooseAndCreate(window_handle, core, use565) && (s_opengl_mode == MODE_OPENGLES3 || s_opengl_mode == MODE_OPENGL)) {
// Fallback to ES 2.0 and try again.
s_opengl_mode = MODE_OPENGLES2;
if (!ChooseAndCreate(window_handle, core, use565)) {
eglTerminate(egl_dpy);
egl_dpy = nullptr;
return false;
}
}
return true;
}
bool cInterfaceEGL::MakeCurrent() {
return eglMakeCurrent(egl_dpy, egl_surf, egl_surf, egl_ctx);
}
bool cInterfaceEGL::ClearCurrent() {
return eglMakeCurrent(egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
void cInterfaceEGL::Shutdown() {
ShutdownPlatform();
if (egl_ctx && !eglMakeCurrent(egl_dpy, egl_surf, egl_surf, egl_ctx)) {
NOTICE_LOG(G3D, "Could not release drawing context.");
}
if (egl_ctx) {
eglMakeCurrent(egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (!eglDestroySurface(egl_dpy, egl_surf))
NOTICE_LOG(G3D, "Could not destroy window surface.");
if (!eglDestroyContext(egl_dpy, egl_ctx))
NOTICE_LOG(G3D, "Could not destroy drawing context.");
if (!eglTerminate(egl_dpy))
NOTICE_LOG(G3D, "Could not destroy display connection.");
egl_ctx = nullptr;
egl_dpy = nullptr;
egl_surf = nullptr;
}
}

View file

@ -0,0 +1,42 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <string>
#include <EGL/egl.h>
#include "Common/Log.h"
#include "Common/GL/GLInterfaceBase.h"
#define EGL_ILOG(...) INFO_LOG(G3D, __VA_ARGS__)
#define EGL_ELOG(...) INFO_LOG(G3D, __VA_ARGS__)
class cInterfaceEGL : public cInterfaceBase {
public:
void SwapInterval(int Interval) override;
void Swap() override;
void SetMode(u32 mode) override { s_opengl_mode = mode; }
void* GetFuncAddress(const std::string& name) override;
bool Create(void *window_handle, bool core, bool use565) override;
bool MakeCurrent() override;
bool ClearCurrent() override;
void Shutdown() override;
protected:
EGLSurface egl_surf;
EGLContext egl_ctx;
EGLDisplay egl_dpy;
virtual EGLDisplay OpenDisplay() = 0;
virtual EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) = 0;
virtual void ShutdownPlatform() = 0;
virtual void SetInternalResolution(int internalWidth, int internalHeight) {}
const char *EGLGetErrorString(EGLint error);
private:
bool ChooseAndCreate(void *window_handle, bool core, bool use565);
void DetectMode();
};

View file

@ -0,0 +1,31 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <android/native_window.h>
#include "Common/Log.h"
#include "Common/GL/GLInterface/EGLAndroid.h"
EGLDisplay cInterfaceEGLAndroid::OpenDisplay() {
return eglGetDisplay(EGL_DEFAULT_DISPLAY);
}
EGLNativeWindowType cInterfaceEGLAndroid::InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) {
EGLint format;
if (EGL_FALSE == eglGetConfigAttrib(egl_dpy, config, EGL_NATIVE_VISUAL_ID, &format)) {
EGL_ELOG("Failed getting EGL_NATIVE_VISUAL_ID: error %s", EGLGetErrorString(eglGetError()));
return NULL;
}
int32_t result = ANativeWindow_setBuffersGeometry(host_window, internalWidth_, internalHeight_, format);
EGL_ILOG("ANativeWindow_setBuffersGeometry returned %d", result);
const int width = ANativeWindow_getWidth(host_window);
const int height = ANativeWindow_getHeight(host_window);
SetBackBufferDimensions(width, height);
return host_window;
}
void cInterfaceEGLAndroid::ShutdownPlatform() {
}

View file

@ -0,0 +1,24 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "Common/GL/GLInterface/EGL.h"
class cInterfaceEGLAndroid : public cInterfaceEGL {
public:
cInterfaceEGLAndroid() : internalWidth_(0), internalHeight_(0) {}
protected:
EGLDisplay OpenDisplay() override;
EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) override;
void ShutdownPlatform() override;
void OverrideBackbufferDimensions(int internalWidth, int internalHeight) override {
internalWidth_ = internalWidth;
internalHeight_ = internalHeight;
}
private:
int internalWidth_;
int internalHeight_;
};

View file

@ -0,0 +1,21 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "ppsspp_config.h"
#if PPSSPP_PLATFORM(SWITCH)
#include <switch.h>
#include "Common/Log.h"
#include "Common/GL/GLInterface/EGLSwitch.h"
EGLDisplay cInterfaceEGLSwitch::OpenDisplay() {
return eglGetDisplay(EGL_DEFAULT_DISPLAY);
}
EGLNativeWindowType cInterfaceEGLSwitch::InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) {
return nwindowGetDefault();
}
void cInterfaceEGLSwitch::ShutdownPlatform() {
}
#endif

View file

@ -0,0 +1,24 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "Common/GL/GLInterface/EGL.h"
class cInterfaceEGLSwitch : public cInterfaceEGL {
public:
cInterfaceEGLSwitch() {}
protected:
EGLDisplay OpenDisplay() override;
EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) override;
void ShutdownPlatform() override;
void OverrideBackbufferDimensions(int internalWidth, int internalHeight) override {
internalWidth_ = internalWidth;
internalHeight_ = internalHeight;
}
private:
int internalWidth_ = 0;
int internalHeight_ = 0;
};

View file

@ -0,0 +1,44 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "ppsspp_config.h"
#include "Common/GL/GLInterfaceBase.h"
#ifdef __ANDROID__
#include "Common/GL/GLInterface/EGLAndroid.h"
#elif PPSSPP_PLATFORM(SWITCH)
#include "Common/GL/GLInterface/EGLSwitch.h"
#elif defined(__APPLE__)
#include "Common/GL/GLInterface/AGL.h"
#elif defined(_WIN32)
#include "Common/GL/GLInterface/WGL.h"
#elif HAVE_X11
#if defined(USE_EGL) && USE_EGL
#include "Common/GL/GLInterface/EGLX11.h"
#else
#include "Common/GL/GLInterface/GLX.h"
#endif
#else
#error Platform doesnt have a GLInterface
#endif
cInterfaceBase* HostGL_CreateGLInterface(){
#ifdef __ANDROID__
return new cInterfaceEGLAndroid;
#elif PPSSPP_PLATFORM(SWITCH)
return new cInterfaceEGLSwitch;
#elif defined(__APPLE__)
return new cInterfaceAGL;
#elif defined(_WIN32)
return new cInterfaceWGL;
#elif defined(HAVE_X11) && HAVE_X11
#if defined(USE_EGL) && USE_EGL
return new cInterfaceEGLX11;
#else
return new cInterfaceGLX;
#endif
#else
return nullptr;
#endif
}

View file

@ -0,0 +1,50 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "Common/CommonTypes.h"
enum GLInterfaceMode {
MODE_DETECT = 0,
MODE_DETECT_ES,
MODE_OPENGL,
MODE_OPENGLES2,
MODE_OPENGLES3,
};
class cInterfaceBase {
protected:
// Window dimensions.
u32 s_backbuffer_width;
u32 s_backbuffer_height;
u32 s_opengl_mode;
public:
cInterfaceBase() : s_backbuffer_width(0), s_backbuffer_height(0), s_opengl_mode(MODE_DETECT) {}
virtual ~cInterfaceBase() {}
virtual void Swap() {}
virtual void SetMode(u32 mode) { s_opengl_mode = GLInterfaceMode::MODE_OPENGL; }
virtual u32 GetMode() { return s_opengl_mode; }
virtual void* GetFuncAddress(const std::string& name) { return nullptr; }
virtual bool Create(void *window_handle, bool core = true, bool use16bit = false) = 0;
virtual bool MakeCurrent() { return true; }
virtual bool ClearCurrent() { return true; }
virtual void Shutdown() {}
virtual void SwapInterval(int Interval) { }
virtual u32 GetBackBufferWidth() { return s_backbuffer_width; }
virtual u32 GetBackBufferHeight() { return s_backbuffer_height; }
virtual void OverrideBackbufferDimensions(int w, int h) = 0;
virtual void SetBackBufferDimensions(u32 W, u32 H) {s_backbuffer_width = W; s_backbuffer_height = H; }
virtual void Update() { }
virtual bool PeekMessages() { return false; }
};
cInterfaceBase* HostGL_CreateGLInterface();

View file

@ -11,6 +11,7 @@ static HMODULE g_D3DCompileModule;
LPCREATEDXGIFACTORY ptr_CreateDXGIFactory; LPCREATEDXGIFACTORY ptr_CreateDXGIFactory;
LPD3D11CREATEDEVICE ptr_D3D11CreateDevice; LPD3D11CREATEDEVICE ptr_D3D11CreateDevice;
LPD3D11CREATEDEVICEANDSWAPCHAIN ptr_D3D11CreateDeviceAndSwapChain;
pD3DCompile ptr_D3DCompile; pD3DCompile ptr_D3DCompile;
LoadD3D11Error LoadD3D11() { LoadD3D11Error LoadD3D11() {
@ -21,6 +22,7 @@ LoadD3D11Error LoadD3D11() {
g_D3D11Module = LoadLibrary(L"d3d11.dll"); g_D3D11Module = LoadLibrary(L"d3d11.dll");
if (g_D3D11Module) { if (g_D3D11Module) {
ptr_D3D11CreateDevice = (LPD3D11CREATEDEVICE)GetProcAddress(g_D3D11Module, "D3D11CreateDevice"); ptr_D3D11CreateDevice = (LPD3D11CREATEDEVICE)GetProcAddress(g_D3D11Module, "D3D11CreateDevice");
ptr_D3D11CreateDeviceAndSwapChain = (LPD3D11CREATEDEVICEANDSWAPCHAIN)GetProcAddress(g_D3D11Module, "D3D11CreateDeviceAndSwapChain");
} else { } else {
return LoadD3D11Error::FAIL_NO_D3D11; return LoadD3D11Error::FAIL_NO_D3D11;
} }

View file

@ -14,10 +14,12 @@
#endif #endif
typedef HRESULT (WINAPI *LPCREATEDXGIFACTORY)(REFIID, void **); typedef HRESULT (WINAPI *LPCREATEDXGIFACTORY)(REFIID, void **);
typedef HRESULT (WINAPI *LPD3D11CREATEDEVICEANDSWAPCHAIN)(__in_opt IDXGIAdapter *pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags, __in_ecount_opt(FeatureLevels) CONST D3D_FEATURE_LEVEL *pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, __in_opt CONST DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, __out_opt IDXGISwapChain **ppSwapChain, __out_opt ID3D11Device **ppDevice, __out_opt D3D_FEATURE_LEVEL *pFeatureLevel, __out_opt ID3D11DeviceContext **ppImmediateContext);
typedef HRESULT (WINAPI *LPD3D11CREATEDEVICE)(IDXGIAdapter *, D3D_DRIVER_TYPE, HMODULE, UINT32, D3D_FEATURE_LEVEL *, UINT, UINT32, ID3D11Device **, D3D_FEATURE_LEVEL *, ID3D11DeviceContext **); typedef HRESULT (WINAPI *LPD3D11CREATEDEVICE)(IDXGIAdapter *, D3D_DRIVER_TYPE, HMODULE, UINT32, D3D_FEATURE_LEVEL *, UINT, UINT32, ID3D11Device **, D3D_FEATURE_LEVEL *, ID3D11DeviceContext **);
extern LPCREATEDXGIFACTORY ptr_CreateDXGIFactory; extern LPCREATEDXGIFACTORY ptr_CreateDXGIFactory;
extern LPD3D11CREATEDEVICE ptr_D3D11CreateDevice; extern LPD3D11CREATEDEVICE ptr_D3D11CreateDevice;
extern LPD3D11CREATEDEVICEANDSWAPCHAIN ptr_D3D11CreateDeviceAndSwapChain;
extern pD3DCompile ptr_D3DCompile; extern pD3DCompile ptr_D3DCompile;
enum class LoadD3D11Error { enum class LoadD3D11Error {

View file

@ -11,7 +11,6 @@
#include "Common/Data/Convert/ColorConv.h" #include "Common/Data/Convert/ColorConv.h"
#include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Data/Convert/SmallDataConvert.h"
#include "Common/Data/Encoding/Utf8.h" #include "Common/Data/Encoding/Utf8.h"
#include "Common/TimeUtil.h"
#include "Common/Log.h" #include "Common/Log.h"
#include <map> #include <map>
@ -63,7 +62,7 @@ public:
class D3D11DrawContext : public DrawContext { class D3D11DrawContext : public DrawContext {
public: public:
D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext, ID3D11Device1 *device1, ID3D11DeviceContext1 *deviceContext1, IDXGISwapChain *swapChain, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector<std::string> deviceList, int maxInflightFrames); D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext, ID3D11Device1 *device1, ID3D11DeviceContext1 *deviceContext1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector<std::string> deviceList);
~D3D11DrawContext(); ~D3D11DrawContext();
const DeviceCaps &GetDeviceCaps() const override { const DeviceCaps &GetDeviceCaps() const override {
@ -89,11 +88,10 @@ public:
Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override; Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override;
void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override; void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override;
void UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) override;
void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override; void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override;
bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override;
bool CopyFramebufferToMemory(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, ReadbackMode mode, const char *tag) override; bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, const char *tag) override;
// These functions should be self explanatory. // These functions should be self explanatory.
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override; void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
@ -106,7 +104,7 @@ public:
void BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) override; void BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) override;
void BindNativeTexture(int index, void *nativeTexture) override; void BindNativeTexture(int index, void *nativeTexture) override;
void BindSamplerStates(int start, int count, SamplerState **states) override; void BindSamplerStates(int start, int count, SamplerState **states) override;
void BindVertexBuffer(Buffer *buffers, int offset) override; void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) override;
void BindIndexBuffer(Buffer *indexBuffer, int offset) override; void BindIndexBuffer(Buffer *indexBuffer, int offset) override;
void BindPipeline(Pipeline *pipeline) override; void BindPipeline(Pipeline *pipeline) override;
@ -114,7 +112,7 @@ public:
// Raster state // Raster state
void SetScissorRect(int left, int top, int width, int height) override; void SetScissorRect(int left, int top, int width, int height) override;
void SetViewport(const Viewport &viewport) override; void SetViewports(int count, Viewport *viewports) override;
void SetBlendFactor(float color[4]) override { void SetBlendFactor(float color[4]) override {
if (memcmp(blendFactor_, color, sizeof(float) * 4)) { if (memcmp(blendFactor_, color, sizeof(float) * 4)) {
memcpy(blendFactor_, color, sizeof(float) * 4); memcpy(blendFactor_, color, sizeof(float) * 4);
@ -128,17 +126,14 @@ public:
stencilDirty_ = true; stencilDirty_ = true;
} }
void EndFrame() override;
void Draw(int vertexCount, int offset) override; void Draw(int vertexCount, int offset) override;
void DrawIndexed(int vertexCount, int offset) override; void DrawIndexed(int vertexCount, int offset) override;
void DrawUP(const void *vdata, int vertexCount) override; void DrawUP(const void *vdata, int vertexCount) override;
void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override; void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override;
void BeginFrame(DebugFlags debugFlags) override; void BeginFrame() override;
void EndFrame() override;
void Present(PresentMode presentMode, int vblanks) override;
int GetFrameCount() override { return frameCount_; }
std::string GetInfoString(InfoField info) const override { std::string GetInfoString(InfoField info) const override {
switch (info) { switch (info) {
@ -179,10 +174,9 @@ private:
HWND hWnd_; HWND hWnd_;
ID3D11Device *device_; ID3D11Device *device_;
ID3D11Device1 *device1_;
ID3D11DeviceContext *context_; ID3D11DeviceContext *context_;
ID3D11Device1 *device1_;
ID3D11DeviceContext1 *context1_; ID3D11DeviceContext1 *context1_;
IDXGISwapChain *swapChain_;
ID3D11Texture2D *bbRenderTargetTex_ = nullptr; // NOT OWNED ID3D11Texture2D *bbRenderTargetTex_ = nullptr; // NOT OWNED
ID3D11RenderTargetView *bbRenderTargetView_ = nullptr; ID3D11RenderTargetView *bbRenderTargetView_ = nullptr;
@ -214,15 +208,14 @@ private:
ID3D11GeometryShader *curGS_ = nullptr; ID3D11GeometryShader *curGS_ = nullptr;
D3D11_PRIMITIVE_TOPOLOGY curTopology_ = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; D3D11_PRIMITIVE_TOPOLOGY curTopology_ = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
ID3D11Buffer *nextVertexBuffer_ = nullptr; ID3D11Buffer *nextVertexBuffers_[4]{};
UINT nextVertexBufferOffset_ = 0; int nextVertexBufferOffsets_[4]{};
bool dirtyIndexBuffer_ = false; bool dirtyIndexBuffer_ = false;
ID3D11Buffer *nextIndexBuffer_ = nullptr; ID3D11Buffer *nextIndexBuffer_ = nullptr;
UINT nextIndexBufferOffset_ = 0; int nextIndexBufferOffset_ = 0;
InvalidationCallback invalidationCallback_; InvalidationCallback invalidationCallback_;
int frameCount_ = FRAME_TIME_HISTORY_LENGTH;
// Dynamic state // Dynamic state
float blendFactor_[4]{}; float blendFactor_[4]{};
@ -243,14 +236,13 @@ private:
std::vector<std::string> deviceList_; std::vector<std::string> deviceList_;
}; };
D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext, ID3D11Device1 *device1, ID3D11DeviceContext1 *deviceContext1, IDXGISwapChain *swapChain, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector<std::string> deviceList, int maxInflightFrames) D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext, ID3D11Device1 *device1, ID3D11DeviceContext1 *deviceContext1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector<std::string> deviceList)
: hWnd_(hWnd), : hWnd_(hWnd),
device_(device), device_(device),
context_(deviceContext1), context_(deviceContext1),
device1_(device1), device1_(device1),
context1_(deviceContext1), context1_(deviceContext1),
featureLevel_(featureLevel), featureLevel_(featureLevel),
swapChain_(swapChain),
deviceList_(deviceList) { deviceList_(deviceList) {
// We no longer support Windows Phone. // We no longer support Windows Phone.
@ -281,10 +273,6 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de
caps_.blendMinMaxSupported = true; caps_.blendMinMaxSupported = true;
caps_.multiSampleLevelsMask = 1; // More could be supported with some work. caps_.multiSampleLevelsMask = 1; // More could be supported with some work.
caps_.presentInstantModeChange = true;
caps_.presentMaxInterval = 4;
caps_.presentModesSupported = PresentMode::FIFO | PresentMode::IMMEDIATE;
D3D11_FEATURE_DATA_D3D11_OPTIONS options{}; D3D11_FEATURE_DATA_D3D11_OPTIONS options{};
HRESULT result = device_->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options, sizeof(options)); HRESULT result = device_->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options, sizeof(options));
if (SUCCEEDED(result)) { if (SUCCEEDED(result)) {
@ -350,13 +338,6 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de
const size_t UP_MAX_BYTES = 65536 * 24; const size_t UP_MAX_BYTES = 65536 * 24;
upBuffer_ = CreateBuffer(UP_MAX_BYTES, BufferUsageFlag::DYNAMIC | BufferUsageFlag::VERTEXDATA); upBuffer_ = CreateBuffer(UP_MAX_BYTES, BufferUsageFlag::DYNAMIC | BufferUsageFlag::VERTEXDATA);
IDXGIDevice1 *dxgiDevice1 = nullptr;
hr = device_->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void **>(&dxgiDevice1));
if (SUCCEEDED(hr)) {
caps_.setMaxFrameLatencySupported = true;
dxgiDevice1->SetMaximumFrameLatency(maxInflightFrames);
}
} }
D3D11DrawContext::~D3D11DrawContext() { D3D11DrawContext::~D3D11DrawContext() {
@ -427,43 +408,32 @@ void D3D11DrawContext::HandleEvent(Event ev, int width, int height, void *param1
curRTHeight_ = height; curRTHeight_ = height;
break; break;
} }
case Event::PRESENTED:
// Make sure that we don't eliminate the next time the render target is set.
curRenderTargetView_ = nullptr;
curDepthStencilView_ = nullptr;
break;
} }
} }
void D3D11DrawContext::EndFrame() { void D3D11DrawContext::EndFrame() {
// Fake a submit time.
frameTimeHistory_[frameCount_].firstSubmit = time_now_d();
curPipeline_ = nullptr; curPipeline_ = nullptr;
} }
void D3D11DrawContext::Present(PresentMode presentMode, int vblanks) { void D3D11DrawContext::SetViewports(int count, Viewport *viewports) {
frameTimeHistory_[frameCount_].queuePresent = time_now_d(); D3D11_VIEWPORT vp[4];
for (int i = 0; i < count; i++) {
int interval = vblanks; DisplayRect<float> rc{ viewports[i].TopLeftX , viewports[i].TopLeftY, viewports[i].Width, viewports[i].Height };
if (presentMode != PresentMode::FIFO) { if (curRenderTargetView_ == bbRenderTargetView_) // Only the backbuffer is actually rotated wrong!
interval = 0; RotateRectToDisplay(rc, curRTWidth_, curRTHeight_);
vp[i].TopLeftX = rc.x;
vp[i].TopLeftY = rc.y;
vp[i].Width = rc.w;
vp[i].Height = rc.h;
vp[i].MinDepth = viewports[i].MinDepth;
vp[i].MaxDepth = viewports[i].MaxDepth;
} }
// Safety for libretro context_->RSSetViewports(count, vp);
if (swapChain_) {
swapChain_->Present(interval, 0);
}
curRenderTargetView_ = nullptr;
curDepthStencilView_ = nullptr;
frameCount_++;
}
void D3D11DrawContext::SetViewport(const Viewport &viewport) {
DisplayRect<float> rc{ viewport.TopLeftX , viewport.TopLeftY, viewport.Width, viewport.Height };
if (curRenderTargetView_ == bbRenderTargetView_) // Only the backbuffer is actually rotated wrong!
RotateRectToDisplay(rc, curRTWidth_, curRTHeight_);
D3D11_VIEWPORT vp;
vp.TopLeftX = rc.x;
vp.TopLeftY = rc.y;
vp.Width = rc.w;
vp.Height = rc.h;
vp.MinDepth = viewport.MinDepth;
vp.MaxDepth = viewport.MaxDepth;
context_->RSSetViewports(1, &vp);
} }
void D3D11DrawContext::SetScissorRect(int left, int top, int width, int height) { void D3D11DrawContext::SetScissorRect(int left, int top, int width, int height) {
@ -523,12 +493,7 @@ static DXGI_FORMAT dataFormatToD3D11(DataFormat format) {
case DataFormat::D16: return DXGI_FORMAT_D16_UNORM; case DataFormat::D16: return DXGI_FORMAT_D16_UNORM;
case DataFormat::D32F: return DXGI_FORMAT_D32_FLOAT; case DataFormat::D32F: return DXGI_FORMAT_D32_FLOAT;
case DataFormat::D32F_S8: return DXGI_FORMAT_D32_FLOAT_S8X24_UINT; case DataFormat::D32F_S8: return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
case DataFormat::BC1_RGBA_UNORM_BLOCK: return DXGI_FORMAT_BC1_UNORM; case DataFormat::ETC1:
case DataFormat::BC2_UNORM_BLOCK: return DXGI_FORMAT_BC2_UNORM;
case DataFormat::BC3_UNORM_BLOCK: return DXGI_FORMAT_BC3_UNORM;
case DataFormat::BC4_UNORM_BLOCK: return DXGI_FORMAT_BC4_UNORM;
case DataFormat::BC5_UNORM_BLOCK: return DXGI_FORMAT_BC5_UNORM;
case DataFormat::BC7_UNORM_BLOCK: return DXGI_FORMAT_BC7_UNORM;
default: default:
return DXGI_FORMAT_UNKNOWN; return DXGI_FORMAT_UNKNOWN;
} }
@ -725,7 +690,7 @@ public:
D3D11InputLayout() {} D3D11InputLayout() {}
InputLayoutDesc desc; InputLayoutDesc desc;
std::vector<D3D11_INPUT_ELEMENT_DESC> elements; std::vector<D3D11_INPUT_ELEMENT_DESC> elements;
UINT stride; // type to match function parameter std::vector<int> strides;
}; };
const char *semanticToD3D11(int semantic, UINT *index) { const char *semanticToD3D11(int semantic, UINT *index) {
@ -752,13 +717,15 @@ InputLayout *D3D11DrawContext::CreateInputLayout(const InputLayoutDesc &desc) {
D3D11_INPUT_ELEMENT_DESC el; D3D11_INPUT_ELEMENT_DESC el;
el.AlignedByteOffset = desc.attributes[i].offset; el.AlignedByteOffset = desc.attributes[i].offset;
el.Format = dataFormatToD3D11(desc.attributes[i].format); el.Format = dataFormatToD3D11(desc.attributes[i].format);
el.InstanceDataStepRate = 0; el.InstanceDataStepRate = desc.bindings[desc.attributes[i].binding].instanceRate ? 1 : 0;
el.InputSlot = 0; el.InputSlot = desc.attributes[i].binding;
el.SemanticName = semanticToD3D11(desc.attributes[i].location, &el.SemanticIndex); el.SemanticName = semanticToD3D11(desc.attributes[i].location, &el.SemanticIndex);
el.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; el.InputSlotClass = desc.bindings[desc.attributes[i].binding].instanceRate ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA;
inputLayout->elements.push_back(el); inputLayout->elements.push_back(el);
} }
inputLayout->stride = desc.stride; for (size_t i = 0; i < desc.bindings.size(); i++) {
inputLayout->strides.push_back(desc.bindings[i].stride);
}
return inputLayout; return inputLayout;
} }
@ -821,83 +788,35 @@ public:
width_ = desc.width; width_ = desc.width;
height_ = desc.height; height_ = desc.height;
depth_ = desc.depth; depth_ = desc.depth;
format_ = desc.format;
mipLevels_ = desc.mipLevels;
} }
~D3D11Texture() { ~D3D11Texture() {
if (tex_) if (tex)
tex_->Release(); tex->Release();
if (stagingTex_) if (stagingTex)
stagingTex_->Release(); stagingTex->Release();
if (view_) if (view)
view_->Release(); view->Release();
} }
bool Create(ID3D11DeviceContext *context, ID3D11Device *device, const TextureDesc &desc, bool generateMips); ID3D11Texture2D *tex = nullptr;
ID3D11Texture2D *stagingTex = nullptr;
bool CreateStagingTexture(ID3D11Device *device); ID3D11ShaderResourceView *view = nullptr;
void UpdateTextureLevels(ID3D11DeviceContext *context, ID3D11Device *device, Texture *texture, const uint8_t *const *data, TextureCallback initDataCallback, int numLevels);
ID3D11ShaderResourceView *View() { return view_; }
private:
bool FillLevel(ID3D11DeviceContext *context, int level, int w, int h, int d, const uint8_t *const *data, TextureCallback initDataCallback);
ID3D11Texture2D *tex_ = nullptr;
ID3D11Texture2D *stagingTex_ = nullptr;
ID3D11ShaderResourceView *view_ = nullptr;
int mipLevels_ = 0;
}; };
bool D3D11Texture::FillLevel(ID3D11DeviceContext *context, int level, int w, int h, int d, const uint8_t *const *data, TextureCallback initDataCallback) { Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) {
D3D11_MAPPED_SUBRESOURCE mapped; if (!(GetDataFormatSupport(desc.format) & FMT_TEXTURE)) {
HRESULT hr = context->Map(stagingTex_, level, D3D11_MAP_WRITE, 0, &mapped); // D3D11 does not support this format as a texture format.
if (!SUCCEEDED(hr)) { return nullptr;
tex_->Release();
tex_ = nullptr;
return false;
} }
if (!initDataCallback((uint8_t *)mapped.pData, data[level], w, h, d, mapped.RowPitch, mapped.DepthPitch)) { D3D11Texture *tex = new D3D11Texture(desc);
for (int s = 0; s < d; ++s) {
for (int y = 0; y < h; ++y) { bool generateMips = desc.generateMips;
void *dest = (uint8_t *)mapped.pData + mapped.DepthPitch * s + mapped.RowPitch * y; if (desc.generateMips && !(GetDataFormatSupport(desc.format) & FMT_AUTOGEN_MIPS)) {
uint32_t byteStride = w * (uint32_t)DataFormatSizeInBytes(format_); // D3D11 does not support autogenerating mipmaps for this format.
const void *src = data[level] + byteStride * (y + h * s); generateMips = false;
memcpy(dest, src, byteStride);
}
}
} }
context->Unmap(stagingTex_, level);
return true;
}
bool D3D11Texture::CreateStagingTexture(ID3D11Device *device) {
if (stagingTex_)
return true;
D3D11_TEXTURE2D_DESC descColor{};
descColor.Width = width_;
descColor.Height = height_;
descColor.MipLevels = mipLevels_;
descColor.ArraySize = 1;
descColor.Format = dataFormatToD3D11(format_);
descColor.SampleDesc.Count = 1;
descColor.SampleDesc.Quality = 0;
descColor.Usage = D3D11_USAGE_STAGING;
descColor.BindFlags = 0;
descColor.MiscFlags = 0;
descColor.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
HRESULT hr = device->CreateTexture2D(&descColor, nullptr, &stagingTex_);
if (!SUCCEEDED(hr)) {
stagingTex_->Release();
stagingTex_ = nullptr;
return false;
}
return true;
}
bool D3D11Texture::Create(ID3D11DeviceContext *context, ID3D11Device *device, const TextureDesc &desc, bool generateMips) {
D3D11_TEXTURE2D_DESC descColor{}; D3D11_TEXTURE2D_DESC descColor{};
descColor.Width = desc.width; descColor.Width = desc.width;
descColor.Height = desc.height; descColor.Height = desc.height;
@ -906,16 +825,25 @@ bool D3D11Texture::Create(ID3D11DeviceContext *context, ID3D11Device *device, co
descColor.Format = dataFormatToD3D11(desc.format); descColor.Format = dataFormatToD3D11(desc.format);
descColor.SampleDesc.Count = 1; descColor.SampleDesc.Count = 1;
descColor.SampleDesc.Quality = 0; descColor.SampleDesc.Quality = 0;
if (desc.initDataCallback) {
descColor.Usage = D3D11_USAGE_STAGING;
descColor.BindFlags = 0;
descColor.MiscFlags = 0;
descColor.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
HRESULT hr = device_->CreateTexture2D(&descColor, nullptr, &tex->stagingTex);
if (!SUCCEEDED(hr)) {
delete tex;
return nullptr;
}
}
descColor.Usage = D3D11_USAGE_DEFAULT; descColor.Usage = D3D11_USAGE_DEFAULT;
descColor.BindFlags = generateMips ? (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET) : D3D11_BIND_SHADER_RESOURCE; descColor.BindFlags = generateMips ? (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET) : D3D11_BIND_SHADER_RESOURCE;
descColor.MiscFlags = generateMips ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 0; descColor.MiscFlags = generateMips ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 0;
descColor.CPUAccessFlags = 0; descColor.CPUAccessFlags = 0;
// Make sure we have a staging texture if we'll need it.
if (desc.initDataCallback && !CreateStagingTexture(device)) {
return false;
}
D3D11_SUBRESOURCE_DATA *initDataParam = nullptr; D3D11_SUBRESOURCE_DATA *initDataParam = nullptr;
D3D11_SUBRESOURCE_DATA initData[12]{}; D3D11_SUBRESOURCE_DATA initData[12]{};
std::vector<uint8_t> initDataBuffer[12]; std::vector<uint8_t> initDataBuffer[12];
@ -935,39 +863,62 @@ bool D3D11Texture::Create(ID3D11DeviceContext *context, ID3D11Device *device, co
initDataParam = initData; initDataParam = initData;
} }
HRESULT hr = device->CreateTexture2D(&descColor, initDataParam, &tex_); HRESULT hr = device_->CreateTexture2D(&descColor, initDataParam, &tex->tex);
if (!SUCCEEDED(hr)) { if (!SUCCEEDED(hr)) {
tex_ = nullptr; delete tex;
return false; return nullptr;
} }
hr = device->CreateShaderResourceView(tex_, nullptr, &view_); hr = device_->CreateShaderResourceView(tex->tex, nullptr, &tex->view);
if (!SUCCEEDED(hr)) { if (!SUCCEEDED(hr)) {
return false; delete tex;
return nullptr;
} }
auto populateLevelCallback = [&](int level, int w, int h, int d) {
D3D11_MAPPED_SUBRESOURCE mapped;
hr = context_->Map(tex->stagingTex, level, D3D11_MAP_WRITE, 0, &mapped);
if (!SUCCEEDED(hr)) {
return false;
}
if (!desc.initDataCallback((uint8_t *)mapped.pData, desc.initData[level], w, h, d, mapped.RowPitch, mapped.DepthPitch)) {
for (int s = 0; s < d; ++s) {
for (int y = 0; y < h; ++y) {
void *dest = (uint8_t *)mapped.pData + mapped.DepthPitch * s + mapped.RowPitch * y;
uint32_t byteStride = w * (uint32_t)DataFormatSizeInBytes(desc.format);
const void *src = desc.initData[level] + byteStride * (y + h * d);
memcpy(dest, src, byteStride);
}
}
}
context_->Unmap(tex->stagingTex, level);
return true;
};
if (generateMips && desc.initData.size() >= 1) { if (generateMips && desc.initData.size() >= 1) {
if (desc.initDataCallback) { if (desc.initDataCallback) {
if (!FillLevel(context, 0, desc.width, desc.height, desc.depth, desc.initData.data(), desc.initDataCallback)) { if (!populateLevelCallback(0, desc.width, desc.height, desc.depth)) {
tex_->Release(); delete tex;
return false; return nullptr;
} }
context->CopyResource(tex_, stagingTex_); context_->CopyResource(tex->stagingTex, tex->stagingTex);
stagingTex_->Release(); tex->stagingTex->Release();
stagingTex_ = nullptr; tex->stagingTex = nullptr;
} else { } else {
uint32_t byteStride = desc.width * (uint32_t)DataFormatSizeInBytes(desc.format); uint32_t byteStride = desc.width * (uint32_t)DataFormatSizeInBytes(desc.format);
context->UpdateSubresource(tex_, 0, nullptr, desc.initData[0], byteStride, 0); context_->UpdateSubresource(tex->tex, 0, nullptr, desc.initData[0], byteStride, 0);
} }
context->GenerateMips(view_); context_->GenerateMips(tex->view);
} else if (desc.initDataCallback) { } else if (desc.initDataCallback) {
int w = desc.width; int w = desc.width;
int h = desc.height; int h = desc.height;
int d = desc.depth; int d = desc.depth;
for (int i = 0; i < (int)desc.initData.size(); i++) { for (int i = 0; i < (int)desc.initData.size(); i++) {
if (!FillLevel(context, i, w, h, d, desc.initData.data(), desc.initDataCallback)) { if (!populateLevelCallback(i, desc.width, desc.height, desc.depth)) {
if (i == 0) { if (i == 0) {
return false; delete tex;
return nullptr;
} else { } else {
break; break;
} }
@ -978,62 +929,13 @@ bool D3D11Texture::Create(ID3D11DeviceContext *context, ID3D11Device *device, co
d = (d + 1) / 2; d = (d + 1) / 2;
} }
context->CopyResource(tex_, stagingTex_); context_->CopyResource(tex->tex, tex->stagingTex);
stagingTex_->Release(); tex->stagingTex->Release();
stagingTex_ = nullptr; tex->stagingTex = nullptr;
} }
return true;
}
void D3D11Texture::UpdateTextureLevels(ID3D11DeviceContext *context, ID3D11Device *device, Texture *texture, const uint8_t * const*data, TextureCallback initDataCallback, int numLevels) {
if (!CreateStagingTexture(device)) {
return;
}
int w = width_;
int h = height_;
int d = depth_;
for (int i = 0; i < (int)numLevels; i++) {
if (!FillLevel(context, i, w, h, d, data, initDataCallback)) {
break;
}
w = (w + 1) / 2;
h = (h + 1) / 2;
d = (d + 1) / 2;
}
context->CopyResource(tex_, stagingTex_);
stagingTex_->Release();
stagingTex_ = nullptr;
}
Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) {
if (!(GetDataFormatSupport(desc.format) & FMT_TEXTURE)) {
// D3D11 does not support this format as a texture format.
return nullptr;
}
D3D11Texture *tex = new D3D11Texture(desc);
bool generateMips = desc.generateMips;
if (desc.generateMips && !(GetDataFormatSupport(desc.format) & FMT_AUTOGEN_MIPS)) {
// D3D11 does not support autogenerating mipmaps for this format.
generateMips = false;
}
if (!tex->Create(context_, device_, desc, generateMips)) {
tex->Release();
return nullptr;
}
return tex; return tex;
} }
void D3D11DrawContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) {
D3D11Texture *tex = (D3D11Texture *)texture;
tex->UpdateTextureLevels(context_, device_, texture, data, initDataCallback, numLevels);
}
ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) { ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) {
if (language != ShaderLanguage::HLSL_D3D11) { if (language != ShaderLanguage::HLSL_D3D11) {
ERROR_LOG(G3D, "Unsupported shader language"); ERROR_LOG(G3D, "Unsupported shader language");
@ -1251,7 +1153,8 @@ void D3D11DrawContext::ApplyCurrentState() {
} }
if (curPipeline_->input != nullptr) { if (curPipeline_->input != nullptr) {
context_->IASetVertexBuffers(0, 1, &nextVertexBuffer_, &curPipeline_->input->stride, &nextVertexBufferOffset_); int numVBs = (int)curPipeline_->input->strides.size();
context_->IASetVertexBuffers(0, numVBs, nextVertexBuffers_, (UINT *)curPipeline_->input->strides.data(), (UINT *)nextVertexBufferOffsets_);
} }
if (dirtyIndexBuffer_) { if (dirtyIndexBuffer_) {
context_->IASetIndexBuffer(nextIndexBuffer_, DXGI_FORMAT_R16_UINT, nextIndexBufferOffset_); context_->IASetIndexBuffer(nextIndexBuffer_, DXGI_FORMAT_R16_UINT, nextIndexBufferOffset_);
@ -1320,11 +1223,14 @@ void D3D11DrawContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t
context_->UpdateSubresource(buf->buf, 0, &box, data, 0, 0); context_->UpdateSubresource(buf->buf, 0, &box, data, 0, 0);
} }
void D3D11DrawContext::BindVertexBuffer(Buffer *buffer, int offset) { void D3D11DrawContext::BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) {
_assert_(start + count <= ARRAY_SIZE(nextVertexBuffers_));
// Lazy application // Lazy application
D3D11Buffer *buf = (D3D11Buffer *)buffer; for (int i = 0; i < count; i++) {
nextVertexBuffer_ = buf->buf; D3D11Buffer *buf = (D3D11Buffer *)buffers[i];
nextVertexBufferOffset_ = offset; nextVertexBuffers_[start + i] = buf->buf;
nextVertexBufferOffsets_[start + i] = offsets ? offsets[i] : 0;
}
} }
void D3D11DrawContext::BindIndexBuffer(Buffer *indexBuffer, int offset) { void D3D11DrawContext::BindIndexBuffer(Buffer *indexBuffer, int offset) {
@ -1348,10 +1254,10 @@ void D3D11DrawContext::DrawIndexed(int indexCount, int offset) {
void D3D11DrawContext::DrawUP(const void *vdata, int vertexCount) { void D3D11DrawContext::DrawUP(const void *vdata, int vertexCount) {
ApplyCurrentState(); ApplyCurrentState();
int byteSize = vertexCount * curPipeline_->input->stride; int byteSize = vertexCount * curPipeline_->input->strides[0];
UpdateBuffer(upBuffer_, (const uint8_t *)vdata, 0, byteSize, Draw::UPDATE_DISCARD); UpdateBuffer(upBuffer_, (const uint8_t *)vdata, 0, byteSize, Draw::UPDATE_DISCARD);
BindVertexBuffer(upBuffer_, 0); BindVertexBuffers(0, 1, &upBuffer_, nullptr);
int offset = 0; int offset = 0;
Draw(vertexCount, offset); Draw(vertexCount, offset);
} }
@ -1419,7 +1325,17 @@ Framebuffer *D3D11DrawContext::CreateFramebuffer(const FramebufferDesc &desc) {
// Texture arrays are supported but we don't have any other use cases yet. // Texture arrays are supported but we don't have any other use cases yet.
_dbg_assert_(desc.numLayers == 1); _dbg_assert_(desc.numLayers == 1);
fb->colorFormat = DXGI_FORMAT_R8G8B8A8_UNORM; DXGI_FORMAT colorFormat;
switch (desc.colorFormat) {
case DataFormat::R8G8B8A8_UNORM:
colorFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
break;
default:
_assert_msg_(false, "Framebuffer format not supported");
return nullptr;
}
fb->colorFormat = colorFormat;
D3D11_TEXTURE2D_DESC descColor{}; D3D11_TEXTURE2D_DESC descColor{};
descColor.Width = desc.width; descColor.Width = desc.width;
descColor.Height = desc.height; descColor.Height = desc.height;
@ -1498,7 +1414,7 @@ void D3D11DrawContext::BindTextures(int start, int count, Texture **textures, Te
_assert_(start + count <= ARRAY_SIZE(views)); _assert_(start + count <= ARRAY_SIZE(views));
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
D3D11Texture *tex = (D3D11Texture *)textures[i]; D3D11Texture *tex = (D3D11Texture *)textures[i];
views[i] = tex ? tex->View() : nullptr; views[i] = tex ? tex->view : nullptr;
} }
context_->PSSetShaderResources(start, count, views); context_->PSSetShaderResources(start, count, views);
} }
@ -1535,11 +1451,7 @@ void D3D11DrawContext::Clear(int mask, uint32_t colorval, float depthVal, int st
} }
} }
void D3D11DrawContext::BeginFrame(DebugFlags debugFlags) { void D3D11DrawContext::BeginFrame() {
FrameTimeData &frameTimeData = frameTimeHistory_.Add(frameCount_);
frameTimeData.afterFenceWait = time_now_d();
frameTimeData.frameBegin = frameTimeData.afterFenceWait;
context_->OMSetRenderTargets(1, &curRenderTargetView_, curDepthStencilView_); context_->OMSetRenderTargets(1, &curRenderTargetView_, curDepthStencilView_);
if (curBlend_ != nullptr) { if (curBlend_ != nullptr) {
@ -1559,7 +1471,7 @@ void D3D11DrawContext::BeginFrame(DebugFlags debugFlags) {
context_->IASetPrimitiveTopology(curTopology_); context_->IASetPrimitiveTopology(curTopology_);
} }
if (curPipeline_ != nullptr) { if (curPipeline_ != nullptr) {
context_->IASetVertexBuffers(0, 1, &nextVertexBuffer_, &curPipeline_->input->stride, &nextVertexBufferOffset_); context_->IASetVertexBuffers(0, 1, nextVertexBuffers_, (UINT *)curPipeline_->input->strides.data(), (UINT *)nextVertexBufferOffsets_);
context_->IASetIndexBuffer(nextIndexBuffer_, DXGI_FORMAT_R16_UINT, nextIndexBufferOffset_); context_->IASetIndexBuffer(nextIndexBuffer_, DXGI_FORMAT_R16_UINT, nextIndexBufferOffset_);
if (curPipeline_->dynamicUniforms) { if (curPipeline_->dynamicUniforms) {
context_->VSSetConstantBuffers(0, 1, &curPipeline_->dynamicUniforms); context_->VSSetConstantBuffers(0, 1, &curPipeline_->dynamicUniforms);
@ -1623,7 +1535,7 @@ bool D3D11DrawContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1,
return false; return false;
} }
bool D3D11DrawContext::CopyFramebufferToMemory(Framebuffer *src, int channelBits, int bx, int by, int bw, int bh, Draw::DataFormat destFormat, void *pixels, int pixelStride, ReadbackMode mode, const char *tag) { bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int bx, int by, int bw, int bh, Draw::DataFormat destFormat, void *pixels, int pixelStride, const char *tag) {
D3D11Framebuffer *fb = (D3D11Framebuffer *)src; D3D11Framebuffer *fb = (D3D11Framebuffer *)src;
if (fb) { if (fb) {
@ -1862,7 +1774,7 @@ uint64_t D3D11DrawContext::GetNativeObject(NativeObject obj, void *srcObject) {
case NativeObject::FEATURE_LEVEL: case NativeObject::FEATURE_LEVEL:
return (uint64_t)(uintptr_t)featureLevel_; return (uint64_t)(uintptr_t)featureLevel_;
case NativeObject::TEXTURE_VIEW: case NativeObject::TEXTURE_VIEW:
return (uint64_t)(((D3D11Texture *)srcObject)->View()); return (uint64_t)(((D3D11Texture *)srcObject)->view);
default: default:
return 0; return 0;
} }
@ -1879,8 +1791,8 @@ void D3D11DrawContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h
} }
} }
DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context, ID3D11Device1 *device1, ID3D11DeviceContext1 *context1, IDXGISwapChain *swapChain, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector<std::string> adapterNames, int maxInflightFrames) { DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context, ID3D11Device1 *device1, ID3D11DeviceContext1 *context1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector<std::string> adapterNames) {
return new D3D11DrawContext(device, context, device1, context1, swapChain, featureLevel, hWnd, adapterNames, maxInflightFrames); return new D3D11DrawContext(device, context, device1, context1, featureLevel, hWnd, adapterNames);
} }
} // namespace Draw } // namespace Draw

View file

@ -25,7 +25,6 @@
#include "Common/GPU/D3D9/D3D9StateCache.h" #include "Common/GPU/D3D9/D3D9StateCache.h"
#include "Common/OSVersion.h" #include "Common/OSVersion.h"
#include "Common/StringUtils.h" #include "Common/StringUtils.h"
#include "Common/TimeUtil.h"
#include "Common/Log.h" #include "Common/Log.h"
@ -114,7 +113,7 @@ static const D3DSTENCILOP stencilOpToD3D9[] = {
D3DSTENCILOP_DECR, D3DSTENCILOP_DECR,
}; };
static D3DFORMAT FormatToD3DFMT(DataFormat fmt) { D3DFORMAT FormatToD3DFMT(DataFormat fmt) {
switch (fmt) { switch (fmt) {
case DataFormat::R16_UNORM: return D3DFMT_L16; // closest match, should be a fine substitution if we ignore channels except R. case DataFormat::R16_UNORM: return D3DFMT_L16; // closest match, should be a fine substitution if we ignore channels except R.
case DataFormat::R8G8B8A8_UNORM: return D3DFMT_A8R8G8B8; case DataFormat::R8G8B8A8_UNORM: return D3DFMT_A8R8G8B8;
@ -126,9 +125,6 @@ static D3DFORMAT FormatToD3DFMT(DataFormat fmt) {
case DataFormat::A1R5G5B5_UNORM_PACK16: return D3DFMT_A1R5G5B5; case DataFormat::A1R5G5B5_UNORM_PACK16: return D3DFMT_A1R5G5B5;
case DataFormat::D24_S8: return D3DFMT_D24S8; case DataFormat::D24_S8: return D3DFMT_D24S8;
case DataFormat::D16: return D3DFMT_D16; case DataFormat::D16: return D3DFMT_D16;
case DataFormat::BC1_RGBA_UNORM_BLOCK: return D3DFMT_DXT1;
case DataFormat::BC2_UNORM_BLOCK: return D3DFMT_DXT3; // DXT3 is indeed BC2.
case DataFormat::BC3_UNORM_BLOCK: return D3DFMT_DXT5; // DXT5 is indeed BC3
default: return D3DFMT_UNKNOWN; default: return D3DFMT_UNKNOWN;
} }
} }
@ -231,14 +227,14 @@ public:
decl_->Release(); decl_->Release();
} }
} }
int GetStride() const { return stride_; } int GetStride(int binding) const { return stride_[binding]; }
void Apply(LPDIRECT3DDEVICE9 device) { void Apply(LPDIRECT3DDEVICE9 device) {
device->SetVertexDeclaration(decl_); device->SetVertexDeclaration(decl_);
} }
private: private:
LPDIRECT3DVERTEXDECLARATION9 decl_; LPDIRECT3DVERTEXDECLARATION9 decl_;
int stride_; int stride_[4];
}; };
class D3D9ShaderModule : public ShaderModule { class D3D9ShaderModule : public ShaderModule {
@ -309,14 +305,14 @@ public:
return nullptr; return nullptr;
} }
} }
void UpdateTextureLevels(const uint8_t * const *data, int numLevels, TextureCallback initDataCallback);
private: private:
void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback initDataCallback); void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback);
bool Create(const TextureDesc &desc); bool Create(const TextureDesc &desc);
LPDIRECT3DDEVICE9 device_; LPDIRECT3DDEVICE9 device_;
LPDIRECT3DDEVICE9EX deviceEx_; LPDIRECT3DDEVICE9EX deviceEx_;
TextureType type_; TextureType type_;
DataFormat format_;
D3DFORMAT d3dfmt_; D3DFORMAT d3dfmt_;
LPDIRECT3DTEXTURE9 tex_ = nullptr; LPDIRECT3DTEXTURE9 tex_ = nullptr;
LPDIRECT3DVOLUMETEXTURE9 volTex_ = nullptr; LPDIRECT3DVOLUMETEXTURE9 volTex_ = nullptr;
@ -375,31 +371,27 @@ bool D3D9Texture::Create(const TextureDesc &desc) {
break; break;
} }
if (FAILED(hr)) { if (FAILED(hr)) {
ERROR_LOG(G3D, "D3D9 Texture creation failed"); ERROR_LOG(G3D, "Texture creation failed");
return false; return false;
} }
if (desc.initData.size()) { if (desc.initData.size()) {
// In D3D9, after setting D3DUSAGE_AUTOGENMIPS, we can only access the top layer. The rest will be // In D3D9, after setting D3DUSAGE_AUTOGENMIPS, we can only access the top layer. The rest will be
// automatically generated. // automatically generated.
int numLevels = desc.generateMips ? 1 : (int)desc.initData.size(); int maxLevel = desc.generateMips ? 1 : (int)desc.initData.size();
UpdateTextureLevels(desc.initData.data(), numLevels, desc.initDataCallback); int w = desc.width;
int h = desc.height;
int d = desc.depth;
for (int i = 0; i < maxLevel; i++) {
SetImageData(0, 0, 0, w, h, d, i, 0, desc.initData[i], desc.initDataCallback);
w = (w + 1) / 2;
h = (h + 1) / 2;
d = (d + 1) / 2;
}
} }
return true; return true;
} }
void D3D9Texture::UpdateTextureLevels(const uint8_t * const *data, int numLevels, TextureCallback initDataCallback) {
int w = width_;
int h = height_;
int d = depth_;
for (int i = 0; i < numLevels; i++) {
SetImageData(0, 0, 0, w, h, d, i, 0, data[i], initDataCallback);
w = (w + 1) / 2;
h = (h + 1) / 2;
d = (d + 1) / 2;
}
}
// Just switches R and G. // Just switches R and G.
inline uint32_t Shuffle8888(uint32_t x) { inline uint32_t Shuffle8888(uint32_t x) {
return (x & 0xFF00FF00) | ((x >> 16) & 0xFF) | ((x << 16) & 0xFF0000); return (x & 0xFF00FF00) | ((x >> 16) & 0xFF) | ((x << 16) & 0xFF0000);
@ -533,13 +525,12 @@ public:
Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override; Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override;
void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override; void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override;
void UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) override;
void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override { void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override {
// Not implemented // Not implemented
} }
bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override;
bool CopyFramebufferToMemory(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, ReadbackMode mode, const char *tag) override; bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, const char *tag) override;
// These functions should be self explanatory. // These functions should be self explanatory.
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override; void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
@ -560,9 +551,12 @@ public:
s->Apply(device_, start + i); s->Apply(device_, start + i);
} }
} }
void BindVertexBuffer(Buffer *vertexBuffer, int offset) override { void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) override {
curVBuffer_ = (D3D9Buffer *)vertexBuffer; _assert_(start + count <= ARRAY_SIZE(curVBuffers_));
curVBufferOffset_ = offset; for (int i = 0; i < count; i++) {
curVBuffers_[i + start] = (D3D9Buffer *)buffers[i];
curVBufferOffsets_[i + start] = offsets ? offsets[i] : 0;
}
} }
void BindIndexBuffer(Buffer *indexBuffer, int offset) override { void BindIndexBuffer(Buffer *indexBuffer, int offset) override {
curIBuffer_ = (D3D9Buffer *)indexBuffer; curIBuffer_ = (D3D9Buffer *)indexBuffer;
@ -573,17 +567,13 @@ public:
curPipeline_ = (D3D9Pipeline *)pipeline; curPipeline_ = (D3D9Pipeline *)pipeline;
} }
void BeginFrame(Draw::DebugFlags debugFlags) override;
void EndFrame() override; void EndFrame() override;
void Present(PresentMode presentMode, int vblanks) override;
int GetFrameCount() override { return frameCount_; }
void UpdateDynamicUniformBuffer(const void *ub, size_t size) override; void UpdateDynamicUniformBuffer(const void *ub, size_t size) override;
// Raster state // Raster state
void SetScissorRect(int left, int top, int width, int height) override; void SetScissorRect(int left, int top, int width, int height) override;
void SetViewport(const Viewport &viewport) override; void SetViewports(int count, Viewport *viewports) override;
void SetBlendFactor(float color[4]) override; void SetBlendFactor(float color[4]) override;
void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override; void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override;
@ -638,12 +628,11 @@ private:
D3DCAPS9 d3dCaps_; D3DCAPS9 d3dCaps_;
char shadeLangVersion_[64]{}; char shadeLangVersion_[64]{};
DeviceCaps caps_{}; DeviceCaps caps_{};
int frameCount_ = FRAME_TIME_HISTORY_LENGTH;
// Bound state // Bound state
AutoRef<D3D9Pipeline> curPipeline_; AutoRef<D3D9Pipeline> curPipeline_;
AutoRef<D3D9Buffer> curVBuffer_; AutoRef<D3D9Buffer> curVBuffers_[4];
int curVBufferOffset_ = 0; int curVBufferOffsets_[4]{};
AutoRef<D3D9Buffer> curIBuffer_; AutoRef<D3D9Buffer> curIBuffer_;
int curIBufferOffset_ = 0; int curIBufferOffset_ = 0;
AutoRef<Framebuffer> curRenderTarget_; AutoRef<Framebuffer> curRenderTarget_;
@ -755,10 +744,10 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID
} }
if (SUCCEEDED(result)) { if (SUCCEEDED(result)) {
snprintf(shadeLangVersion_, sizeof(shadeLangVersion_), "PS: %04x VS: %04x", d3dCaps_.PixelShaderVersion & 0xFFFF, d3dCaps_.VertexShaderVersion & 0xFFFF); sprintf(shadeLangVersion_, "PS: %04x VS: %04x", d3dCaps_.PixelShaderVersion & 0xFFFF, d3dCaps_.VertexShaderVersion & 0xFFFF);
} else { } else {
WARN_LOG(G3D, "Direct3D9: Failed to get the device caps!"); WARN_LOG(G3D, "Direct3D9: Failed to get the device caps!");
truncate_cpy(shadeLangVersion_, "N/A"); strcpy(shadeLangVersion_, "N/A");
} }
caps_.deviceID = identifier_.DeviceId; caps_.deviceID = identifier_.DeviceId;
@ -780,9 +769,6 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID
caps_.multiSampleLevelsMask = 1; // More could be supported with some work. caps_.multiSampleLevelsMask = 1; // More could be supported with some work.
caps_.clipPlanesSupported = caps.MaxUserClipPlanes; caps_.clipPlanesSupported = caps.MaxUserClipPlanes;
caps_.presentInstantModeChange = false;
caps_.presentMaxInterval = 1;
caps_.presentModesSupported = PresentMode::FIFO;
if ((caps.RasterCaps & D3DPRASTERCAPS_ANISOTROPY) != 0 && caps.MaxAnisotropy > 1) { if ((caps.RasterCaps & D3DPRASTERCAPS_ANISOTROPY) != 0 && caps.MaxAnisotropy > 1) {
caps_.anisoSupported = true; caps_.anisoSupported = true;
@ -941,12 +927,6 @@ Texture *D3D9Context::CreateTexture(const TextureDesc &desc) {
return tex; return tex;
} }
void D3D9Context::UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) {
D3D9Texture *tex = (D3D9Texture *)texture;
tex->UpdateTextureLevels(data, numLevels, initDataCallback);
}
void D3D9Context::BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) { void D3D9Context::BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) {
_assert_(start + count <= MAX_BOUND_TEXTURES); _assert_(start + count <= MAX_BOUND_TEXTURES);
for (int i = start; i < start + count; i++) { for (int i = start; i < start + count; i++) {
@ -964,31 +944,10 @@ void D3D9Context::BindNativeTexture(int index, void *nativeTexture) {
device_->SetTexture(index, texture); device_->SetTexture(index, texture);
} }
void D3D9Context::BeginFrame(Draw::DebugFlags debugFlags) {
FrameTimeData frameTimeData = frameTimeHistory_.Add(frameCount_);
frameTimeData.frameBegin = time_now_d();
frameTimeData.afterFenceWait = frameTimeData.frameBegin; // no fence wait
}
void D3D9Context::EndFrame() { void D3D9Context::EndFrame() {
frameTimeHistory_[frameCount_].firstSubmit = time_now_d();
curPipeline_ = nullptr; curPipeline_ = nullptr;
} }
void D3D9Context::Present(PresentMode presentMode, int vblanks) {
frameTimeHistory_[frameCount_].queuePresent = time_now_d();
if (deviceEx_) {
deviceEx_->EndScene();
deviceEx_->PresentEx(NULL, NULL, NULL, NULL, 0);
deviceEx_->BeginScene();
} else {
device_->EndScene();
device_->Present(NULL, NULL, NULL, NULL);
device_->BeginScene();
}
frameCount_++;
}
static void SemanticToD3D9UsageAndIndex(int semantic, BYTE *usage, BYTE *index) { static void SemanticToD3D9UsageAndIndex(int semantic, BYTE *usage, BYTE *index) {
*index = 0; *index = 0;
switch (semantic) { switch (semantic) {
@ -1025,7 +984,7 @@ D3D9InputLayout::D3D9InputLayout(LPDIRECT3DDEVICE9 device, const InputLayoutDesc
D3DVERTEXELEMENT9 *elements = new D3DVERTEXELEMENT9[desc.attributes.size() + 1]; D3DVERTEXELEMENT9 *elements = new D3DVERTEXELEMENT9[desc.attributes.size() + 1];
size_t i; size_t i;
for (i = 0; i < desc.attributes.size(); i++) { for (i = 0; i < desc.attributes.size(); i++) {
elements[i].Stream = 0; elements[i].Stream = desc.attributes[i].binding;
elements[i].Offset = desc.attributes[i].offset; elements[i].Offset = desc.attributes[i].offset;
elements[i].Method = D3DDECLMETHOD_DEFAULT; elements[i].Method = D3DDECLMETHOD_DEFAULT;
SemanticToD3D9UsageAndIndex(desc.attributes[i].location, &elements[i].Usage, &elements[i].UsageIndex); SemanticToD3D9UsageAndIndex(desc.attributes[i].location, &elements[i].Usage, &elements[i].UsageIndex);
@ -1035,7 +994,9 @@ D3D9InputLayout::D3D9InputLayout(LPDIRECT3DDEVICE9 device, const InputLayoutDesc
// Zero the last one. // Zero the last one.
memcpy(&elements[i], &end, sizeof(elements[i])); memcpy(&elements[i], &end, sizeof(elements[i]));
stride_ = desc.stride; for (i = 0; i < desc.bindings.size(); i++) {
stride_[i] = desc.bindings[i].stride;
}
HRESULT hr = device->CreateVertexDeclaration(elements, &decl_); HRESULT hr = device->CreateVertexDeclaration(elements, &decl_);
if (FAILED(hr)) { if (FAILED(hr)) {
@ -1169,7 +1130,7 @@ inline int D3DPrimCount(D3DPRIMITIVETYPE prim, int size) {
} }
void D3D9Context::Draw(int vertexCount, int offset) { void D3D9Context::Draw(int vertexCount, int offset) {
device_->SetStreamSource(0, curVBuffer_->vbuffer_, curVBufferOffset_, curPipeline_->inputLayout->GetStride()); device_->SetStreamSource(0, curVBuffers_[0]->vbuffer_, curVBufferOffsets_[0], curPipeline_->inputLayout->GetStride(0));
curPipeline_->inputLayout->Apply(device_); curPipeline_->inputLayout->Apply(device_);
curPipeline_->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_); curPipeline_->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_);
ApplyDynamicState(); ApplyDynamicState();
@ -1180,7 +1141,7 @@ void D3D9Context::DrawIndexed(int vertexCount, int offset) {
curPipeline_->inputLayout->Apply(device_); curPipeline_->inputLayout->Apply(device_);
curPipeline_->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_); curPipeline_->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_);
ApplyDynamicState(); ApplyDynamicState();
device_->SetStreamSource(0, curVBuffer_->vbuffer_, curVBufferOffset_, curPipeline_->inputLayout->GetStride()); device_->SetStreamSource(0, curVBuffers_[0]->vbuffer_, curVBufferOffsets_[0], curPipeline_->inputLayout->GetStride(0));
device_->SetIndices(curIBuffer_->ibuffer_); device_->SetIndices(curIBuffer_->ibuffer_);
device_->DrawIndexedPrimitive(curPipeline_->prim, 0, 0, vertexCount, offset, D3DPrimCount(curPipeline_->prim, vertexCount)); device_->DrawIndexedPrimitive(curPipeline_->prim, 0, 0, vertexCount, offset, D3DPrimCount(curPipeline_->prim, vertexCount));
} }
@ -1190,7 +1151,7 @@ void D3D9Context::DrawUP(const void *vdata, int vertexCount) {
curPipeline_->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_); curPipeline_->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_);
ApplyDynamicState(); ApplyDynamicState();
device_->DrawPrimitiveUP(curPipeline_->prim, D3DPrimCount(curPipeline_->prim, vertexCount), vdata, curPipeline_->inputLayout->GetStride()); device_->DrawPrimitiveUP(curPipeline_->prim, D3DPrimCount(curPipeline_->prim, vertexCount), vdata, curPipeline_->inputLayout->GetStride(0));
} }
static uint32_t SwapRB(uint32_t c) { static uint32_t SwapRB(uint32_t c) {
@ -1212,12 +1173,12 @@ void D3D9Context::SetScissorRect(int left, int top, int width, int height) {
dxstate.scissorTest.set(true); dxstate.scissorTest.set(true);
} }
void D3D9Context::SetViewport(const Viewport &viewport) { void D3D9Context::SetViewports(int count, Viewport *viewports) {
int x = (int)viewport.TopLeftX; int x = (int)viewports[0].TopLeftX;
int y = (int)viewport.TopLeftY; int y = (int)viewports[0].TopLeftY;
int w = (int)viewport.Width; int w = (int)viewports[0].Width;
int h = (int)viewport.Height; int h = (int)viewports[0].Height;
dxstate.viewport.set(x, y, w, h, viewport.MinDepth, viewport.MaxDepth); dxstate.viewport.set(x, y, w, h, viewports[0].MinDepth, viewports[0].MaxDepth);
} }
void D3D9Context::SetBlendFactor(float color[4]) { void D3D9Context::SetBlendFactor(float color[4]) {
@ -1303,7 +1264,18 @@ Framebuffer *D3D9Context::CreateFramebuffer(const FramebufferDesc &desc) {
D3D9Framebuffer *fbo = new D3D9Framebuffer(desc.width, desc.height); D3D9Framebuffer *fbo = new D3D9Framebuffer(desc.width, desc.height);
fbo->depthstenciltex = nullptr; fbo->depthstenciltex = nullptr;
HRESULT rtResult = device_->CreateTexture(desc.width, desc.height, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &fbo->tex, nullptr); D3DFORMAT colorFormat;
switch (desc.colorFormat) {
case DataFormat::R8G8B8A8_UNORM:
// We pretend to support this, although in reality we use the reverse format.
colorFormat = D3DFMT_A8R8G8B8;
break;
default:
_assert_msg_(false, "Framebuffer format not supported");
return nullptr;
}
HRESULT rtResult = device_->CreateTexture(desc.width, desc.height, 1, D3DUSAGE_RENDERTARGET, colorFormat, D3DPOOL_DEFAULT, &fbo->tex, nullptr);
if (FAILED(rtResult)) { if (FAILED(rtResult)) {
ERROR_LOG(G3D, "Failed to create render target"); ERROR_LOG(G3D, "Failed to create render target");
fbo->Release(); fbo->Release();
@ -1465,7 +1437,7 @@ bool D3D9Context::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int
return SUCCEEDED(device_->StretchRect(srcSurf, &srcRect, dstSurf, &dstRect, (filter == FB_BLIT_LINEAR && channelBits == FB_COLOR_BIT) ? D3DTEXF_LINEAR : D3DTEXF_POINT)); return SUCCEEDED(device_->StretchRect(srcSurf, &srcRect, dstSurf, &dstRect, (filter == FB_BLIT_LINEAR && channelBits == FB_COLOR_BIT) ? D3DTEXF_LINEAR : D3DTEXF_POINT));
} }
bool D3D9Context::CopyFramebufferToMemory(Framebuffer *src, int channelBits, int bx, int by, int bw, int bh, Draw::DataFormat destFormat, void *pixels, int pixelStride, ReadbackMode mode, const char *tag) { bool D3D9Context::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int bx, int by, int bw, int bh, Draw::DataFormat destFormat, void *pixels, int pixelStride, const char *tag) {
D3D9Framebuffer *fb = (D3D9Framebuffer *)src; D3D9Framebuffer *fb = (D3D9Framebuffer *)src;
if (fb) { if (fb) {
@ -1628,7 +1600,6 @@ uint32_t D3D9Context::GetDataFormatSupport(DataFormat fmt) const {
case DataFormat::BC1_RGBA_UNORM_BLOCK: case DataFormat::BC1_RGBA_UNORM_BLOCK:
case DataFormat::BC2_UNORM_BLOCK: case DataFormat::BC2_UNORM_BLOCK:
case DataFormat::BC3_UNORM_BLOCK: case DataFormat::BC3_UNORM_BLOCK:
// DXT1, DXT3, DXT5.
return FMT_TEXTURE; return FMT_TEXTURE;
default: default:
return 0; return 0;

View file

@ -46,20 +46,22 @@ enum class DataFormat : uint8_t {
// Block compression formats. // Block compression formats.
// These are modern names for DXT and friends, now patent free. // These are modern names for DXT and friends, now patent free.
// https://msdn.microsoft.com/en-us/library/bb694531.aspx // https://msdn.microsoft.com/en-us/library/bb694531.aspx
BC1_RGBA_UNORM_BLOCK, // 64 bits per 4x4 block. Used by Basis, along with ETC2_R8G8B8_UNORM_BLOCK. BC1_RGBA_UNORM_BLOCK,
BC2_UNORM_BLOCK, // 4-bit straight alpha + DXT1 color. 128 bits per block. Usually not worth using BC1_RGBA_SRGB_BLOCK,
BC3_UNORM_BLOCK, // 3-bit alpha with 2 ref values (+ magic) + DXT1 color. 128 bits per block. BC2_UNORM_BLOCK, // 4-bit straight alpha + DXT1 color. Usually not worth using
BC4_UNORM_BLOCK, // 1-channel, same storage as BC3 alpha. 64 bits per block. BC2_SRGB_BLOCK,
BC5_UNORM_BLOCK, // 2-channel RG, each has same storage as BC3 alpha. 128 bits per block. BC3_UNORM_BLOCK, // 3-bit alpha with 2 ref values (+ magic) + DXT1 color
BC7_UNORM_BLOCK, // Highly advanced RGBA, very expensive to compress, very good quality. 128 bits per block. BC3_SRGB_BLOCK,
BC4_UNORM_BLOCK, // 1-channel, same storage as BC3 alpha
BC4_SNORM_BLOCK,
BC5_UNORM_BLOCK, // 2-channel RG, each has same storage as BC3 alpha
BC5_SNORM_BLOCK,
BC6H_UFLOAT_BLOCK, // TODO
BC6H_SFLOAT_BLOCK,
BC7_UNORM_BLOCK, // Highly advanced, very expensive to compress, very good quality.
BC7_SRGB_BLOCK,
// Ericsson texture compression. ETC1,
ETC2_R8G8B8_UNORM_BLOCK, // Color-only, 64 bits per 4x4 block.
ETC2_R8G8B8A1_UNORM_BLOCK, // Color + alpha, 128 bits per 4x4 block.
ETC2_R8G8B8A8_UNORM_BLOCK, // Color + alpha, 128 bits per 4x4 block.
// This is the one ASTC format used by UASTC / basis Universal.
ASTC_4x4_UNORM_BLOCK,
S8, S8,
D16, D16,
@ -74,7 +76,6 @@ bool DataFormatIsDepthStencil(DataFormat fmt);
inline bool DataFormatIsColor(DataFormat fmt) { inline bool DataFormatIsColor(DataFormat fmt) {
return !DataFormatIsDepthStencil(fmt); return !DataFormatIsDepthStencil(fmt);
} }
bool DataFormatIsBlockCompressed(DataFormat fmt, int *blockSize);
// Limited format support for now. // Limited format support for now.
const char *DataFormatToString(DataFormat fmt); const char *DataFormatToString(DataFormat fmt);

View file

@ -1,28 +0,0 @@
#include <mutex>
#include <set>
#include "Common/GPU/GPUBackendCommon.h"
// Global push buffer tracker for GPU memory profiling.
// Don't want to manually dig up all the active push buffers.
static std::mutex g_pushBufferListMutex;
static std::set<GPUMemoryManager *> g_pushBuffers;
std::vector<GPUMemoryManager *> GetActiveGPUMemoryManagers() {
std::vector<GPUMemoryManager *> buffers;
std::lock_guard<std::mutex> guard(g_pushBufferListMutex);
for (auto iter : g_pushBuffers) {
buffers.push_back(iter);
}
return buffers;
}
void RegisterGPUMemoryManager(GPUMemoryManager *manager) {
std::lock_guard<std::mutex> guard(g_pushBufferListMutex);
g_pushBuffers.insert(manager);
}
void UnregisterGPUMemoryManager(GPUMemoryManager *manager) {
std::lock_guard<std::mutex> guard(g_pushBufferListMutex);
g_pushBuffers.erase(manager);
}

View file

@ -1,17 +0,0 @@
#pragma once
#include <vector>
// Just an abstract thing to get debug information.
class GPUMemoryManager {
public:
virtual ~GPUMemoryManager() {}
virtual void GetDebugString(char *buffer, size_t bufSize) const = 0;
virtual const char *Name() const = 0; // for sorting
};
std::vector<GPUMemoryManager *> GetActiveGPUMemoryManagers();
void RegisterGPUMemoryManager(GPUMemoryManager *manager);
void UnregisterGPUMemoryManager(GPUMemoryManager *manager);

View file

@ -2,8 +2,6 @@
#include "Common/Common.h" #include "Common/Common.h"
// Flags and structs shared between backends that haven't found a good home.
enum class InvalidationFlags { enum class InvalidationFlags {
CACHED_RENDER_STATE = 1, CACHED_RENDER_STATE = 1,
}; };
@ -16,22 +14,3 @@ enum class InvalidationCallbackFlags {
ENUM_CLASS_BITOPS(InvalidationCallbackFlags); ENUM_CLASS_BITOPS(InvalidationCallbackFlags);
typedef std::function<void(InvalidationCallbackFlags)> InvalidationCallback; typedef std::function<void(InvalidationCallbackFlags)> InvalidationCallback;
// These are separate from FrameData because we store some history of these.
// Also, this might be joined with more non-GPU timing information later.
struct FrameTimeData {
uint64_t frameId;
int waitCount;
double frameBegin;
double afterFenceWait;
double firstSubmit;
double queuePresent;
double actualPresent;
double desiredPresentTime;
double earliestPresentTime;
double presentMargin;
};
constexpr size_t FRAME_TIME_HISTORY_LENGTH = 32;

View file

@ -55,7 +55,7 @@ bool Thin3DFormatToGLFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuin
internalFormat = GL_RGB; internalFormat = GL_RGB;
format = GL_RGB; format = GL_RGB;
type = GL_UNSIGNED_BYTE; type = GL_UNSIGNED_BYTE;
alignment = 3; alignment = 1;
break; break;
case DataFormat::R4G4B4A4_UNORM_PACK16: case DataFormat::R4G4B4A4_UNORM_PACK16:
@ -86,75 +86,6 @@ bool Thin3DFormatToGLFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuin
alignment = 16; alignment = 16;
break; break;
#ifndef USING_GLES2
case DataFormat::BC1_RGBA_UNORM_BLOCK:
internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
format = GL_RGB;
type = GL_FLOAT;
alignment = 8;
break;
case DataFormat::BC2_UNORM_BLOCK:
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
format = GL_RGBA;
type = GL_FLOAT;
alignment = 16;
break;
case DataFormat::BC3_UNORM_BLOCK:
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
format = GL_RGBA;
type = GL_FLOAT;
alignment = 16;
break;
case DataFormat::BC4_UNORM_BLOCK:
internalFormat = GL_COMPRESSED_RED_RGTC1;
format = GL_R;
type = GL_FLOAT;
alignment = 16;
break;
case DataFormat::BC5_UNORM_BLOCK:
internalFormat = GL_COMPRESSED_RG_RGTC2;
format = GL_RG;
type = GL_FLOAT;
alignment = 16;
break;
case DataFormat::BC7_UNORM_BLOCK:
internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM;
format = GL_RGBA;
type = GL_FLOAT;
alignment = 16;
break;
#endif
case DataFormat::ETC2_R8G8B8_UNORM_BLOCK:
internalFormat = GL_COMPRESSED_RGB8_ETC2;
format = GL_RGB;
type = GL_FLOAT;
alignment = 8;
break;
case DataFormat::ETC2_R8G8B8A1_UNORM_BLOCK:
internalFormat = GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
format = GL_RGBA;
type = GL_FLOAT;
alignment = 16;
break;
case DataFormat::ETC2_R8G8B8A8_UNORM_BLOCK:
internalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC;
format = GL_RGBA;
type = GL_FLOAT;
alignment = 16;
break;
#ifdef GL_COMPRESSED_RGBA_ASTC_4x4_KHR
case DataFormat::ASTC_4x4_UNORM_BLOCK:
internalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
format = GL_RGBA;
type = GL_FLOAT;
alignment = 16;
break;
#endif
default: default:
return false; return false;
} }

View file

@ -182,9 +182,7 @@ bool CheckGLExtensions() {
gl_extensions.gpuVendor = GPU_VENDOR_IMGTEC; gl_extensions.gpuVendor = GPU_VENDOR_IMGTEC;
} else if (vendor == "Qualcomm") { } else if (vendor == "Qualcomm") {
gl_extensions.gpuVendor = GPU_VENDOR_QUALCOMM; gl_extensions.gpuVendor = GPU_VENDOR_QUALCOMM;
if (1 != sscanf(renderer, "Adreno (TM) %d", &gl_extensions.modelNumber)) { sscanf(renderer, "Adreno (TM) %d", &gl_extensions.modelNumber);
gl_extensions.modelNumber = 300; // or what should we default to?
}
} else if (vendor == "Broadcom") { } else if (vendor == "Broadcom") {
gl_extensions.gpuVendor = GPU_VENDOR_BROADCOM; gl_extensions.gpuVendor = GPU_VENDOR_BROADCOM;
// Just for reference: Galaxy Y has renderer == "VideoCore IV HW" // Just for reference: Galaxy Y has renderer == "VideoCore IV HW"
@ -380,12 +378,6 @@ bool CheckGLExtensions() {
gl_extensions.ARB_explicit_attrib_location = g_set_gl_extensions.count("GL_ARB_explicit_attrib_location") != 0; gl_extensions.ARB_explicit_attrib_location = g_set_gl_extensions.count("GL_ARB_explicit_attrib_location") != 0;
gl_extensions.ARB_texture_non_power_of_two = g_set_gl_extensions.count("GL_ARB_texture_non_power_of_two") != 0; gl_extensions.ARB_texture_non_power_of_two = g_set_gl_extensions.count("GL_ARB_texture_non_power_of_two") != 0;
gl_extensions.ARB_shader_stencil_export = g_set_gl_extensions.count("GL_ARB_shader_stencil_export") != 0; gl_extensions.ARB_shader_stencil_export = g_set_gl_extensions.count("GL_ARB_shader_stencil_export") != 0;
gl_extensions.ARB_texture_compression_bptc = g_set_gl_extensions.count("GL_ARB_texture_compression_bptc") != 0;
gl_extensions.ARB_texture_compression_rgtc = g_set_gl_extensions.count("GL_ARB_texture_compression_rgtc") != 0;
gl_extensions.KHR_texture_compression_astc_ldr = g_set_gl_extensions.count("GL_KHR_texture_compression_astc_ldr") != 0;
gl_extensions.EXT_texture_compression_s3tc = g_set_gl_extensions.count("GL_EXT_texture_compression_s3tc") != 0;
gl_extensions.OES_texture_compression_astc = g_set_gl_extensions.count("GL_OES_texture_compression_astc") != 0;
if (gl_extensions.IsGLES) { if (gl_extensions.IsGLES) {
gl_extensions.EXT_blend_func_extended = g_set_gl_extensions.count("GL_EXT_blend_func_extended") != 0; gl_extensions.EXT_blend_func_extended = g_set_gl_extensions.count("GL_EXT_blend_func_extended") != 0;
gl_extensions.OES_texture_npot = g_set_gl_extensions.count("GL_OES_texture_npot") != 0; gl_extensions.OES_texture_npot = g_set_gl_extensions.count("GL_OES_texture_npot") != 0;
@ -583,40 +575,6 @@ bool CheckGLExtensions() {
gl_extensions.EXT_clip_cull_distance = false; gl_extensions.EXT_clip_cull_distance = false;
} }
// Check the old query API. It doesn't seem to be very reliable (can miss stuff).
GLint numCompressedFormats = 0;
glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numCompressedFormats);
GLint *compressedFormats = new GLint[numCompressedFormats];
if (numCompressedFormats > 0) {
glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, compressedFormats);
for (int i = 0; i < numCompressedFormats; i++) {
switch (compressedFormats[i]) {
case GL_COMPRESSED_RGB8_ETC2: gl_extensions.supportsETC2 = true; break;
#ifdef GL_COMPRESSED_RGBA_ASTC_4x4_KHR
case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: gl_extensions.supportsASTC = true; break;
#endif
#ifndef USING_GLES2
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: gl_extensions.supportsBC123 = true; break;
case GL_COMPRESSED_RGBA_BPTC_UNORM: gl_extensions.supportsBC7 = true; break;
#endif
}
}
}
// Enable additional formats based on extensions.
if (gl_extensions.EXT_texture_compression_s3tc) gl_extensions.supportsBC123 = true;
if (gl_extensions.ARB_texture_compression_bptc) gl_extensions.supportsBC7 = true;
if (gl_extensions.ARB_texture_compression_rgtc) gl_extensions.supportsBC45 = true;
if (gl_extensions.KHR_texture_compression_astc_ldr) gl_extensions.supportsASTC = true;
if (gl_extensions.OES_texture_compression_astc) gl_extensions.supportsASTC = true;
// Now, disable known-emulated texture formats.
if (gl_extensions.gpuVendor == GPU_VENDOR_NVIDIA && !gl_extensions.IsGLES) {
gl_extensions.supportsETC2 = false;
gl_extensions.supportsASTC = false;
}
delete[] compressedFormats;
ProcessGPUFeatures(); ProcessGPUFeatures();
int error = glGetError(); int error = glGetError();
@ -628,13 +586,11 @@ bool CheckGLExtensions() {
} }
void SetGLCoreContext(bool flag) { void SetGLCoreContext(bool flag) {
if (!extensionsDone) { _assert_msg_(!extensionsDone, "SetGLCoreContext() after CheckGLExtensions()");
useCoreContext = flag;
// For convenience, it'll get reset later. useCoreContext = flag;
gl_extensions.IsCoreContext = useCoreContext; // For convenience, it'll get reset later.
} else { gl_extensions.IsCoreContext = useCoreContext;
_assert_(flag == useCoreContext);
}
} }
void ResetGLExtensions() { void ResetGLExtensions() {

View file

@ -5,7 +5,6 @@
#pragma once #pragma once
#include <string> #include <string>
#include <cstdint>
// TODO: Replace with thin3d's vendor enum. // TODO: Replace with thin3d's vendor enum.
enum { enum {
@ -18,7 +17,6 @@ enum {
GPU_VENDOR_BROADCOM = 7, // Raspberry PI etc GPU_VENDOR_BROADCOM = 7, // Raspberry PI etc
GPU_VENDOR_VIVANTE = 8, GPU_VENDOR_VIVANTE = 8,
GPU_VENDOR_APPLE = 9, GPU_VENDOR_APPLE = 9,
GPU_VENDOR_MESA = 10,
GPU_VENDOR_UNKNOWN = 0, GPU_VENDOR_UNKNOWN = 0,
}; };
@ -54,7 +52,6 @@ struct GLExtensions {
bool OES_copy_image; bool OES_copy_image;
bool OES_texture_float; bool OES_texture_float;
bool OES_texture_3D; bool OES_texture_3D;
bool OES_texture_compression_astc;
// ARB // ARB
bool ARB_framebuffer_object; bool ARB_framebuffer_object;
@ -76,14 +73,8 @@ struct GLExtensions {
bool ARB_texture_non_power_of_two; bool ARB_texture_non_power_of_two;
bool ARB_stencil_texturing; bool ARB_stencil_texturing;
bool ARB_shader_stencil_export; bool ARB_shader_stencil_export;
bool ARB_texture_compression_bptc;
bool ARB_texture_compression_rgtc;
// KHR
bool KHR_texture_compression_astc_ldr;
// EXT // EXT
bool EXT_texture_compression_s3tc;
bool EXT_swap_control_tear; bool EXT_swap_control_tear;
bool EXT_discard_framebuffer; bool EXT_discard_framebuffer;
bool EXT_unpack_subimage; // always supported on desktop and ES3 bool EXT_unpack_subimage; // always supported on desktop and ES3
@ -124,12 +115,6 @@ struct GLExtensions {
int maxVertexTextureUnits; int maxVertexTextureUnits;
bool supportsETC2;
bool supportsBC123;
bool supportsBC45;
bool supportsBC7;
bool supportsASTC;
// greater-or-equal than // greater-or-equal than
bool VersionGEThan(int major, int minor, int sub = 0); bool VersionGEThan(int major, int minor, int sub = 0);
int GLSLVersion(); int GLSLVersion();

View file

@ -1,75 +0,0 @@
#include "Common/GPU/OpenGL/GLCommon.h"
#include "Common/GPU/OpenGL/GLFrameData.h"
#include "Common/GPU/OpenGL/GLRenderManager.h"
#include "Common/Log.h"
void GLDeleter::Take(GLDeleter &other) {
_assert_msg_(IsEmpty(), "Deleter already has stuff");
shaders = std::move(other.shaders);
programs = std::move(other.programs);
buffers = std::move(other.buffers);
textures = std::move(other.textures);
inputLayouts = std::move(other.inputLayouts);
framebuffers = std::move(other.framebuffers);
pushBuffers = std::move(other.pushBuffers);
other.shaders.clear();
other.programs.clear();
other.buffers.clear();
other.textures.clear();
other.inputLayouts.clear();
other.framebuffers.clear();
other.pushBuffers.clear();
}
// Runs on the GPU thread.
void GLDeleter::Perform(GLRenderManager *renderManager, bool skipGLCalls) {
for (auto pushBuffer : pushBuffers) {
renderManager->UnregisterPushBuffer(pushBuffer);
if (skipGLCalls) {
pushBuffer->Destroy(false);
}
delete pushBuffer;
}
pushBuffers.clear();
for (auto shader : shaders) {
if (skipGLCalls && shader)
shader->shader = 0; // prevent the glDeleteShader
delete shader;
}
shaders.clear();
for (auto program : programs) {
if (skipGLCalls && program)
program->program = 0; // prevent the glDeleteProgram
delete program;
}
programs.clear();
for (auto buffer : buffers) {
if (skipGLCalls && buffer)
buffer->buffer_ = 0;
delete buffer;
}
buffers.clear();
for (auto texture : textures) {
if (skipGLCalls && texture)
texture->texture = 0;
delete texture;
}
textures.clear();
for (auto inputLayout : inputLayouts) {
// No GL objects in an inputLayout yet
delete inputLayout;
}
inputLayouts.clear();
for (auto framebuffer : framebuffers) {
if (skipGLCalls) {
framebuffer->handle = 0;
framebuffer->color_texture.texture = 0;
framebuffer->z_stencil_buffer = 0;
framebuffer->z_stencil_texture.texture = 0;
framebuffer->z_buffer = 0;
framebuffer->stencil_buffer = 0;
}
delete framebuffer;
}
framebuffers.clear();
}

View file

@ -1,68 +0,0 @@
#pragma once
#include <mutex>
#include <condition_variable>
#include <vector>
#include <string>
#include <set>
#include "Common/GPU/OpenGL/GLCommon.h"
class GLRShader;
class GLRBuffer;
class GLRTexture;
class GLRInputLayout;
class GLRFramebuffer;
class GLPushBuffer;
class GLRProgram;
class GLRenderManager;
class GLDeleter {
public:
void Perform(GLRenderManager *renderManager, bool skipGLCalls);
bool IsEmpty() const {
return shaders.empty() && programs.empty() && buffers.empty() && textures.empty() && inputLayouts.empty() && framebuffers.empty() && pushBuffers.empty();
}
void Take(GLDeleter &other);
std::vector<GLRShader *> shaders;
std::vector<GLRProgram *> programs;
std::vector<GLRBuffer *> buffers;
std::vector<GLRTexture *> textures;
std::vector<GLRInputLayout *> inputLayouts;
std::vector<GLRFramebuffer *> framebuffers;
std::vector<GLPushBuffer *> pushBuffers;
};
struct GLQueueProfileContext {
bool enabled;
double cpuStartTime;
double cpuEndTime;
std::string passesString;
int commandCounts[25]; // Can't grab count from the enum as it would mean a circular include. Might clean this up later.
};
// Per-frame data, round-robin so we can overlap submission with execution of the previous frame.
struct GLFrameData {
bool skipSwap = false;
// Frames need unique IDs to wait for present on, let's keep them here.
// Also used for indexing into the frame timing history buffer.
uint64_t frameId;
std::mutex fenceMutex;
std::condition_variable fenceCondVar;
bool readyForFence = true;
// Swapchain.
bool hasBegun = false;
GLDeleter deleter;
GLDeleter deleter_prev;
std::set<GLPushBuffer *> activePushBuffers;
GLQueueProfileContext profile;
};

View file

@ -1,288 +0,0 @@
#include "Common/MemoryUtil.h"
#include "Common/GPU/OpenGL/GLMemory.h"
#include "Common/GPU/OpenGL/GLRenderManager.h"
#include "Common/GPU/OpenGL/GLFeatures.h"
#include "Common/Data/Text/Parsers.h"
extern std::thread::id renderThreadId;
#if MAX_LOGLEVEL >= DEBUG_LEVEL
static bool OnRenderThread() {
return std::this_thread::get_id() == renderThreadId;
}
#endif
void *GLRBuffer::Map(GLBufferStrategy strategy) {
_assert_(buffer_ != 0);
GLbitfield access = GL_MAP_WRITE_BIT;
if ((strategy & GLBufferStrategy::MASK_FLUSH) != 0) {
access |= GL_MAP_FLUSH_EXPLICIT_BIT;
}
if ((strategy & GLBufferStrategy::MASK_INVALIDATE) != 0) {
access |= GL_MAP_INVALIDATE_BUFFER_BIT;
}
void *p = nullptr;
bool allowNativeBuffer = strategy != GLBufferStrategy::SUBDATA;
if (allowNativeBuffer) {
glBindBuffer(target_, buffer_);
if (gl_extensions.ARB_buffer_storage || gl_extensions.EXT_buffer_storage) {
#if !PPSSPP_PLATFORM(IOS)
if (!hasStorage_) {
GLbitfield storageFlags = access & ~(GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
#ifdef USING_GLES2
#ifdef GL_EXT_buffer_storage
glBufferStorageEXT(target_, size_, nullptr, storageFlags);
#endif
#else
glBufferStorage(target_, size_, nullptr, storageFlags);
#endif
hasStorage_ = true;
}
#endif
p = glMapBufferRange(target_, 0, size_, access);
} else if (gl_extensions.VersionGEThan(3, 0, 0)) {
// GLES3 or desktop 3.
p = glMapBufferRange(target_, 0, size_, access);
} else if (!gl_extensions.IsGLES) {
#ifndef USING_GLES2
p = glMapBuffer(target_, GL_READ_WRITE);
#endif
}
}
mapped_ = p != nullptr;
return p;
}
bool GLRBuffer::Unmap() {
glBindBuffer(target_, buffer_);
mapped_ = false;
return glUnmapBuffer(target_) == GL_TRUE;
}
GLPushBuffer::GLPushBuffer(GLRenderManager *render, GLuint target, size_t size, const char *tag) : render_(render), size_(size), target_(target), tag_(tag) {
bool res = AddBuffer();
_assert_(res);
RegisterGPUMemoryManager(this);
}
GLPushBuffer::~GLPushBuffer() {
UnregisterGPUMemoryManager(this);
Destroy(true);
}
void GLPushBuffer::Map() {
_assert_(!writePtr_);
auto &info = buffers_[buf_];
writePtr_ = info.deviceMemory ? info.deviceMemory : info.localMemory;
info.flushOffset = 0;
// Force alignment. This is needed for PushAligned() to work as expected.
while ((intptr_t)writePtr_ & 15) {
writePtr_++;
offset_++;
info.flushOffset++;
}
_assert_(writePtr_);
}
void GLPushBuffer::Unmap() {
_assert_(writePtr_);
if (!buffers_[buf_].deviceMemory) {
// Here we simply upload the data to the last buffer.
// Might be worth trying with size_ instead of offset_, so the driver can replace
// the whole buffer. At least if it's close.
render_->BufferSubdata(buffers_[buf_].buffer, 0, offset_, buffers_[buf_].localMemory, false);
} else {
buffers_[buf_].flushOffset = offset_;
}
writePtr_ = nullptr;
}
void GLPushBuffer::Flush() {
// Must be called from the render thread.
_dbg_assert_(OnRenderThread());
if (buf_ >= buffers_.size()) {
_dbg_assert_msg_(false, "buf_ somehow got out of sync: %d vs %d", (int)buf_, (int)buffers_.size());
return;
}
buffers_[buf_].flushOffset = offset_;
if (!buffers_[buf_].deviceMemory && writePtr_) {
auto &info = buffers_[buf_];
if (info.flushOffset != 0) {
_assert_(info.buffer->buffer_);
glBindBuffer(target_, info.buffer->buffer_);
glBufferSubData(target_, 0, info.flushOffset, info.localMemory);
}
// Here we will submit all the draw calls, with the already known buffer and offsets.
// Might as well reset the write pointer here and start over the current buffer.
writePtr_ = info.localMemory;
offset_ = 0;
info.flushOffset = 0;
}
// For device memory, we flush all buffers here.
if ((strategy_ & GLBufferStrategy::MASK_FLUSH) != 0) {
for (auto &info : buffers_) {
if (info.flushOffset == 0 || !info.deviceMemory)
continue;
glBindBuffer(target_, info.buffer->buffer_);
glFlushMappedBufferRange(target_, 0, info.flushOffset);
info.flushOffset = 0;
}
}
}
bool GLPushBuffer::AddBuffer() {
// INFO_LOG(G3D, "GLPushBuffer(%s): Allocating %d bytes", tag_, size_);
BufInfo info;
info.localMemory = (uint8_t *)AllocateAlignedMemory(size_, 16);
if (!info.localMemory)
return false;
info.buffer = render_->CreateBuffer(target_, size_, GL_DYNAMIC_DRAW);
info.size = size_;
buf_ = buffers_.size();
buffers_.push_back(info);
return true;
}
void GLPushBuffer::Destroy(bool onRenderThread) {
if (buf_ == -1)
return; // Already destroyed
for (BufInfo &info : buffers_) {
// This will automatically unmap device memory, if needed.
// NOTE: We immediately delete the buffer, don't go through the deleter, if we're on the render thread.
if (onRenderThread) {
delete info.buffer;
} else {
render_->DeleteBuffer(info.buffer);
}
FreeAlignedMemory(info.localMemory);
}
buffers_.clear();
buf_ = -1;
}
void GLPushBuffer::NextBuffer(size_t minSize) {
// First, unmap the current memory.
Unmap();
buf_++;
if (buf_ >= buffers_.size() || minSize > size_) {
// Before creating the buffer, adjust to the new size_ if necessary.
while (size_ < minSize) {
size_ <<= 1;
}
bool res = AddBuffer();
_assert_(res);
if (!res) {
// Let's try not to crash at least?
buf_ = 0;
}
}
// Now, move to the next buffer and map it.
offset_ = 0;
Map();
}
void GLPushBuffer::Defragment() {
_dbg_assert_msg_(!OnRenderThread(), "Defragment must not run on the render thread");
if (buffers_.size() <= 1) {
// Let's take this opportunity to jettison any localMemory we don't need.
for (auto &info : buffers_) {
if (info.deviceMemory) {
FreeAlignedMemory(info.localMemory);
info.localMemory = nullptr;
}
}
return;
}
// Okay, we have more than one. Destroy them all and start over with a larger one.
// When calling AddBuffer, we sometimes increase size_. So if we allocated multiple buffers in a frame,
// they won't all have the same size. Sum things up properly.
size_t newSize = 0;
for (int i = 0; i < (int)buffers_.size(); i++) {
newSize += buffers_[i].size;
}
Destroy(false);
// Set some sane but very free limits. If there's another spike, we'll just allocate more anyway.
size_ = std::min(std::max(newSize, (size_t)65536), (size_t)(512 * 1024 * 1024));
bool res = AddBuffer();
_assert_msg_(res, "AddBuffer failed");
}
size_t GLPushBuffer::GetTotalSize() const {
size_t sum = 0;
// When calling AddBuffer, we sometimes increase size_. So if we allocated multiple buffers in a frame,
// they won't all have the same size. Sum things up properly.
if (buffers_.size() > 1) {
for (int i = 0; i < (int)buffers_.size() - 1; i++) {
sum += buffers_[i].size;
}
}
sum += offset_;
return sum;
}
void GLPushBuffer::MapDevice(GLBufferStrategy strategy) {
_dbg_assert_msg_(OnRenderThread(), "MapDevice must run on render thread");
strategy_ = strategy;
if (strategy_ == GLBufferStrategy::SUBDATA) {
return;
}
bool mapChanged = false;
for (auto &info : buffers_) {
if (!info.buffer->buffer_ || info.deviceMemory) {
// Can't map - no device buffer associated yet or already mapped.
continue;
}
info.deviceMemory = (uint8_t *)info.buffer->Map(strategy_);
mapChanged = mapChanged || info.deviceMemory != nullptr;
if (!info.deviceMemory && !info.localMemory) {
// Somehow it failed, let's dodge crashing.
info.localMemory = (uint8_t *)AllocateAlignedMemory(info.buffer->size_, 16);
mapChanged = true;
}
_dbg_assert_msg_(info.localMemory || info.deviceMemory, "Local or device memory must succeed");
}
if (writePtr_ && mapChanged) {
// This can happen during a sync. Remap.
writePtr_ = nullptr;
Map();
}
}
void GLPushBuffer::UnmapDevice() {
_dbg_assert_msg_(OnRenderThread(), "UnmapDevice must run on render thread");
for (auto &info : buffers_) {
if (info.deviceMemory) {
// TODO: Technically this can return false?
info.buffer->Unmap();
info.deviceMemory = nullptr;
}
}
}
void GLPushBuffer::GetDebugString(char *buffer, size_t bufSize) const {
snprintf(buffer, bufSize, "%s: %s/%s (%d)", tag_, NiceSizeFormat(this->offset_).c_str(), NiceSizeFormat(this->size_).c_str(), (int)buffers_.size());
}

View file

@ -1,185 +0,0 @@
#pragma once
#include <vector>
#include <cstdint>
#include <cstring>
#include "Common/GPU/GPUBackendCommon.h"
#include "Common/GPU/OpenGL/GLCommon.h"
#include "Common/Log.h"
enum class GLBufferStrategy {
SUBDATA = 0,
MASK_FLUSH = 0x10,
MASK_INVALIDATE = 0x20,
// Map/unmap the buffer each frame.
FRAME_UNMAP = 1,
// Map/unmap and also invalidate the buffer on map.
INVALIDATE_UNMAP = MASK_INVALIDATE,
// Map/unmap and explicitly flushed changed ranges.
FLUSH_UNMAP = MASK_FLUSH,
// Map/unmap, invalidate on map, and explicit flush.
FLUSH_INVALIDATE_UNMAP = MASK_FLUSH | MASK_INVALIDATE,
};
static inline int operator &(const GLBufferStrategy &lhs, const GLBufferStrategy &rhs) {
return (int)lhs & (int)rhs;
}
class GLRBuffer {
public:
GLRBuffer(GLuint target, size_t size) : target_(target), size_((int)size) {}
~GLRBuffer() {
if (buffer_) {
glDeleteBuffers(1, &buffer_);
}
}
void *Map(GLBufferStrategy strategy);
bool Unmap();
bool Mapped() const {
return mapped_;
}
GLuint buffer_ = 0;
GLuint target_;
int size_;
private:
bool mapped_ = false;
bool hasStorage_ = false;
};
class GLRenderManager;
// Similar to VulkanPushBuffer but is currently less efficient - it collects all the data in
// RAM then does a big memcpy/buffer upload at the end of the frame. This is at least a lot
// faster than the hundreds of buffer uploads or memory array buffers we used before.
// On modern GL we could avoid the copy using glBufferStorage but not sure it's worth the
// trouble.
// We need to manage the lifetime of this together with the other resources so its destructor
// runs on the render thread.
class GLPushBuffer : public GPUMemoryManager {
public:
friend class GLRenderManager;
struct BufInfo {
GLRBuffer *buffer = nullptr;
uint8_t *localMemory = nullptr;
uint8_t *deviceMemory = nullptr;
size_t flushOffset = 0;
size_t size;
};
GLPushBuffer(GLRenderManager *render, GLuint target, size_t size, const char *tag);
~GLPushBuffer();
void Reset() { offset_ = 0; }
void GetDebugString(char *buffer, size_t bufSize) const override;
const char *Name() const override { return tag_; }; // for sorting
// Utility for users of this class, not used internally.
enum { INVALID_OFFSET = 0xFFFFFFFF };
private:
// Needs context in case of defragment.
void Begin() {
buf_ = 0;
offset_ = 0;
// Note: we must defrag because some buffers may be smaller than size_.
Defragment();
Map();
_dbg_assert_(writePtr_);
}
void BeginNoReset() {
Map();
}
void End() {
Unmap();
}
public:
void Map();
void Unmap();
bool IsReady() const {
return writePtr_ != nullptr;
}
// Recommended - lets you write directly into the buffer through the returned pointer.
// If you didn't end up using all the memory you grabbed here, then before calling Allocate or Push
// again, call Rewind (see below).
uint8_t *Allocate(uint32_t numBytes, uint32_t alignment, GLRBuffer **buf, uint32_t *bindOffset) {
uint32_t offset = ((uint32_t)offset_ + alignment - 1) & ~(alignment - 1);
if (offset + numBytes <= size_) {
// Common path.
offset_ = offset + numBytes;
*buf = buffers_[buf_].buffer;
*bindOffset = offset;
return writePtr_ + offset;
}
NextBuffer(numBytes);
*bindOffset = 0;
*buf = buffers_[buf_].buffer;
// Need to mark the allocated range used in the new buffer. How did things work before this?
offset_ = numBytes;
return writePtr_;
}
// For convenience if all you'll do is to copy.
uint32_t Push(const void *data, uint32_t numBytes, int alignment, GLRBuffer **buf) {
uint32_t bindOffset;
uint8_t *ptr = Allocate(numBytes, alignment, buf, &bindOffset);
memcpy(ptr, data, numBytes);
return bindOffset;
}
uint8_t *GetPtr(uint32_t offset) {
return writePtr_ + offset;
}
// If you didn't use all of the previous allocation you just made (obviously can't be another one),
// you can return memory to the buffer by specifying the offset up until which you wrote data.
// Pass in the buffer you got last time. If that buffer has been filled already, no rewind can be safely done.
// (well technically would be possible but not worth the trouble).
void Rewind(GLRBuffer *buffer, uint32_t offset) {
if (buffer == buffers_[buf_].buffer) {
_dbg_assert_(offset != INVALID_OFFSET);
_dbg_assert_(offset <= offset_);
offset_ = offset;
}
}
size_t GetOffset() const { return offset_; }
size_t GetTotalSize() const;
void Destroy(bool onRenderThread);
void Flush();
protected:
void MapDevice(GLBufferStrategy strategy);
void UnmapDevice();
private:
bool AddBuffer();
void NextBuffer(size_t minSize);
void Defragment();
GLRenderManager *render_;
std::vector<BufInfo> buffers_;
size_t buf_ = 0;
size_t offset_ = 0;
size_t size_ = 0;
uint8_t *writePtr_ = nullptr;
GLuint target_;
GLBufferStrategy strategy_ = GLBufferStrategy::SUBDATA;
const char *tag_;
};

View file

@ -73,19 +73,24 @@ void GLQueueRunner::CreateDeviceObjects() {
populate(GL_SHADING_LANGUAGE_VERSION); populate(GL_SHADING_LANGUAGE_VERSION);
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
#if !PPSSPP_ARCH(X86) // Doesn't work on AMD for some reason. See issue #17787
useDebugGroups_ = !gl_extensions.IsGLES && gl_extensions.VersionGEThan(4, 3); useDebugGroups_ = !gl_extensions.IsGLES && gl_extensions.VersionGEThan(4, 3);
#endif
} }
void GLQueueRunner::DestroyDeviceObjects() { void GLQueueRunner::DestroyDeviceObjects() {
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
if (!nameCache_.empty()) {
glDeleteTextures((GLsizei)nameCache_.size(), &nameCache_[0]);
nameCache_.clear();
}
if (gl_extensions.ARB_vertex_array_object) { if (gl_extensions.ARB_vertex_array_object) {
glDeleteVertexArrays(1, &globalVAO_); glDeleteVertexArrays(1, &globalVAO_);
} }
delete[] readbackBuffer_; delete[] readbackBuffer_;
readbackBuffer_ = nullptr; readbackBuffer_ = nullptr;
readbackBufferSize_ = 0; readbackBufferSize_ = 0;
delete[] tempBuffer_;
tempBuffer_ = nullptr;
tempBufferSize_ = 0;
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
} }
@ -107,20 +112,20 @@ static std::string GetInfoLog(GLuint name, Getiv getiv, GetLog getLog) {
return infoLog; return infoLog;
} }
static int GetStereoBufferIndex(const char *uniformName) { int GLQueueRunner::GetStereoBufferIndex(const char *uniformName) {
if (!uniformName) return -1; if (!uniformName) return -1;
else if (strcmp(uniformName, "u_view") == 0) return 0; else if (strcmp(uniformName, "u_view") == 0) return 0;
else if (strcmp(uniformName, "u_proj_lens") == 0) return 1; else if (strcmp(uniformName, "u_proj_lens") == 0) return 1;
else return -1; else return -1;
} }
static std::string GetStereoBufferLayout(const char *uniformName) { std::string GLQueueRunner::GetStereoBufferLayout(const char *uniformName) {
if (strcmp(uniformName, "u_view") == 0) return "ViewMatrices"; if (strcmp(uniformName, "u_view") == 0) return "ViewMatrices";
else if (strcmp(uniformName, "u_proj_lens") == 0) return "ProjectionMatrix"; else if (strcmp(uniformName, "u_proj_lens") == 0) return "ProjectionMatrix";
else return "undefined"; else return "undefined";
} }
void GLQueueRunner::RunInitSteps(const FastVec<GLRInitStep> &steps, bool skipGLCalls) { void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool skipGLCalls) {
if (skipGLCalls) { if (skipGLCalls) {
// Some bookkeeping still needs to be done. // Some bookkeeping still needs to be done.
for (size_t i = 0; i < steps.size(); i++) { for (size_t i = 0; i < steps.size(); i++) {
@ -325,8 +330,8 @@ void GLQueueRunner::RunInitSteps(const FastVec<GLRInitStep> &steps, bool skipGLC
glCompileShader(shader); glCompileShader(shader);
GLint success = 0; GLint success = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success); glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
std::string infoLog = GetInfoLog(shader, glGetShaderiv, glGetShaderInfoLog);
if (!success) { if (!success) {
std::string infoLog = GetInfoLog(shader, glGetShaderiv, glGetShaderInfoLog);
std::string errorString = StringFromFormat( std::string errorString = StringFromFormat(
"Error in shader compilation for: %s\n" "Error in shader compilation for: %s\n"
"Info log: %s\n" "Info log: %s\n"
@ -334,10 +339,10 @@ void GLQueueRunner::RunInitSteps(const FastVec<GLRInitStep> &steps, bool skipGLC
step.create_shader.shader->desc.c_str(), step.create_shader.shader->desc.c_str(),
infoLog.c_str(), infoLog.c_str(),
LineNumberString(code).c_str()); LineNumberString(code).c_str());
std::vector<std::string_view> lines; std::vector<std::string> lines;
SplitString(errorString, '\n', lines); SplitString(errorString, '\n', lines);
for (auto line : lines) { for (auto &line : lines) {
ERROR_LOG(G3D, "%.*s", (int)line.size(), line.data()); ERROR_LOG(G3D, "%s", line.c_str());
} }
if (errorCallback_) { if (errorCallback_) {
std::string desc = StringFromFormat("Shader compilation failed: %s", step.create_shader.stage == GL_VERTEX_SHADER ? "vertex" : "fragment"); std::string desc = StringFromFormat("Shader compilation failed: %s", step.create_shader.stage == GL_VERTEX_SHADER ? "vertex" : "fragment");
@ -385,23 +390,14 @@ void GLQueueRunner::RunInitSteps(const FastVec<GLRInitStep> &steps, bool skipGLC
// For things to show in RenderDoc, need to split into glTexImage2D(..., nullptr) and glTexSubImage. // For things to show in RenderDoc, need to split into glTexImage2D(..., nullptr) and glTexSubImage.
int blockSize = 0;
bool bc = Draw::DataFormatIsBlockCompressed(step.texture_image.format, &blockSize);
GLenum internalFormat, format, type; GLenum internalFormat, format, type;
int alignment; int alignment;
Thin3DFormatToGLFormatAndType(step.texture_image.format, internalFormat, format, type, alignment); Thin3DFormatToGLFormatAndType(step.texture_image.format, internalFormat, format, type, alignment);
if (step.texture_image.depth == 1) { if (step.texture_image.depth == 1) {
if (bc) { glTexImage2D(tex->target,
int dataSize = ((step.texture_image.width + 3) & ~3) * ((step.texture_image.height + 3) & ~3) * blockSize / 16; step.texture_image.level, internalFormat,
glCompressedTexImage2D(tex->target, step.texture_image.level, internalFormat, step.texture_image.width, step.texture_image.height, 0,
step.texture_image.width, step.texture_image.height, 0, dataSize, step.texture_image.data); format, type, step.texture_image.data);
} else {
glTexImage2D(tex->target,
step.texture_image.level, internalFormat,
step.texture_image.width, step.texture_image.height, 0,
format, type, step.texture_image.data);
}
} else { } else {
glTexImage3D(tex->target, glTexImage3D(tex->target,
step.texture_image.level, internalFormat, step.texture_image.level, internalFormat,
@ -512,8 +508,8 @@ void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tex.wrapS); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tex.wrapS);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tex.wrapT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tex.wrapT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex.magFilter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex.magFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex.minFilter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex.minFilter);
if (!gl_extensions.IsGLES || gl_extensions.GLES3) { if (!gl_extensions.IsGLES || gl_extensions.GLES3) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
} }
@ -521,7 +517,22 @@ void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) {
// Color texture is same everywhere // Color texture is same everywhere
glGenFramebuffers(1, &fbo->handle); glGenFramebuffers(1, &fbo->handle);
initFBOTexture(fbo->color_texture, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, true);
GLint colorInternalFormat;
GLint colorFormat;
GLint colorElementType;
switch (fbo->colorFormat) {
case Draw::DataFormat::R8G8B8A8_UNORM:
colorInternalFormat = GL_RGBA;
colorFormat = GL_RGBA;
colorElementType = GL_UNSIGNED_BYTE;
break;
default:
_assert_msg_(false, "Data format not supported");
return;
}
initFBOTexture(fbo->color_texture, colorInternalFormat, colorFormat, colorElementType, true);
retry_depth: retry_depth:
if (!fbo->z_stencil_) { if (!fbo->z_stencil_) {
@ -653,7 +664,7 @@ retry_depth:
currentReadHandle_ = fbo->handle; currentReadHandle_ = fbo->handle;
} }
void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, GLFrameData &frameData, bool skipGLCalls, bool keepSteps, bool useVR) { void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCalls, bool keepSteps, bool useVR) {
if (skipGLCalls) { if (skipGLCalls) {
if (keepSteps) { if (keepSteps) {
return; return;
@ -702,7 +713,7 @@ void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, GLFrameData &f
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
size_t renderCount = 0; size_t renderCount = 0;
for (size_t i = 0; i < steps.size(); i++) { for (size_t i = 0; i < steps.size(); i++) {
GLRStep &step = *steps[i]; const GLRStep &step = *steps[i];
#if !defined(USING_GLES2) #if !defined(USING_GLES2)
if (useDebugGroups_) if (useDebugGroups_)
@ -713,10 +724,11 @@ void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, GLFrameData &f
case GLRStepType::RENDER: case GLRStepType::RENDER:
renderCount++; renderCount++;
if (IsVREnabled()) { if (IsVREnabled()) {
PreprocessStepVR(&step); GLRStep vrStep = step;
PerformRenderPass(step, renderCount == 1, renderCount == totalRenderCount, frameData.profile); PreprocessStepVR(&vrStep);
PerformRenderPass(vrStep, renderCount == 1, renderCount == totalRenderCount);
} else { } else {
PerformRenderPass(step, renderCount == 1, renderCount == totalRenderCount, frameData.profile); PerformRenderPass(step, renderCount == 1, renderCount == totalRenderCount);
} }
break; break;
case GLRStepType::COPY: case GLRStepType::COPY:
@ -742,17 +754,19 @@ void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, GLFrameData &f
if (useDebugGroups_) if (useDebugGroups_)
glPopDebugGroup(); glPopDebugGroup();
#endif #endif
if (frameData.profile.enabled) {
frameData.profile.passesString += StepToString(step);
}
if (!keepSteps) { if (!keepSteps) {
delete steps[i]; delete steps[i];
} }
} }
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
} }
void GLQueueRunner::LogSteps(const std::vector<GLRStep *> &steps) {
}
void GLQueueRunner::PerformBlit(const GLRStep &step) { void GLQueueRunner::PerformBlit(const GLRStep &step) {
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
// Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing // Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing
@ -782,20 +796,7 @@ void GLQueueRunner::PerformBlit(const GLRStep &step) {
} }
} }
static void EnableDisableVertexArrays(uint32_t prevAttr, uint32_t newAttr) { void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last) {
int enable = (~prevAttr) & newAttr;
int disable = prevAttr & (~newAttr);
for (int i = 0; i < 7; i++) { // SEM_MAX
if (enable & (1 << i)) {
glEnableVertexAttribArray(i);
}
if (disable & (1 << i)) {
glDisableVertexAttribArray(i);
}
}
}
void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last, GLQueueProfileContext &profile) {
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
PerformBindFramebufferAsRenderTarget(step); PerformBindFramebufferAsRenderTarget(step);
@ -812,9 +813,10 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
glDisable(GL_COLOR_LOGIC_OP); glDisable(GL_COLOR_LOGIC_OP);
} }
#endif #endif
if (gl_extensions.ARB_vertex_array_object) { }
glBindVertexArray(globalVAO_);
} if (first && gl_extensions.ARB_vertex_array_object) {
glBindVertexArray(globalVAO_);
} }
GLRProgram *curProgram = nullptr; GLRProgram *curProgram = nullptr;
@ -840,43 +842,12 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
bool clipDistanceEnabled[8]{}; bool clipDistanceEnabled[8]{};
GLuint blendEqColor = (GLuint)-1; GLuint blendEqColor = (GLuint)-1;
GLuint blendEqAlpha = (GLuint)-1; GLuint blendEqAlpha = (GLuint)-1;
GLenum blendSrcColor = (GLenum)-1;
GLenum blendDstColor = (GLenum)-1;
GLenum blendSrcAlpha = (GLenum)-1;
GLenum blendDstAlpha = (GLenum)-1;
GLuint stencilWriteMask = (GLuint)-1;
GLenum stencilFunc = (GLenum)-1;
GLuint stencilRef = (GLuint)-1;
GLuint stencilCompareMask = (GLuint)-1;
GLenum stencilSFail = (GLenum)-1;
GLenum stencilZFail = (GLenum)-1;
GLenum stencilPass = (GLenum)-1;
GLenum frontFace = (GLenum)-1;
GLenum cullFace = (GLenum)-1;
GLRTexture *curTex[MAX_GL_TEXTURE_SLOTS]{}; GLRTexture *curTex[MAX_GL_TEXTURE_SLOTS]{};
GLRViewport viewport = {
-1000000000.0f,
-1000000000.0f,
-1000000000.0f,
-1000000000.0f,
-1000000000.0f,
-1000000000.0f,
};
GLRect2D scissorRc = { -1, -1, -1, -1 };
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
auto &commands = step.commands; auto &commands = step.commands;
for (const auto &c : commands) { for (const auto &c : commands) {
#ifdef _DEBUG
if (profile.enabled) {
if ((size_t)c.cmd < ARRAY_SIZE(profile.commandCounts)) {
profile.commandCounts[(size_t)c.cmd]++;
}
}
#endif
switch (c.cmd) { switch (c.cmd) {
case GLRRenderCommand::DEPTH: case GLRRenderCommand::DEPTH:
if (c.depth.enabled) { if (c.depth.enabled) {
@ -892,39 +863,28 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
glDepthFunc(c.depth.func); glDepthFunc(c.depth.func);
depthFunc = c.depth.func; depthFunc = c.depth.func;
} }
} else if (/* !c.depth.enabled && */ depthEnabled) { } else if (!c.depth.enabled && depthEnabled) {
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
depthEnabled = false; depthEnabled = false;
} }
break; break;
case GLRRenderCommand::STENCIL: case GLRRenderCommand::STENCILFUNC:
if (c.stencil.enabled) { if (c.stencilFunc.enabled) {
if (!stencilEnabled) { if (!stencilEnabled) {
glEnable(GL_STENCIL_TEST); glEnable(GL_STENCIL_TEST);
stencilEnabled = true; stencilEnabled = true;
} }
if (c.stencil.func != stencilFunc || c.stencil.ref != stencilRef || c.stencil.compareMask != stencilCompareMask) { glStencilFunc(c.stencilFunc.func, c.stencilFunc.ref, c.stencilFunc.compareMask);
glStencilFunc(c.stencil.func, c.stencil.ref, c.stencil.compareMask); } else if (stencilEnabled) {
stencilFunc = c.stencil.func;
stencilRef = c.stencil.ref;
stencilCompareMask = c.stencil.compareMask;
}
if (c.stencil.sFail != stencilSFail || c.stencil.zFail != stencilZFail || c.stencil.pass != stencilPass) {
glStencilOp(c.stencil.sFail, c.stencil.zFail, c.stencil.pass);
stencilSFail = c.stencil.sFail;
stencilZFail = c.stencil.zFail;
stencilPass = c.stencil.pass;
}
if (c.stencil.writeMask != stencilWriteMask) {
glStencilMask(c.stencil.writeMask);
stencilWriteMask = c.stencil.writeMask;
}
} else if (/* !c.stencilFunc.enabled && */stencilEnabled) {
glDisable(GL_STENCIL_TEST); glDisable(GL_STENCIL_TEST);
stencilEnabled = false; stencilEnabled = false;
} }
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
break; break;
case GLRRenderCommand::STENCILOP:
glStencilOp(c.stencilOp.sFail, c.stencilOp.zFail, c.stencilOp.pass);
glStencilMask(c.stencilOp.writeMask);
break;
case GLRRenderCommand::BLEND: case GLRRenderCommand::BLEND:
if (c.blend.enabled) { if (c.blend.enabled) {
if (!blendEnabled) { if (!blendEnabled) {
@ -936,14 +896,8 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
blendEqColor = c.blend.funcColor; blendEqColor = c.blend.funcColor;
blendEqAlpha = c.blend.funcAlpha; blendEqAlpha = c.blend.funcAlpha;
} }
if (blendSrcColor != c.blend.srcColor || blendDstColor != c.blend.dstColor || blendSrcAlpha != c.blend.srcAlpha || blendDstAlpha != c.blend.dstAlpha) { glBlendFuncSeparate(c.blend.srcColor, c.blend.dstColor, c.blend.srcAlpha, c.blend.dstAlpha);
glBlendFuncSeparate(c.blend.srcColor, c.blend.dstColor, c.blend.srcAlpha, c.blend.dstAlpha); } else if (!c.blend.enabled && blendEnabled) {
blendSrcColor = c.blend.srcColor;
blendDstColor = c.blend.dstColor;
blendSrcAlpha = c.blend.srcAlpha;
blendDstAlpha = c.blend.dstAlpha;
}
} else if (/* !c.blend.enabled && */ blendEnabled) {
glDisable(GL_BLEND); glDisable(GL_BLEND);
blendEnabled = false; blendEnabled = false;
} }
@ -963,7 +917,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
if (logicOp != c.logic.logicOp) { if (logicOp != c.logic.logicOp) {
glLogicOp(c.logic.logicOp); glLogicOp(c.logic.logicOp);
} }
} else if (/* !c.logic.enabled && */ logicEnabled) { } else if (!c.logic.enabled && logicEnabled) {
glDisable(GL_COLOR_LOGIC_OP); glDisable(GL_COLOR_LOGIC_OP);
logicEnabled = false; logicEnabled = false;
} }
@ -1020,27 +974,16 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
y = curFBHeight_ - y - c.viewport.vp.h; y = curFBHeight_ - y - c.viewport.vp.h;
// TODO: Support FP viewports through glViewportArrays // TODO: Support FP viewports through glViewportArrays
if (viewport.x != c.viewport.vp.x || viewport.y != y || viewport.w != c.viewport.vp.w || viewport.h != c.viewport.vp.h) { glViewport((GLint)c.viewport.vp.x, (GLint)y, (GLsizei)c.viewport.vp.w, (GLsizei)c.viewport.vp.h);
glViewport((GLint)c.viewport.vp.x, (GLint)y, (GLsizei)c.viewport.vp.w, (GLsizei)c.viewport.vp.h);
viewport.x = c.viewport.vp.x;
viewport.y = y;
viewport.w = c.viewport.vp.w;
viewport.h = c.viewport.vp.h;
}
if (viewport.minZ != c.viewport.vp.minZ || viewport.maxZ != c.viewport.vp.maxZ) {
viewport.minZ = c.viewport.vp.minZ;
viewport.maxZ = c.viewport.vp.maxZ;
#if !defined(USING_GLES2) #if !defined(USING_GLES2)
if (gl_extensions.IsGLES) { if (gl_extensions.IsGLES) {
glDepthRangef(c.viewport.vp.minZ, c.viewport.vp.maxZ);
} else {
glDepthRange(c.viewport.vp.minZ, c.viewport.vp.maxZ);
}
#else
glDepthRangef(c.viewport.vp.minZ, c.viewport.vp.maxZ); glDepthRangef(c.viewport.vp.minZ, c.viewport.vp.maxZ);
#endif } else {
glDepthRange(c.viewport.vp.minZ, c.viewport.vp.maxZ);
} }
#else
glDepthRangef(c.viewport.vp.minZ, c.viewport.vp.maxZ);
#endif
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
break; break;
} }
@ -1049,30 +992,30 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
int y = c.scissor.rc.y; int y = c.scissor.rc.y;
if (!curFB_) if (!curFB_)
y = curFBHeight_ - y - c.scissor.rc.h; y = curFBHeight_ - y - c.scissor.rc.h;
if (scissorRc.x != c.scissor.rc.x || scissorRc.y != y || scissorRc.w != c.scissor.rc.w || scissorRc.h != c.scissor.rc.h) { glScissor(c.scissor.rc.x, y, c.scissor.rc.w, c.scissor.rc.h);
glScissor(c.scissor.rc.x, y, c.scissor.rc.w, c.scissor.rc.h);
scissorRc.x = c.scissor.rc.x;
scissorRc.y = y;
scissorRc.w = c.scissor.rc.w;
scissorRc.h = c.scissor.rc.h;
}
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
break; break;
} }
case GLRRenderCommand::UNIFORM4F: case GLRRenderCommand::UNIFORM4F:
{ {
_dbg_assert_(curProgram);
int loc = c.uniform4.loc ? *c.uniform4.loc : -1; int loc = c.uniform4.loc ? *c.uniform4.loc : -1;
if (c.uniform4.name) { if (c.uniform4.name) {
loc = curProgram->GetUniformLoc(c.uniform4.name); loc = curProgram->GetUniformLoc(c.uniform4.name);
} }
if (loc >= 0) { if (loc >= 0) {
_dbg_assert_(c.uniform4.count >=1 && c.uniform4.count <=4);
switch (c.uniform4.count) { switch (c.uniform4.count) {
case 1: glUniform1f(loc, c.uniform4.v[0]); break; case 1:
case 2: glUniform2fv(loc, 1, c.uniform4.v); break; glUniform1f(loc, c.uniform4.v[0]);
case 3: glUniform3fv(loc, 1, c.uniform4.v); break; break;
case 4: glUniform4fv(loc, 1, c.uniform4.v); break; case 2:
glUniform2fv(loc, 1, c.uniform4.v);
break;
case 3:
glUniform3fv(loc, 1, c.uniform4.v);
break;
case 4:
glUniform4fv(loc, 1, c.uniform4.v);
break;
} }
} }
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
@ -1086,7 +1029,6 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
loc = curProgram->GetUniformLoc(c.uniform4.name); loc = curProgram->GetUniformLoc(c.uniform4.name);
} }
if (loc >= 0) { if (loc >= 0) {
_dbg_assert_(c.uniform4.count >=1 && c.uniform4.count <=4);
switch (c.uniform4.count) { switch (c.uniform4.count) {
case 1: glUniform1uiv(loc, 1, (GLuint *)c.uniform4.v); break; case 1: glUniform1uiv(loc, 1, (GLuint *)c.uniform4.v); break;
case 2: glUniform2uiv(loc, 1, (GLuint *)c.uniform4.v); break; case 2: glUniform2uiv(loc, 1, (GLuint *)c.uniform4.v); break;
@ -1105,7 +1047,6 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
loc = curProgram->GetUniformLoc(c.uniform4.name); loc = curProgram->GetUniformLoc(c.uniform4.name);
} }
if (loc >= 0) { if (loc >= 0) {
_dbg_assert_(c.uniform4.count >=1 && c.uniform4.count <=4);
switch (c.uniform4.count) { switch (c.uniform4.count) {
case 1: glUniform1iv(loc, 1, (GLint *)c.uniform4.v); break; case 1: glUniform1iv(loc, 1, (GLint *)c.uniform4.v); break;
case 2: glUniform2iv(loc, 1, (GLint *)c.uniform4.v); break; case 2: glUniform2iv(loc, 1, (GLint *)c.uniform4.v); break;
@ -1120,34 +1061,28 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
{ {
_dbg_assert_(curProgram); _dbg_assert_(curProgram);
if (IsMultiviewSupported()) { if (IsMultiviewSupported()) {
int layout = GetStereoBufferIndex(c.uniformStereoMatrix4.name); int layout = GetStereoBufferIndex(c.uniformMatrix4.name);
if (layout >= 0) { if (layout >= 0) {
int size = 2 * 16 * sizeof(float); int size = 2 * 16 * sizeof(float);
glBindBufferBase(GL_UNIFORM_BUFFER, layout, *c.uniformStereoMatrix4.loc); glBindBufferBase(GL_UNIFORM_BUFFER, layout, *c.uniformMatrix4.loc);
glBindBuffer(GL_UNIFORM_BUFFER, *c.uniformStereoMatrix4.loc); glBindBuffer(GL_UNIFORM_BUFFER, *c.uniformMatrix4.loc);
void *matrices = glMapBufferRange(GL_UNIFORM_BUFFER, 0, size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); void *matrices = glMapBufferRange(GL_UNIFORM_BUFFER, 0, size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
memcpy(matrices, c.uniformStereoMatrix4.mData, size); memcpy(matrices, c.uniformMatrix4.m, size);
glUnmapBuffer(GL_UNIFORM_BUFFER); glUnmapBuffer(GL_UNIFORM_BUFFER);
glBindBuffer(GL_UNIFORM_BUFFER, 0); glBindBuffer(GL_UNIFORM_BUFFER, 0);
} }
delete[] c.uniformStereoMatrix4.mData; // We only playback once.
} else { } else {
int loc = c.uniformStereoMatrix4.loc ? *c.uniformStereoMatrix4.loc : -1; int loc = c.uniformMatrix4.loc ? *c.uniformMatrix4.loc : -1;
if (c.uniformStereoMatrix4.name) { if (c.uniformMatrix4.name) {
loc = curProgram->GetUniformLoc(c.uniformStereoMatrix4.name); loc = curProgram->GetUniformLoc(c.uniformMatrix4.name);
} }
if (loc >= 0) { if (loc >= 0) {
if (GetVRFBOIndex() == 0) { if (GetVRFBOIndex() == 0) {
glUniformMatrix4fv(loc, 1, false, c.uniformStereoMatrix4.mData); glUniformMatrix4fv(loc, 1, false, c.uniformMatrix4.m);
} else { } else {
glUniformMatrix4fv(loc, 1, false, c.uniformStereoMatrix4.mData + 16); glUniformMatrix4fv(loc, 1, false, &c.uniformMatrix4.m[16]);
} }
} }
if (GetVRFBOIndex() == 1 || GetVRPassesCount() == 1) {
// Only delete the data if we're rendering the only or the second eye.
// If we delete during the first eye, we get a use-after-free or double delete.
delete[] c.uniformStereoMatrix4.mData;
}
} }
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
break; break;
@ -1236,37 +1171,52 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
break; break;
} }
case GLRRenderCommand::DRAW: case GLRRenderCommand::BIND_VERTEX_BUFFER:
{ {
GLRInputLayout *layout = c.draw.inputLayout; // TODO: Add fast path for glBindVertexBuffer
GLuint buf = c.draw.vertexBuffer->buffer_; GLRInputLayout *layout = c.bindVertexBuffer.inputLayout;
_dbg_assert_(!c.draw.vertexBuffer->Mapped()); GLuint buf = c.bindVertexBuffer.buffer ? c.bindVertexBuffer.buffer->buffer_ : 0;
_dbg_assert_(!c.bindVertexBuffer.buffer || !c.bindVertexBuffer.buffer->Mapped());
if (buf != curArrayBuffer) { if (buf != curArrayBuffer) {
glBindBuffer(GL_ARRAY_BUFFER, buf); glBindBuffer(GL_ARRAY_BUFFER, buf);
curArrayBuffer = buf; curArrayBuffer = buf;
} }
if (attrMask != layout->semanticsMask_) { if (attrMask != layout->semanticsMask_) {
EnableDisableVertexArrays(attrMask, layout->semanticsMask_); int enable = layout->semanticsMask_ & ~attrMask;
int disable = (~layout->semanticsMask_) & attrMask;
for (int i = 0; i < 7; i++) { // SEM_MAX
if (enable & (1 << i)) {
glEnableVertexAttribArray(i);
}
if (disable & (1 << i)) {
glDisableVertexAttribArray(i);
}
}
attrMask = layout->semanticsMask_; attrMask = layout->semanticsMask_;
} }
for (size_t i = 0; i < layout->entries.size(); i++) { for (size_t i = 0; i < layout->entries.size(); i++) {
auto &entry = layout->entries[i]; auto &entry = layout->entries[i];
glVertexAttribPointer(entry.location, entry.count, entry.type, entry.normalized, layout->stride, (const void *)(c.draw.vertexOffset + entry.offset)); glVertexAttribPointer(entry.location, entry.count, entry.type, entry.normalized, entry.stride, (const void *)(c.bindVertexBuffer.offset + entry.offset));
} }
if (c.draw.indexBuffer) { CHECK_GL_ERROR_IF_DEBUG();
GLuint buf = c.draw.indexBuffer->buffer_; break;
_dbg_assert_(!c.draw.indexBuffer->Mapped()); }
case GLRRenderCommand::BIND_BUFFER:
{
if (c.bind_buffer.target == GL_ARRAY_BUFFER) {
Crash();
} else if (c.bind_buffer.target == GL_ELEMENT_ARRAY_BUFFER) {
GLuint buf = c.bind_buffer.buffer ? c.bind_buffer.buffer->buffer_ : 0;
_dbg_assert_(!c.bind_buffer.buffer->Mapped());
if (buf != curElemArrayBuffer) { if (buf != curElemArrayBuffer) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf);
curElemArrayBuffer = buf; curElemArrayBuffer = buf;
} }
if (c.draw.instances == 1) {
glDrawElements(c.draw.mode, c.draw.count, c.draw.indexType, (void *)(intptr_t)c.draw.indexOffset);
} else {
glDrawElementsInstanced(c.draw.mode, c.draw.count, c.draw.indexType, (void *)(intptr_t)c.draw.indexOffset, c.draw.instances);
}
} else { } else {
glDrawArrays(c.draw.mode, c.draw.first, c.draw.count); GLuint buf = c.bind_buffer.buffer ? c.bind_buffer.buffer->buffer_ : 0;
_dbg_assert_(!c.bind_buffer.buffer->Mapped());
glBindBuffer(c.bind_buffer.target, buf);
} }
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
break; break;
@ -1275,6 +1225,16 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
// TODO: Should we include the texture handle in the command? // TODO: Should we include the texture handle in the command?
// Also, should this not be an init command? // Also, should this not be an init command?
glGenerateMipmap(GL_TEXTURE_2D); glGenerateMipmap(GL_TEXTURE_2D);
break;
case GLRRenderCommand::DRAW:
glDrawArrays(c.draw.mode, c.draw.first, c.draw.count);
break;
case GLRRenderCommand::DRAW_INDEXED:
if (c.drawIndexed.instances == 1) {
glDrawElements(c.drawIndexed.mode, c.drawIndexed.count, c.drawIndexed.indexType, c.drawIndexed.indices);
} else {
glDrawElementsInstanced(c.drawIndexed.mode, c.drawIndexed.count, c.drawIndexed.indexType, c.drawIndexed.indices, c.drawIndexed.instances);
}
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
break; break;
case GLRRenderCommand::TEXTURESAMPLER: case GLRRenderCommand::TEXTURESAMPLER:
@ -1322,7 +1282,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
} }
case GLRRenderCommand::TEXTURELOD: case GLRRenderCommand::TEXTURELOD:
{ {
GLint slot = c.textureLod.slot; GLint slot = c.textureSampler.slot;
if (slot != activeSlot) { if (slot != activeSlot) {
glActiveTexture(GL_TEXTURE0 + slot); glActiveTexture(GL_TEXTURE0 + slot);
activeSlot = slot; activeSlot = slot;
@ -1378,15 +1338,9 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
cullEnabled = true; cullEnabled = true;
} }
if (frontFace != c.raster.frontFace) { glFrontFace(c.raster.frontFace);
glFrontFace(c.raster.frontFace); glCullFace(c.raster.cullFace);
frontFace = c.raster.frontFace; } else if (!c.raster.cullEnable && cullEnabled) {
}
if (cullFace != c.raster.cullFace) {
glCullFace(c.raster.cullFace);
cullFace = c.raster.cullFace;
}
} else if (/* !c.raster.cullEnable && */ cullEnabled) {
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
cullEnabled = false; cullEnabled = false;
} }
@ -1395,7 +1349,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
glEnable(GL_DITHER); glEnable(GL_DITHER);
ditherEnabled = true; ditherEnabled = true;
} }
} else if (/* !c.raster.ditherEnable && */ ditherEnabled) { } else if (!c.raster.ditherEnable && ditherEnabled) {
glDisable(GL_DITHER); glDisable(GL_DITHER);
ditherEnabled = false; ditherEnabled = false;
} }
@ -1405,7 +1359,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
glEnable(GL_DEPTH_CLAMP); glEnable(GL_DEPTH_CLAMP);
depthClampEnabled = true; depthClampEnabled = true;
} }
} else if (/* !c.raster.depthClampEnable && */ depthClampEnabled) { } else if (!c.raster.depthClampEnable && depthClampEnabled) {
glDisable(GL_DEPTH_CLAMP); glDisable(GL_DEPTH_CLAMP);
depthClampEnabled = false; depthClampEnabled = false;
} }
@ -1542,24 +1496,26 @@ void GLQueueRunner::PerformReadback(const GLRStep &pass) {
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
// Always read back in 8888 format for the color aspect. // Always read back in 8888 format for the color aspect.
GLuint internalFormat = GL_RGBA;
GLuint format = GL_RGBA; GLuint format = GL_RGBA;
GLuint type = GL_UNSIGNED_BYTE; GLuint type = GL_UNSIGNED_BYTE;
int srcAlignment = 4; int srcAlignment = 4;
int dstAlignment = (int)DataFormatSizeInBytes(pass.readback.dstFormat);
#ifndef USING_GLES2 #ifndef USING_GLES2
if (pass.readback.aspectMask & GL_DEPTH_BUFFER_BIT) { if (pass.readback.aspectMask & GL_DEPTH_BUFFER_BIT) {
internalFormat = GL_DEPTH_COMPONENT;
format = GL_DEPTH_COMPONENT; format = GL_DEPTH_COMPONENT;
type = GL_FLOAT; type = GL_FLOAT;
srcAlignment = 4; srcAlignment = 4;
} else if (pass.readback.aspectMask & GL_STENCIL_BUFFER_BIT) { } else if (pass.readback.aspectMask & GL_STENCIL_BUFFER_BIT) {
internalFormat = GL_STENCIL_INDEX;
format = GL_STENCIL_INDEX; format = GL_STENCIL_INDEX;
type = GL_UNSIGNED_BYTE; type = GL_UNSIGNED_BYTE;
srcAlignment = 1; srcAlignment = 1;
} }
#endif #endif
readbackAspectMask_ = pass.readback.aspectMask;
int pixelStride = pass.readback.srcRect.w; int pixelStride = pass.readback.srcRect.w;
// Apply the correct alignment. // Apply the correct alignment.
glPixelStorei(GL_PACK_ALIGNMENT, srcAlignment); glPixelStorei(GL_PACK_ALIGNMENT, srcAlignment);
@ -1570,20 +1526,31 @@ void GLQueueRunner::PerformReadback(const GLRStep &pass) {
GLRect2D rect = pass.readback.srcRect; GLRect2D rect = pass.readback.srcRect;
int readbackSize = srcAlignment * rect.w * rect.h; bool convert = internalFormat == GL_RGBA && pass.readback.dstFormat != DataFormat::R8G8B8A8_UNORM;
int tempSize = srcAlignment * rect.w * rect.h;
int readbackSize = dstAlignment * rect.w * rect.h;
if (convert && tempSize > tempBufferSize_) {
delete[] tempBuffer_;
tempBuffer_ = new uint8_t[tempSize];
tempBufferSize_ = tempSize;
}
if (readbackSize > readbackBufferSize_) { if (readbackSize > readbackBufferSize_) {
delete[] readbackBuffer_; delete[] readbackBuffer_;
readbackBuffer_ = new uint8_t[readbackSize]; readbackBuffer_ = new uint8_t[readbackSize];
readbackBufferSize_ = readbackSize; readbackBufferSize_ = readbackSize;
} }
glReadPixels(rect.x, rect.y, rect.w, rect.h, format, type, readbackBuffer_); glReadPixels(rect.x, rect.y, rect.w, rect.h, format, type, convert ? tempBuffer_ : readbackBuffer_);
#ifdef DEBUG_READ_PIXELS #ifdef DEBUG_READ_PIXELS
LogReadPixelsError(glGetError()); LogReadPixelsError(glGetError());
#endif #endif
if (!gl_extensions.IsGLES || gl_extensions.GLES3) { if (!gl_extensions.IsGLES || gl_extensions.GLES3) {
glPixelStorei(GL_PACK_ROW_LENGTH, 0); glPixelStorei(GL_PACK_ROW_LENGTH, 0);
} }
if (convert && tempBuffer_ && readbackBuffer_) {
ConvertFromRGBA8888(readbackBuffer_, tempBuffer_, pixelStride, pixelStride, rect.w, rect.h, pass.readback.dstFormat);
}
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
} }
@ -1612,7 +1579,7 @@ void GLQueueRunner::PerformReadbackImage(const GLRStep &pass) {
glGetTexLevelParameteriv(GL_TEXTURE_2D, pass.readback_image.mipLevel, GL_TEXTURE_WIDTH, &w); glGetTexLevelParameteriv(GL_TEXTURE_2D, pass.readback_image.mipLevel, GL_TEXTURE_WIDTH, &w);
glGetTexLevelParameteriv(GL_TEXTURE_2D, pass.readback_image.mipLevel, GL_TEXTURE_HEIGHT, &h); glGetTexLevelParameteriv(GL_TEXTURE_2D, pass.readback_image.mipLevel, GL_TEXTURE_HEIGHT, &h);
int size = 4 * std::max((int)w, rect.x + rect.w) * std::max((int)h, rect.y + rect.h); int size = 4 * std::max((int)w, rect.x + rect.w) * std::max((int)h, rect.h);
if (size > readbackBufferSize_) { if (size > readbackBufferSize_) {
delete[] readbackBuffer_; delete[] readbackBuffer_;
readbackBuffer_ = new uint8_t[size]; readbackBuffer_ = new uint8_t[size];
@ -1663,7 +1630,7 @@ void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) {
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
} }
void GLQueueRunner::CopyFromReadbackBuffer(GLRFramebuffer *framebuffer, int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) { void GLQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) {
// TODO: Maybe move data format conversion here, and always read back 8888. Drivers // TODO: Maybe move data format conversion here, and always read back 8888. Drivers
// don't usually provide very optimized conversion implementations, though some do. // don't usually provide very optimized conversion implementations, though some do.
// Just need to be careful about dithering, which may break Danganronpa. // Just need to be careful about dithering, which may break Danganronpa.
@ -1672,26 +1639,20 @@ void GLQueueRunner::CopyFromReadbackBuffer(GLRFramebuffer *framebuffer, int widt
// Something went wrong during the read and no readback buffer was allocated, probably. // Something went wrong during the read and no readback buffer was allocated, probably.
return; return;
} }
for (int y = 0; y < height; y++) {
// Always read back in 8888 format for the color aspect. memcpy(pixels + y * pixelStride * bpp, readbackBuffer_ + y * width * bpp, width * bpp);
GLuint internalFormat = GL_RGBA;
#ifndef USING_GLES2
if (readbackAspectMask_ & GL_DEPTH_BUFFER_BIT) {
internalFormat = GL_DEPTH_COMPONENT;
} else if (readbackAspectMask_ & GL_STENCIL_BUFFER_BIT) {
internalFormat = GL_STENCIL_INDEX;
} }
#endif }
bool convert = internalFormat == GL_RGBA && destFormat != Draw::DataFormat::R8G8B8A8_UNORM; GLuint GLQueueRunner::AllocTextureName() {
if (convert) { if (nameCache_.empty()) {
// srcStride is width because we read back "packed" (with no gaps) from GL. nameCache_.resize(TEXCACHE_NAME_CACHE_SIZE);
ConvertFromRGBA8888(pixels, readbackBuffer_, pixelStride, width, width, height, destFormat); glGenTextures(TEXCACHE_NAME_CACHE_SIZE, &nameCache_[0]);
} else {
for (int y = 0; y < height; y++) {
memcpy(pixels + y * pixelStride * bpp, readbackBuffer_ + y * width * bpp, width * bpp);
}
} }
u32 name = nameCache_.back();
nameCache_.pop_back();
CHECK_GL_ERROR_IF_DEBUG();
return name;
} }
// On PC, we always use GL_DEPTH24_STENCIL8. // On PC, we always use GL_DEPTH24_STENCIL8.
@ -1812,7 +1773,7 @@ void GLQueueRunner::fbo_unbind() {
glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO); glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO);
#endif #endif
#if PPSSPP_PLATFORM(IOS) && !defined(__LIBRETRO__) #if PPSSPP_PLATFORM(IOS)
bindDefaultFBO(); bindDefaultFBO();
#endif #endif
@ -1853,74 +1814,3 @@ GLRFramebuffer::~GLRFramebuffer() {
glDeleteRenderbuffers(1, &stencil_buffer); glDeleteRenderbuffers(1, &stencil_buffer);
CHECK_GL_ERROR_IF_DEBUG(); CHECK_GL_ERROR_IF_DEBUG();
} }
std::string GLQueueRunner::StepToString(const GLRStep &step) const {
char buffer[256];
switch (step.stepType) {
case GLRStepType::RENDER:
{
int w = step.render.framebuffer ? step.render.framebuffer->width : targetWidth_;
int h = step.render.framebuffer ? step.render.framebuffer->height : targetHeight_;
snprintf(buffer, sizeof(buffer), "RENDER %s %s (commands: %d, %dx%d)\n", step.tag, step.render.framebuffer ? step.render.framebuffer->Tag() : "", (int)step.commands.size(), w, h);
break;
}
case GLRStepType::COPY:
snprintf(buffer, sizeof(buffer), "COPY '%s' %s -> %s (%dx%d, %s)\n", step.tag, step.copy.src->Tag(), step.copy.dst->Tag(), step.copy.srcRect.w, step.copy.srcRect.h, GLRAspectToString((GLRAspect)step.copy.aspectMask));
break;
case GLRStepType::BLIT:
snprintf(buffer, sizeof(buffer), "BLIT '%s' %s -> %s (%dx%d->%dx%d, %s)\n", step.tag, step.copy.src->Tag(), step.copy.dst->Tag(), step.blit.srcRect.w, step.blit.srcRect.h, step.blit.dstRect.w, step.blit.dstRect.h, GLRAspectToString((GLRAspect)step.blit.aspectMask));
break;
case GLRStepType::READBACK:
snprintf(buffer, sizeof(buffer), "READBACK '%s' %s (%dx%d, %s)\n", step.tag, step.readback.src ? step.readback.src->Tag() : "(backbuffer)", step.readback.srcRect.w, step.readback.srcRect.h, GLRAspectToString((GLRAspect)step.readback.aspectMask));
break;
case GLRStepType::READBACK_IMAGE:
snprintf(buffer, sizeof(buffer), "READBACK_IMAGE '%s' (%dx%d)\n", step.tag, step.readback_image.srcRect.w, step.readback_image.srcRect.h);
break;
case GLRStepType::RENDER_SKIP:
snprintf(buffer, sizeof(buffer), "(RENDER_SKIP) %s\n", step.tag);
break;
default:
buffer[0] = 0;
break;
}
return std::string(buffer);
}
const char *GLRAspectToString(GLRAspect aspect) {
switch (aspect) {
case GLR_ASPECT_COLOR: return "COLOR";
case GLR_ASPECT_DEPTH: return "DEPTH";
case GLR_ASPECT_STENCIL: return "STENCIL";
default: return "N/A";
}
}
const char *RenderCommandToString(GLRRenderCommand cmd) {
switch (cmd) {
case GLRRenderCommand::DEPTH: return "DEPTH";
case GLRRenderCommand::STENCIL: return "STENCIL";
case GLRRenderCommand::BLEND: return "BLEND";
case GLRRenderCommand::BLENDCOLOR: return "BLENDCOLOR";
case GLRRenderCommand::LOGICOP: return "LOGICOP";
case GLRRenderCommand::UNIFORM4I: return "UNIFORM4I";
case GLRRenderCommand::UNIFORM4UI: return "UNIFORM4UI";
case GLRRenderCommand::UNIFORM4F: return "UNIFORM4F";
case GLRRenderCommand::UNIFORMMATRIX: return "UNIFORMMATRIX";
case GLRRenderCommand::UNIFORMSTEREOMATRIX: return "UNIFORMSTEREOMATRIX";
case GLRRenderCommand::TEXTURESAMPLER: return "TEXTURESAMPLER";
case GLRRenderCommand::TEXTURELOD: return "TEXTURELOD";
case GLRRenderCommand::VIEWPORT: return "VIEWPORT";
case GLRRenderCommand::SCISSOR: return "SCISSOR";
case GLRRenderCommand::RASTER: return "RASTER";
case GLRRenderCommand::CLEAR: return "CLEAR";
case GLRRenderCommand::INVALIDATE: return "INVALIDATE";
case GLRRenderCommand::BINDPROGRAM: return "BINDPROGRAM";
case GLRRenderCommand::BINDTEXTURE: return "BINDTEXTURE";
case GLRRenderCommand::BIND_FB_TEXTURE: return "BIND_FB_TEXTURE";
case GLRRenderCommand::BIND_VERTEX_BUFFER: return "BIND_VERTEX_BUFFER";
case GLRRenderCommand::GENMIPS: return "GENMIPS";
case GLRRenderCommand::DRAW: return "DRAW";
case GLRRenderCommand::TEXTURE_SUBIMAGE: return "TEXTURE_SUBIMAGE";
default: return "N/A";
}
}

View file

@ -6,12 +6,11 @@
#include <unordered_map> #include <unordered_map>
#include "Common/GPU/OpenGL/GLCommon.h" #include "Common/GPU/OpenGL/GLCommon.h"
#include "Common/GPU/OpenGL/GLFrameData.h"
#include "Common/GPU/DataFormat.h" #include "Common/GPU/DataFormat.h"
#include "Common/GPU/Shader.h" #include "Common/GPU/Shader.h"
#include "Common/GPU/thin3d.h" #include "Common/GPU/thin3d.h"
#include "Common/Data/Collections/TinySet.h" #include "Common/Data/Collections/TinySet.h"
#include "Common/Data/Collections/FastVec.h"
struct GLRViewport { struct GLRViewport {
float x, y, w, h, minZ, maxZ; float x, y, w, h, minZ, maxZ;
@ -25,7 +24,7 @@ struct GLOffset2D {
int x, y; int x, y;
}; };
enum class GLRAllocType : uint8_t { enum class GLRAllocType {
NONE, NONE,
NEW, NEW,
ALIGNED, ALIGNED,
@ -40,7 +39,8 @@ class GLRInputLayout;
enum class GLRRenderCommand : uint8_t { enum class GLRRenderCommand : uint8_t {
DEPTH, DEPTH,
STENCIL, STENCILFUNC,
STENCILOP,
BLEND, BLEND,
BLENDCOLOR, BLENDCOLOR,
LOGICOP, LOGICOP,
@ -60,8 +60,10 @@ enum class GLRRenderCommand : uint8_t {
BINDTEXTURE, BINDTEXTURE,
BIND_FB_TEXTURE, BIND_FB_TEXTURE,
BIND_VERTEX_BUFFER, BIND_VERTEX_BUFFER,
BIND_BUFFER,
GENMIPS, GENMIPS,
DRAW, DRAW,
DRAW_INDEXED,
TEXTURE_SUBIMAGE, TEXTURE_SUBIMAGE,
}; };
@ -69,7 +71,6 @@ enum class GLRRenderCommand : uint8_t {
// type field, smashed right after each other?) // type field, smashed right after each other?)
// Also, all GLenums are really only 16 bits. // Also, all GLenums are really only 16 bits.
struct GLRRenderData { struct GLRRenderData {
GLRRenderData(GLRRenderCommand _cmd) : cmd(_cmd) {}
GLRRenderCommand cmd; GLRRenderCommand cmd;
union { union {
struct { struct {
@ -99,23 +100,26 @@ struct GLRRenderData {
GLenum func; GLenum func;
uint8_t ref; uint8_t ref;
uint8_t compareMask; uint8_t compareMask;
} stencilFunc;
struct {
GLenum sFail; GLenum sFail;
GLenum zFail; GLenum zFail;
GLenum pass; GLenum pass;
uint8_t writeMask; uint8_t writeMask;
} stencil; } stencilOp; // also write mask
struct { struct {
GLRInputLayout *inputLayout;
GLRBuffer *vertexBuffer;
GLRBuffer *indexBuffer;
uint32_t vertexOffset;
uint32_t indexOffset;
GLenum mode; // primitive GLenum mode; // primitive
GLint buffer;
GLint first; GLint first;
GLint count; GLint count;
GLint indexType;
GLint instances;
} draw; } draw;
struct {
GLenum mode; // primitive
GLint count;
GLint instances;
GLint indexType;
void *indices;
} drawIndexed;
struct { struct {
const char *name; // if null, use loc const char *name; // if null, use loc
const GLint *loc; // NOTE: This is a pointer so we can immediately use things that are "queried" during program creation. const GLint *loc; // NOTE: This is a pointer so we can immediately use things that are "queried" during program creation.
@ -125,13 +129,8 @@ struct GLRRenderData {
struct { struct {
const char *name; // if null, use loc const char *name; // if null, use loc
const GLint *loc; const GLint *loc;
float m[16]; float m[32];
} uniformMatrix4; } uniformMatrix4;
struct {
const char *name; // if null, use loc
const GLint *loc;
float *mData; // new'd, 32 entries
} uniformStereoMatrix4;
struct { struct {
uint32_t clearColor; uint32_t clearColor;
float clearZ; float clearZ;
@ -171,6 +170,11 @@ struct GLRRenderData {
struct { struct {
GLRProgram *program; GLRProgram *program;
} program; } program;
struct {
GLRInputLayout *inputLayout;
GLRBuffer *buffer;
size_t offset;
} bindVertexBuffer;
struct { struct {
int slot; int slot;
GLenum wrapS; GLenum wrapS;
@ -223,6 +227,7 @@ struct GLRInitStep {
union { union {
struct { struct {
GLRTexture *texture; GLRTexture *texture;
GLenum target;
} create_texture; } create_texture;
struct { struct {
GLRShader *shader; GLRShader *shader;
@ -290,17 +295,16 @@ enum class GLRRenderPassAction {
class GLRFramebuffer; class GLRFramebuffer;
enum GLRAspect { enum {
GLR_ASPECT_COLOR = 1, GLR_ASPECT_COLOR = 1,
GLR_ASPECT_DEPTH = 2, GLR_ASPECT_DEPTH = 2,
GLR_ASPECT_STENCIL = 3, GLR_ASPECT_STENCIL = 3,
}; };
const char *GLRAspectToString(GLRAspect aspect);
struct GLRStep { struct GLRStep {
GLRStep(GLRStepType _type) : stepType(_type) {} GLRStep(GLRStepType _type) : stepType(_type) {}
GLRStepType stepType; GLRStepType stepType;
FastVec<GLRRenderData> commands; std::vector<GLRRenderData> commands;
TinySet<const GLRFramebuffer *, 8> dependencies; TinySet<const GLRFramebuffer *, 8> dependencies;
const char *tag; const char *tag;
union { union {
@ -309,6 +313,8 @@ struct GLRStep {
GLRRenderPassAction color; GLRRenderPassAction color;
GLRRenderPassAction depth; GLRRenderPassAction depth;
GLRRenderPassAction stencil; GLRRenderPassAction stencil;
// Note: not accurate.
int numDraws;
} render; } render;
struct { struct {
GLRFramebuffer *src; GLRFramebuffer *src;
@ -352,14 +358,22 @@ public:
caps_ = caps; caps_ = caps;
} }
void RunInitSteps(const FastVec<GLRInitStep> &steps, bool skipGLCalls); int GetStereoBufferIndex(const char *uniformName);
std::string GetStereoBufferLayout(const char *uniformName);
void RunSteps(const std::vector<GLRStep *> &steps, GLFrameData &frameData, bool skipGLCalls, bool keepSteps, bool useVR); void RunInitSteps(const std::vector<GLRInitStep> &steps, bool skipGLCalls);
void RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCalls, bool keepSteps, bool useVR);
void LogSteps(const std::vector<GLRStep *> &steps);
void CreateDeviceObjects(); void CreateDeviceObjects();
void DestroyDeviceObjects(); void DestroyDeviceObjects();
void CopyFromReadbackBuffer(GLRFramebuffer *framebuffer, int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels); inline int RPIndex(GLRRenderPassAction color, GLRRenderPassAction depth) {
return (int)depth * 3 + (int)color;
}
void CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels);
void Resize(int width, int height) { void Resize(int width, int height) {
targetWidth_ = width; targetWidth_ = width;
@ -379,7 +393,7 @@ private:
void InitCreateFramebuffer(const GLRInitStep &step); void InitCreateFramebuffer(const GLRInitStep &step);
void PerformBindFramebufferAsRenderTarget(const GLRStep &pass); void PerformBindFramebufferAsRenderTarget(const GLRStep &pass);
void PerformRenderPass(const GLRStep &pass, bool first, bool last, GLQueueProfileContext &profile); void PerformRenderPass(const GLRStep &pass, bool first, bool last);
void PerformCopy(const GLRStep &pass); void PerformCopy(const GLRStep &pass);
void PerformBlit(const GLRStep &pass); void PerformBlit(const GLRStep &pass);
void PerformReadback(const GLRStep &pass); void PerformReadback(const GLRStep &pass);
@ -390,8 +404,6 @@ private:
GLenum fbo_get_fb_target(bool read, GLuint **cached); GLenum fbo_get_fb_target(bool read, GLuint **cached);
void fbo_unbind(); void fbo_unbind();
std::string StepToString(const GLRStep &step) const;
GLRFramebuffer *curFB_ = nullptr; GLRFramebuffer *curFB_ = nullptr;
GLuint globalVAO_ = 0; GLuint globalVAO_ = 0;
@ -407,7 +419,9 @@ private:
// We size it generously. // We size it generously.
uint8_t *readbackBuffer_ = nullptr; uint8_t *readbackBuffer_ = nullptr;
int readbackBufferSize_ = 0; int readbackBufferSize_ = 0;
uint32_t readbackAspectMask_ = 0; // Temp buffer for color conversion
uint8_t *tempBuffer_ = nullptr;
int tempBufferSize_ = 0;
float maxAnisotropyLevel_ = 0.0f; float maxAnisotropyLevel_ = 0.0f;
@ -415,6 +429,10 @@ private:
GLuint currentDrawHandle_ = 0; GLuint currentDrawHandle_ = 0;
GLuint currentReadHandle_ = 0; GLuint currentReadHandle_ = 0;
GLuint AllocTextureName();
// Texture name cache. Ripped straight from TextureCacheGLES.
std::vector<GLuint> nameCache_;
std::unordered_map<int, std::string> glStrings_; std::unordered_map<int, std::string> glStrings_;
bool sawOutOfMemory_ = false; bool sawOutOfMemory_ = false;
@ -423,5 +441,3 @@ private:
ErrorCallbackFn errorCallback_ = nullptr; ErrorCallbackFn errorCallback_ = nullptr;
void *errorCallbackUserData_ = nullptr; void *errorCallbackUserData_ = nullptr;
}; };
const char *RenderCommandToString(GLRRenderCommand cmd);

View file

@ -8,7 +8,6 @@
#include "Common/Log.h" #include "Common/Log.h"
#include "Common/TimeUtil.h" #include "Common/TimeUtil.h"
#include "Common/MemoryUtil.h" #include "Common/MemoryUtil.h"
#include "Common/StringUtils.h"
#include "Common/Math/math_util.h" #include "Common/Math/math_util.h"
#if 0 // def _DEBUG #if 0 // def _DEBUG
@ -17,7 +16,12 @@
#define VLOG(...) #define VLOG(...)
#endif #endif
std::thread::id renderThreadId; static std::thread::id renderThreadId;
#if MAX_LOGLEVEL >= DEBUG_LEVEL
static bool OnRenderThread() {
return std::this_thread::get_id() == renderThreadId;
}
#endif
GLRTexture::GLRTexture(const Draw::DeviceCaps &caps, int width, int height, int depth, int numMips) { GLRTexture::GLRTexture(const Draw::DeviceCaps &caps, int width, int height, int depth, int numMips) {
if (caps.textureNPOTFullySupported) { if (caps.textureNPOTFullySupported) {
@ -37,9 +41,76 @@ GLRTexture::~GLRTexture() {
} }
} }
GLRenderManager::GLRenderManager(HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory) : frameTimeHistory_(frameTimeHistory) { void GLDeleter::Take(GLDeleter &other) {
// size_t sz = sizeof(GLRRenderData); _assert_msg_(IsEmpty(), "Deleter already has stuff");
// _dbg_assert_(sz == 88); shaders = std::move(other.shaders);
programs = std::move(other.programs);
buffers = std::move(other.buffers);
textures = std::move(other.textures);
inputLayouts = std::move(other.inputLayouts);
framebuffers = std::move(other.framebuffers);
pushBuffers = std::move(other.pushBuffers);
other.shaders.clear();
other.programs.clear();
other.buffers.clear();
other.textures.clear();
other.inputLayouts.clear();
other.framebuffers.clear();
other.pushBuffers.clear();
}
// Runs on the GPU thread.
void GLDeleter::Perform(GLRenderManager *renderManager, bool skipGLCalls) {
for (auto pushBuffer : pushBuffers) {
renderManager->UnregisterPushBuffer(pushBuffer);
if (skipGLCalls) {
pushBuffer->Destroy(false);
}
delete pushBuffer;
}
pushBuffers.clear();
for (auto shader : shaders) {
if (skipGLCalls)
shader->shader = 0; // prevent the glDeleteShader
delete shader;
}
shaders.clear();
for (auto program : programs) {
if (skipGLCalls)
program->program = 0; // prevent the glDeleteProgram
delete program;
}
programs.clear();
for (auto buffer : buffers) {
if (skipGLCalls)
buffer->buffer_ = 0;
delete buffer;
}
buffers.clear();
for (auto texture : textures) {
if (skipGLCalls)
texture->texture = 0;
delete texture;
}
textures.clear();
for (auto inputLayout : inputLayouts) {
// No GL objects in an inputLayout yet
delete inputLayout;
}
inputLayouts.clear();
for (auto framebuffer : framebuffers) {
if (skipGLCalls) {
framebuffer->handle = 0;
framebuffer->color_texture.texture = 0;
framebuffer->z_stencil_buffer = 0;
framebuffer->z_stencil_texture.texture = 0;
framebuffer->z_buffer = 0;
framebuffer->stencil_buffer = 0;
framebuffer->colorFormat = Draw::DataFormat::UNDEFINED;
}
delete framebuffer;
}
framebuffers.clear();
} }
GLRenderManager::~GLRenderManager() { GLRenderManager::~GLRenderManager() {
@ -129,25 +200,25 @@ bool GLRenderManager::ThreadFrame() {
return false; return false;
} }
GLRRenderThreadTask *task = nullptr; GLRRenderThreadTask task;
// In case of syncs or other partial completion, we keep going until we complete a frame. // In case of syncs or other partial completion, we keep going until we complete a frame.
while (true) { while (true) {
// Pop a task of the queue and execute it. // Pop a task of the queue and execute it.
// NOTE: We need to actually wait for a task, we can't just bail! // NOTE: We need to actually wait for a task, we can't just bail!
{ {
std::unique_lock<std::mutex> lock(pushMutex_); std::unique_lock<std::mutex> lock(pushMutex_);
while (renderThreadQueue_.empty()) { while (renderThreadQueue_.empty()) {
pushCondVar_.wait(lock); pushCondVar_.wait(lock);
} }
task = std::move(renderThreadQueue_.front()); task = renderThreadQueue_.front();
renderThreadQueue_.pop(); renderThreadQueue_.pop();
} }
// We got a task! We can now have pushMutex_ unlocked, allowing the host to // We got a task! We can now have pushMutex_ unlocked, allowing the host to
// push more work when it feels like it, and just start working. // push more work when it feels like it, and just start working.
if (task->runType == GLRRunType::EXIT) { if (task.runType == GLRRunType::EXIT) {
delete task;
// Oh, host wanted out. Let's leave, and also let's notify the host. // Oh, host wanted out. Let's leave, and also let's notify the host.
// This is unlike Vulkan too which can just block on the thread existing. // This is unlike Vulkan too which can just block on the thread existing.
std::unique_lock<std::mutex> lock(syncMutex_); std::unique_lock<std::mutex> lock(syncMutex_);
@ -157,13 +228,11 @@ bool GLRenderManager::ThreadFrame() {
} }
// Render the scene. // Render the scene.
VLOG(" PULL: Frame %d RUN (%0.3f)", task->frame, time_now_d()); VLOG(" PULL: Frame %d RUN (%0.3f)", task.frame, time_now_d());
if (Run(*task)) { if (Run(task)) {
// Swap requested, so we just bail the loop. // Swap requested, so we just bail the loop.
delete task;
break; break;
} }
delete task;
}; };
return true; return true;
@ -176,21 +245,15 @@ void GLRenderManager::StopThread() {
run_ = false; run_ = false;
std::unique_lock<std::mutex> lock(pushMutex_); std::unique_lock<std::mutex> lock(pushMutex_);
renderThreadQueue_.push(new GLRRenderThreadTask(GLRRunType::EXIT)); GLRRenderThreadTask exitTask{};
exitTask.runType = GLRRunType::EXIT;
renderThreadQueue_.push(exitTask);
pushCondVar_.notify_one(); pushCondVar_.notify_one();
} else { } else {
WARN_LOG(G3D, "GL submission thread was already paused."); WARN_LOG(G3D, "GL submission thread was already paused.");
} }
} }
std::string GLRenderManager::GetGpuProfileString() const {
int curFrame = curFrame_;
const GLQueueProfileContext &profile = frameData_[curFrame].profile;
float cputime_ms = 1000.0f * (profile.cpuEndTime - profile.cpuStartTime);
return StringFromFormat("CPU time to run the list: %0.2f ms\n\n%s", cputime_ms, profilePassesString_.c_str());
}
void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, GLRRenderPassAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag) { void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, GLRRenderPassAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag) {
_assert_(insideFrame_); _assert_(insideFrame_);
#ifdef _DEBUG #ifdef _DEBUG
@ -198,7 +261,7 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende
#endif #endif
// Eliminate dupes. // Eliminate dupes.
if (steps_.size() && steps_.back()->stepType == GLRStepType::RENDER && steps_.back()->render.framebuffer == fb) { if (steps_.size() && steps_.back()->render.framebuffer == fb && steps_.back()->stepType == GLRStepType::RENDER) {
if (color != GLRRenderPassAction::CLEAR && depth != GLRRenderPassAction::CLEAR && stencil != GLRRenderPassAction::CLEAR) { if (color != GLRRenderPassAction::CLEAR && depth != GLRRenderPassAction::CLEAR && stencil != GLRRenderPassAction::CLEAR) {
// We don't move to a new step, this bind was unnecessary and we can safely skip it. // We don't move to a new step, this bind was unnecessary and we can safely skip it.
curRenderStep_ = steps_.back(); curRenderStep_ = steps_.back();
@ -206,7 +269,7 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende
} }
} }
if (curRenderStep_ && curRenderStep_->commands.size() == 0) { if (curRenderStep_ && curRenderStep_->commands.size() == 0) {
VLOG("Empty render step. Usually happens after uploading pixels."); VLOG("Empty render step. Usually happens after uploading pixels..");
} }
GLRStep *step = new GLRStep{ GLRStepType::RENDER }; GLRStep *step = new GLRStep{ GLRStepType::RENDER };
@ -215,11 +278,13 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende
step->render.color = color; step->render.color = color;
step->render.depth = depth; step->render.depth = depth;
step->render.stencil = stencil; step->render.stencil = stencil;
step->render.numDraws = 0;
step->tag = tag; step->tag = tag;
steps_.push_back(step); steps_.push_back(step);
GLuint clearMask = 0; GLuint clearMask = 0;
GLRRenderData data(GLRRenderCommand::CLEAR); GLRRenderData data;
data.cmd = GLRRenderCommand::CLEAR;
if (color == GLRRenderPassAction::CLEAR) { if (color == GLRRenderPassAction::CLEAR) {
clearMask |= GL_COLOR_BUFFER_BIT; clearMask |= GL_COLOR_BUFFER_BIT;
data.clear.clearColor = clearColor; data.clear.clearColor = clearColor;
@ -296,7 +361,7 @@ void GLRenderManager::BlitFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLR
steps_.push_back(step); steps_.push_back(step);
} }
bool GLRenderManager::CopyFramebufferToMemory(GLRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, Draw::ReadbackMode mode, const char *tag) { bool GLRenderManager::CopyFramebufferToMemorySync(GLRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag) {
_assert_(pixels); _assert_(pixels);
GLRStep *step = new GLRStep{ GLRStepType::READBACK }; GLRStep *step = new GLRStep{ GLRStepType::READBACK };
@ -323,7 +388,7 @@ bool GLRenderManager::CopyFramebufferToMemory(GLRFramebuffer *src, int aspectBit
} else { } else {
return false; return false;
} }
queueRunner_.CopyFromReadbackBuffer(src, w, h, srcFormat, destFormat, pixelStride, pixels); queueRunner_.CopyReadbackBuffer(w, h, srcFormat, destFormat, pixelStride, pixels);
return true; return true;
} }
@ -340,28 +405,20 @@ void GLRenderManager::CopyImageToMemorySync(GLRTexture *texture, int mipLevel, i
curRenderStep_ = nullptr; curRenderStep_ = nullptr;
FlushSync(); FlushSync();
queueRunner_.CopyFromReadbackBuffer(nullptr, w, h, Draw::DataFormat::R8G8B8A8_UNORM, destFormat, pixelStride, pixels); queueRunner_.CopyReadbackBuffer(w, h, Draw::DataFormat::R8G8B8A8_UNORM, destFormat, pixelStride, pixels);
} }
void GLRenderManager::BeginFrame(bool enableProfiling) { void GLRenderManager::BeginFrame() {
#ifdef _DEBUG #ifdef _DEBUG
curProgram_ = nullptr; curProgram_ = nullptr;
#endif #endif
int curFrame = GetCurFrame(); int curFrame = GetCurFrame();
FrameTimeData &frameTimeData = frameTimeHistory_.Add(frameIdGen_); FrameData &frameData = frameData_[curFrame];
frameTimeData.frameBegin = time_now_d();
frameTimeData.afterFenceWait = frameTimeData.frameBegin;
GLFrameData &frameData = frameData_[curFrame];
frameData.frameId = frameIdGen_;
frameData.profile.enabled = enableProfiling;
frameIdGen_++;
{ {
std::unique_lock<std::mutex> lock(frameData.fenceMutex);
VLOG("PUSH: BeginFrame (curFrame = %d, readyForFence = %d, time=%0.3f)", curFrame, (int)frameData.readyForFence, time_now_d()); VLOG("PUSH: BeginFrame (curFrame = %d, readyForFence = %d, time=%0.3f)", curFrame, (int)frameData.readyForFence, time_now_d());
std::unique_lock<std::mutex> lock(frameData.fenceMutex);
while (!frameData.readyForFence) { while (!frameData.readyForFence) {
frameData.fenceCondVar.wait(lock); frameData.fenceCondVar.wait(lock);
} }
@ -378,99 +435,36 @@ void GLRenderManager::BeginFrame(bool enableProfiling) {
void GLRenderManager::Finish() { void GLRenderManager::Finish() {
curRenderStep_ = nullptr; // EndCurRenderStep is this simple here. curRenderStep_ = nullptr; // EndCurRenderStep is this simple here.
int curFrame = curFrame_; int curFrame = GetCurFrame();
GLFrameData &frameData = frameData_[curFrame]; FrameData &frameData = frameData_[curFrame];
frameTimeHistory_[frameData.frameId].firstSubmit = time_now_d();
frameData_[curFrame].deleter.Take(deleter_); frameData_[curFrame].deleter.Take(deleter_);
if (frameData.profile.enabled) {
profilePassesString_ = std::move(frameData.profile.passesString);
#ifdef _DEBUG
std::string cmdString;
for (int i = 0; i < ARRAY_SIZE(frameData.profile.commandCounts); i++) {
if (frameData.profile.commandCounts[i] > 0) {
cmdString += StringFromFormat("%s: %d\n", RenderCommandToString((GLRRenderCommand)i), frameData.profile.commandCounts[i]);
}
}
memset(frameData.profile.commandCounts, 0, sizeof(frameData.profile.commandCounts));
profilePassesString_ = cmdString + profilePassesString_;
#endif
frameData.profile.passesString.clear();
}
VLOG("PUSH: Finish, pushing task. curFrame = %d", curFrame); VLOG("PUSH: Finish, pushing task. curFrame = %d", curFrame);
GLRRenderThreadTask *task = new GLRRenderThreadTask(GLRRunType::SUBMIT); GLRRenderThreadTask task;
task->frame = curFrame; task.frame = curFrame;
task.runType = GLRRunType::PRESENT;
{ {
std::unique_lock<std::mutex> lock(pushMutex_); std::unique_lock<std::mutex> lock(pushMutex_);
renderThreadQueue_.push(task); renderThreadQueue_.push(task);
renderThreadQueue_.back()->initSteps = std::move(initSteps_); renderThreadQueue_.back().initSteps = std::move(initSteps_);
renderThreadQueue_.back()->steps = std::move(steps_); renderThreadQueue_.back().steps = std::move(steps_);
initSteps_.clear(); initSteps_.clear();
steps_.clear(); steps_.clear();
pushCondVar_.notify_one(); pushCondVar_.notify_one();
} }
}
void GLRenderManager::Present() { curFrame_++;
GLRRenderThreadTask *presentTask = new GLRRenderThreadTask(GLRRunType::PRESENT); if (curFrame_ >= inflightFrames_)
presentTask->frame = curFrame_; curFrame_ = 0;
{
std::unique_lock<std::mutex> lock(pushMutex_);
renderThreadQueue_.push(presentTask);
pushCondVar_.notify_one();
}
int newCurFrame = curFrame_ + 1;
if (newCurFrame >= inflightFrames_) {
newCurFrame = 0;
}
curFrame_ = newCurFrame;
insideFrame_ = false; insideFrame_ = false;
} }
// Render thread. Returns true if the caller should handle a swap. // Render thread. Returns true if the caller should handle a swap.
bool GLRenderManager::Run(GLRRenderThreadTask &task) { bool GLRenderManager::Run(GLRRenderThreadTask &task) {
_dbg_assert_(task.frame >= 0); FrameData &frameData = frameData_[task.frame];
GLFrameData &frameData = frameData_[task.frame];
if (task.runType == GLRRunType::PRESENT) {
bool swapRequest = false;
if (!frameData.skipSwap) {
frameTimeHistory_[frameData.frameId].queuePresent = time_now_d();
if (swapIntervalChanged_) {
swapIntervalChanged_ = false;
if (swapIntervalFunction_) {
swapIntervalFunction_(swapInterval_);
}
}
// This is the swapchain framebuffer flip.
if (swapFunction_) {
VLOG(" PULL: SwapFunction()");
swapFunction_();
}
swapRequest = true;
} else {
frameData.skipSwap = false;
}
frameData.hasBegun = false;
VLOG(" PULL: Frame %d.readyForFence = true", task.frame);
{
std::lock_guard<std::mutex> lock(frameData.fenceMutex);
frameData.readyForFence = true;
frameData.fenceCondVar.notify_one();
// At this point, we're done with this framedata (for now).
}
return swapRequest;
}
if (!frameData.hasBegun) { if (!frameData.hasBegun) {
frameData.hasBegun = true; frameData.hasBegun = true;
@ -490,23 +484,15 @@ bool GLRenderManager::Run(GLRRenderThreadTask &task) {
} }
} }
if (frameData.profile.enabled) {
frameData.profile.cpuStartTime = time_now_d();
}
if (IsVREnabled()) { if (IsVREnabled()) {
int passes = GetVRPassesCount(); int passes = GetVRPassesCount();
for (int i = 0; i < passes; i++) { for (int i = 0; i < passes; i++) {
PreVRFrameRender(i); PreVRFrameRender(i);
queueRunner_.RunSteps(task.steps, frameData, skipGLCalls_, i < passes - 1, true); queueRunner_.RunSteps(task.steps, skipGLCalls_, i < passes - 1, true);
PostVRFrameRender(); PostVRFrameRender();
} }
} else { } else {
queueRunner_.RunSteps(task.steps, frameData, skipGLCalls_, false, false); queueRunner_.RunSteps(task.steps, skipGLCalls_, false, false);
}
if (frameData.profile.enabled) {
frameData.profile.cpuEndTime = time_now_d();
} }
if (!skipGLCalls_) { if (!skipGLCalls_) {
@ -515,8 +501,43 @@ bool GLRenderManager::Run(GLRRenderThreadTask &task) {
} }
} }
bool swapRequest = false;
switch (task.runType) { switch (task.runType) {
case GLRRunType::SUBMIT: case GLRRunType::PRESENT:
if (!frameData.skipSwap) {
if (swapIntervalChanged_) {
swapIntervalChanged_ = false;
if (swapIntervalFunction_) {
swapIntervalFunction_(swapInterval_);
}
}
// This is the swapchain framebuffer flip.
if (swapFunction_) {
VLOG(" PULL: SwapFunction()");
swapFunction_();
if (!retainControl_) {
// get out of here.
swapRequest = true;
}
} else {
VLOG(" PULL: SwapRequested");
swapRequest = true;
}
} else {
frameData.skipSwap = false;
}
frameData.hasBegun = false;
VLOG(" PULL: Frame %d.readyForFence = true", task.frame);
{
std::lock_guard<std::mutex> lock(frameData.fenceMutex);
frameData.readyForFence = true;
frameData.fenceCondVar.notify_one();
// At this point, we're done with this framedata (for now).
}
break; break;
case GLRRunType::SYNC: case GLRRunType::SYNC:
@ -525,7 +546,7 @@ bool GLRenderManager::Run(GLRRenderThreadTask &task) {
// glFinish is not actually necessary here, and won't be unless we start using // glFinish is not actually necessary here, and won't be unless we start using
// glBufferStorage. Then we need to use fences. // glBufferStorage. Then we need to use fences.
{ {
std::lock_guard<std::mutex> lock(syncMutex_); std::unique_lock<std::mutex> lock(syncMutex_);
syncDone_ = true; syncDone_ = true;
syncCondVar_.notify_one(); syncCondVar_.notify_one();
} }
@ -535,20 +556,21 @@ bool GLRenderManager::Run(GLRRenderThreadTask &task) {
_assert_(false); _assert_(false);
} }
VLOG(" PULL: ::Run(): Done running tasks"); VLOG(" PULL: ::Run(): Done running tasks");
return false; return swapRequest;
} }
void GLRenderManager::FlushSync() { void GLRenderManager::FlushSync() {
{ {
VLOG("PUSH: Frame[%d].readyForRun = true (sync)", curFrame_); VLOG("PUSH: Frame[%d].readyForRun = true (sync)", curFrame_);
GLRRenderThreadTask *task = new GLRRenderThreadTask(GLRRunType::SYNC); GLRRenderThreadTask task;
task->frame = curFrame_; task.frame = curFrame_;
task.runType = GLRRunType::SYNC;
std::unique_lock<std::mutex> lock(pushMutex_); std::unique_lock<std::mutex> lock(pushMutex_);
renderThreadQueue_.push(task); renderThreadQueue_.push(task);
renderThreadQueue_.back()->initSteps = std::move(initSteps_); renderThreadQueue_.back().initSteps = std::move(initSteps_);
renderThreadQueue_.back()->steps = std::move(steps_); renderThreadQueue_.back().steps = std::move(steps_);
pushCondVar_.notify_one(); pushCondVar_.notify_one();
steps_.clear(); steps_.clear();
} }
@ -563,3 +585,254 @@ void GLRenderManager::FlushSync() {
syncDone_ = false; syncDone_ = false;
} }
} }
GLPushBuffer::GLPushBuffer(GLRenderManager *render, GLuint target, size_t size) : render_(render), size_(size), target_(target) {
bool res = AddBuffer();
_assert_(res);
}
GLPushBuffer::~GLPushBuffer() {
Destroy(true);
}
void GLPushBuffer::Map() {
_assert_(!writePtr_);
auto &info = buffers_[buf_];
writePtr_ = info.deviceMemory ? info.deviceMemory : info.localMemory;
info.flushOffset = 0;
// Force alignment. This is needed for PushAligned() to work as expected.
while ((intptr_t)writePtr_ & 15) {
writePtr_++;
offset_++;
info.flushOffset++;
}
_assert_(writePtr_);
}
void GLPushBuffer::Unmap() {
_assert_(writePtr_);
if (!buffers_[buf_].deviceMemory) {
// Here we simply upload the data to the last buffer.
// Might be worth trying with size_ instead of offset_, so the driver can replace
// the whole buffer. At least if it's close.
render_->BufferSubdata(buffers_[buf_].buffer, 0, offset_, buffers_[buf_].localMemory, false);
} else {
buffers_[buf_].flushOffset = offset_;
}
writePtr_ = nullptr;
}
void GLPushBuffer::Flush() {
// Must be called from the render thread.
_dbg_assert_(OnRenderThread());
buffers_[buf_].flushOffset = offset_;
if (!buffers_[buf_].deviceMemory && writePtr_) {
auto &info = buffers_[buf_];
if (info.flushOffset != 0) {
_assert_(info.buffer->buffer_);
glBindBuffer(target_, info.buffer->buffer_);
glBufferSubData(target_, 0, info.flushOffset, info.localMemory);
}
// Here we will submit all the draw calls, with the already known buffer and offsets.
// Might as well reset the write pointer here and start over the current buffer.
writePtr_ = info.localMemory;
offset_ = 0;
info.flushOffset = 0;
}
// For device memory, we flush all buffers here.
if ((strategy_ & GLBufferStrategy::MASK_FLUSH) != 0) {
for (auto &info : buffers_) {
if (info.flushOffset == 0 || !info.deviceMemory)
continue;
glBindBuffer(target_, info.buffer->buffer_);
glFlushMappedBufferRange(target_, 0, info.flushOffset);
info.flushOffset = 0;
}
}
}
bool GLPushBuffer::AddBuffer() {
BufInfo info;
info.localMemory = (uint8_t *)AllocateAlignedMemory(size_, 16);
if (!info.localMemory)
return false;
info.buffer = render_->CreateBuffer(target_, size_, GL_DYNAMIC_DRAW);
buf_ = buffers_.size();
buffers_.push_back(info);
return true;
}
void GLPushBuffer::Destroy(bool onRenderThread) {
if (buf_ == -1)
return; // Already destroyed
for (BufInfo &info : buffers_) {
// This will automatically unmap device memory, if needed.
// NOTE: We immediately delete the buffer, don't go through the deleter, if we're on the render thread.
if (onRenderThread) {
delete info.buffer;
} else {
render_->DeleteBuffer(info.buffer);
}
FreeAlignedMemory(info.localMemory);
}
buffers_.clear();
buf_ = -1;
}
void GLPushBuffer::NextBuffer(size_t minSize) {
// First, unmap the current memory.
Unmap();
buf_++;
if (buf_ >= buffers_.size() || minSize > size_) {
// Before creating the buffer, adjust to the new size_ if necessary.
while (size_ < minSize) {
size_ <<= 1;
}
bool res = AddBuffer();
_assert_(res);
if (!res) {
// Let's try not to crash at least?
buf_ = 0;
}
}
// Now, move to the next buffer and map it.
offset_ = 0;
Map();
}
void GLPushBuffer::Defragment() {
_dbg_assert_msg_(!OnRenderThread(), "Defragment must not run on the render thread");
if (buffers_.size() <= 1) {
// Let's take this chance to jetison localMemory we don't need.
for (auto &info : buffers_) {
if (info.deviceMemory) {
FreeAlignedMemory(info.localMemory);
info.localMemory = nullptr;
}
}
return;
}
// Okay, we have more than one. Destroy them all and start over with a larger one.
size_t newSize = size_ * buffers_.size();
Destroy(false);
size_ = newSize;
bool res = AddBuffer();
_assert_msg_(res, "AddBuffer failed");
}
size_t GLPushBuffer::GetTotalSize() const {
size_t sum = 0;
if (buffers_.size() > 1)
sum += size_ * (buffers_.size() - 1);
sum += offset_;
return sum;
}
void GLPushBuffer::MapDevice(GLBufferStrategy strategy) {
_dbg_assert_msg_(OnRenderThread(), "MapDevice must run on render thread");
strategy_ = strategy;
if (strategy_ == GLBufferStrategy::SUBDATA) {
return;
}
bool mapChanged = false;
for (auto &info : buffers_) {
if (!info.buffer->buffer_ || info.deviceMemory) {
// Can't map - no device buffer associated yet or already mapped.
continue;
}
info.deviceMemory = (uint8_t *)info.buffer->Map(strategy_);
mapChanged = mapChanged || info.deviceMemory != nullptr;
if (!info.deviceMemory && !info.localMemory) {
// Somehow it failed, let's dodge crashing.
info.localMemory = (uint8_t *)AllocateAlignedMemory(info.buffer->size_, 16);
mapChanged = true;
}
_dbg_assert_msg_(info.localMemory || info.deviceMemory, "Local or device memory must succeed");
}
if (writePtr_ && mapChanged) {
// This can happen during a sync. Remap.
writePtr_ = nullptr;
Map();
}
}
void GLPushBuffer::UnmapDevice() {
_dbg_assert_msg_(OnRenderThread(), "UnmapDevice must run on render thread");
for (auto &info : buffers_) {
if (info.deviceMemory) {
// TODO: Technically this can return false?
info.buffer->Unmap();
info.deviceMemory = nullptr;
}
}
}
void *GLRBuffer::Map(GLBufferStrategy strategy) {
_assert_(buffer_ != 0);
GLbitfield access = GL_MAP_WRITE_BIT;
if ((strategy & GLBufferStrategy::MASK_FLUSH) != 0) {
access |= GL_MAP_FLUSH_EXPLICIT_BIT;
}
if ((strategy & GLBufferStrategy::MASK_INVALIDATE) != 0) {
access |= GL_MAP_INVALIDATE_BUFFER_BIT;
}
void *p = nullptr;
bool allowNativeBuffer = strategy != GLBufferStrategy::SUBDATA;
if (allowNativeBuffer) {
glBindBuffer(target_, buffer_);
if (gl_extensions.ARB_buffer_storage || gl_extensions.EXT_buffer_storage) {
#if !PPSSPP_PLATFORM(IOS)
if (!hasStorage_) {
GLbitfield storageFlags = access & ~(GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
#ifdef USING_GLES2
#ifdef GL_EXT_buffer_storage
glBufferStorageEXT(target_, size_, nullptr, storageFlags);
#endif
#else
glBufferStorage(target_, size_, nullptr, storageFlags);
#endif
hasStorage_ = true;
}
#endif
p = glMapBufferRange(target_, 0, size_, access);
} else if (gl_extensions.VersionGEThan(3, 0, 0)) {
// GLES3 or desktop 3.
p = glMapBufferRange(target_, 0, size_, access);
} else if (!gl_extensions.IsGLES) {
#ifndef USING_GLES2
p = glMapBuffer(target_, GL_READ_WRITE);
#endif
}
}
mapped_ = p != nullptr;
return p;
}
bool GLRBuffer::Unmap() {
glBindBuffer(target_, buffer_);
mapped_ = false;
return glUnmapBuffer(target_) == GL_TRUE;
}

View file

@ -10,13 +10,11 @@
#include <queue> #include <queue>
#include <condition_variable> #include <condition_variable>
#include "Common/GPU/OpenGL/GLCommon.h"
#include "Common/GPU/MiscTypes.h" #include "Common/GPU/MiscTypes.h"
#include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Data/Convert/SmallDataConvert.h"
#include "Common/Log.h" #include "Common/Log.h"
#include "Common/GPU/OpenGL/GLQueueRunner.h" #include "GLQueueRunner.h"
#include "Common/GPU/OpenGL/GLFrameData.h"
#include "Common/GPU/OpenGL/GLCommon.h"
#include "Common/GPU/OpenGL/GLMemory.h"
class GLRInputLayout; class GLRInputLayout;
class GLPushBuffer; class GLPushBuffer;
@ -53,13 +51,11 @@ public:
class GLRFramebuffer { class GLRFramebuffer {
public: public:
GLRFramebuffer(const Draw::DeviceCaps &caps, int _width, int _height, bool z_stencil, const char *tag) GLRFramebuffer(const Draw::DeviceCaps &caps, Draw::DataFormat _colorFormat, int _width, int _height, bool z_stencil)
: color_texture(caps, _width, _height, 1, 1), z_stencil_texture(caps, _width, _height, 1, 1), : color_texture(caps, _width, _height, 1, 1), z_stencil_texture(caps, _width, _height, 1, 1),
width(_width), height(_height), z_stencil_(z_stencil) { colorFormat(_colorFormat), width(_width), height(_height), z_stencil_(z_stencil) {}
}
~GLRFramebuffer();
const char *Tag() const { return tag_.c_str(); } ~GLRFramebuffer();
GLuint handle = 0; GLuint handle = 0;
GLRTexture color_texture; GLRTexture color_texture;
@ -68,14 +64,13 @@ public:
GLRTexture z_stencil_texture; GLRTexture z_stencil_texture;
GLuint z_buffer = 0; GLuint z_buffer = 0;
GLuint stencil_buffer = 0; GLuint stencil_buffer = 0;
Draw::DataFormat colorFormat;
int width; int width;
int height; int height;
GLuint colorDepth = 0; GLuint colorDepth = 0;
bool z_stencil_;
private: bool z_stencil_;
std::string tag_;
}; };
// We need to create some custom heap-allocated types so we can forward things that need to be created on the GL thread, before // We need to create some custom heap-allocated types so we can forward things that need to be created on the GL thread, before
@ -183,6 +178,178 @@ private:
std::unordered_map<std::string, UniformInfo> uniformCache_; std::unordered_map<std::string, UniformInfo> uniformCache_;
}; };
enum class GLBufferStrategy {
SUBDATA = 0,
MASK_FLUSH = 0x10,
MASK_INVALIDATE = 0x20,
// Map/unmap the buffer each frame.
FRAME_UNMAP = 1,
// Map/unmap and also invalidate the buffer on map.
INVALIDATE_UNMAP = MASK_INVALIDATE,
// Map/unmap and explicitly flushed changed ranges.
FLUSH_UNMAP = MASK_FLUSH,
// Map/unmap, invalidate on map, and explicit flush.
FLUSH_INVALIDATE_UNMAP = MASK_FLUSH | MASK_INVALIDATE,
};
static inline int operator &(const GLBufferStrategy &lhs, const GLBufferStrategy &rhs) {
return (int)lhs & (int)rhs;
}
class GLRBuffer {
public:
GLRBuffer(GLuint target, size_t size) : target_(target), size_((int)size) {}
~GLRBuffer() {
if (buffer_) {
glDeleteBuffers(1, &buffer_);
}
}
void *Map(GLBufferStrategy strategy);
bool Unmap();
bool Mapped() const {
return mapped_;
}
GLuint buffer_ = 0;
GLuint target_;
int size_;
private:
bool mapped_ = false;
bool hasStorage_ = false;
};
class GLRenderManager;
// Similar to VulkanPushBuffer but is currently less efficient - it collects all the data in
// RAM then does a big memcpy/buffer upload at the end of the frame. This is at least a lot
// faster than the hundreds of buffer uploads or memory array buffers we used before.
// On modern GL we could avoid the copy using glBufferStorage but not sure it's worth the
// trouble.
// We need to manage the lifetime of this together with the other resources so its destructor
// runs on the render thread.
class GLPushBuffer {
public:
friend class GLRenderManager;
struct BufInfo {
GLRBuffer *buffer = nullptr;
uint8_t *localMemory = nullptr;
uint8_t *deviceMemory = nullptr;
size_t flushOffset = 0;
};
GLPushBuffer(GLRenderManager *render, GLuint target, size_t size);
~GLPushBuffer();
void Reset() { offset_ = 0; }
private:
// Needs context in case of defragment.
void Begin() {
buf_ = 0;
offset_ = 0;
// Note: we must defrag because some buffers may be smaller than size_.
Defragment();
Map();
_dbg_assert_(writePtr_);
}
void BeginNoReset() {
Map();
}
void End() {
Unmap();
}
public:
void Map();
void Unmap();
bool IsReady() const {
return writePtr_ != nullptr;
}
// When using the returned memory, make sure to bind the returned vkbuf.
// This will later allow for handling overflow correctly.
size_t Allocate(size_t numBytes, GLRBuffer **vkbuf) {
size_t out = offset_;
if (offset_ + ((numBytes + 3) & ~3) >= size_) {
NextBuffer(numBytes);
out = offset_;
offset_ += (numBytes + 3) & ~3;
} else {
offset_ += (numBytes + 3) & ~3; // Round up to 4 bytes.
}
*vkbuf = buffers_[buf_].buffer;
return out;
}
// Returns the offset that should be used when binding this buffer to get this data.
size_t Push(const void *data, size_t size, GLRBuffer **vkbuf) {
_dbg_assert_(writePtr_);
size_t off = Allocate(size, vkbuf);
memcpy(writePtr_ + off, data, size);
return off;
}
uint32_t PushAligned(const void *data, size_t size, int align, GLRBuffer **vkbuf) {
_dbg_assert_(writePtr_);
offset_ = (offset_ + align - 1) & ~(align - 1);
size_t off = Allocate(size, vkbuf);
memcpy(writePtr_ + off, data, size);
return (uint32_t)off;
}
size_t GetOffset() const {
return offset_;
}
// "Zero-copy" variant - you can write the data directly as you compute it.
// Recommended.
void *Push(size_t size, uint32_t *bindOffset, GLRBuffer **vkbuf) {
_dbg_assert_(writePtr_);
size_t off = Allocate(size, vkbuf);
*bindOffset = (uint32_t)off;
return writePtr_ + off;
}
void *PushAligned(size_t size, uint32_t *bindOffset, GLRBuffer **vkbuf, int align) {
_dbg_assert_(writePtr_);
offset_ = (offset_ + align - 1) & ~(align - 1);
size_t off = Allocate(size, vkbuf);
*bindOffset = (uint32_t)off;
return writePtr_ + off;
}
size_t GetTotalSize() const;
void Destroy(bool onRenderThread);
void Flush();
protected:
void MapDevice(GLBufferStrategy strategy);
void UnmapDevice();
private:
bool AddBuffer();
void NextBuffer(size_t minSize);
void Defragment();
GLRenderManager *render_;
std::vector<BufInfo> buffers_;
size_t buf_ = 0;
size_t offset_ = 0;
size_t size_ = 0;
uint8_t *writePtr_ = nullptr;
GLuint target_;
GLBufferStrategy strategy_ = GLBufferStrategy::SUBDATA;
};
class GLRInputLayout { class GLRInputLayout {
public: public:
struct Entry { struct Entry {
@ -190,15 +357,14 @@ public:
int count; int count;
GLenum type; GLenum type;
GLboolean normalized; GLboolean normalized;
int stride;
intptr_t offset; intptr_t offset;
}; };
std::vector<Entry> entries; std::vector<Entry> entries;
int stride;
int semanticsMask_ = 0; int semanticsMask_ = 0;
}; };
enum class GLRRunType { enum class GLRRunType {
SUBMIT,
PRESENT, PRESENT,
SYNC, SYNC,
EXIT, EXIT,
@ -207,19 +373,33 @@ enum class GLRRunType {
class GLRenderManager; class GLRenderManager;
class GLPushBuffer; class GLPushBuffer;
// These are enqueued from the main thread, and the render thread pops them off class GLDeleter {
public:
void Perform(GLRenderManager *renderManager, bool skipGLCalls);
bool IsEmpty() const {
return shaders.empty() && programs.empty() && buffers.empty() && textures.empty() && inputLayouts.empty() && framebuffers.empty() && pushBuffers.empty();
}
void Take(GLDeleter &other);
std::vector<GLRShader *> shaders;
std::vector<GLRProgram *> programs;
std::vector<GLRBuffer *> buffers;
std::vector<GLRTexture *> textures;
std::vector<GLRInputLayout *> inputLayouts;
std::vector<GLRFramebuffer *> framebuffers;
std::vector<GLPushBuffer *> pushBuffers;
};
// These are enqueued from the main thread,
// and the render thread pops them off
struct GLRRenderThreadTask { struct GLRRenderThreadTask {
GLRRenderThreadTask(GLRRunType _runType) : runType(_runType) {}
std::vector<GLRStep *> steps; std::vector<GLRStep *> steps;
FastVec<GLRInitStep> initSteps; std::vector<GLRInitStep> initSteps;
int frame = -1; int frame;
GLRRunType runType; GLRRunType runType;
// Avoid copying these by accident.
GLRRenderThreadTask(GLRRenderThreadTask &) = delete;
GLRRenderThreadTask& operator =(GLRRenderThreadTask &) = delete;
}; };
// Note: The GLRenderManager is created and destroyed on the render thread, and the latter // Note: The GLRenderManager is created and destroyed on the render thread, and the latter
@ -227,11 +407,9 @@ struct GLRRenderThreadTask {
// directly in the destructor. // directly in the destructor.
class GLRenderManager { class GLRenderManager {
public: public:
GLRenderManager(HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory); GLRenderManager() {}
~GLRenderManager(); ~GLRenderManager();
GLRenderManager(GLRenderManager &) = delete;
GLRenderManager &operator=(GLRenderManager &) = delete;
void SetInvalidationCallback(InvalidationCallback callback) { void SetInvalidationCallback(InvalidationCallback callback) {
invalidationCallback_ = callback; invalidationCallback_ = callback;
@ -249,52 +427,47 @@ public:
caps_ = caps; caps_ = caps;
} }
std::string GetGpuProfileString() const;
// Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again.
void BeginFrame(bool enableProfiling); void BeginFrame();
// Can run on a different thread! // Can run on a different thread!
void Finish(); void Finish();
void Present(); bool Run(GLRRenderThreadTask &task);
// Creation commands. These were not needed in Vulkan since there we can do that on the main thread. // Creation commands. These were not needed in Vulkan since there we can do that on the main thread.
// We pass in width/height here even though it's not strictly needed until we support glTextureStorage // We pass in width/height here even though it's not strictly needed until we support glTextureStorage
// and then we'll also need formats and stuff. // and then we'll also need formats and stuff.
GLRTexture *CreateTexture(GLenum target, int width, int height, int depth, int numMips) { GLRTexture *CreateTexture(GLenum target, int width, int height, int depth, int numMips) {
_dbg_assert_(target != 0); GLRInitStep step { GLRInitStepType::CREATE_TEXTURE };
GLRInitStep &step = initSteps_.push_uninitialized();
step.stepType = GLRInitStepType::CREATE_TEXTURE;
step.create_texture.texture = new GLRTexture(caps_, width, height, depth, numMips); step.create_texture.texture = new GLRTexture(caps_, width, height, depth, numMips);
step.create_texture.texture->target = target; step.create_texture.texture->target = target;
initSteps_.push_back(step);
return step.create_texture.texture; return step.create_texture.texture;
} }
GLRBuffer *CreateBuffer(GLuint target, size_t size, GLuint usage) { GLRBuffer *CreateBuffer(GLuint target, size_t size, GLuint usage) {
GLRInitStep &step = initSteps_.push_uninitialized(); GLRInitStep step{ GLRInitStepType::CREATE_BUFFER };
step.stepType = GLRInitStepType::CREATE_BUFFER;
step.create_buffer.buffer = new GLRBuffer(target, size); step.create_buffer.buffer = new GLRBuffer(target, size);
step.create_buffer.size = (int)size; step.create_buffer.size = (int)size;
step.create_buffer.usage = usage; step.create_buffer.usage = usage;
initSteps_.push_back(step);
return step.create_buffer.buffer; return step.create_buffer.buffer;
} }
GLRShader *CreateShader(GLuint stage, const std::string &code, const std::string &desc) { GLRShader *CreateShader(GLuint stage, const std::string &code, const std::string &desc) {
GLRInitStep &step = initSteps_.push_uninitialized(); GLRInitStep step{ GLRInitStepType::CREATE_SHADER };
step.stepType = GLRInitStepType::CREATE_SHADER;
step.create_shader.shader = new GLRShader(); step.create_shader.shader = new GLRShader();
step.create_shader.shader->desc = desc; step.create_shader.shader->desc = desc;
step.create_shader.stage = stage; step.create_shader.stage = stage;
step.create_shader.code = new char[code.size() + 1]; step.create_shader.code = new char[code.size() + 1];
memcpy(step.create_shader.code, code.data(), code.size() + 1); memcpy(step.create_shader.code, code.data(), code.size() + 1);
initSteps_.push_back(step);
return step.create_shader.shader; return step.create_shader.shader;
} }
GLRFramebuffer *CreateFramebuffer(int width, int height, bool z_stencil, const char *tag) { GLRFramebuffer *CreateFramebuffer(Draw::DataFormat colorFormat, int width, int height, bool z_stencil) {
_dbg_assert_(width > 0 && height > 0 && tag != nullptr); GLRInitStep step{ GLRInitStepType::CREATE_FRAMEBUFFER };
step.create_framebuffer.framebuffer = new GLRFramebuffer(caps_, colorFormat, width, height, z_stencil);
GLRInitStep &step = initSteps_.push_uninitialized(); initSteps_.push_back(step);
step.stepType = GLRInitStepType::CREATE_FRAMEBUFFER;
step.create_framebuffer.framebuffer = new GLRFramebuffer(caps_, width, height, z_stencil, tag);
return step.create_framebuffer.framebuffer; return step.create_framebuffer.framebuffer;
} }
@ -303,8 +476,7 @@ public:
GLRProgram *CreateProgram( GLRProgram *CreateProgram(
std::vector<GLRShader *> shaders, std::vector<GLRProgram::Semantic> semantics, std::vector<GLRProgram::UniformLocQuery> queries, std::vector<GLRShader *> shaders, std::vector<GLRProgram::Semantic> semantics, std::vector<GLRProgram::UniformLocQuery> queries,
std::vector<GLRProgram::Initializer> initializers, GLRProgramLocData *locData, const GLRProgramFlags &flags) { std::vector<GLRProgram::Initializer> initializers, GLRProgramLocData *locData, const GLRProgramFlags &flags) {
GLRInitStep &step = initSteps_.push_uninitialized(); GLRInitStep step{ GLRInitStepType::CREATE_PROGRAM };
step.stepType = GLRInitStepType::CREATE_PROGRAM;
_assert_(shaders.size() <= ARRAY_SIZE(step.create_program.shaders)); _assert_(shaders.size() <= ARRAY_SIZE(step.create_program.shaders));
step.create_program.program = new GLRProgram(); step.create_program.program = new GLRProgram();
step.create_program.program->semantics_ = semantics; step.create_program.program->semantics_ = semantics;
@ -328,53 +500,46 @@ public:
} }
#endif #endif
step.create_program.num_shaders = (int)shaders.size(); step.create_program.num_shaders = (int)shaders.size();
initSteps_.push_back(step);
return step.create_program.program; return step.create_program.program;
} }
GLRInputLayout *CreateInputLayout(const std::vector<GLRInputLayout::Entry> &entries, int stride) { GLRInputLayout *CreateInputLayout(const std::vector<GLRInputLayout::Entry> &entries) {
GLRInitStep &step = initSteps_.push_uninitialized(); GLRInitStep step{ GLRInitStepType::CREATE_INPUT_LAYOUT };
step.stepType = GLRInitStepType::CREATE_INPUT_LAYOUT;
step.create_input_layout.inputLayout = new GLRInputLayout(); step.create_input_layout.inputLayout = new GLRInputLayout();
step.create_input_layout.inputLayout->entries = entries; step.create_input_layout.inputLayout->entries = entries;
step.create_input_layout.inputLayout->stride = stride;
for (auto &iter : step.create_input_layout.inputLayout->entries) { for (auto &iter : step.create_input_layout.inputLayout->entries) {
step.create_input_layout.inputLayout->semanticsMask_ |= 1 << iter.location; step.create_input_layout.inputLayout->semanticsMask_ |= 1 << iter.location;
} }
initSteps_.push_back(step);
return step.create_input_layout.inputLayout; return step.create_input_layout.inputLayout;
} }
GLPushBuffer *CreatePushBuffer(int frame, GLuint target, size_t size, const char *tag) { GLPushBuffer *CreatePushBuffer(int frame, GLuint target, size_t size) {
GLPushBuffer *push = new GLPushBuffer(this, target, size, tag); GLPushBuffer *push = new GLPushBuffer(this, target, size);
RegisterPushBuffer(frame, push); RegisterPushBuffer(frame, push);
return push; return push;
} }
void DeleteShader(GLRShader *shader) { void DeleteShader(GLRShader *shader) {
_dbg_assert_(shader != nullptr);
deleter_.shaders.push_back(shader); deleter_.shaders.push_back(shader);
} }
void DeleteProgram(GLRProgram *program) { void DeleteProgram(GLRProgram *program) {
_dbg_assert_(program != nullptr);
deleter_.programs.push_back(program); deleter_.programs.push_back(program);
} }
void DeleteBuffer(GLRBuffer *buffer) { void DeleteBuffer(GLRBuffer *buffer) {
_dbg_assert_(buffer != nullptr);
deleter_.buffers.push_back(buffer); deleter_.buffers.push_back(buffer);
} }
void DeleteTexture(GLRTexture *texture) { void DeleteTexture(GLRTexture *texture) {
_dbg_assert_(texture != nullptr);
deleter_.textures.push_back(texture); deleter_.textures.push_back(texture);
} }
void DeleteInputLayout(GLRInputLayout *inputLayout) { void DeleteInputLayout(GLRInputLayout *inputLayout) {
_dbg_assert_(inputLayout != nullptr);
deleter_.inputLayouts.push_back(inputLayout); deleter_.inputLayouts.push_back(inputLayout);
} }
void DeleteFramebuffer(GLRFramebuffer *framebuffer) { void DeleteFramebuffer(GLRFramebuffer *framebuffer) {
_dbg_assert_(framebuffer != nullptr);
deleter_.framebuffers.push_back(framebuffer); deleter_.framebuffers.push_back(framebuffer);
} }
void DeletePushBuffer(GLPushBuffer *pushbuffer) { void DeletePushBuffer(GLPushBuffer *pushbuffer) {
_dbg_assert_(pushbuffer != nullptr);
deleter_.pushBuffers.push_back(pushbuffer); deleter_.pushBuffers.push_back(pushbuffer);
} }
@ -386,13 +551,9 @@ public:
pushbuffer->End(); pushbuffer->End();
} }
bool IsInRenderPass() const {
return curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER;
}
// This starts a new step (like a "render pass" in Vulkan). // This starts a new step (like a "render pass" in Vulkan).
// //
// After a "CopyFramebuffer" or the other functions that start "steps", you need to call this before // After a "CopyFramebuffer" or the other functions that start "steps", you need to call this beforce
// making any new render state changes or draw calls. // making any new render state changes or draw calls.
// //
// The following state needs to be reset by the caller after calling this (and will thus not safely carry over from // The following state needs to be reset by the caller after calling this (and will thus not safely carry over from
@ -409,7 +570,7 @@ public:
// Binds a framebuffer as a texture, for the following draws. // Binds a framebuffer as a texture, for the following draws.
void BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit); void BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit);
bool CopyFramebufferToMemory(GLRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, Draw::ReadbackMode mode, const char *tag); bool CopyFramebufferToMemorySync(GLRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag);
void CopyImageToMemorySync(GLRTexture *texture, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag); void CopyImageToMemorySync(GLRTexture *texture, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag);
void CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLOffset2D dstPos, int aspectMask, const char *tag); void CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLOffset2D dstPos, int aspectMask, const char *tag);
@ -419,8 +580,7 @@ public:
void BufferSubdata(GLRBuffer *buffer, size_t offset, size_t size, uint8_t *data, bool deleteData = true) { void BufferSubdata(GLRBuffer *buffer, size_t offset, size_t size, uint8_t *data, bool deleteData = true) {
// TODO: Maybe should be a render command instead of an init command? When possible it's better as // TODO: Maybe should be a render command instead of an init command? When possible it's better as
// an init command, that's for sure. // an init command, that's for sure.
GLRInitStep &step = initSteps_.push_uninitialized(); GLRInitStep step{ GLRInitStepType::BUFFER_SUBDATA };
step.stepType = GLRInitStepType::BUFFER_SUBDATA;
_dbg_assert_(offset >= 0); _dbg_assert_(offset >= 0);
_dbg_assert_(offset <= buffer->size_ - size); _dbg_assert_(offset <= buffer->size_ - size);
step.buffer_subdata.buffer = buffer; step.buffer_subdata.buffer = buffer;
@ -428,12 +588,12 @@ public:
step.buffer_subdata.size = (int)size; step.buffer_subdata.size = (int)size;
step.buffer_subdata.data = data; step.buffer_subdata.data = data;
step.buffer_subdata.deleteData = deleteData; step.buffer_subdata.deleteData = deleteData;
initSteps_.push_back(step);
} }
// Takes ownership over the data pointer and delete[]-s it. // Takes ownership over the data pointer and delete[]-s it.
void TextureImage(GLRTexture *texture, int level, int width, int height, int depth, Draw::DataFormat format, uint8_t *data, GLRAllocType allocType = GLRAllocType::NEW, bool linearFilter = false) { void TextureImage(GLRTexture *texture, int level, int width, int height, int depth, Draw::DataFormat format, uint8_t *data, GLRAllocType allocType = GLRAllocType::NEW, bool linearFilter = false) {
GLRInitStep &step = initSteps_.push_uninitialized(); GLRInitStep step{ GLRInitStepType::TEXTURE_IMAGE };
step.stepType = GLRInitStepType::TEXTURE_IMAGE;
step.texture_image.texture = texture; step.texture_image.texture = texture;
step.texture_image.data = data; step.texture_image.data = data;
step.texture_image.format = format; step.texture_image.format = format;
@ -443,11 +603,12 @@ public:
step.texture_image.depth = depth; step.texture_image.depth = depth;
step.texture_image.allocType = allocType; step.texture_image.allocType = allocType;
step.texture_image.linearFilter = linearFilter; step.texture_image.linearFilter = linearFilter;
initSteps_.push_back(step);
} }
void TextureSubImage(int slot, GLRTexture *texture, int level, int x, int y, int width, int height, Draw::DataFormat format, uint8_t *data, GLRAllocType allocType = GLRAllocType::NEW) { void TextureSubImage(int slot, GLRTexture *texture, int level, int x, int y, int width, int height, Draw::DataFormat format, uint8_t *data, GLRAllocType allocType = GLRAllocType::NEW) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData _data(GLRRenderCommand::TEXTURE_SUBIMAGE); GLRRenderData _data{ GLRRenderCommand::TEXTURE_SUBIMAGE };
_data.texture_subimage.texture = texture; _data.texture_subimage.texture = texture;
_data.texture_subimage.data = data; _data.texture_subimage.data = data;
_data.texture_subimage.format = format; _data.texture_subimage.format = format;
@ -462,59 +623,80 @@ public:
} }
void FinalizeTexture(GLRTexture *texture, int loadedLevels, bool genMips) { void FinalizeTexture(GLRTexture *texture, int loadedLevels, bool genMips) {
GLRInitStep &step = initSteps_.push_uninitialized(); GLRInitStep step{ GLRInitStepType::TEXTURE_FINALIZE };
step.stepType = GLRInitStepType::TEXTURE_FINALIZE;
step.texture_finalize.texture = texture; step.texture_finalize.texture = texture;
step.texture_finalize.loadedLevels = loadedLevels; step.texture_finalize.loadedLevels = loadedLevels;
step.texture_finalize.genMips = genMips; step.texture_finalize.genMips = genMips;
initSteps_.push_back(step);
} }
void BindTexture(int slot, GLRTexture *tex) { void BindTexture(int slot, GLRTexture *tex) {
if (!curRenderStep_ && !tex) {
// Likely a pre-emptive bindtexture for D3D11 to avoid hazards. Not necessary.
// This can happen in BlitUsingRaster.
return;
}
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
_dbg_assert_(slot < MAX_GL_TEXTURE_SLOTS); _dbg_assert_(slot < MAX_GL_TEXTURE_SLOTS);
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::BINDTEXTURE };
data.cmd = GLRRenderCommand::BINDTEXTURE;
data.texture.slot = slot; data.texture.slot = slot;
data.texture.texture = tex; data.texture.texture = tex;
curRenderStep_->commands.push_back(data);
} }
void BindProgram(GLRProgram *program) { void BindProgram(GLRProgram *program) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::BINDPROGRAM };
data.cmd = GLRRenderCommand::BINDPROGRAM;
_dbg_assert_(program != nullptr); _dbg_assert_(program != nullptr);
data.program.program = program; data.program.program = program;
curRenderStep_->commands.push_back(data);
#ifdef _DEBUG #ifdef _DEBUG
curProgram_ = program; curProgram_ = program;
#endif #endif
} }
void BindPixelPackBuffer(GLRBuffer *buffer) { // Want to support an offset but can't in ES 2.0. We supply an offset when binding the buffers instead.
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::BIND_BUFFER };
data.bind_buffer.buffer = buffer;
data.bind_buffer.target = GL_PIXEL_PACK_BUFFER;
curRenderStep_->commands.push_back(data);
}
void BindIndexBuffer(GLRBuffer *buffer) { // Want to support an offset but can't in ES 2.0. We supply an offset when binding the buffers instead.
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::BIND_BUFFER};
data.bind_buffer.buffer = buffer;
data.bind_buffer.target = GL_ELEMENT_ARRAY_BUFFER;
curRenderStep_->commands.push_back(data);
}
void BindVertexBuffer(GLRInputLayout *inputLayout, GLRBuffer *buffer, size_t offset) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
_dbg_assert_(inputLayout);
GLRRenderData data{ GLRRenderCommand::BIND_VERTEX_BUFFER };
data.bindVertexBuffer.inputLayout = inputLayout;
data.bindVertexBuffer.offset = offset;
data.bindVertexBuffer.buffer = buffer;
curRenderStep_->commands.push_back(data);
}
void SetDepth(bool enabled, bool write, GLenum func) { void SetDepth(bool enabled, bool write, GLenum func) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::DEPTH };
data.cmd = GLRRenderCommand::DEPTH;
data.depth.enabled = enabled; data.depth.enabled = enabled;
data.depth.write = write; data.depth.write = write;
data.depth.func = func; data.depth.func = func;
curRenderStep_->commands.push_back(data);
} }
void SetViewport(const GLRViewport &vp) { void SetViewport(const GLRViewport &vp) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::VIEWPORT };
data.cmd = GLRRenderCommand::VIEWPORT;
data.viewport.vp = vp; data.viewport.vp = vp;
curRenderStep_->commands.push_back(data);
} }
void SetScissor(const GLRect2D &rc) { void SetScissor(const GLRect2D &rc) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::SCISSOR };
data.cmd = GLRRenderCommand::SCISSOR;
data.scissor.rc = rc; data.scissor.rc = rc;
curRenderStep_->commands.push_back(data);
} }
void SetUniformI(const GLint *loc, int count, const int *udata) { void SetUniformI(const GLint *loc, int count, const int *udata) {
@ -522,12 +704,11 @@ public:
#ifdef _DEBUG #ifdef _DEBUG
_dbg_assert_(curProgram_); _dbg_assert_(curProgram_);
#endif #endif
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::UNIFORM4I };
data.cmd = GLRRenderCommand::UNIFORM4I;
data.uniform4.name = nullptr;
data.uniform4.loc = loc; data.uniform4.loc = loc;
data.uniform4.count = count; data.uniform4.count = count;
memcpy(data.uniform4.v, udata, sizeof(int) * count); memcpy(data.uniform4.v, udata, sizeof(int) * count);
curRenderStep_->commands.push_back(data);
} }
void SetUniformI1(const GLint *loc, int udata) { void SetUniformI1(const GLint *loc, int udata) {
@ -535,12 +716,11 @@ public:
#ifdef _DEBUG #ifdef _DEBUG
_dbg_assert_(curProgram_); _dbg_assert_(curProgram_);
#endif #endif
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::UNIFORM4I };
data.cmd = GLRRenderCommand::UNIFORM4I;
data.uniform4.name = nullptr;
data.uniform4.loc = loc; data.uniform4.loc = loc;
data.uniform4.count = 1; data.uniform4.count = 1;
memcpy(data.uniform4.v, &udata, sizeof(udata)); memcpy(data.uniform4.v, &udata, sizeof(udata));
curRenderStep_->commands.push_back(data);
} }
void SetUniformUI(const GLint *loc, int count, const uint32_t *udata) { void SetUniformUI(const GLint *loc, int count, const uint32_t *udata) {
@ -548,12 +728,11 @@ public:
#ifdef _DEBUG #ifdef _DEBUG
_dbg_assert_(curProgram_); _dbg_assert_(curProgram_);
#endif #endif
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::UNIFORM4UI };
data.cmd = GLRRenderCommand::UNIFORM4UI;
data.uniform4.name = nullptr;
data.uniform4.loc = loc; data.uniform4.loc = loc;
data.uniform4.count = count; data.uniform4.count = count;
memcpy(data.uniform4.v, udata, sizeof(uint32_t) * count); memcpy(data.uniform4.v, udata, sizeof(uint32_t) * count);
curRenderStep_->commands.push_back(data);
} }
void SetUniformUI1(const GLint *loc, uint32_t udata) { void SetUniformUI1(const GLint *loc, uint32_t udata) {
@ -561,12 +740,11 @@ public:
#ifdef _DEBUG #ifdef _DEBUG
_dbg_assert_(curProgram_); _dbg_assert_(curProgram_);
#endif #endif
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::UNIFORM4UI };
data.cmd = GLRRenderCommand::UNIFORM4UI;
data.uniform4.name = nullptr;
data.uniform4.loc = loc; data.uniform4.loc = loc;
data.uniform4.count = 1; data.uniform4.count = 1;
memcpy(data.uniform4.v, &udata, sizeof(udata)); memcpy(data.uniform4.v, &udata, sizeof(udata));
curRenderStep_->commands.push_back(data);
} }
void SetUniformF(const GLint *loc, int count, const float *udata) { void SetUniformF(const GLint *loc, int count, const float *udata) {
@ -574,12 +752,11 @@ public:
#ifdef _DEBUG #ifdef _DEBUG
_dbg_assert_(curProgram_); _dbg_assert_(curProgram_);
#endif #endif
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::UNIFORM4F };
data.cmd = GLRRenderCommand::UNIFORM4F;
data.uniform4.name = nullptr;
data.uniform4.loc = loc; data.uniform4.loc = loc;
data.uniform4.count = count; data.uniform4.count = count;
memcpy(data.uniform4.v, udata, sizeof(float) * count); memcpy(data.uniform4.v, udata, sizeof(float) * count);
curRenderStep_->commands.push_back(data);
} }
void SetUniformF1(const GLint *loc, const float udata) { void SetUniformF1(const GLint *loc, const float udata) {
@ -587,12 +764,11 @@ public:
#ifdef _DEBUG #ifdef _DEBUG
_dbg_assert_(curProgram_); _dbg_assert_(curProgram_);
#endif #endif
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::UNIFORM4F };
data.cmd = GLRRenderCommand::UNIFORM4F;
data.uniform4.name = nullptr;
data.uniform4.loc = loc; data.uniform4.loc = loc;
data.uniform4.count = 1; data.uniform4.count = 1;
memcpy(data.uniform4.v, &udata, sizeof(float)); memcpy(data.uniform4.v, &udata, sizeof(float));
curRenderStep_->commands.push_back(data);
} }
void SetUniformF(const char *name, int count, const float *udata) { void SetUniformF(const char *name, int count, const float *udata) {
@ -600,12 +776,11 @@ public:
#ifdef _DEBUG #ifdef _DEBUG
_dbg_assert_(curProgram_); _dbg_assert_(curProgram_);
#endif #endif
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::UNIFORM4F };
data.cmd = GLRRenderCommand::UNIFORM4F;
data.uniform4.name = name; data.uniform4.name = name;
data.uniform4.loc = nullptr;
data.uniform4.count = count; data.uniform4.count = count;
memcpy(data.uniform4.v, udata, sizeof(float) * count); memcpy(data.uniform4.v, udata, sizeof(float) * count);
curRenderStep_->commands.push_back(data);
} }
void SetUniformM4x4(const GLint *loc, const float *udata) { void SetUniformM4x4(const GLint *loc, const float *udata) {
@ -613,11 +788,10 @@ public:
#ifdef _DEBUG #ifdef _DEBUG
_dbg_assert_(curProgram_); _dbg_assert_(curProgram_);
#endif #endif
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::UNIFORMMATRIX };
data.cmd = GLRRenderCommand::UNIFORMMATRIX;
data.uniformMatrix4.name = nullptr;
data.uniformMatrix4.loc = loc; data.uniformMatrix4.loc = loc;
memcpy(data.uniformMatrix4.m, udata, sizeof(float) * 16); memcpy(data.uniformMatrix4.m, udata, sizeof(float) * 16);
curRenderStep_->commands.push_back(data);
} }
void SetUniformM4x4Stereo(const char *name, const GLint *loc, const float *left, const float *right) { void SetUniformM4x4Stereo(const char *name, const GLint *loc, const float *left, const float *right) {
@ -625,13 +799,12 @@ public:
#ifdef _DEBUG #ifdef _DEBUG
_dbg_assert_(curProgram_); _dbg_assert_(curProgram_);
#endif #endif
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::UNIFORMSTEREOMATRIX };
data.cmd = GLRRenderCommand::UNIFORMSTEREOMATRIX; data.uniformMatrix4.name = name;
data.uniformStereoMatrix4.name = name; data.uniformMatrix4.loc = loc;
data.uniformStereoMatrix4.loc = loc; memcpy(&data.uniformMatrix4.m[0], left, sizeof(float) * 16);
data.uniformStereoMatrix4.mData = new float[32]; memcpy(&data.uniformMatrix4.m[16], right, sizeof(float) * 16);
memcpy(&data.uniformStereoMatrix4.mData[0], left, sizeof(float) * 16); curRenderStep_->commands.push_back(data);
memcpy(&data.uniformStereoMatrix4.mData[16], right, sizeof(float) * 16);
} }
void SetUniformM4x4(const char *name, const float *udata) { void SetUniformM4x4(const char *name, const float *udata) {
@ -639,19 +812,17 @@ public:
#ifdef _DEBUG #ifdef _DEBUG
_dbg_assert_(curProgram_); _dbg_assert_(curProgram_);
#endif #endif
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::UNIFORMMATRIX };
data.cmd = GLRRenderCommand::UNIFORMMATRIX;
data.uniformMatrix4.name = name; data.uniformMatrix4.name = name;
data.uniformMatrix4.loc = nullptr;
memcpy(data.uniformMatrix4.m, udata, sizeof(float) * 16); memcpy(data.uniformMatrix4.m, udata, sizeof(float) * 16);
curRenderStep_->commands.push_back(data);
} }
void SetBlendAndMask(int colorMask, bool blendEnabled, GLenum srcColor, GLenum dstColor, GLenum srcAlpha, GLenum dstAlpha, GLenum funcColor, GLenum funcAlpha) { void SetBlendAndMask(int colorMask, bool blendEnabled, GLenum srcColor, GLenum dstColor, GLenum srcAlpha, GLenum dstAlpha, GLenum funcColor, GLenum funcAlpha) {
// Make this one only a non-debug _assert_, since it often comes first. // Make this one only a non-debug _assert_, since it often comes first.
// Lets us collect info about this potential crash through assert extra data. // Lets us collect info about this potential crash through assert extra data.
_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::BLEND };
data.cmd = GLRRenderCommand::BLEND;
data.blend.mask = colorMask; data.blend.mask = colorMask;
data.blend.enabled = blendEnabled; data.blend.enabled = blendEnabled;
data.blend.srcColor = srcColor; data.blend.srcColor = srcColor;
@ -660,88 +831,96 @@ public:
data.blend.dstAlpha = dstAlpha; data.blend.dstAlpha = dstAlpha;
data.blend.funcColor = funcColor; data.blend.funcColor = funcColor;
data.blend.funcAlpha = funcAlpha; data.blend.funcAlpha = funcAlpha;
curRenderStep_->commands.push_back(data);
} }
void SetNoBlendAndMask(int colorMask) { void SetNoBlendAndMask(int colorMask) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::BLEND };
data.cmd = GLRRenderCommand::BLEND;
data.blend.mask = colorMask; data.blend.mask = colorMask;
data.blend.enabled = false; data.blend.enabled = false;
curRenderStep_->commands.push_back(data);
} }
#ifndef USING_GLES2 #ifndef USING_GLES2
void SetLogicOp(bool enabled, GLenum logicOp) { void SetLogicOp(bool enabled, GLenum logicOp) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::LOGICOP };
data.cmd = GLRRenderCommand::LOGICOP;
data.logic.enabled = enabled; data.logic.enabled = enabled;
data.logic.logicOp = logicOp; data.logic.logicOp = logicOp;
curRenderStep_->commands.push_back(data);
} }
#endif #endif
void SetStencil(bool enabled, GLenum func, uint8_t refValue, uint8_t compareMask, uint8_t writeMask, GLenum sFail, GLenum zFail, GLenum pass) { void SetStencilFunc(bool enabled, GLenum func, uint8_t refValue, uint8_t compareMask) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::STENCILFUNC };
data.cmd = GLRRenderCommand::STENCIL; data.stencilFunc.enabled = enabled;
data.stencil.enabled = enabled; data.stencilFunc.func = func;
data.stencil.func = func; data.stencilFunc.ref = refValue;
data.stencil.ref = refValue; data.stencilFunc.compareMask = compareMask;
data.stencil.compareMask = compareMask; curRenderStep_->commands.push_back(data);
data.stencil.writeMask = writeMask; }
data.stencil.sFail = sFail;
data.stencil.zFail = zFail; void SetStencilOp(uint8_t writeMask, GLenum sFail, GLenum zFail, GLenum pass) {
data.stencil.pass = pass; _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::STENCILOP };
data.stencilOp.writeMask = writeMask;
data.stencilOp.sFail = sFail;
data.stencilOp.zFail = zFail;
data.stencilOp.pass = pass;
curRenderStep_->commands.push_back(data);
} }
void SetStencilDisabled() { void SetStencilDisabled() {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data;
data.cmd = GLRRenderCommand::STENCIL; data.cmd = GLRRenderCommand::STENCILFUNC;
data.stencil.enabled = false; data.stencilFunc.enabled = false;
curRenderStep_->commands.push_back(data);
} }
void SetBlendFactor(const float color[4]) { void SetBlendFactor(const float color[4]) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::BLENDCOLOR };
data.cmd = GLRRenderCommand::BLENDCOLOR;
CopyFloat4(data.blendColor.color, color); CopyFloat4(data.blendColor.color, color);
curRenderStep_->commands.push_back(data);
} }
void SetRaster(GLboolean cullEnable, GLenum frontFace, GLenum cullFace, GLboolean ditherEnable, GLboolean depthClamp) { void SetRaster(GLboolean cullEnable, GLenum frontFace, GLenum cullFace, GLboolean ditherEnable, GLboolean depthClamp) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::RASTER };
data.cmd = GLRRenderCommand::RASTER;
data.raster.cullEnable = cullEnable; data.raster.cullEnable = cullEnable;
data.raster.frontFace = frontFace; data.raster.frontFace = frontFace;
data.raster.cullFace = cullFace; data.raster.cullFace = cullFace;
data.raster.ditherEnable = ditherEnable; data.raster.ditherEnable = ditherEnable;
data.raster.depthClampEnable = depthClamp; data.raster.depthClampEnable = depthClamp;
curRenderStep_->commands.push_back(data);
} }
// Modifies the current texture as per GL specs, not global state. // Modifies the current texture as per GL specs, not global state.
void SetTextureSampler(int slot, GLenum wrapS, GLenum wrapT, GLenum magFilter, GLenum minFilter, float anisotropy) { void SetTextureSampler(int slot, GLenum wrapS, GLenum wrapT, GLenum magFilter, GLenum minFilter, float anisotropy) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
_dbg_assert_(slot < MAX_GL_TEXTURE_SLOTS); _dbg_assert_(slot < MAX_GL_TEXTURE_SLOTS);
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::TEXTURESAMPLER };
data.cmd = GLRRenderCommand::TEXTURESAMPLER;
data.textureSampler.slot = slot; data.textureSampler.slot = slot;
data.textureSampler.wrapS = wrapS; data.textureSampler.wrapS = wrapS;
data.textureSampler.wrapT = wrapT; data.textureSampler.wrapT = wrapT;
data.textureSampler.magFilter = magFilter; data.textureSampler.magFilter = magFilter;
data.textureSampler.minFilter = minFilter; data.textureSampler.minFilter = minFilter;
data.textureSampler.anisotropy = anisotropy; data.textureSampler.anisotropy = anisotropy;
curRenderStep_->commands.push_back(data);
} }
void SetTextureLod(int slot, float minLod, float maxLod, float lodBias) { void SetTextureLod(int slot, float minLod, float maxLod, float lodBias) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
_dbg_assert_(slot < MAX_GL_TEXTURE_SLOTS); _dbg_assert_(slot < MAX_GL_TEXTURE_SLOTS);
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::TEXTURELOD};
data.cmd = GLRRenderCommand::TEXTURELOD;
data.textureLod.slot = slot; data.textureLod.slot = slot;
data.textureLod.minLod = minLod; data.textureLod.minLod = minLod;
data.textureLod.maxLod = maxLod; data.textureLod.maxLod = maxLod;
data.textureLod.lodBias = lodBias; data.textureLod.lodBias = lodBias;
curRenderStep_->commands.push_back(data);
} }
// If scissorW == 0, no scissor is applied (the whole render target is cleared). // If scissorW == 0, no scissor is applied (the whole render target is cleared).
@ -749,8 +928,7 @@ public:
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
if (!clearMask) if (!clearMask)
return; return;
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::CLEAR };
data.cmd = GLRRenderCommand::CLEAR;
data.clear.clearMask = clearMask; data.clear.clearMask = clearMask;
data.clear.clearColor = clearColor; data.clear.clearColor = clearColor;
data.clear.clearZ = clearZ; data.clear.clearZ = clearZ;
@ -760,36 +938,30 @@ public:
data.clear.scissorY = scissorY; data.clear.scissorY = scissorY;
data.clear.scissorW = scissorW; data.clear.scissorW = scissorW;
data.clear.scissorH = scissorH; data.clear.scissorH = scissorH;
curRenderStep_->commands.push_back(data);
} }
void Draw(GLRInputLayout *inputLayout, GLRBuffer *vertexBuffer, uint32_t vertexOffset, GLenum mode, int first, int count) { void Draw(GLenum mode, int first, int count) {
_dbg_assert_(vertexBuffer && curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); GLRRenderData data{ GLRRenderCommand::DRAW };
data.cmd = GLRRenderCommand::DRAW;
data.draw.inputLayout = inputLayout;
data.draw.vertexOffset = vertexOffset;
data.draw.vertexBuffer = vertexBuffer;
data.draw.indexBuffer = nullptr;
data.draw.mode = mode; data.draw.mode = mode;
data.draw.first = first; data.draw.first = first;
data.draw.count = count; data.draw.count = count;
data.draw.indexType = 0; data.draw.buffer = 0;
curRenderStep_->commands.push_back(data);
curRenderStep_->render.numDraws++;
} }
// Would really love to have a basevertex parameter, but impossible in unextended GLES, without glDrawElementsBaseVertex, unfortunately. void DrawIndexed(GLenum mode, int count, GLenum indexType, void *indices, int instances = 1) {
void DrawIndexed(GLRInputLayout *inputLayout, GLRBuffer *vertexBuffer, uint32_t vertexOffset, GLRBuffer *indexBuffer, uint32_t indexOffset, GLenum mode, int count, GLenum indexType, int instances = 1) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
_dbg_assert_(vertexBuffer && indexBuffer && curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData data{ GLRRenderCommand::DRAW_INDEXED };
GLRRenderData &data = curRenderStep_->commands.push_uninitialized(); data.drawIndexed.mode = mode;
data.cmd = GLRRenderCommand::DRAW; data.drawIndexed.count = count;
data.draw.inputLayout = inputLayout; data.drawIndexed.indexType = indexType;
data.draw.vertexOffset = vertexOffset; data.drawIndexed.instances = instances;
data.draw.vertexBuffer = vertexBuffer; data.drawIndexed.indices = indices;
data.draw.indexBuffer = indexBuffer; curRenderStep_->commands.push_back(data);
data.draw.indexOffset = indexOffset; curRenderStep_->render.numDraws++;
data.draw.mode = mode;
data.draw.count = count;
data.draw.indexType = indexType;
data.draw.instances = instances;
} }
enum { MAX_INFLIGHT_FRAMES = 3 }; enum { MAX_INFLIGHT_FRAMES = 3 };
@ -820,8 +992,9 @@ public:
_dbg_assert_(foundCount == 1); _dbg_assert_(foundCount == 1);
} }
void SetSwapFunction(std::function<void()> swapFunction) { void SetSwapFunction(std::function<void()> swapFunction, bool retainControl) {
swapFunction_ = swapFunction; swapFunction_ = swapFunction;
retainControl_ = retainControl;
} }
void SetSwapIntervalFunction(std::function<void(int)> swapIntervalFunction) { void SetSwapIntervalFunction(std::function<void(int)> swapIntervalFunction) {
@ -853,8 +1026,6 @@ public:
} }
private: private:
bool Run(GLRRenderThreadTask &task);
// Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot).
void FlushSync(); void FlushSync();
@ -864,14 +1035,30 @@ private:
frameData_[frame].activePushBuffers.insert(buffer); frameData_[frame].activePushBuffers.insert(buffer);
} }
GLFrameData frameData_[MAX_INFLIGHT_FRAMES]; // Per-frame data, round-robin so we can overlap submission with execution of the previous frame.
struct FrameData {
bool skipSwap = false;
std::mutex fenceMutex;
std::condition_variable fenceCondVar;
bool readyForFence = true;
// Swapchain.
bool hasBegun = false;
GLDeleter deleter;
GLDeleter deleter_prev;
std::set<GLPushBuffer *> activePushBuffers;
};
FrameData frameData_[MAX_INFLIGHT_FRAMES];
// Submission time state // Submission time state
bool insideFrame_ = false; bool insideFrame_ = false;
GLRStep *curRenderStep_ = nullptr; GLRStep *curRenderStep_ = nullptr;
std::vector<GLRStep *> steps_; std::vector<GLRStep *> steps_;
FastVec<GLRInitStep> initSteps_; std::vector<GLRInitStep> initSteps_;
// Execution time state // Execution time state
bool run_ = true; bool run_ = true;
@ -883,7 +1070,7 @@ private:
std::mutex pushMutex_; std::mutex pushMutex_;
std::condition_variable pushCondVar_; std::condition_variable pushCondVar_;
std::queue<GLRRenderThreadTask *> renderThreadQueue_; std::queue<GLRRenderThreadTask> renderThreadQueue_;
// For readbacks and other reasons we need to sync with the render thread. // For readbacks and other reasons we need to sync with the render thread.
std::mutex syncMutex_; std::mutex syncMutex_;
@ -900,6 +1087,7 @@ private:
std::function<void()> swapFunction_; std::function<void()> swapFunction_;
std::function<void(int)> swapIntervalFunction_; std::function<void(int)> swapIntervalFunction_;
bool retainControl_ = false;
GLBufferStrategy bufferStrategy_ = GLBufferStrategy::SUBDATA; GLBufferStrategy bufferStrategy_ = GLBufferStrategy::SUBDATA;
int inflightFrames_ = MAX_INFLIGHT_FRAMES; int inflightFrames_ = MAX_INFLIGHT_FRAMES;
@ -916,9 +1104,5 @@ private:
#endif #endif
Draw::DeviceCaps caps_{}; Draw::DeviceCaps caps_{};
std::string profilePassesString_;
InvalidationCallback invalidationCallback_; InvalidationCallback invalidationCallback_;
uint64_t frameIdGen_ = FRAME_TIME_HISTORY_LENGTH;
HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory_;
}; };

View file

@ -102,7 +102,7 @@ bool glsl_recompile(GLSLProgram *program, std::string *error_message) {
if (!program->vshader_source && !vsh_src) { if (!program->vshader_source && !vsh_src) {
size_t sz; size_t sz;
vsh_src.reset((char *)g_VFS.ReadFile(program->vshader_filename, &sz)); vsh_src.reset((char *)VFSReadFile(program->vshader_filename, &sz));
} }
if (!program->vshader_source && !vsh_src) { if (!program->vshader_source && !vsh_src) {
ERROR_LOG(G3D, "File missing: %s", program->vshader_filename); ERROR_LOG(G3D, "File missing: %s", program->vshader_filename);
@ -113,7 +113,7 @@ bool glsl_recompile(GLSLProgram *program, std::string *error_message) {
} }
if (!program->fshader_source && !fsh_src) { if (!program->fshader_source && !fsh_src) {
size_t sz; size_t sz;
fsh_src.reset((char *)g_VFS.ReadFile(program->fshader_filename, &sz)); fsh_src.reset((char *)VFSReadFile(program->fshader_filename, &sz));
} }
if (!program->fshader_source && !fsh_src) { if (!program->fshader_source && !fsh_src) {
ERROR_LOG(G3D, "File missing: %s", program->fshader_filename); ERROR_LOG(G3D, "File missing: %s", program->fshader_filename);

View file

@ -180,9 +180,8 @@ public:
void Apply(GLRenderManager *render, uint8_t stencilRef, uint8_t stencilWriteMask, uint8_t stencilCompareMask) { void Apply(GLRenderManager *render, uint8_t stencilRef, uint8_t stencilWriteMask, uint8_t stencilCompareMask) {
render->SetDepth(depthTestEnabled, depthWriteEnabled, depthComp); render->SetDepth(depthTestEnabled, depthWriteEnabled, depthComp);
render->SetStencil( render->SetStencilFunc(stencilEnabled, stencilCompareOp, stencilRef, stencilCompareMask);
stencilEnabled, stencilCompareOp, stencilRef, stencilCompareMask, render->SetStencilOp(stencilWriteMask, stencilFail, stencilZFail, stencilPass);
stencilWriteMask, stencilFail, stencilZFail, stencilPass);
} }
}; };
@ -322,7 +321,7 @@ class OpenGLTexture;
class OpenGLContext : public DrawContext { class OpenGLContext : public DrawContext {
public: public:
OpenGLContext(bool canChangeSwapInterval); OpenGLContext();
~OpenGLContext(); ~OpenGLContext();
void SetTargetSize(int w, int h) override { void SetTargetSize(int w, int h) override {
@ -359,20 +358,14 @@ public:
Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override; Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override;
Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override; Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override;
void BeginFrame(DebugFlags debugFlags) override; void BeginFrame() override;
void EndFrame() override; void EndFrame() override;
void Present(PresentMode mode, int vblanks) override;
int GetFrameCount() override {
return frameCount_;
}
void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override; void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override;
void UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) override;
void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override; void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override;
bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override;
bool CopyFramebufferToMemory(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, ReadbackMode mode, const char *tag) override; bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, const char *tag) override;
// These functions should be self explanatory. // These functions should be self explanatory.
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override; void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
@ -392,9 +385,9 @@ public:
renderManager_.SetScissor({ left, top, width, height }); renderManager_.SetScissor({ left, top, width, height });
} }
void SetViewport(const Viewport &viewport) override { void SetViewports(int count, Viewport *viewports) override {
// Same structure, different name. // Same structure, different name.
renderManager_.SetViewport((GLRViewport &)viewport); renderManager_.SetViewport((GLRViewport &)*viewports);
} }
void SetBlendFactor(float color[4]) override { void SetBlendFactor(float color[4]) override {
@ -406,11 +399,12 @@ public:
stencilWriteMask_ = writeMask; stencilWriteMask_ = writeMask;
stencilCompareMask_ = compareMask; stencilCompareMask_ = compareMask;
// Do we need to update on the fly here? // Do we need to update on the fly here?
renderManager_.SetStencil( renderManager_.SetStencilFunc(
curPipeline_->depthStencil->stencilEnabled, curPipeline_->depthStencil->stencilEnabled,
curPipeline_->depthStencil->stencilCompareOp, curPipeline_->depthStencil->stencilCompareOp,
refValue, refValue,
compareMask, compareMask);
renderManager_.SetStencilOp(
writeMask, writeMask,
curPipeline_->depthStencil->stencilFail, curPipeline_->depthStencil->stencilFail,
curPipeline_->depthStencil->stencilZFail, curPipeline_->depthStencil->stencilZFail,
@ -421,9 +415,12 @@ public:
void BindNativeTexture(int sampler, void *nativeTexture) override; void BindNativeTexture(int sampler, void *nativeTexture) override;
void BindPipeline(Pipeline *pipeline) override; void BindPipeline(Pipeline *pipeline) override;
void BindVertexBuffer(Buffer *buffer, int offset) override { void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) override {
curVBuffer_ = (OpenGLBuffer *)buffer; _assert_(start + count <= ARRAY_SIZE(curVBuffers_));
curVBufferOffset_ = offset; for (int i = 0; i < count; i++) {
curVBuffers_[i + start] = (OpenGLBuffer *)buffers[i];
curVBufferOffsets_[i + start] = offsets ? offsets[i] : 0;
}
} }
void BindIndexBuffer(Buffer *indexBuffer, int offset) override { void BindIndexBuffer(Buffer *indexBuffer, int offset) override {
curIBuffer_ = (OpenGLBuffer *)indexBuffer; curIBuffer_ = (OpenGLBuffer *)indexBuffer;
@ -460,7 +457,6 @@ public:
case GPUVendor::VENDOR_BROADCOM: return "VENDOR_BROADCOM"; case GPUVendor::VENDOR_BROADCOM: return "VENDOR_BROADCOM";
case GPUVendor::VENDOR_VIVANTE: return "VENDOR_VIVANTE"; case GPUVendor::VENDOR_VIVANTE: return "VENDOR_VIVANTE";
case GPUVendor::VENDOR_APPLE: return "VENDOR_APPLE"; case GPUVendor::VENDOR_APPLE: return "VENDOR_APPLE";
case GPUVendor::VENDOR_MESA: return "VENDOR_MESA";
case GPUVendor::VENDOR_UNKNOWN: case GPUVendor::VENDOR_UNKNOWN:
default: default:
return "VENDOR_UNKNOWN"; return "VENDOR_UNKNOWN";
@ -483,15 +479,10 @@ public:
renderManager_.SetInvalidationCallback(callback); renderManager_.SetInvalidationCallback(callback);
} }
std::string GetGpuProfileString() const override {
return renderManager_.GetGpuProfileString();
}
private: private:
void ApplySamplers(); void ApplySamplers();
GLRenderManager renderManager_; GLRenderManager renderManager_;
int frameCount_ = 0;
DeviceCaps caps_{}; DeviceCaps caps_{};
@ -502,9 +493,9 @@ private:
const GLRTexture *boundTextures_[MAX_TEXTURE_SLOTS]{}; const GLRTexture *boundTextures_[MAX_TEXTURE_SLOTS]{};
AutoRef<OpenGLPipeline> curPipeline_; AutoRef<OpenGLPipeline> curPipeline_;
AutoRef<OpenGLBuffer> curVBuffer_; AutoRef<OpenGLBuffer> curVBuffers_[4]{};
int curVBufferOffsets_[4]{};
AutoRef<OpenGLBuffer> curIBuffer_; AutoRef<OpenGLBuffer> curIBuffer_;
int curVBufferOffset_ = 0;
int curIBufferOffset_ = 0; int curIBufferOffset_ = 0;
AutoRef<Framebuffer> curRenderTarget_; AutoRef<Framebuffer> curRenderTarget_;
@ -540,7 +531,7 @@ static bool HasIntelDualSrcBug(const int versions[4]) {
} }
} }
OpenGLContext::OpenGLContext(bool canChangeSwapInterval) : renderManager_(frameTimeHistory_) { OpenGLContext::OpenGLContext() {
if (gl_extensions.IsGLES) { if (gl_extensions.IsGLES) {
if (gl_extensions.OES_packed_depth_stencil || gl_extensions.OES_depth24) { if (gl_extensions.OES_packed_depth_stencil || gl_extensions.OES_depth24) {
caps_.preferredDepthBufferFormat = DataFormat::D24_S8; caps_.preferredDepthBufferFormat = DataFormat::D24_S8;
@ -564,7 +555,6 @@ OpenGLContext::OpenGLContext(bool canChangeSwapInterval) : renderManager_(frameT
caps_.textureDepthSupported = true; caps_.textureDepthSupported = true;
} }
caps_.setMaxFrameLatencySupported = true;
caps_.dualSourceBlend = gl_extensions.ARB_blend_func_extended || gl_extensions.EXT_blend_func_extended; caps_.dualSourceBlend = gl_extensions.ARB_blend_func_extended || gl_extensions.EXT_blend_func_extended;
caps_.anisoSupported = gl_extensions.EXT_texture_filter_anisotropic; caps_.anisoSupported = gl_extensions.EXT_texture_filter_anisotropic;
caps_.framebufferCopySupported = gl_extensions.OES_copy_image || gl_extensions.NV_copy_image || gl_extensions.EXT_copy_image || gl_extensions.ARB_copy_image; caps_.framebufferCopySupported = gl_extensions.OES_copy_image || gl_extensions.NV_copy_image || gl_extensions.EXT_copy_image || gl_extensions.ARB_copy_image;
@ -613,7 +603,6 @@ OpenGLContext::OpenGLContext(bool canChangeSwapInterval) : renderManager_(frameT
case GPU_VENDOR_IMGTEC: caps_.vendor = GPUVendor::VENDOR_IMGTEC; break; case GPU_VENDOR_IMGTEC: caps_.vendor = GPUVendor::VENDOR_IMGTEC; break;
case GPU_VENDOR_VIVANTE: caps_.vendor = GPUVendor::VENDOR_VIVANTE; break; case GPU_VENDOR_VIVANTE: caps_.vendor = GPUVendor::VENDOR_VIVANTE; break;
case GPU_VENDOR_APPLE: caps_.vendor = GPUVendor::VENDOR_APPLE; break; case GPU_VENDOR_APPLE: caps_.vendor = GPUVendor::VENDOR_APPLE; break;
case GPU_VENDOR_MESA: caps_.vendor = GPUVendor::VENDOR_MESA; break;
case GPU_VENDOR_UNKNOWN: case GPU_VENDOR_UNKNOWN:
default: default:
caps_.vendor = GPUVendor::VENDOR_UNKNOWN; caps_.vendor = GPUVendor::VENDOR_UNKNOWN;
@ -632,7 +621,7 @@ OpenGLContext::OpenGLContext(bool canChangeSwapInterval) : renderManager_(frameT
caps_.isTilingGPU = gl_extensions.IsGLES && caps_.vendor != GPUVendor::VENDOR_NVIDIA && caps_.vendor != GPUVendor::VENDOR_INTEL; caps_.isTilingGPU = gl_extensions.IsGLES && caps_.vendor != GPUVendor::VENDOR_NVIDIA && caps_.vendor != GPUVendor::VENDOR_INTEL;
for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) { for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) {
frameData_[i].push = renderManager_.CreatePushBuffer(i, GL_ARRAY_BUFFER, 64 * 1024, "thin3d_vbuf"); frameData_[i].push = renderManager_.CreatePushBuffer(i, GL_ARRAY_BUFFER, 64 * 1024);
} }
if (!gl_extensions.VersionGEThan(3, 0, 0)) { if (!gl_extensions.VersionGEThan(3, 0, 0)) {
@ -751,9 +740,7 @@ OpenGLContext::OpenGLContext(bool canChangeSwapInterval) : renderManager_(frameT
// This too... // This too...
shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_1xx; shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_1xx;
if (gl_extensions.EXT_gpu_shader4) { if (gl_extensions.EXT_gpu_shader4) {
// Older macOS devices seem to have problems defining uint uniforms. shaderLanguageDesc_.bitwiseOps = true;
// Let's just assume OpenGL 3.0+ is required.
shaderLanguageDesc_.bitwiseOps = gl_extensions.VersionGEThan(3, 0, 0);
shaderLanguageDesc_.texelFetch = "texelFetch2D"; shaderLanguageDesc_.texelFetch = "texelFetch2D";
} }
} }
@ -772,16 +759,6 @@ OpenGLContext::OpenGLContext(bool canChangeSwapInterval) : renderManager_(frameT
} }
} }
if (canChangeSwapInterval) {
caps_.presentInstantModeChange = true;
caps_.presentMaxInterval = 4;
caps_.presentModesSupported = PresentMode::FIFO | PresentMode::IMMEDIATE;
} else {
caps_.presentInstantModeChange = false;
caps_.presentModesSupported = PresentMode::FIFO;
caps_.presentMaxInterval = 1;
}
renderManager_.SetDeviceCaps(caps_); renderManager_.SetDeviceCaps(caps_);
} }
@ -793,8 +770,8 @@ OpenGLContext::~OpenGLContext() {
} }
} }
void OpenGLContext::BeginFrame(DebugFlags debugFlags) { void OpenGLContext::BeginFrame() {
renderManager_.BeginFrame(debugFlags & DebugFlags::PROFILE_TIMESTAMPS); renderManager_.BeginFrame();
FrameData &frameData = frameData_[renderManager_.GetCurFrame()]; FrameData &frameData = frameData_[renderManager_.GetCurFrame()];
renderManager_.BeginPushBuffer(frameData.push); renderManager_.BeginPushBuffer(frameData.push);
} }
@ -803,12 +780,8 @@ void OpenGLContext::EndFrame() {
FrameData &frameData = frameData_[renderManager_.GetCurFrame()]; FrameData &frameData = frameData_[renderManager_.GetCurFrame()];
renderManager_.EndPushBuffer(frameData.push); // upload the data! renderManager_.EndPushBuffer(frameData.push); // upload the data!
renderManager_.Finish(); renderManager_.Finish();
Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
}
void OpenGLContext::Present(PresentMode presentMode, int vblanks) { Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
renderManager_.Present();
frameCount_++;
} }
void OpenGLContext::Invalidate(InvalidationFlags flags) { void OpenGLContext::Invalidate(InvalidationFlags flags) {
@ -830,7 +803,7 @@ InputLayout *OpenGLContext::CreateInputLayout(const InputLayoutDesc &desc) {
return fmt; return fmt;
} }
static GLuint TypeToTarget(TextureType type) { GLuint TypeToTarget(TextureType type) {
switch (type) { switch (type) {
#ifndef USING_GLES2 #ifndef USING_GLES2
case TextureType::LINEAR1D: return GL_TEXTURE_1D; case TextureType::LINEAR1D: return GL_TEXTURE_1D;
@ -868,33 +841,25 @@ public:
return tex_; return tex_;
} }
void UpdateTextureLevels(GLRenderManager *render, const uint8_t *const *data, int numLevels, TextureCallback initDataCallback);
private: private:
void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback initDataCallback); void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback);
GLRenderManager *render_; GLRenderManager *render_;
GLRTexture *tex_; GLRTexture *tex_;
DataFormat format_;
TextureType type_; TextureType type_;
int mipLevels_; int mipLevels_;
bool generateMips_; // Generate mips requested bool generatedMips_;
bool generatedMips_; // Has generated mips
}; };
OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : render_(render) { OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : render_(render) {
_dbg_assert_(desc.format != Draw::DataFormat::UNDEFINED);
_dbg_assert_(desc.width > 0 && desc.height > 0 && desc.depth > 0);
_dbg_assert_(desc.type != Draw::TextureType::UNKNOWN);
generatedMips_ = false; generatedMips_ = false;
generateMips_ = desc.generateMips;
width_ = desc.width; width_ = desc.width;
height_ = desc.height; height_ = desc.height;
depth_ = desc.depth; depth_ = desc.depth;
format_ = desc.format; format_ = desc.format;
type_ = desc.type; type_ = desc.type;
GLenum target = TypeToTarget(desc.type); GLenum target = TypeToTarget(desc.type);
tex_ = render->CreateTexture(target, desc.width, desc.height, 1, desc.mipLevels); tex_ = render->CreateTexture(target, desc.width, desc.height, 1, desc.mipLevels);
@ -902,25 +867,21 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) :
if (desc.initData.empty()) if (desc.initData.empty())
return; return;
UpdateTextureLevels(render, desc.initData.data(), (int)desc.initData.size(), desc.initDataCallback);
}
void OpenGLTexture::UpdateTextureLevels(GLRenderManager *render, const uint8_t * const *data, int numLevels, TextureCallback initDataCallback) {
int level = 0; int level = 0;
int width = width_; int width = width_;
int height = height_; int height = height_;
int depth = depth_; int depth = depth_;
for (int i = 0; i < numLevels; i++) { for (auto data : desc.initData) {
SetImageData(0, 0, 0, width, height, depth, level, 0, data[i], initDataCallback); SetImageData(0, 0, 0, width, height, depth, level, 0, data, desc.initDataCallback);
width = (width + 1) / 2; width = (width + 1) / 2;
height = (height + 1) / 2; height = (height + 1) / 2;
depth = (depth + 1) / 2; depth = (depth + 1) / 2;
level++; level++;
} }
mipLevels_ = generateMips_ ? mipLevels_ : level; mipLevels_ = desc.generateMips ? desc.mipLevels : level;
bool genMips = false; bool genMips = false;
if (numLevels < mipLevels_ && generateMips_) { if ((int)desc.initData.size() < desc.mipLevels && desc.generateMips) {
// Assumes the texture is bound for editing // Assumes the texture is bound for editing
genMips = true; genMips = true;
generatedMips_ = true; generatedMips_ = true;
@ -931,7 +892,7 @@ void OpenGLTexture::UpdateTextureLevels(GLRenderManager *render, const uint8_t *
OpenGLTexture::~OpenGLTexture() { OpenGLTexture::~OpenGLTexture() {
if (tex_) { if (tex_) {
render_->DeleteTexture(tex_); render_->DeleteTexture(tex_);
tex_ = nullptr; tex_ = 0;
generatedMips_ = false; generatedMips_ = false;
} }
} }
@ -950,7 +911,7 @@ public:
GLRFramebuffer *framebuffer_ = nullptr; GLRFramebuffer *framebuffer_ = nullptr;
}; };
void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback initDataCallback) { void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback) {
if ((width != width_ || height != height_ || depth != depth_) && level == 0) { if ((width != width_ || height != height_ || depth != depth_) && level == 0) {
// When switching to texStorage we need to handle this correctly. // When switching to texStorage we need to handle this correctly.
width_ = width; width_ = width;
@ -966,8 +927,8 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int
uint8_t *texData = new uint8_t[(size_t)(width * height * depth * alignment)]; uint8_t *texData = new uint8_t[(size_t)(width * height * depth * alignment)];
bool texDataPopulated = false; bool texDataPopulated = false;
if (initDataCallback) { if (callback) {
texDataPopulated = initDataCallback(texData, data, width, height, depth, width * (int)alignment, height * width * (int)alignment); texDataPopulated = callback(texData, data, width, height, depth, width * (int)alignment, height * width * (int)alignment);
} }
if (texDataPopulated) { if (texDataPopulated) {
if (format_ == DataFormat::A1R5G5B5_UNORM_PACK16) { if (format_ == DataFormat::A1R5G5B5_UNORM_PACK16) {
@ -1027,7 +988,7 @@ static void LogReadPixelsError(GLenum error) {
} }
#endif #endif
bool OpenGLContext::CopyFramebufferToMemory(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat dataFormat, void *pixels, int pixelStride, ReadbackMode mode, const char *tag) { bool OpenGLContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat dataFormat, void *pixels, int pixelStride, const char *tag) {
if (gl_extensions.IsGLES && (channelBits & FB_COLOR_BIT) == 0) { if (gl_extensions.IsGLES && (channelBits & FB_COLOR_BIT) == 0) {
// Can't readback depth or stencil on GLES. // Can't readback depth or stencil on GLES.
return false; return false;
@ -1040,7 +1001,8 @@ bool OpenGLContext::CopyFramebufferToMemory(Framebuffer *src, int channelBits, i
aspect |= GL_DEPTH_BUFFER_BIT; aspect |= GL_DEPTH_BUFFER_BIT;
if (channelBits & FB_STENCIL_BIT) if (channelBits & FB_STENCIL_BIT)
aspect |= GL_STENCIL_BUFFER_BIT; aspect |= GL_STENCIL_BUFFER_BIT;
return renderManager_.CopyFramebufferToMemory(fb ? fb->framebuffer_ : nullptr, aspect, x, y, w, h, dataFormat, (uint8_t *)pixels, pixelStride, mode, tag); renderManager_.CopyFramebufferToMemorySync(fb ? fb->framebuffer_ : nullptr, aspect, x, y, w, h, dataFormat, (uint8_t *)pixels, pixelStride, tag);
return true;
} }
@ -1048,11 +1010,6 @@ Texture *OpenGLContext::CreateTexture(const TextureDesc &desc) {
return new OpenGLTexture(&renderManager_, desc); return new OpenGLTexture(&renderManager_, desc);
} }
void OpenGLContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) {
OpenGLTexture *tex = (OpenGLTexture *)texture;
tex->UpdateTextureLevels(&renderManager_, data, numLevels, initDataCallback);
}
DepthStencilState *OpenGLContext::CreateDepthStencilState(const DepthStencilStateDesc &desc) { DepthStencilState *OpenGLContext::CreateDepthStencilState(const DepthStencilStateDesc &desc) {
OpenGLDepthStencilState *ds = new OpenGLDepthStencilState(); OpenGLDepthStencilState *ds = new OpenGLDepthStencilState();
ds->depthTestEnabled = desc.depthTestEnabled; ds->depthTestEnabled = desc.depthTestEnabled;
@ -1306,7 +1263,7 @@ bool OpenGLPipeline::LinkShaders(const PipelineDesc &desc) {
for (int i = 0; i < (int)std::min((const uint32_t)samplers_.size(), MAX_TEXTURE_SLOTS); i++) { for (int i = 0; i < (int)std::min((const uint32_t)samplers_.size(), MAX_TEXTURE_SLOTS); i++) {
queries.push_back({ &locs_->samplerLocs_[i], samplers_[i].name, true }); queries.push_back({ &locs_->samplerLocs_[i], samplers_[i].name, true });
} }
samplersToCheck = (int)std::min((const uint32_t)samplers_.size(), MAX_TEXTURE_SLOTS); samplersToCheck = (int)samplers_.size();
} else { } else {
queries.push_back({ &locs_->samplerLocs_[0], "sampler0" }); queries.push_back({ &locs_->samplerLocs_[0], "sampler0" });
queries.push_back({ &locs_->samplerLocs_[1], "sampler1" }); queries.push_back({ &locs_->samplerLocs_[1], "sampler1" });
@ -1367,39 +1324,40 @@ void OpenGLContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {
} }
void OpenGLContext::Draw(int vertexCount, int offset) { void OpenGLContext::Draw(int vertexCount, int offset) {
_dbg_assert_msg_(curVBuffer_ != nullptr, "Can't call Draw without a vertex buffer"); _dbg_assert_msg_(curVBuffers_[0] != nullptr, "Can't call Draw without a vertex buffer");
ApplySamplers(); ApplySamplers();
_assert_(curPipeline_->inputLayout); if (curPipeline_->inputLayout) {
renderManager_.Draw(curPipeline_->inputLayout->inputLayout_, curVBuffer_->buffer_, curVBufferOffset_, curPipeline_->prim, offset, vertexCount); renderManager_.BindVertexBuffer(curPipeline_->inputLayout->inputLayout_, curVBuffers_[0]->buffer_, curVBufferOffsets_[0]);
}
renderManager_.Draw(curPipeline_->prim, offset, vertexCount);
} }
void OpenGLContext::DrawIndexed(int vertexCount, int offset) { void OpenGLContext::DrawIndexed(int vertexCount, int offset) {
_dbg_assert_msg_(curVBuffer_ != nullptr, "Can't call DrawIndexed without a vertex buffer"); _dbg_assert_msg_(curVBuffers_[0] != nullptr, "Can't call DrawIndexed without a vertex buffer");
_dbg_assert_msg_(curIBuffer_ != nullptr, "Can't call DrawIndexed without an index buffer"); _dbg_assert_msg_(curIBuffer_ != nullptr, "Can't call DrawIndexed without an index buffer");
ApplySamplers(); ApplySamplers();
_assert_(curPipeline_->inputLayout); if (curPipeline_->inputLayout) {
renderManager_.DrawIndexed( renderManager_.BindVertexBuffer(curPipeline_->inputLayout->inputLayout_, curVBuffers_[0]->buffer_, curVBufferOffsets_[0]);
curPipeline_->inputLayout->inputLayout_, }
curVBuffer_->buffer_, curVBufferOffset_, renderManager_.BindIndexBuffer(curIBuffer_->buffer_);
curIBuffer_->buffer_, curIBufferOffset_ + offset * sizeof(uint32_t), renderManager_.DrawIndexed(curPipeline_->prim, vertexCount, GL_UNSIGNED_SHORT, (void *)((intptr_t)curIBufferOffset_ + offset * sizeof(uint32_t)));
curPipeline_->prim, vertexCount, GL_UNSIGNED_SHORT);
} }
void OpenGLContext::DrawUP(const void *vdata, int vertexCount) { void OpenGLContext::DrawUP(const void *vdata, int vertexCount) {
_assert_(curPipeline_->inputLayout != nullptr); _assert_(curPipeline_->inputLayout != nullptr);
int stride = curPipeline_->inputLayout->stride; int stride = curPipeline_->inputLayout->stride;
uint32_t dataSize = stride * vertexCount; size_t dataSize = stride * vertexCount;
FrameData &frameData = frameData_[renderManager_.GetCurFrame()]; FrameData &frameData = frameData_[renderManager_.GetCurFrame()];
GLRBuffer *buf; GLRBuffer *buf;
uint32_t offset; size_t offset = frameData.push->Push(vdata, dataSize, &buf);
uint8_t *dest = frameData.push->Allocate(dataSize, 4, &buf, &offset);
memcpy(dest, vdata, dataSize);
ApplySamplers(); ApplySamplers();
_assert_(curPipeline_->inputLayout); if (curPipeline_->inputLayout) {
renderManager_.Draw(curPipeline_->inputLayout->inputLayout_, buf, offset, curPipeline_->prim, 0, vertexCount); renderManager_.BindVertexBuffer(curPipeline_->inputLayout->inputLayout_, buf, offset);
}
renderManager_.Draw(curPipeline_->prim, 0, vertexCount);
} }
void OpenGLContext::Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) { void OpenGLContext::Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) {
@ -1418,8 +1376,8 @@ void OpenGLContext::Clear(int mask, uint32_t colorval, float depthVal, int stenc
renderManager_.Clear(colorval, depthVal, stencilVal, glMask, 0xF, 0, 0, targetWidth_, targetHeight_); renderManager_.Clear(colorval, depthVal, stencilVal, glMask, 0xF, 0, 0, targetWidth_, targetHeight_);
} }
DrawContext *T3DCreateGLContext(bool canChangeSwapInterval) { DrawContext *T3DCreateGLContext() {
return new OpenGLContext(canChangeSwapInterval); return new OpenGLContext();
} }
OpenGLInputLayout::~OpenGLInputLayout() { OpenGLInputLayout::~OpenGLInputLayout() {
@ -1429,12 +1387,13 @@ OpenGLInputLayout::~OpenGLInputLayout() {
void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) { void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) {
// TODO: This is only accurate if there's only one stream. But whatever, for now we // TODO: This is only accurate if there's only one stream. But whatever, for now we
// never use multiple streams anyway. // never use multiple streams anyway.
stride = desc.stride; stride = desc.bindings.empty() ? 0 : (GLsizei)desc.bindings[0].stride;
std::vector<GLRInputLayout::Entry> entries; std::vector<GLRInputLayout::Entry> entries;
for (auto &attr : desc.attributes) { for (auto &attr : desc.attributes) {
GLRInputLayout::Entry entry; GLRInputLayout::Entry entry;
entry.location = attr.location; entry.location = attr.location;
entry.stride = (GLsizei)desc.bindings[attr.binding].stride;
entry.offset = attr.offset; entry.offset = attr.offset;
switch (attr.format) { switch (attr.format) {
case DataFormat::R32G32_FLOAT: case DataFormat::R32G32_FLOAT:
@ -1466,7 +1425,7 @@ void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) {
entries.push_back(entry); entries.push_back(entry);
} }
if (!entries.empty()) { if (!entries.empty()) {
inputLayout_ = render_->CreateInputLayout(entries, stride); inputLayout_ = render_->CreateInputLayout(entries);
} else { } else {
inputLayout_ = nullptr; inputLayout_ = nullptr;
} }
@ -1478,7 +1437,7 @@ Framebuffer *OpenGLContext::CreateFramebuffer(const FramebufferDesc &desc) {
// TODO: Support multiview later. (It's our only use case for multi layers). // TODO: Support multiview later. (It's our only use case for multi layers).
_dbg_assert_(desc.numLayers == 1); _dbg_assert_(desc.numLayers == 1);
GLRFramebuffer *framebuffer = renderManager_.CreateFramebuffer(desc.width, desc.height, desc.z_stencil, desc.tag); GLRFramebuffer *framebuffer = renderManager_.CreateFramebuffer(desc.colorFormat, desc.width, desc.height, desc.z_stencil);
OpenGLFramebuffer *fbo = new OpenGLFramebuffer(&renderManager_, framebuffer); OpenGLFramebuffer *fbo = new OpenGLFramebuffer(&renderManager_, framebuffer);
return fbo; return fbo;
} }
@ -1598,23 +1557,7 @@ uint32_t OpenGLContext::GetDataFormatSupport(DataFormat fmt) const {
case DataFormat::BC1_RGBA_UNORM_BLOCK: case DataFormat::BC1_RGBA_UNORM_BLOCK:
case DataFormat::BC2_UNORM_BLOCK: case DataFormat::BC2_UNORM_BLOCK:
case DataFormat::BC3_UNORM_BLOCK: case DataFormat::BC3_UNORM_BLOCK:
return gl_extensions.supportsBC123 ? FMT_TEXTURE : 0; return FMT_TEXTURE;
case DataFormat::BC4_UNORM_BLOCK:
case DataFormat::BC5_UNORM_BLOCK:
return gl_extensions.supportsBC45 ? FMT_TEXTURE : 0;
case DataFormat::BC7_UNORM_BLOCK:
return gl_extensions.supportsBC7 ? FMT_TEXTURE : 0;
case DataFormat::ASTC_4x4_UNORM_BLOCK:
return gl_extensions.supportsASTC ? FMT_TEXTURE : 0;
case DataFormat::ETC2_R8G8B8_UNORM_BLOCK:
case DataFormat::ETC2_R8G8B8A1_UNORM_BLOCK:
case DataFormat::ETC2_R8G8B8A8_UNORM_BLOCK:
return gl_extensions.supportsETC2 ? FMT_TEXTURE : 0;
default: default:
return 0; return 0;
} }

View file

@ -63,10 +63,15 @@ static EShLanguage GetShLanguageFromStage(const ShaderStage stage) {
} }
void ShaderTranslationInit() { void ShaderTranslationInit() {
// TODO: We have TLS issues on UWP
#if !PPSSPP_PLATFORM(UWP)
glslang::InitializeProcess(); glslang::InitializeProcess();
#endif
} }
void ShaderTranslationShutdown() { void ShaderTranslationShutdown() {
#if !PPSSPP_PLATFORM(UWP)
glslang::FinalizeProcess(); glslang::FinalizeProcess();
#endif
} }
struct Builtin { struct Builtin {
@ -224,6 +229,11 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, const ShaderLan
return result; return result;
} }
#if PPSSPP_PLATFORM(UWP)
*errorMessage = "No shader translation available (UWP)";
return false;
#endif
errorMessage->clear(); errorMessage->clear();
glslang::TProgram program; glslang::TProgram program;

View file

@ -57,13 +57,12 @@ std::string VulkanVendorString(uint32_t vendorId) {
case VULKAN_VENDOR_QUALCOMM: return "Qualcomm"; case VULKAN_VENDOR_QUALCOMM: return "Qualcomm";
case VULKAN_VENDOR_IMGTEC: return "Imagination"; case VULKAN_VENDOR_IMGTEC: return "Imagination";
case VULKAN_VENDOR_APPLE: return "Apple"; case VULKAN_VENDOR_APPLE: return "Apple";
case VULKAN_VENDOR_MESA: return "Mesa";
default: default:
return StringFromFormat("%08x", vendorId); return StringFromFormat("%08x", vendorId);
} }
} }
const char *VulkanPresentModeToString(VkPresentModeKHR presentMode) { const char *PresentModeString(VkPresentModeKHR presentMode) {
switch (presentMode) { switch (presentMode) {
case VK_PRESENT_MODE_IMMEDIATE_KHR: return "IMMEDIATE"; case VK_PRESENT_MODE_IMMEDIATE_KHR: return "IMMEDIATE";
case VK_PRESENT_MODE_MAILBOX_KHR: return "MAILBOX"; case VK_PRESENT_MODE_MAILBOX_KHR: return "MAILBOX";
@ -536,7 +535,7 @@ int VulkanContext::GetBestPhysicalDevice() {
void VulkanContext::ChooseDevice(int physical_device) { void VulkanContext::ChooseDevice(int physical_device) {
physical_device_ = physical_device; physical_device_ = physical_device;
INFO_LOG(G3D, "Chose physical device %d: %s", physical_device, physicalDeviceProperties_[physical_device].properties.deviceName); INFO_LOG(G3D, "Chose physical device %d: %p", physical_device, physical_devices_[physical_device]);
GetDeviceLayerProperties(); GetDeviceLayerProperties();
if (!CheckLayers(device_layer_properties_, device_layer_names_)) { if (!CheckLayers(device_layer_properties_, device_layer_names_)) {
@ -550,13 +549,12 @@ void VulkanContext::ChooseDevice(int physical_device) {
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices_[physical_device_], &queue_count, queueFamilyProperties_.data()); vkGetPhysicalDeviceQueueFamilyProperties(physical_devices_[physical_device_], &queue_count, queueFamilyProperties_.data());
_dbg_assert_(queue_count >= 1); _dbg_assert_(queue_count >= 1);
// Detect preferred depth/stencil formats, in this order. All supported devices will support at least one of these. // Detect preferred formats, in this order.
static const VkFormat depthStencilFormats[] = { static const VkFormat depthStencilFormats[] = {
VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT,
}; };
deviceInfo_.preferredDepthStencilFormat = VK_FORMAT_UNDEFINED; deviceInfo_.preferredDepthStencilFormat = VK_FORMAT_UNDEFINED;
for (size_t i = 0; i < ARRAY_SIZE(depthStencilFormats); i++) { for (size_t i = 0; i < ARRAY_SIZE(depthStencilFormats); i++) {
VkFormatProperties props; VkFormatProperties props;
@ -575,8 +573,7 @@ void VulkanContext::ChooseDevice(int physical_device) {
deviceInfo_.canBlitToPreferredDepthStencilFormat = true; deviceInfo_.canBlitToPreferredDepthStencilFormat = true;
} }
// This is as good a place as any to do this. Though, we don't use this much anymore after we added // This is as good a place as any to do this.
// support for VMA.
vkGetPhysicalDeviceMemoryProperties(physical_devices_[physical_device_], &memory_properties_); vkGetPhysicalDeviceMemoryProperties(physical_devices_[physical_device_], &memory_properties_);
INFO_LOG(G3D, "Memory Types (%d):", memory_properties_.memoryTypeCount); INFO_LOG(G3D, "Memory Types (%d):", memory_properties_.memoryTypeCount);
for (int i = 0; i < (int)memory_properties_.memoryTypeCount; i++) { for (int i = 0; i < (int)memory_properties_.memoryTypeCount; i++) {
@ -595,24 +592,31 @@ void VulkanContext::ChooseDevice(int physical_device) {
VkPhysicalDeviceFeatures2 features2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR}; VkPhysicalDeviceFeatures2 features2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR};
// Add to chain even if not supported, GetPhysicalDeviceFeatures is supposed to ignore unknown structs. // Add to chain even if not supported, GetPhysicalDeviceFeatures is supposed to ignore unknown structs.
VkPhysicalDeviceMultiviewFeatures multiViewFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES }; VkPhysicalDeviceMultiviewFeatures multiViewFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES };
VkPhysicalDevicePresentWaitFeaturesKHR presentWaitFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR };
VkPhysicalDevicePresentIdFeaturesKHR presentIdFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR };
features2.pNext = &multiViewFeatures; features2.pNext = &multiViewFeatures;
multiViewFeatures.pNext = &presentWaitFeatures;
presentWaitFeatures.pNext = &presentIdFeatures;
presentIdFeatures.pNext = nullptr;
vkGetPhysicalDeviceFeatures2KHR(physical_devices_[physical_device_], &features2); vkGetPhysicalDeviceFeatures2KHR(physical_devices_[physical_device_], &features2);
deviceFeatures_.available.standard = features2.features; deviceFeatures_.available.standard = features2.features;
deviceFeatures_.available.multiview = multiViewFeatures; deviceFeatures_.available.multiview = multiViewFeatures;
deviceFeatures_.available.presentWait = presentWaitFeatures;
deviceFeatures_.available.presentId = presentIdFeatures;
} else { } else {
vkGetPhysicalDeviceFeatures(physical_devices_[physical_device_], &deviceFeatures_.available.standard); vkGetPhysicalDeviceFeatures(physical_devices_[physical_device_], &deviceFeatures_.available.standard);
deviceFeatures_.available.multiview = {}; deviceFeatures_.available.multiview = {};
} }
deviceFeatures_.enabled = {};
// Enable a few safe ones if they are available.
deviceFeatures_.enabled.standard.dualSrcBlend = deviceFeatures_.available.standard.dualSrcBlend;
deviceFeatures_.enabled.standard.logicOp = deviceFeatures_.available.standard.logicOp;
deviceFeatures_.enabled.standard.depthClamp = deviceFeatures_.available.standard.depthClamp;
deviceFeatures_.enabled.standard.depthBounds = deviceFeatures_.available.standard.depthBounds;
deviceFeatures_.enabled.standard.samplerAnisotropy = deviceFeatures_.available.standard.samplerAnisotropy;
deviceFeatures_.enabled.standard.shaderClipDistance = deviceFeatures_.available.standard.shaderClipDistance;
deviceFeatures_.enabled.standard.shaderCullDistance = deviceFeatures_.available.standard.shaderCullDistance;
deviceFeatures_.enabled.standard.geometryShader = deviceFeatures_.available.standard.geometryShader;
deviceFeatures_.enabled.standard.sampleRateShading = deviceFeatures_.available.standard.sampleRateShading;
deviceFeatures_.enabled.multiview = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES };
deviceFeatures_.enabled.multiview.multiview = deviceFeatures_.available.multiview.multiview;
// deviceFeatures_.enabled.multiview.multiviewGeometryShader = deviceFeatures_.available.multiview.multiviewGeometryShader;
GetDeviceLayerExtensionList(nullptr, device_extension_properties_); GetDeviceLayerExtensionList(nullptr, device_extension_properties_);
device_extensions_enabled_.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); device_extensions_enabled_.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
@ -644,8 +648,8 @@ VkResult VulkanContext::CreateDevice() {
return VK_ERROR_INITIALIZATION_FAILED; return VK_ERROR_INITIALIZATION_FAILED;
} }
VkDeviceQueueCreateInfo queue_info{ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; VkDeviceQueueCreateInfo queue_info{VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO};
float queue_priorities[1] = { 1.0f }; float queue_priorities[1] = {1.0f};
queue_info.queueCount = 1; queue_info.queueCount = 1;
queue_info.pQueuePriorities = queue_priorities; queue_info.pQueuePriorities = queue_priorities;
bool found = false; bool found = false;
@ -668,6 +672,11 @@ VkResult VulkanContext::CreateDevice() {
extensionsLookup_.KHR_get_memory_requirements2 = true; extensionsLookup_.KHR_get_memory_requirements2 = true;
extensionsLookup_.KHR_dedicated_allocation = EnableDeviceExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME); extensionsLookup_.KHR_dedicated_allocation = EnableDeviceExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
} }
if (EnableDeviceExtension(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME)) {
if (EnableDeviceExtension(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME)) {
extensionsLookup_.EXT_external_memory_host = EnableDeviceExtension(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME);
}
}
if (EnableDeviceExtension(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME)) { if (EnableDeviceExtension(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME)) {
extensionsLookup_.KHR_create_renderpass2 = true; extensionsLookup_.KHR_create_renderpass2 = true;
extensionsLookup_.KHR_depth_stencil_resolve = EnableDeviceExtension(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME); extensionsLookup_.KHR_depth_stencil_resolve = EnableDeviceExtension(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
@ -677,41 +686,6 @@ VkResult VulkanContext::CreateDevice() {
extensionsLookup_.EXT_fragment_shader_interlock = EnableDeviceExtension(VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME); extensionsLookup_.EXT_fragment_shader_interlock = EnableDeviceExtension(VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME);
extensionsLookup_.ARM_rasterization_order_attachment_access = EnableDeviceExtension(VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME); extensionsLookup_.ARM_rasterization_order_attachment_access = EnableDeviceExtension(VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME);
#if !PPSSPP_PLATFORM(MAC) && !PPSSPP_PLATFORM(IOS)
extensionsLookup_.GOOGLE_display_timing = EnableDeviceExtension(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME);
#endif
if (!extensionsLookup_.GOOGLE_display_timing) {
extensionsLookup_.KHR_present_id = EnableDeviceExtension(VK_KHR_PRESENT_ID_EXTENSION_NAME);
extensionsLookup_.KHR_present_wait = EnableDeviceExtension(VK_KHR_PRESENT_WAIT_EXTENSION_NAME);
}
deviceFeatures_.enabled = {};
// Enable a few safe ones if they are available.
deviceFeatures_.enabled.standard.dualSrcBlend = deviceFeatures_.available.standard.dualSrcBlend;
deviceFeatures_.enabled.standard.logicOp = deviceFeatures_.available.standard.logicOp;
deviceFeatures_.enabled.standard.depthClamp = deviceFeatures_.available.standard.depthClamp;
deviceFeatures_.enabled.standard.depthBounds = deviceFeatures_.available.standard.depthBounds;
deviceFeatures_.enabled.standard.samplerAnisotropy = deviceFeatures_.available.standard.samplerAnisotropy;
deviceFeatures_.enabled.standard.shaderClipDistance = deviceFeatures_.available.standard.shaderClipDistance;
deviceFeatures_.enabled.standard.shaderCullDistance = deviceFeatures_.available.standard.shaderCullDistance;
deviceFeatures_.enabled.standard.geometryShader = deviceFeatures_.available.standard.geometryShader;
deviceFeatures_.enabled.standard.sampleRateShading = deviceFeatures_.available.standard.sampleRateShading;
deviceFeatures_.enabled.multiview = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES };
if (extensionsLookup_.KHR_multiview) {
deviceFeatures_.enabled.multiview.multiview = deviceFeatures_.available.multiview.multiview;
}
// Strangely, on Intel, it reports these as available even though the extension isn't in the list.
deviceFeatures_.enabled.presentId = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR };
if (extensionsLookup_.KHR_present_id) {
deviceFeatures_.enabled.presentId.presentId = deviceFeatures_.available.presentId.presentId;
}
deviceFeatures_.enabled.presentWait = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR };
if (extensionsLookup_.KHR_present_wait) {
deviceFeatures_.enabled.presentWait.presentWait = deviceFeatures_.available.presentWait.presentWait;
}
// deviceFeatures_.enabled.multiview.multiviewGeometryShader = deviceFeatures_.available.multiview.multiviewGeometryShader;
VkPhysicalDeviceFeatures2 features2{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 }; VkPhysicalDeviceFeatures2 features2{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 };
VkDeviceCreateInfo device_info{ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; VkDeviceCreateInfo device_info{ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
@ -726,9 +700,6 @@ VkResult VulkanContext::CreateDevice() {
device_info.pNext = &features2; device_info.pNext = &features2;
features2.features = deviceFeatures_.enabled.standard; features2.features = deviceFeatures_.enabled.standard;
features2.pNext = &deviceFeatures_.enabled.multiview; features2.pNext = &deviceFeatures_.enabled.multiview;
deviceFeatures_.enabled.multiview.pNext = &deviceFeatures_.enabled.presentWait;
deviceFeatures_.enabled.presentWait.pNext = &deviceFeatures_.enabled.presentId;
deviceFeatures_.enabled.presentId.pNext = nullptr;
} else { } else {
device_info.pEnabledFeatures = &deviceFeatures_.enabled.standard; device_info.pEnabledFeatures = &deviceFeatures_.enabled.standard;
} }
@ -740,9 +711,7 @@ VkResult VulkanContext::CreateDevice() {
} else { } else {
VulkanLoadDeviceFunctions(device_, extensionsLookup_); VulkanLoadDeviceFunctions(device_, extensionsLookup_);
} }
INFO_LOG(G3D, "Vulkan Device created: %s", physicalDeviceProperties_[physical_device_].properties.deviceName); INFO_LOG(G3D, "Vulkan Device created");
// Since we successfully created a device (however we got here, might be interesting in debug), we force the choice to be visible in the menu.
VulkanSetAvailable(true); VulkanSetAvailable(true);
VmaAllocatorCreateInfo allocatorInfo = {}; VmaAllocatorCreateInfo allocatorInfo = {};
@ -917,187 +886,6 @@ VkResult VulkanContext::ReinitSurface() {
case WINDOWSYSTEM_DISPLAY: case WINDOWSYSTEM_DISPLAY:
{ {
VkDisplaySurfaceCreateInfoKHR display{ VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR }; VkDisplaySurfaceCreateInfoKHR display{ VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR };
#if !defined(__LIBRETRO__)
/*
And when not to use libretro need VkDisplaySurfaceCreateInfoKHR this extension,
then you need to use dlopen to read vulkan loader in VulkanLoader.cpp.
huangzihan China
*/
if(!vkGetPhysicalDeviceDisplayPropertiesKHR ||
!vkGetPhysicalDeviceDisplayPlanePropertiesKHR ||
!vkGetDisplayModePropertiesKHR ||
!vkGetDisplayPlaneSupportedDisplaysKHR ||
!vkGetDisplayPlaneCapabilitiesKHR ) {
_assert_msg_(false, "DISPLAY Vulkan cannot find any vulkan function symbols.");
return VK_ERROR_INITIALIZATION_FAILED;
}
//The following code is for reference:
// https://github.com/vanfanel/ppsspp
// When using the VK_KHR_display extension and not using LIBRETRO, a complete
// VkDisplaySurfaceCreateInfoKHR is needed.
uint32_t display_count;
uint32_t plane_count;
VkDisplayPropertiesKHR *display_props = NULL;
VkDisplayPlanePropertiesKHR *plane_props = NULL;
VkDisplayModePropertiesKHR* mode_props = NULL;
VkExtent2D image_size;
// This is the chosen physical_device, it has been chosen elsewhere.
VkPhysicalDevice phys_device = physical_devices_[physical_device_];
VkDisplayModeKHR display_mode = VK_NULL_HANDLE;
VkDisplayPlaneAlphaFlagBitsKHR alpha_mode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
uint32_t plane = UINT32_MAX;
// For now, use the first available (connected) display.
int display_index = 0;
VkResult result;
bool ret = false;
bool mode_found = false;
int i, j;
// 1 physical device can have N displays connected.
// Vulkan only counts the connected displays.
// Get a list of displays on the physical device.
display_count = 0;
vkGetPhysicalDeviceDisplayPropertiesKHR(phys_device, &display_count, NULL);
if (display_count == 0) {
_assert_msg_(false, "DISPLAY Vulkan couldn't find any displays.");
return VK_ERROR_INITIALIZATION_FAILED;
}
display_props = new VkDisplayPropertiesKHR[display_count];
vkGetPhysicalDeviceDisplayPropertiesKHR(phys_device, &display_count, display_props);
// Get a list of display planes on the physical device.
plane_count = 0;
vkGetPhysicalDeviceDisplayPlanePropertiesKHR(phys_device, &plane_count, NULL);
if (plane_count == 0) {
_assert_msg_(false, "DISPLAY Vulkan couldn't find any planes on the physical device");
return VK_ERROR_INITIALIZATION_FAILED;
}
plane_props = new VkDisplayPlanePropertiesKHR[plane_count];
vkGetPhysicalDeviceDisplayPlanePropertiesKHR(phys_device, &plane_count, plane_props);
// Get the Vulkan display we are going to use.
VkDisplayKHR myDisplay = display_props[display_index].display;
// Get the list of display modes of the display
uint32_t mode_count = 0;
vkGetDisplayModePropertiesKHR(phys_device, myDisplay, &mode_count, NULL);
if (mode_count == 0) {
_assert_msg_(false, "DISPLAY Vulkan couldn't find any video modes on the display");
return VK_ERROR_INITIALIZATION_FAILED;
}
mode_props = new VkDisplayModePropertiesKHR[mode_count];
vkGetDisplayModePropertiesKHR(phys_device, myDisplay, &mode_count, mode_props);
// See if there's an appropiate mode available on the display
display_mode = VK_NULL_HANDLE;
for (i = 0; i < mode_count; ++i)
{
const VkDisplayModePropertiesKHR* mode = &mode_props[i];
if (mode->parameters.visibleRegion.width == g_display.pixel_xres &&
mode->parameters.visibleRegion.height == g_display.pixel_yres)
{
display_mode = mode->displayMode;
mode_found = true;
break;
}
}
// Free the mode list now.
delete [] mode_props;
// If there are no useable modes found on the display, error out
if (display_mode == VK_NULL_HANDLE)
{
_assert_msg_(false, "DISPLAY Vulkan couldn't find any video modes on the display");
return VK_ERROR_INITIALIZATION_FAILED;
}
/* Iterate on the list of planes of the physical device
to find a plane that matches these criteria:
-It must be compatible with the chosen display + mode.
-It isn't currently bound to another display.
-It supports per-pixel alpha, if possible. */
for (i = 0; i < plane_count; i++) {
uint32_t supported_displays_count = 0;
VkDisplayKHR* supported_displays;
VkDisplayPlaneCapabilitiesKHR plane_caps;
/* See if the plane is compatible with the current display. */
vkGetDisplayPlaneSupportedDisplaysKHR(phys_device, i, &supported_displays_count, NULL);
if (supported_displays_count == 0) {
/* This plane doesn't support any displays. Continue to the next plane. */
continue;
}
/* Get the list of displays supported by this plane. */
supported_displays = new VkDisplayKHR[supported_displays_count];
vkGetDisplayPlaneSupportedDisplaysKHR(phys_device, i,
&supported_displays_count, supported_displays);
/* The plane must be bound to the chosen display, or not in use.
If none of these is true, iterate to another plane. */
if ( !( (plane_props[i].currentDisplay == myDisplay) ||
(plane_props[i].currentDisplay == VK_NULL_HANDLE)))
continue;
/* Iterate the list of displays supported by this plane
in order to find out if the chosen display is among them. */
bool plane_supports_display = false;
for (j = 0; j < supported_displays_count; j++) {
if (supported_displays[j] == myDisplay) {
plane_supports_display = true;
break;
}
}
/* Free the list of displays supported by this plane. */
delete [] supported_displays;
/* If the display is not supported by this plane, iterate to the next plane. */
if (!plane_supports_display)
continue;
/* Want a plane that supports the alpha mode we have chosen. */
vkGetDisplayPlaneCapabilitiesKHR(phys_device, display_mode, i, &plane_caps);
if (plane_caps.supportedAlpha & alpha_mode) {
/* Yep, this plane is alright. */
plane = i;
break;
}
}
/* If we couldn't find an appropiate plane, error out. */
if (plane == UINT32_MAX) {
_assert_msg_(false, "DISPLAY Vulkan couldn't find an appropiate plane");
return VK_ERROR_INITIALIZATION_FAILED;
}
// Finally, create the vulkan surface.
image_size.width = g_display.pixel_xres;
image_size.height = g_display.pixel_yres;
display.displayMode = display_mode;
display.imageExtent = image_size;
display.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
display.alphaMode = alpha_mode;
display.globalAlpha = 1.0f;
display.planeIndex = plane;
display.planeStackIndex = plane_props[plane].currentStackIndex;
display.pNext = nullptr;
delete [] display_props;
delete [] plane_props;
#endif
display.flags = 0; display.flags = 0;
retval = vkCreateDisplayPlaneSurfaceKHR(instance_, &display, nullptr, &surface_); retval = vkCreateDisplayPlaneSurfaceKHR(instance_, &display, nullptr, &surface_);
break; break;
@ -1233,12 +1021,6 @@ static std::string surface_transforms_to_string(VkSurfaceTransformFlagsKHR trans
} }
bool VulkanContext::InitSwapchain() { bool VulkanContext::InitSwapchain() {
_assert_(physical_device_ >= 0 && physical_device_ < physical_devices_.size());
if (!surface_) {
ERROR_LOG(G3D, "VK: No surface, can't create swapchain");
return false;
}
VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_devices_[physical_device_], surface_, &surfCapabilities_); VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_devices_[physical_device_], surface_, &surfCapabilities_);
if (res == VK_ERROR_SURFACE_LOST_KHR) { if (res == VK_ERROR_SURFACE_LOST_KHR) {
// Not much to do. // Not much to do.
@ -1273,26 +1055,23 @@ bool VulkanContext::InitSwapchain() {
surfCapabilities_.maxImageExtent.width, surfCapabilities_.maxImageExtent.height, surfCapabilities_.maxImageExtent.width, surfCapabilities_.maxImageExtent.height,
swapChainExtent_.width, swapChainExtent_.height); swapChainExtent_.width, swapChainExtent_.height);
availablePresentModes_.clear();
// TODO: Find a better way to specify the prioritized present mode while being able // TODO: Find a better way to specify the prioritized present mode while being able
// to fall back in a sensible way. // to fall back in a sensible way.
VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_MAX_ENUM_KHR; VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_MAX_ENUM_KHR;
std::string modes = ""; std::string modes = "";
for (size_t i = 0; i < presentModeCount; i++) { for (size_t i = 0; i < presentModeCount; i++) {
modes += VulkanPresentModeToString(presentModes[i]); modes += PresentModeString(presentModes[i]);
if (i != presentModeCount - 1) { if (i != presentModeCount - 1) {
modes += ", "; modes += ", ";
} }
availablePresentModes_.push_back(presentModes[i]);
} }
INFO_LOG(G3D, "Supported present modes: %s", modes.c_str()); INFO_LOG(G3D, "Supported present modes: %s", modes.c_str());
for (size_t i = 0; i < presentModeCount; i++) { for (size_t i = 0; i < presentModeCount; i++) {
bool match = false; bool match = false;
match = match || ((flags_ & VULKAN_FLAG_PRESENT_MAILBOX) && presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR); match = match || ((flags_ & VULKAN_FLAG_PRESENT_MAILBOX) && presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR);
match = match || ((flags_ & VULKAN_FLAG_PRESENT_IMMEDIATE) && presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR);
match = match || ((flags_ & VULKAN_FLAG_PRESENT_FIFO_RELAXED) && presentModes[i] == VK_PRESENT_MODE_FIFO_RELAXED_KHR); match = match || ((flags_ & VULKAN_FLAG_PRESENT_FIFO_RELAXED) && presentModes[i] == VK_PRESENT_MODE_FIFO_RELAXED_KHR);
match = match || ((flags_ & VULKAN_FLAG_PRESENT_FIFO) && presentModes[i] == VK_PRESENT_MODE_FIFO_KHR); match = match || ((flags_ & VULKAN_FLAG_PRESENT_FIFO) && presentModes[i] == VK_PRESENT_MODE_FIFO_KHR);
match = match || ((flags_ & VULKAN_FLAG_PRESENT_IMMEDIATE) && presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR);
// Default to the first present mode from the list. // Default to the first present mode from the list.
if (match || swapchainPresentMode == VK_PRESENT_MODE_MAX_ENUM_KHR) { if (match || swapchainPresentMode == VK_PRESENT_MODE_MAX_ENUM_KHR) {
@ -1302,6 +1081,10 @@ bool VulkanContext::InitSwapchain() {
break; break;
} }
} }
#ifdef __ANDROID__
// HACK
swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
#endif
delete[] presentModes; delete[] presentModes;
// Determine the number of VkImage's to use in the swap chain (we desire to // Determine the number of VkImage's to use in the swap chain (we desire to
// own only 1 image at a time, besides the images being displayed and // own only 1 image at a time, besides the images being displayed and
@ -1315,7 +1098,7 @@ bool VulkanContext::InitSwapchain() {
} }
INFO_LOG(G3D, "Chosen present mode: %d (%s). numSwapChainImages: %d/%d", INFO_LOG(G3D, "Chosen present mode: %d (%s). numSwapChainImages: %d/%d",
swapchainPresentMode, VulkanPresentModeToString(swapchainPresentMode), swapchainPresentMode, PresentModeString(swapchainPresentMode),
desiredNumberOfSwapChainImages, surfCapabilities_.maxImageCount); desiredNumberOfSwapChainImages, surfCapabilities_.maxImageCount);
// We mostly follow the practices from // We mostly follow the practices from
@ -1324,8 +1107,8 @@ bool VulkanContext::InitSwapchain() {
VkSurfaceTransformFlagBitsKHR preTransform; VkSurfaceTransformFlagBitsKHR preTransform;
std::string supportedTransforms = surface_transforms_to_string(surfCapabilities_.supportedTransforms); std::string supportedTransforms = surface_transforms_to_string(surfCapabilities_.supportedTransforms);
std::string currentTransform = surface_transforms_to_string(surfCapabilities_.currentTransform); std::string currentTransform = surface_transforms_to_string(surfCapabilities_.currentTransform);
g_display.rotation = DisplayRotation::ROTATE_0; g_display_rotation = DisplayRotation::ROTATE_0;
g_display.rot_matrix.setIdentity(); g_display_rot_matrix.setIdentity();
uint32_t allowedRotations = VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR; uint32_t allowedRotations = VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR;
// Hack: Don't allow 270 degrees pretransform (inverse landscape), it creates bizarre issues on some devices (see #15773). // Hack: Don't allow 270 degrees pretransform (inverse landscape), it creates bizarre issues on some devices (see #15773).
@ -1336,20 +1119,20 @@ bool VulkanContext::InitSwapchain() {
} else if (surfCapabilities_.currentTransform & allowedRotations) { } else if (surfCapabilities_.currentTransform & allowedRotations) {
// Normal, sensible rotations. Let's handle it. // Normal, sensible rotations. Let's handle it.
preTransform = surfCapabilities_.currentTransform; preTransform = surfCapabilities_.currentTransform;
g_display.rot_matrix.setIdentity(); g_display_rot_matrix.setIdentity();
switch (surfCapabilities_.currentTransform) { switch (surfCapabilities_.currentTransform) {
case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
g_display.rotation = DisplayRotation::ROTATE_90; g_display_rotation = DisplayRotation::ROTATE_90;
g_display.rot_matrix.setRotationZ90(); g_display_rot_matrix.setRotationZ90();
std::swap(swapChainExtent_.width, swapChainExtent_.height); std::swap(swapChainExtent_.width, swapChainExtent_.height);
break; break;
case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR: case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
g_display.rotation = DisplayRotation::ROTATE_180; g_display_rotation = DisplayRotation::ROTATE_180;
g_display.rot_matrix.setRotationZ180(); g_display_rot_matrix.setRotationZ180();
break; break;
case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR: case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
g_display.rotation = DisplayRotation::ROTATE_270; g_display_rotation = DisplayRotation::ROTATE_270;
g_display.rot_matrix.setRotationZ270(); g_display_rot_matrix.setRotationZ270();
std::swap(swapChainExtent_.width, swapChainExtent_.height); std::swap(swapChainExtent_.width, swapChainExtent_.height);
break; break;
default: default:
@ -1395,8 +1178,6 @@ bool VulkanContext::InitSwapchain() {
swap_chain_info.clipped = true; swap_chain_info.clipped = true;
swap_chain_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; swap_chain_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
presentMode_ = swapchainPresentMode;
// Don't ask for TRANSFER_DST for the swapchain image, we don't use that. // Don't ask for TRANSFER_DST for the swapchain image, we don't use that.
// if (surfCapabilities_.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) // if (surfCapabilities_.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)
// swap_chain_info.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; // swap_chain_info.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
@ -1666,101 +1447,80 @@ void VulkanDeleteList::Take(VulkanDeleteList &del) {
} }
void VulkanDeleteList::PerformDeletes(VulkanContext *vulkan, VmaAllocator allocator) { void VulkanDeleteList::PerformDeletes(VulkanContext *vulkan, VmaAllocator allocator) {
int deleteCount = 0;
for (auto &callback : callbacks_) { for (auto &callback : callbacks_) {
callback.func(vulkan, callback.userdata); callback.func(vulkan, callback.userdata);
deleteCount++;
} }
callbacks_.clear(); callbacks_.clear();
VkDevice device = vulkan->GetDevice(); VkDevice device = vulkan->GetDevice();
for (auto &cmdPool : cmdPools_) { for (auto &cmdPool : cmdPools_) {
vkDestroyCommandPool(device, cmdPool, nullptr); vkDestroyCommandPool(device, cmdPool, nullptr);
deleteCount++;
} }
cmdPools_.clear(); cmdPools_.clear();
for (auto &descPool : descPools_) { for (auto &descPool : descPools_) {
vkDestroyDescriptorPool(device, descPool, nullptr); vkDestroyDescriptorPool(device, descPool, nullptr);
deleteCount++;
} }
descPools_.clear(); descPools_.clear();
for (auto &module : modules_) { for (auto &module : modules_) {
vkDestroyShaderModule(device, module, nullptr); vkDestroyShaderModule(device, module, nullptr);
deleteCount++;
} }
modules_.clear(); modules_.clear();
for (auto &buf : buffers_) { for (auto &buf : buffers_) {
vkDestroyBuffer(device, buf, nullptr); vkDestroyBuffer(device, buf, nullptr);
deleteCount++;
} }
buffers_.clear(); buffers_.clear();
for (auto &buf : buffersWithAllocs_) { for (auto &buf : buffersWithAllocs_) {
vmaDestroyBuffer(allocator, buf.buffer, buf.alloc); vmaDestroyBuffer(allocator, buf.buffer, buf.alloc);
deleteCount++;
} }
buffersWithAllocs_.clear(); buffersWithAllocs_.clear();
for (auto &bufView : bufferViews_) { for (auto &bufView : bufferViews_) {
vkDestroyBufferView(device, bufView, nullptr); vkDestroyBufferView(device, bufView, nullptr);
deleteCount++;
} }
bufferViews_.clear(); bufferViews_.clear();
for (auto &imageWithAlloc : imagesWithAllocs_) { for (auto &imageWithAlloc : imagesWithAllocs_) {
vmaDestroyImage(allocator, imageWithAlloc.image, imageWithAlloc.alloc); vmaDestroyImage(allocator, imageWithAlloc.image, imageWithAlloc.alloc);
deleteCount++;
} }
imagesWithAllocs_.clear(); imagesWithAllocs_.clear();
for (auto &imageView : imageViews_) { for (auto &imageView : imageViews_) {
vkDestroyImageView(device, imageView, nullptr); vkDestroyImageView(device, imageView, nullptr);
deleteCount++;
} }
imageViews_.clear(); imageViews_.clear();
for (auto &mem : deviceMemory_) { for (auto &mem : deviceMemory_) {
vkFreeMemory(device, mem, nullptr); vkFreeMemory(device, mem, nullptr);
deleteCount++;
} }
deviceMemory_.clear(); deviceMemory_.clear();
for (auto &sampler : samplers_) { for (auto &sampler : samplers_) {
vkDestroySampler(device, sampler, nullptr); vkDestroySampler(device, sampler, nullptr);
deleteCount++;
} }
samplers_.clear(); samplers_.clear();
for (auto &pipeline : pipelines_) { for (auto &pipeline : pipelines_) {
vkDestroyPipeline(device, pipeline, nullptr); vkDestroyPipeline(device, pipeline, nullptr);
deleteCount++;
} }
pipelines_.clear(); pipelines_.clear();
for (auto &pcache : pipelineCaches_) { for (auto &pcache : pipelineCaches_) {
vkDestroyPipelineCache(device, pcache, nullptr); vkDestroyPipelineCache(device, pcache, nullptr);
deleteCount++;
} }
pipelineCaches_.clear(); pipelineCaches_.clear();
for (auto &renderPass : renderPasses_) { for (auto &renderPass : renderPasses_) {
vkDestroyRenderPass(device, renderPass, nullptr); vkDestroyRenderPass(device, renderPass, nullptr);
deleteCount++;
} }
renderPasses_.clear(); renderPasses_.clear();
for (auto &framebuffer : framebuffers_) { for (auto &framebuffer : framebuffers_) {
vkDestroyFramebuffer(device, framebuffer, nullptr); vkDestroyFramebuffer(device, framebuffer, nullptr);
deleteCount++;
} }
framebuffers_.clear(); framebuffers_.clear();
for (auto &pipeLayout : pipelineLayouts_) { for (auto &pipeLayout : pipelineLayouts_) {
vkDestroyPipelineLayout(device, pipeLayout, nullptr); vkDestroyPipelineLayout(device, pipeLayout, nullptr);
deleteCount++;
} }
pipelineLayouts_.clear(); pipelineLayouts_.clear();
for (auto &descSetLayout : descSetLayouts_) { for (auto &descSetLayout : descSetLayouts_) {
vkDestroyDescriptorSetLayout(device, descSetLayout, nullptr); vkDestroyDescriptorSetLayout(device, descSetLayout, nullptr);
deleteCount++;
} }
descSetLayouts_.clear(); descSetLayouts_.clear();
for (auto &queryPool : queryPools_) { for (auto &queryPool : queryPools_) {
vkDestroyQueryPool(device, queryPool, nullptr); vkDestroyQueryPool(device, queryPool, nullptr);
deleteCount++;
} }
queryPools_.clear(); queryPools_.clear();
deleteCount_ = deleteCount;
} }
void VulkanContext::GetImageMemoryRequirements(VkImage image, VkMemoryRequirements *mem_reqs, bool *dedicatedAllocation) { void VulkanContext::GetImageMemoryRequirements(VkImage image, VkMemoryRequirements *mem_reqs, bool *dedicatedAllocation) {

View file

@ -36,7 +36,6 @@ enum {
VULKAN_VENDOR_QUALCOMM = 0x00005143, VULKAN_VENDOR_QUALCOMM = 0x00005143,
VULKAN_VENDOR_IMGTEC = 0x00001010, // PowerVR VULKAN_VENDOR_IMGTEC = 0x00001010, // PowerVR
VULKAN_VENDOR_APPLE = 0x0000106b, // Apple through MoltenVK VULKAN_VENDOR_APPLE = 0x0000106b, // Apple through MoltenVK
VULKAN_VENDOR_MESA = 0x00010005, // lavapipe
}; };
VK_DEFINE_HANDLE(VmaAllocator); VK_DEFINE_HANDLE(VmaAllocator);
@ -138,10 +137,6 @@ public:
void Take(VulkanDeleteList &del); void Take(VulkanDeleteList &del);
void PerformDeletes(VulkanContext *vulkan, VmaAllocator allocator); void PerformDeletes(VulkanContext *vulkan, VmaAllocator allocator);
int GetLastDeleteCount() const {
return deleteCount_;
}
private: private:
std::vector<VkCommandPool> cmdPools_; std::vector<VkCommandPool> cmdPools_;
std::vector<VkDescriptorPool> descPools_; std::vector<VkDescriptorPool> descPools_;
@ -161,7 +156,6 @@ private:
std::vector<VkDescriptorSetLayout> descSetLayouts_; std::vector<VkDescriptorSetLayout> descSetLayouts_;
std::vector<VkQueryPool> queryPools_; std::vector<VkQueryPool> queryPools_;
std::vector<Callback> callbacks_; std::vector<Callback> callbacks_;
int deleteCount_ = 0;
}; };
// VulkanContext manages the device and swapchain, and deferred deletion of objects. // VulkanContext manages the device and swapchain, and deferred deletion of objects.
@ -228,8 +222,7 @@ public:
// Simple workaround for the casting warning. // Simple workaround for the casting warning.
template <class T> template <class T>
void SetDebugName(T handle, VkObjectType type, const char *name) { void SetDebugName(T handle, VkObjectType type, const char *name) {
if (extensionsLookup_.EXT_debug_utils && handle != VK_NULL_HANDLE) { if (extensionsLookup_.EXT_debug_utils) {
_dbg_assert_(handle != VK_NULL_HANDLE);
SetDebugNameImpl((uint64_t)handle, type, name); SetDebugNameImpl((uint64_t)handle, type, name);
} }
} }
@ -270,8 +263,6 @@ public:
struct AllPhysicalDeviceFeatures { struct AllPhysicalDeviceFeatures {
VkPhysicalDeviceFeatures standard; VkPhysicalDeviceFeatures standard;
VkPhysicalDeviceMultiviewFeatures multiview; VkPhysicalDeviceMultiviewFeatures multiview;
VkPhysicalDevicePresentWaitFeaturesKHR presentWait;
VkPhysicalDevicePresentIdFeaturesKHR presentId;
}; };
const PhysicalDeviceProps &GetPhysicalDeviceProperties(int i = -1) const { const PhysicalDeviceProps &GetPhysicalDeviceProperties(int i = -1) const {
@ -297,13 +288,6 @@ public:
return device_extensions_enabled_; return device_extensions_enabled_;
} }
const std::vector<VkExtensionProperties> &GetInstanceExtensionsAvailable() const {
return instance_extension_properties_;
}
const std::vector<const char *> &GetInstanceExtensionsEnabled() const {
return instance_extensions_enabled_;
}
const VkPhysicalDeviceMemoryProperties &GetMemoryProperties() const { const VkPhysicalDeviceMemoryProperties &GetMemoryProperties() const {
return memory_properties_; return memory_properties_;
} }
@ -327,6 +311,7 @@ public:
for (const auto &iter : instance_layer_properties_) { for (const auto &iter : instance_layer_properties_) {
for (const auto &ext : iter.extensions) { for (const auto &ext : iter.extensions) {
if (!strcmp(extensionName, ext.extensionName)) { if (!strcmp(extensionName, ext.extensionName)) {
INFO_LOG(G3D, "%s found in layer extensions: %s", extensionName, iter.properties.layerName);
return true; return true;
} }
} }
@ -344,7 +329,6 @@ public:
} }
int GetInflightFrames() const { int GetInflightFrames() const {
// out of MAX_INFLIGHT_FRAMES.
return inflightFrames_; return inflightFrames_;
} }
// Don't call while a frame is in progress. // Don't call while a frame is in progress.
@ -389,18 +373,6 @@ public:
return surfFormats_; return surfFormats_;
} }
VkPresentModeKHR GetPresentMode() const {
return presentMode_;
}
std::vector<VkPresentModeKHR> GetAvailablePresentModes() const {
return availablePresentModes_;
}
int GetLastDeleteCount() const {
return frame_[curFrame_].deleteList.GetLastDeleteCount();
}
private: private:
bool ChooseQueue(); bool ChooseQueue();
@ -488,9 +460,6 @@ private:
VkSurfaceCapabilitiesKHR surfCapabilities_{}; VkSurfaceCapabilitiesKHR surfCapabilities_{};
std::vector<VkSurfaceFormatKHR> surfFormats_{}; std::vector<VkSurfaceFormatKHR> surfFormats_{};
VkPresentModeKHR presentMode_;
std::vector<VkPresentModeKHR> availablePresentModes_;
std::vector<VkCommandBuffer> cmdQueue_; std::vector<VkCommandBuffer> cmdQueue_;
VmaAllocator allocator_ = VK_NULL_HANDLE; VmaAllocator allocator_ = VK_NULL_HANDLE;
@ -516,7 +485,6 @@ bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *sourceCode,
const char *VulkanColorSpaceToString(VkColorSpaceKHR colorSpace); const char *VulkanColorSpaceToString(VkColorSpaceKHR colorSpace);
const char *VulkanFormatToString(VkFormat format); const char *VulkanFormatToString(VkFormat format);
const char *VulkanPresentModeToString(VkPresentModeKHR presentMode);
std::string FormatDriverVersion(const VkPhysicalDeviceProperties &props); std::string FormatDriverVersion(const VkPhysicalDeviceProperties &props);

View file

@ -21,7 +21,6 @@
#include <mutex> #include <mutex>
#include "Common/Log.h" #include "Common/Log.h"
#include "Common/System/System.h"
#include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/GPU/Vulkan/VulkanContext.h"
#include "Common/GPU/Vulkan/VulkanDebug.h" #include "Common/GPU/Vulkan/VulkanDebug.h"
@ -48,62 +47,22 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback(
const char *pMessage = pCallbackData->pMessage; const char *pMessage = pCallbackData->pMessage;
int messageCode = pCallbackData->messageIdNumber; int messageCode = pCallbackData->messageIdNumber;
switch (messageCode) { if (messageCode == 101294395) {
case 101294395:
// UNASSIGNED-CoreValidation-Shader-OutputNotConsumed - benign perf warning // UNASSIGNED-CoreValidation-Shader-OutputNotConsumed - benign perf warning
return false; return false;
case 1303270965: }
if (messageCode == 1303270965) {
// Benign perf warning, image blit using GENERAL layout. // Benign perf warning, image blit using GENERAL layout.
// TODO: Oops, turns out we filtered out a bit too much here! // UNASSIGNED
// We really need that performance flag check to sort out the stuff that matters. return false;
// Will enable it soon, but it'll take some fixing. }
// if (messageCode == 606910136 || messageCode == -392708513 || messageCode == -384083808) {
if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)
return false;
break;
case 606910136:
case -392708513:
case -384083808:
// VUID-vkCmdDraw-None-02686 // VUID-vkCmdDraw-None-02686
// Kinda false positive, or at least very unnecessary, now that I solved the real issue. // Kinda false positive, or at least very unnecessary, now that I solved the real issue.
// See https://github.com/hrydgard/ppsspp/pull/16354 // See https://github.com/hrydgard/ppsspp/pull/16354
return false; return false;
case -375211665:
// VUID-vkAllocateMemory-pAllocateInfo-01713
// Can happen when VMA aggressively tries to allocate aperture memory for upload. It gracefully
// falls back to regular video memory, so we just ignore this. I'd argue this is a VMA bug, actually.
return false;
case 181611958:
// Extended validation.
// UNASSIGNED-BestPractices-vkCreateDevice-deprecated-extension
// Doing what this one says doesn't seem very reliable - if I rely strictly on the Vulkan version, I don't get some function pointers? Like createrenderpass2.
return false;
case 657182421:
// Extended validation (ARM best practices)
// Non-fifo validation not recommended
return false;
case 337425955:
// False positive
// https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/3615
return false;
default:
break;
} }
/*
// Can be used to temporarily turn errors into info for easier debugging.
switch (messageCode) {
case 1544472022:
if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
messageSeverity = (VkDebugUtilsMessageSeverityFlagBitsEXT)((messageSeverity & ~VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT);
}
break;
default:
break;
}
*/
int count; int count;
{ {
std::lock_guard<std::mutex> lock(g_errorCountMutex); std::lock_guard<std::mutex> lock(g_errorCountMutex);
@ -140,7 +99,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback(
#ifdef _WIN32 #ifdef _WIN32
OutputDebugStringA(msg.c_str()); OutputDebugStringA(msg.c_str());
if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
if (options->breakOnError && System_GetPropertyBool(SYSPROP_DEBUGGER_PRESENT)) { if (options->breakOnError && IsDebuggerPresent()) {
DebugBreak(); DebugBreak();
} }
if (options->msgBoxOnError) { if (options->msgBoxOnError) {
@ -148,7 +107,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback(
} }
} else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { } else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
// Don't break on perf warnings for now, even with a debugger. We log them at least. // Don't break on perf warnings for now, even with a debugger. We log them at least.
if (options->breakOnWarning && System_GetPropertyBool(SYSPROP_DEBUGGER_PRESENT) && 0 == (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)) { if (options->breakOnWarning && IsDebuggerPresent() && 0 == (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)) {
DebugBreak(); DebugBreak();
} }
} }

View file

@ -1,129 +0,0 @@
#include "Common/GPU/Vulkan/VulkanDescSet.h"
VulkanDescSetPool::~VulkanDescSetPool() {
_assert_msg_(descPool_ == VK_NULL_HANDLE, "VulkanDescSetPool %s never destroyed", tag_);
}
void VulkanDescSetPool::Create(VulkanContext *vulkan, const BindingType *bindingTypes, uint32_t bindingTypesCount, uint32_t descriptorCount) {
_assert_msg_(descPool_ == VK_NULL_HANDLE, "VulkanDescSetPool::Create when already exists");
vulkan_ = vulkan;
info_ = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
info_.maxSets = descriptorCount;
_dbg_assert_(sizes_.empty());
uint32_t storageImageCount = 0;
uint32_t storageBufferCount = 0;
uint32_t combinedImageSamplerCount = 0;
uint32_t uniformBufferDynamicCount = 0;
for (uint32_t i = 0; i < bindingTypesCount; i++) {
switch (bindingTypes[i]) {
case BindingType::COMBINED_IMAGE_SAMPLER: combinedImageSamplerCount++; break;
case BindingType::UNIFORM_BUFFER_DYNAMIC_VERTEX:
case BindingType::UNIFORM_BUFFER_DYNAMIC_ALL: uniformBufferDynamicCount++; break;
case BindingType::STORAGE_BUFFER_VERTEX:
case BindingType::STORAGE_BUFFER_COMPUTE: storageBufferCount++; break;
case BindingType::STORAGE_IMAGE_COMPUTE: storageImageCount++; break;
}
}
if (combinedImageSamplerCount) {
sizes_.push_back(VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, combinedImageSamplerCount * descriptorCount });
}
if (uniformBufferDynamicCount) {
sizes_.push_back(VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, uniformBufferDynamicCount * descriptorCount });
}
if (storageBufferCount) {
sizes_.push_back(VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, storageBufferCount * descriptorCount });
}
if (storageImageCount) {
sizes_.push_back(VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, storageImageCount * descriptorCount });
}
VkResult res = Recreate(false);
_assert_msg_(res == VK_SUCCESS, "Could not create VulkanDescSetPool %s", tag_);
}
bool VulkanDescSetPool::Allocate(VkDescriptorSet *descriptorSets, int count, const VkDescriptorSetLayout *layouts) {
if (descPool_ == VK_NULL_HANDLE || usage_ + count >= info_.maxSets) {
// Missing or out of space, need to recreate.
VkResult res = Recreate(grow_);
_assert_msg_(res == VK_SUCCESS, "Could not grow VulkanDescSetPool %s on usage %d", tag_, (int)usage_);
}
VkDescriptorSetAllocateInfo descAlloc{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
descAlloc.descriptorPool = descPool_;
descAlloc.descriptorSetCount = count;
descAlloc.pSetLayouts = layouts;
VkResult result = vkAllocateDescriptorSets(vulkan_->GetDevice(), &descAlloc, descriptorSets);
if (result == VK_ERROR_FRAGMENTED_POOL || result < 0) {
WARN_LOG(G3D, "Pool %s %s - recreating", tag_, result == VK_ERROR_FRAGMENTED_POOL ? "fragmented" : "full");
// There seems to have been a spec revision. Here we should apparently recreate the descriptor pool,
// so let's do that. See https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkAllocateDescriptorSets.html
// Fragmentation shouldn't really happen though since we wipe the pool every frame.
VkResult res = Recreate(false);
_assert_msg_(res == VK_SUCCESS, "Ran out of descriptor space (frag?) and failed to recreate a descriptor pool. sz=%d res=%d", usage_, (int)res);
// Need to update this pointer since we have allocated a new one.
descAlloc.descriptorPool = descPool_;
result = vkAllocateDescriptorSets(vulkan_->GetDevice(), &descAlloc, descriptorSets);
_assert_msg_(result == VK_SUCCESS, "Ran out of descriptor space (frag?) and failed to allocate after recreating a descriptor pool. res=%d", (int)result);
}
if (result != VK_SUCCESS) {
return false;
}
usage_ += count;
return true;
}
void VulkanDescSetPool::Reset() {
_assert_msg_(descPool_ != VK_NULL_HANDLE, "VulkanDescSetPool::Reset without valid pool");
vkResetDescriptorPool(vulkan_->GetDevice(), descPool_, 0);
usage_ = 0;
}
void VulkanDescSetPool::Destroy() {
if (descPool_ != VK_NULL_HANDLE) {
vulkan_->Delete().QueueDeleteDescriptorPool(descPool_);
usage_ = 0;
}
sizes_.clear();
}
void VulkanDescSetPool::DestroyImmediately() {
if (descPool_ != VK_NULL_HANDLE) {
vkDestroyDescriptorPool(vulkan_->GetDevice(), descPool_, nullptr);
descPool_ = VK_NULL_HANDLE;
usage_ = 0;
}
sizes_.clear();
}
VkResult VulkanDescSetPool::Recreate(bool grow) {
_assert_msg_(vulkan_ != nullptr, "VulkanDescSetPool::Recreate without VulkanContext");
uint32_t prevSize = info_.maxSets;
if (grow) {
info_.maxSets *= 2;
for (auto &size : sizes_)
size.descriptorCount *= 2;
}
// Delete the pool if it already exists.
if (descPool_ != VK_NULL_HANDLE) {
INFO_LOG(G3D, "Reallocating %s desc pool from %d to %d", tag_, prevSize, info_.maxSets);
vulkan_->Delete().QueueDeleteDescriptorPool(descPool_);
usage_ = 0;
}
info_.pPoolSizes = &sizes_[0];
info_.poolSizeCount = (uint32_t)sizes_.size();
VkResult result = vkCreateDescriptorPool(vulkan_->GetDevice(), &info_, nullptr, &descPool_);
if (result == VK_SUCCESS) {
vulkan_->SetDebugName(descPool_, VK_OBJECT_TYPE_DESCRIPTOR_POOL, tag_);
}
return result;
}

View file

@ -1,48 +0,0 @@
#pragma once
#include "Common/Data/Collections/FastVec.h"
#include "Common/GPU/Vulkan/VulkanContext.h"
#include <vector>
enum class BindingType {
COMBINED_IMAGE_SAMPLER,
UNIFORM_BUFFER_DYNAMIC_VERTEX,
UNIFORM_BUFFER_DYNAMIC_ALL,
STORAGE_BUFFER_VERTEX,
STORAGE_BUFFER_COMPUTE,
STORAGE_IMAGE_COMPUTE,
};
// Only appropriate for use in a per-frame pool.
class VulkanDescSetPool {
public:
VulkanDescSetPool(const char *tag, bool grow = true) : tag_(tag), grow_(grow) {}
~VulkanDescSetPool();
void Create(VulkanContext *vulkan, const BindingType *bindingTypes, uint32_t bindingTypesCount, uint32_t descriptorCount);
// Allocate a new set, which may resize and empty the current sets.
// Use only for the current frame.
bool Allocate(VkDescriptorSet *descriptorSets, int count, const VkDescriptorSetLayout *layouts);
void Reset();
// This queues up destruction.
void Destroy();
// This actually destroys immediately.
void DestroyImmediately();
bool IsDestroyed() const {
return !descPool_;
}
private:
VkResult Recreate(bool grow);
const char *tag_;
VulkanContext *vulkan_ = nullptr;
VkDescriptorPool descPool_ = VK_NULL_HANDLE;
VkDescriptorPoolCreateInfo info_{};
std::vector<VkDescriptorPoolSize> sizes_;
uint32_t usage_ = 0;
bool grow_;
};

View file

@ -4,19 +4,6 @@
#include "Common/Log.h" #include "Common/Log.h"
#include "Common/StringUtils.h" #include "Common/StringUtils.h"
#if 0 // def _DEBUG
#define VLOG(...) NOTICE_LOG(G3D, __VA_ARGS__)
#else
#define VLOG(...)
#endif
void CachedReadback::Destroy(VulkanContext *vulkan) {
if (buffer) {
vulkan->Delete().QueueDeleteBufferAllocation(buffer, allocation);
}
bufferSize = 0;
}
void FrameData::Init(VulkanContext *vulkan, int index) { void FrameData::Init(VulkanContext *vulkan, int index) {
this->index = index; this->index = index;
VkDevice device = vulkan->GetDevice(); VkDevice device = vulkan->GetDevice();
@ -49,6 +36,11 @@ void FrameData::Init(VulkanContext *vulkan, int index) {
vulkan->SetDebugName(fence, VK_OBJECT_TYPE_FENCE, StringFromFormat("fence%d", index).c_str()); vulkan->SetDebugName(fence, VK_OBJECT_TYPE_FENCE, StringFromFormat("fence%d", index).c_str());
readyForFence = true; readyForFence = true;
// This fence is used for synchronizing readbacks. Does not need preinitialization.
// TODO: Put this in frameDataShared, only one is needed.
readbackFence = vulkan->CreateFence(false);
vulkan->SetDebugName(fence, VK_OBJECT_TYPE_FENCE, "readbackFence");
VkQueryPoolCreateInfo query_ci{ VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO }; VkQueryPoolCreateInfo query_ci{ VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO };
query_ci.queryCount = MAX_TIMESTAMP_QUERIES; query_ci.queryCount = MAX_TIMESTAMP_QUERIES;
query_ci.queryType = VK_QUERY_TYPE_TIMESTAMP; query_ci.queryType = VK_QUERY_TYPE_TIMESTAMP;
@ -60,13 +52,8 @@ void FrameData::Destroy(VulkanContext *vulkan) {
vkDestroyCommandPool(device, cmdPoolInit, nullptr); vkDestroyCommandPool(device, cmdPoolInit, nullptr);
vkDestroyCommandPool(device, cmdPoolMain, nullptr); vkDestroyCommandPool(device, cmdPoolMain, nullptr);
vkDestroyFence(device, fence, nullptr); vkDestroyFence(device, fence, nullptr);
vkDestroyFence(device, readbackFence, nullptr);
vkDestroyQueryPool(device, profile.queryPool, nullptr); vkDestroyQueryPool(device, profile.queryPool, nullptr);
readbacks_.IterateMut([=](const ReadbackKey &key, CachedReadback *value) {
value->Destroy(vulkan);
delete value;
});
readbacks_.Clear();
} }
void FrameData::AcquireNextImage(VulkanContext *vulkan, FrameDataShared &shared) { void FrameData::AcquireNextImage(VulkanContext *vulkan, FrameDataShared &shared) {
@ -90,10 +77,6 @@ void FrameData::AcquireNextImage(VulkanContext *vulkan, FrameDataShared &shared)
WARN_LOG(G3D, "%s returned from AcquireNextImage - processing the frame, but not presenting", VulkanResultToString(res)); WARN_LOG(G3D, "%s returned from AcquireNextImage - processing the frame, but not presenting", VulkanResultToString(res));
skipSwap = true; skipSwap = true;
break; break;
case VK_ERROR_SURFACE_LOST_KHR:
ERROR_LOG(G3D, "%s returned from AcquireNextImage - ignoring, but this better be during shutdown", VulkanResultToString(res));
skipSwap = true;
break;
default: default:
// Weird, shouldn't get any other values. Maybe lost device? // Weird, shouldn't get any other values. Maybe lost device?
_assert_msg_(false, "vkAcquireNextImageKHR failed! result=%s", VulkanResultToString(res)); _assert_msg_(false, "vkAcquireNextImageKHR failed! result=%s", VulkanResultToString(res));
@ -114,25 +97,6 @@ VkResult FrameData::QueuePresent(VulkanContext *vulkan, FrameDataShared &shared)
present.pWaitSemaphores = &shared.renderingCompleteSemaphore; present.pWaitSemaphores = &shared.renderingCompleteSemaphore;
present.waitSemaphoreCount = 1; present.waitSemaphoreCount = 1;
// Can't move these into the if.
VkPresentIdKHR presentID{ VK_STRUCTURE_TYPE_PRESENT_ID_KHR };
VkPresentTimesInfoGOOGLE presentGOOGLE{ VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE };
uint64_t frameId = this->frameId;
VkPresentTimeGOOGLE presentTimeGOOGLE{ (uint32_t)frameId, 0 }; // it's ok to truncate this. it'll wrap around and work (if we ever reach 4 billion frames..)
if (shared.measurePresentTime) {
if (vulkan->Extensions().KHR_present_id && vulkan->GetDeviceFeatures().enabled.presentId.presentId) {
presentID.pPresentIds = &frameId;
presentID.swapchainCount = 1;
present.pNext = &presentID;
} else if (vulkan->Extensions().GOOGLE_display_timing) {
presentGOOGLE.pTimes = &presentTimeGOOGLE;
presentGOOGLE.swapchainCount = 1;
present.pNext = &presentGOOGLE;
}
}
return vkQueuePresentKHR(vulkan->GetGraphicsQueue(), &present); return vkQueuePresentKHR(vulkan->GetGraphicsQueue(), &present);
} }
@ -150,7 +114,7 @@ VkCommandBuffer FrameData::GetInitCmd(VulkanContext *vulkan) {
} }
// Good spot to reset the query pool. // Good spot to reset the query pool.
if (profile.enabled) { if (profilingEnabled_) {
vkCmdResetQueryPool(initCmd, profile.queryPool, 0, MAX_TIMESTAMP_QUERIES); vkCmdResetQueryPool(initCmd, profile.queryPool, 0, MAX_TIMESTAMP_QUERIES);
vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, profile.queryPool, 0); vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, profile.queryPool, 0);
} }
@ -167,7 +131,7 @@ void FrameData::SubmitPending(VulkanContext *vulkan, FrameSubmitType type, Frame
VkFence fenceToTrigger = VK_NULL_HANDLE; VkFence fenceToTrigger = VK_NULL_HANDLE;
if (hasInitCommands) { if (hasInitCommands) {
if (profile.enabled) { if (profilingEnabled_) {
// Pre-allocated query ID 1 - end of init cmdbuf. // Pre-allocated query ID 1 - end of init cmdbuf.
vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, profile.queryPool, 1); vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, profile.queryPool, 1);
} }
@ -180,7 +144,7 @@ void FrameData::SubmitPending(VulkanContext *vulkan, FrameSubmitType type, Frame
} }
if ((hasMainCommands || hasPresentCommands) && type == FrameSubmitType::Sync) { if ((hasMainCommands || hasPresentCommands) && type == FrameSubmitType::Sync) {
fenceToTrigger = sharedData.readbackFence; fenceToTrigger = readbackFence;
} }
if (hasMainCommands) { if (hasMainCommands) {
@ -225,16 +189,12 @@ void FrameData::SubmitPending(VulkanContext *vulkan, FrameSubmitType type, Frame
VkResult res; VkResult res;
if (fenceToTrigger == fence) { if (fenceToTrigger == fence) {
VLOG("Doing queue submit, fencing frame %d", this->index);
// The fence is waited on by the main thread, they are not allowed to access it simultaneously. // The fence is waited on by the main thread, they are not allowed to access it simultaneously.
res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger); res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger);
if (sharedData.useMultiThreading) { std::lock_guard<std::mutex> lock(fenceMutex);
std::lock_guard<std::mutex> lock(fenceMutex); readyForFence = true;
readyForFence = true; fenceCondVar.notify_one();
fenceCondVar.notify_one();
}
} else { } else {
VLOG("Doing queue submit, fencing something (%p)", fenceToTrigger);
res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger); res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger);
} }
@ -246,31 +206,23 @@ void FrameData::SubmitPending(VulkanContext *vulkan, FrameSubmitType type, Frame
if (type == FrameSubmitType::Sync) { if (type == FrameSubmitType::Sync) {
// Hard stall of the GPU, not ideal, but necessary so the CPU has the contents of the readback. // Hard stall of the GPU, not ideal, but necessary so the CPU has the contents of the readback.
vkWaitForFences(vulkan->GetDevice(), 1, &sharedData.readbackFence, true, UINT64_MAX); vkWaitForFences(vulkan->GetDevice(), 1, &readbackFence, true, UINT64_MAX);
vkResetFences(vulkan->GetDevice(), 1, &sharedData.readbackFence); vkResetFences(vulkan->GetDevice(), 1, &readbackFence);
syncDone = true; syncDone = true;
} }
} }
void FrameDataShared::Init(VulkanContext *vulkan, bool useMultiThreading, bool measurePresentTime) { void FrameDataShared::Init(VulkanContext *vulkan) {
VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
semaphoreCreateInfo.flags = 0; semaphoreCreateInfo.flags = 0;
VkResult res = vkCreateSemaphore(vulkan->GetDevice(), &semaphoreCreateInfo, nullptr, &acquireSemaphore); VkResult res = vkCreateSemaphore(vulkan->GetDevice(), &semaphoreCreateInfo, nullptr, &acquireSemaphore);
_dbg_assert_(res == VK_SUCCESS); _dbg_assert_(res == VK_SUCCESS);
res = vkCreateSemaphore(vulkan->GetDevice(), &semaphoreCreateInfo, nullptr, &renderingCompleteSemaphore); res = vkCreateSemaphore(vulkan->GetDevice(), &semaphoreCreateInfo, nullptr, &renderingCompleteSemaphore);
_dbg_assert_(res == VK_SUCCESS); _dbg_assert_(res == VK_SUCCESS);
// This fence is used for synchronizing readbacks. Does not need preinitialization.
readbackFence = vulkan->CreateFence(false);
vulkan->SetDebugName(readbackFence, VK_OBJECT_TYPE_FENCE, "readbackFence");
this->useMultiThreading = useMultiThreading;
this->measurePresentTime = measurePresentTime;
} }
void FrameDataShared::Destroy(VulkanContext *vulkan) { void FrameDataShared::Destroy(VulkanContext *vulkan) {
VkDevice device = vulkan->GetDevice(); VkDevice device = vulkan->GetDevice();
vkDestroySemaphore(device, acquireSemaphore, nullptr); vkDestroySemaphore(device, acquireSemaphore, nullptr);
vkDestroySemaphore(device, renderingCompleteSemaphore, nullptr); vkDestroySemaphore(device, renderingCompleteSemaphore, nullptr);
vkDestroyFence(device, readbackFence, nullptr);
} }

View file

@ -6,46 +6,23 @@
#include <condition_variable> #include <condition_variable>
#include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/GPU/Vulkan/VulkanContext.h"
#include "Common/Data/Collections/Hashmaps.h"
enum { enum {
MAX_TIMESTAMP_QUERIES = 128, MAX_TIMESTAMP_QUERIES = 128,
}; };
enum class VKRRunType { enum class VKRRunType {
SUBMIT,
PRESENT, PRESENT,
SYNC, SYNC,
EXIT, EXIT,
}; };
struct QueueProfileContext { struct QueueProfileContext {
bool enabled = false;
bool timestampsEnabled = false;
VkQueryPool queryPool; VkQueryPool queryPool;
std::vector<std::string> timestampDescriptions; std::vector<std::string> timestampDescriptions;
std::string profileSummary; std::string profileSummary;
double cpuStartTime; double cpuStartTime;
double cpuEndTime; double cpuEndTime;
double descWriteTime;
int descriptorsWritten;
};
class VKRFramebuffer;
struct ReadbackKey {
const VKRFramebuffer *framebuf;
int width;
int height;
};
struct CachedReadback {
VkBuffer buffer;
VmaAllocation allocation;
VkDeviceSize bufferSize;
bool isCoherent;
void Destroy(VulkanContext *vulkan);
}; };
struct FrameDataShared { struct FrameDataShared {
@ -53,12 +30,7 @@ struct FrameDataShared {
VkSemaphore acquireSemaphore = VK_NULL_HANDLE; VkSemaphore acquireSemaphore = VK_NULL_HANDLE;
VkSemaphore renderingCompleteSemaphore = VK_NULL_HANDLE; VkSemaphore renderingCompleteSemaphore = VK_NULL_HANDLE;
// For synchronous readbacks. void Init(VulkanContext *vulkan);
VkFence readbackFence = VK_NULL_HANDLE;
bool useMultiThreading;
bool measurePresentTime;
void Init(VulkanContext *vulkan, bool useMultiThreading, bool measurePresentTime);
void Destroy(VulkanContext *vulkan); void Destroy(VulkanContext *vulkan);
}; };
@ -77,6 +49,7 @@ struct FrameData {
bool readyForFence = true; bool readyForFence = true;
VkFence fence = VK_NULL_HANDLE; VkFence fence = VK_NULL_HANDLE;
VkFence readbackFence = VK_NULL_HANDLE; // Strictly speaking we might only need one global of these.
// These are on different threads so need separate pools. // These are on different threads so need separate pools.
VkCommandPool cmdPoolInit = VK_NULL_HANDLE; // Written to from main thread VkCommandPool cmdPoolInit = VK_NULL_HANDLE; // Written to from main thread
@ -98,17 +71,9 @@ struct FrameData {
// Swapchain. // Swapchain.
uint32_t curSwapchainImage = -1; uint32_t curSwapchainImage = -1;
// Frames need unique IDs to wait for present on, let's keep them here.
// Also used for indexing into the frame timing history buffer.
uint64_t frameId = 0;
// Profiling. // Profiling.
QueueProfileContext profile{}; QueueProfileContext profile;
bool profilingEnabled_ = false;
// Async readback cache.
DenseHashMap<ReadbackKey, CachedReadback *> readbacks_;
FrameData() : readbacks_(8) {}
void Init(VulkanContext *vulkan, int index); void Init(VulkanContext *vulkan, int index);
void Destroy(VulkanContext *vulkan); void Destroy(VulkanContext *vulkan);
@ -124,5 +89,5 @@ struct FrameData {
private: private:
// Metadata for logging etc // Metadata for logging etc
int index = -1; int index;
}; };

Some files were not shown because too many files have changed in this diff Show more