Compare commits
3 commits
master
...
minor-read
Author | SHA1 | Date | |
---|---|---|---|
|
a989b08648 | ||
|
6c9963bd26 | ||
|
49f830d88c |
1142 changed files with 52938 additions and 112329 deletions
22
.github/workflows/build.yml
vendored
22
.github/workflows/build.yml
vendored
|
@ -139,6 +139,12 @@ jobs:
|
|||
cxx: clang++
|
||||
args: cd android && ./ab.sh -j2 APP_ABI=armeabi-v7a UNITTEST=1 HEADLESS=1
|
||||
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
|
||||
extra: android
|
||||
cc: clang
|
||||
|
@ -149,8 +155,14 @@ jobs:
|
|||
extra: android
|
||||
cc: clang
|
||||
cxx: clang++
|
||||
args: cd android && ./ab.sh -j2 APP_ABI=arm64-v8a OPENXR=1
|
||||
id: android-vr
|
||||
args: cd android && ./ab.sh -j2 APP_ABI=arm64-v8a OPENXR=1 OPENXR_PLATFORM_QUEST=1
|
||||
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
|
||||
extra: android
|
||||
cc: clang
|
||||
|
@ -219,7 +231,7 @@ jobs:
|
|||
run: |
|
||||
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 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
|
||||
if: startsWith(github.ref, 'refs/tags/') && runner.os == 'macOS' && matrix.extra == 'test'
|
||||
|
@ -229,8 +241,6 @@ jobs:
|
|||
|
||||
- name: Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
# Disable ccache on macos for now, it's become buggy for some reason.
|
||||
if: matrix.id != 'macos'
|
||||
with:
|
||||
key: ${{ matrix.id }}
|
||||
|
||||
|
@ -333,7 +343,7 @@ jobs:
|
|||
run: |
|
||||
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 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
|
||||
if: runner.os == 'macOS'
|
||||
|
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -105,7 +105,6 @@ Windows/*.ipch
|
|||
# For vim
|
||||
*.swp
|
||||
tags
|
||||
*.ctags
|
||||
|
||||
# Other VCS
|
||||
.bzr/
|
||||
|
@ -118,8 +117,6 @@ debian/ppsspp/
|
|||
|
||||
# Libretro build
|
||||
*.o
|
||||
*.tmp
|
||||
*.a
|
||||
|
||||
# YouCompleteMe file
|
||||
.ycm_extra_conf.pyc
|
||||
|
@ -132,7 +129,3 @@ CMakeFiles
|
|||
|
||||
# Clangd
|
||||
.cache/
|
||||
build
|
||||
libretro/obj/local
|
||||
|
||||
ppsspp_retroachievements.dat
|
||||
|
|
|
@ -49,14 +49,6 @@ include:
|
|||
- project: 'libretro-infrastructure/ci-templates'
|
||||
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 ################################
|
||||
|
||||
#################################### MISC ##################################
|
||||
|
@ -141,21 +133,3 @@ libretro-build-android-x86:
|
|||
extends:
|
||||
- .libretro-android-cmake-x86
|
||||
- .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
12
.gitmodules
vendored
|
@ -1,6 +1,3 @@
|
|||
[submodule "libretro/libretro-common"]
|
||||
path = libretro/libretro-common
|
||||
url = https://github.com/libretro/libretro-common.git
|
||||
[submodule "pspautotests"]
|
||||
path = pspautotests
|
||||
url = https://github.com/hrydgard/pspautotests.git
|
||||
|
@ -44,12 +41,3 @@
|
|||
[submodule "cpu_features"]
|
||||
path = ext/cpu_features
|
||||
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
|
||||
|
|
394
CMakeLists.txt
394
CMakeLists.txt
|
@ -67,8 +67,6 @@ if(CMAKE_SYSTEM_PROCESSOR)
|
|||
set(MIPS_DEVICE ON)
|
||||
elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^riscv64")
|
||||
set(RISCV64_DEVICE ON)
|
||||
elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^loongarch64")
|
||||
set(LOONGARCH64_DEVICE ON)
|
||||
else()
|
||||
message("Unknown CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
|
@ -104,18 +102,15 @@ if(ANDROID OR WIN32 OR (UNIX AND NOT ARM_NO_VULKAN))
|
|||
set(VULKAN ON)
|
||||
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)
|
||||
if(NOT IOS)
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/sdl)
|
||||
endif()
|
||||
|
||||
if(MACOSX AND NOT USE_SYSTEM_LIBSDL2)
|
||||
set(SDL2_LIBRARY ${CMAKE_SOURCE_DIR}/SDL/macOS/SDL2.framework)
|
||||
endif()
|
||||
|
||||
include(ccache)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
|
@ -124,8 +119,16 @@ add_definitions(-DASSETS_DIR="${CMAKE_INSTALL_FULL_DATADIR}/ppsspp/assets/")
|
|||
if(OPENXR)
|
||||
add_definitions(-DOPENXR)
|
||||
add_library(openxr SHARED IMPORTED)
|
||||
set_property(TARGET openxr PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/ext/openxr/stub/arm64-v8a/libopenxr_loader.so")
|
||||
message("OpenXR enabled")
|
||||
if(OPENXR_PLATFORM_PICO)
|
||||
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()
|
||||
|
||||
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(MIPS "Set to ON if targeting a MIPS processor" ${MIPS_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_64 "Set to ON if targeting an X86_64 processor" ${X86_64_DEVICE})
|
||||
# :: 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_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(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})
|
||||
# :: Frontends
|
||||
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_FFMPEG "Dynamically link against system FFMPEG" ${USE_SYSTEM_FFMPEG})
|
||||
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_ZSTD "Dynamically link against system zstd" ${USE_SYSTEM_ZSTD})
|
||||
option(USE_SYSTEM_MINIUPNPC "Dynamically link against system miniUPnPc" ${USE_SYSTEM_MINIUPNPC})
|
||||
|
@ -177,21 +179,21 @@ option(USE_UBSAN "Use undefined behaviour sanitizer" OFF)
|
|||
if(UNIX AND NOT (APPLE OR ANDROID) AND VULKAN)
|
||||
if(USING_X11_VULKAN)
|
||||
message("Using X11 for Vulkan")
|
||||
find_package(X11)
|
||||
include_directories(${X11_Xlib_INCLUDE_PATH})
|
||||
add_definitions(-DVK_USE_PLATFORM_XLIB_KHR)
|
||||
else()
|
||||
message("NOT using X11 for Vulkan")
|
||||
endif()
|
||||
|
||||
# add_definitions(-DVK_USE_PLATFORM_XCB_KHR)
|
||||
if(USE_WAYLAND_WSI)
|
||||
find_package(Wayland)
|
||||
if(NOT WAYLAND_FOUND)
|
||||
message(STATUS "Could not find Wayland libraries, disabling Wayland WSI support for Vulkan.")
|
||||
elseif(USE_WAYLAND_WSI)
|
||||
else()
|
||||
include_directories(${WAYLAND_INCLUDE_DIR})
|
||||
add_definitions(-DVK_USE_PLATFORM_WAYLAND_KHR)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(USE_VULKAN_DISPLAY_KHR)
|
||||
message(STATUS "Using experimental full-screen display for Vulkan.")
|
||||
|
@ -220,16 +222,6 @@ else()
|
|||
set(CoreLinkType STATIC)
|
||||
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
|
||||
include_directories(ext/glslang)
|
||||
|
||||
|
@ -257,31 +249,9 @@ if(USING_EGL)
|
|||
set(OPENGL_LIBRARIES ${OPENGL_LIBRARIES} ${EGL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(NOT LIBRETRO AND NOT IOS AND NOT MACOSX)
|
||||
if(NOT LIBRETRO AND NOT IOS)
|
||||
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()
|
||||
|
||||
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)
|
||||
|
||||
if(APPLE)
|
||||
|
@ -328,9 +298,6 @@ endif()
|
|||
if(RISCV64)
|
||||
message("Generating for RISCV64, ${CMAKE_BUILD_TYPE}")
|
||||
endif()
|
||||
if(LOONGARCH64)
|
||||
message("Generating for LOONGARCH64, ${CMAKE_BUILD_TYPE}")
|
||||
endif()
|
||||
if(X86)
|
||||
message("Generating for x86, ${CMAKE_BUILD_TYPE}")
|
||||
endif()
|
||||
|
@ -406,15 +373,9 @@ if(NOT MSVC)
|
|||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -parallel -fopenmp")
|
||||
endif()
|
||||
|
||||
add_definitions(-fno-math-errno)
|
||||
|
||||
if(X86 OR X86_64)
|
||||
# enable sse2 code generation
|
||||
add_definitions(-msse2)
|
||||
if(NOT X86_64 AND NOT CLANG)
|
||||
add_definitions(-mfpmath=sse)
|
||||
# add_definitions(-mstackrealign)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(IOS)
|
||||
|
@ -433,13 +394,8 @@ if(NOT MSVC)
|
|||
add_definitions(-Wno-psabi)
|
||||
endif()
|
||||
add_definitions(-D_XOPEN_SOURCE=700)
|
||||
add_definitions(-D_XOPEN_SOURCE_EXTENDED -D__BSD_VISIBLE=1 -D_BSD_SOURCE -D_DEFAULT_SOURCE)
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux|SunOS" OR MINGW)
|
||||
add_definitions(-D_XOPEN_SOURCE_EXTENDED -D__BSD_VISIBLE=1)
|
||||
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)
|
||||
add_definitions(-fsigned-char)
|
||||
endif()
|
||||
|
@ -451,7 +407,6 @@ else()
|
|||
endif()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -D_NDEBUG")
|
||||
set(CMAKE_EXE_LINKER_FLAGS /NODEFAULTLIB:"libcmt.lib")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
|
@ -544,14 +499,6 @@ set(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)
|
||||
set(CommonD3D
|
||||
Common/GPU/D3D9/D3D9ShaderCompiler.cpp
|
||||
|
@ -590,7 +537,6 @@ add_library(Common STATIC
|
|||
${CommonARM64}
|
||||
${CommonMIPS}
|
||||
${CommonRISCV64}
|
||||
${CommonLOONGARCH64}
|
||||
${CommonD3D}
|
||||
${CommonVR}
|
||||
Common/Serialize/Serializer.cpp
|
||||
|
@ -610,7 +556,6 @@ add_library(Common STATIC
|
|||
Common/Data/Collections/FixedSizeQueue.h
|
||||
Common/Data/Collections/Hashmaps.h
|
||||
Common/Data/Collections/TinySet.h
|
||||
Common/Data/Collections/FastVec.h
|
||||
Common/Data/Collections/ThreadSafeList.h
|
||||
Common/Data/Color/RGBAUtil.cpp
|
||||
Common/Data/Color/RGBAUtil.h
|
||||
|
@ -634,8 +579,6 @@ add_library(Common STATIC
|
|||
Common/Data/Format/JSONReader.cpp
|
||||
Common/Data/Format/JSONWriter.h
|
||||
Common/Data/Format/JSONWriter.cpp
|
||||
Common/Data/Format/DDSLoad.cpp
|
||||
Common/Data/Format/DDSLoad.h
|
||||
Common/Data/Format/PNGLoad.cpp
|
||||
Common/Data/Format/PNGLoad.h
|
||||
Common/Data/Format/ZIMLoad.cpp
|
||||
|
@ -653,14 +596,10 @@ add_library(Common STATIC
|
|||
Common/Data/Random/Rng.h
|
||||
Common/File/VFS/VFS.h
|
||||
Common/File/VFS/VFS.cpp
|
||||
Common/File/VFS/ZipFileReader.cpp
|
||||
Common/File/VFS/ZipFileReader.h
|
||||
Common/File/VFS/DirectoryReader.cpp
|
||||
Common/File/VFS/DirectoryReader.h
|
||||
Common/File/VFS/AssetReader.cpp
|
||||
Common/File/VFS/AssetReader.h
|
||||
Common/File/AndroidStorage.h
|
||||
Common/File/AndroidStorage.cpp
|
||||
Common/File/AndroidContentURI.h
|
||||
Common/File/AndroidContentURI.cpp
|
||||
Common/File/DiskFree.h
|
||||
Common/File/DiskFree.cpp
|
||||
Common/File/Path.h
|
||||
|
@ -675,8 +614,6 @@ add_library(Common STATIC
|
|||
Common/File/FileDescriptor.h
|
||||
Common/GPU/DataFormat.h
|
||||
Common/GPU/MiscTypes.h
|
||||
Common/GPU/GPUBackendCommon.cpp
|
||||
Common/GPU/GPUBackendCommon.h
|
||||
Common/GPU/thin3d.cpp
|
||||
Common/GPU/thin3d.h
|
||||
Common/GPU/thin3d_create.h
|
||||
|
@ -695,11 +632,7 @@ add_library(Common STATIC
|
|||
Common/GPU/OpenGL/gl3stub.h
|
||||
Common/GPU/OpenGL/GLFeatures.cpp
|
||||
Common/GPU/OpenGL/GLFeatures.h
|
||||
Common/GPU/OpenGL/GLFrameData.cpp
|
||||
Common/GPU/OpenGL/GLFrameData.h
|
||||
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.h
|
||||
Common/GPU/OpenGL/GLQueueRunner.cpp
|
||||
|
@ -712,8 +645,6 @@ add_library(Common STATIC
|
|||
Common/GPU/Vulkan/VulkanDebug.h
|
||||
Common/GPU/Vulkan/VulkanContext.cpp
|
||||
Common/GPU/Vulkan/VulkanContext.h
|
||||
Common/GPU/Vulkan/VulkanDescSet.cpp
|
||||
Common/GPU/Vulkan/VulkanDescSet.h
|
||||
Common/GPU/Vulkan/VulkanFramebuffer.cpp
|
||||
Common/GPU/Vulkan/VulkanFramebuffer.h
|
||||
Common/GPU/Vulkan/VulkanImage.cpp
|
||||
|
@ -753,10 +684,6 @@ add_library(Common STATIC
|
|||
Common/Net/HTTPClient.h
|
||||
Common/Net/HTTPHeaders.cpp
|
||||
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.h
|
||||
Common/Net/NetBuffer.cpp
|
||||
|
@ -781,20 +708,12 @@ add_library(Common STATIC
|
|||
Common/Render/Text/draw_text.h
|
||||
Common/Render/Text/draw_text_android.cpp
|
||||
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.h
|
||||
Common/Render/Text/draw_text_uwp.cpp
|
||||
Common/Render/Text/draw_text_uwp.h
|
||||
Common/System/Display.cpp
|
||||
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/ParallelLoop.cpp
|
||||
Common/Thread/ParallelLoop.h
|
||||
|
@ -813,8 +732,6 @@ add_library(Common STATIC
|
|||
Common/UI/UI.h
|
||||
Common/UI/Context.cpp
|
||||
Common/UI/Context.h
|
||||
Common/UI/IconCache.cpp
|
||||
Common/UI/IconCache.h
|
||||
Common/UI/UIScreen.cpp
|
||||
Common/UI/UIScreen.h
|
||||
Common/UI/Tween.cpp
|
||||
|
@ -852,10 +769,8 @@ add_library(Common STATIC
|
|||
Common/MemArenaDarwin.cpp
|
||||
Common/MemArenaPosix.cpp
|
||||
Common/MemArenaWin32.cpp
|
||||
Common/MemArenaHorizon.cpp
|
||||
Common/MemArena.h
|
||||
Common/MemoryUtil.cpp
|
||||
Common/MemoryUtilHorizon.cpp
|
||||
Common/MemoryUtil.h
|
||||
Common/OSVersion.cpp
|
||||
Common/OSVersion.h
|
||||
|
@ -901,11 +816,7 @@ if(USE_FFMPEG)
|
|||
set(PLATFORM_ARCH "android/x86")
|
||||
endif()
|
||||
elseif(IOS)
|
||||
if(IOS_PLATFORM STREQUAL "TVOS")
|
||||
set(PLATFORM_ARCH "tvos/arm64")
|
||||
else()
|
||||
set(PLATFORM_ARCH "ios/universal")
|
||||
endif()
|
||||
elseif(MACOSX)
|
||||
set(PLATFORM_ARCH "macosx/universal")
|
||||
elseif(LINUX)
|
||||
|
@ -919,8 +830,6 @@ if(USE_FFMPEG)
|
|||
set(PLATFORM_ARCH "linux/mips32")
|
||||
elseif(RISCV64)
|
||||
set(PLATFORM_ARCH "linux/riscv64")
|
||||
elseif(LOONGARCH64)
|
||||
set(PLATFORM_ARCH "linux/loongarch64")
|
||||
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(PLATFORM_ARCH "linux/x86_64")
|
||||
elseif(X86)
|
||||
|
@ -1001,7 +910,6 @@ endif()
|
|||
|
||||
find_package(LIBZIP)
|
||||
if(LIBZIP_FOUND AND USE_SYSTEM_LIBZIP)
|
||||
include_directories(${LIBZIP_INCLUDE_DIRS})
|
||||
add_definitions(-DSHARED_LIBZIP)
|
||||
else()
|
||||
add_library(libzip STATIC
|
||||
|
@ -1193,32 +1101,19 @@ else()
|
|||
include_directories(ext/libpng17)
|
||||
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(nativeExtraLibs)
|
||||
|
||||
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}
|
||||
android/jni/app-android.cpp
|
||||
android/jni/AndroidJavaGLContext.cpp
|
||||
|
@ -1235,7 +1130,7 @@ if(ANDROID)
|
|||
set(nativeExtraLibs ${nativeExtraLibs} openxr)
|
||||
endif()
|
||||
# No target
|
||||
elseif(IOS AND NOT LIBRETRO)
|
||||
elseif(IOS)
|
||||
set(nativeExtra ${nativeExtra}
|
||||
ios/main.mm
|
||||
ios/AppDelegate.mm
|
||||
|
@ -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(Common/Battery/AppleBatteryClient.m PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||
set(TargetBin PPSSPP)
|
||||
elseif(IOS AND LIBRETRO)
|
||||
set(nativeExtraLibs ${nativeExtraLibs} "-framework GLKit")
|
||||
elseif(USING_QT_UI)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
find_package(Qt5 COMPONENTS OpenGL Gui Core Multimedia)
|
||||
list(APPEND NativeAppSource
|
||||
Qt/QtMain.cpp
|
||||
Qt/QtMain.h
|
||||
Qt/QtHost.cpp
|
||||
Qt/QtHost.h
|
||||
Qt/mainwindow.cpp
|
||||
Qt/mainwindow.h
|
||||
)
|
||||
|
@ -1297,15 +1192,7 @@ elseif(USING_QT_UI)
|
|||
if(USING_GLES2)
|
||||
add_definitions(-DQT_OPENGL_ES -DQT_OPENGL_ES_2)
|
||||
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(${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(nativeExtraLibs ${nativeExtraLibs} Qt5::OpenGL Qt5::Gui Qt5::Core Qt5::Multimedia)
|
||||
|
@ -1329,13 +1216,8 @@ elseif(WIN32)
|
|||
else()
|
||||
link_directories(dx9sdk/Lib/x86)
|
||||
endif()
|
||||
elseif(LIBRETRO)
|
||||
else()
|
||||
if(GOLD)
|
||||
set(TargetBin PPSSPPGold)
|
||||
else()
|
||||
elseif(TARGET SDL2::SDL2)
|
||||
set(TargetBin PPSSPPSDL)
|
||||
endif()
|
||||
# Require SDL
|
||||
add_definitions(-DSDL)
|
||||
set(nativeExtra ${nativeExtra}
|
||||
|
@ -1349,55 +1231,17 @@ else()
|
|||
SDL/SDLVulkanGraphicsContext.cpp
|
||||
)
|
||||
endif()
|
||||
if(SDL2_ttf_FOUND OR
|
||||
(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()
|
||||
set(nativeExtraLibs ${nativeExtraLibs} SDL2::SDL2)
|
||||
if(APPLE)
|
||||
set(nativeExtra ${nativeExtra}
|
||||
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(nativeExtra ${nativeExtra} SDL/SDLMain.h SDL/SDLMain.mm SDL/SDLCocoaMetalLayer.h SDL/SDLCocoaMetalLayer.mm UI/DarwinFileSystemServices.mm UI/DarwinFileSystemServices.h Common/Battery/AppleBatteryClient.m)
|
||||
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(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)
|
||||
set(nativeExtraLibs ${nativeExtraLibs} pthread SDL2::SDL2)
|
||||
else()
|
||||
set(nativeExtraLibs ${nativeExtraLibs} SDL2::SDL2)
|
||||
set(nativeExtraLibs ${nativeExtraLibs} pthread)
|
||||
endif()
|
||||
elseif(NOT LIBRETRO)
|
||||
message(FATAL_ERROR "Could not find SDL2. Failing.")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
|
@ -1411,13 +1255,6 @@ if(WIN32)
|
|||
target_link_libraries(Common winmm d3d9 dsound)
|
||||
endif()
|
||||
|
||||
if(NOT LIBRETRO)
|
||||
list(APPEND NativeAppSource
|
||||
UI/AudioCommon.h
|
||||
UI/AudioCommon.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
list(APPEND NativeAppSource
|
||||
android/jni/TestRunner.cpp
|
||||
UI/DiscordIntegration.cpp
|
||||
|
@ -1426,8 +1263,6 @@ list(APPEND NativeAppSource
|
|||
UI/BackgroundAudio.cpp
|
||||
UI/ChatScreen.h
|
||||
UI/ChatScreen.cpp
|
||||
UI/DebugOverlay.cpp
|
||||
UI/DebugOverlay.h
|
||||
UI/DevScreens.cpp
|
||||
UI/DevScreens.h
|
||||
UI/DisplayLayoutScreen.cpp
|
||||
|
@ -1442,8 +1277,6 @@ list(APPEND NativeAppSource
|
|||
UI/MiscScreens.cpp
|
||||
UI/PauseScreen.h
|
||||
UI/PauseScreen.cpp
|
||||
UI/TabbedDialogScreen.h
|
||||
UI/TabbedDialogScreen.cpp
|
||||
UI/GameScreen.h
|
||||
UI/GameScreen.cpp
|
||||
UI/GameSettingsScreen.h
|
||||
|
@ -1480,12 +1313,10 @@ list(APPEND NativeAppSource
|
|||
UI/MemStickScreen.cpp
|
||||
UI/ProfilerDraw.h
|
||||
UI/ProfilerDraw.cpp
|
||||
UI/CustomButtonMappingScreen.h
|
||||
UI/CustomButtonMappingScreen.cpp
|
||||
UI/ComboKeyMappingScreen.h
|
||||
UI/ComboKeyMappingScreen.cpp
|
||||
UI/Theme.h
|
||||
UI/Theme.cpp
|
||||
UI/RetroAchievementScreens.cpp
|
||||
UI/RetroAchievementScreens.h
|
||||
)
|
||||
|
||||
if(ANDROID)
|
||||
|
@ -1519,11 +1350,11 @@ if(LINUX AND NOT ANDROID)
|
|||
endif()
|
||||
|
||||
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)
|
||||
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)
|
||||
target_link_libraries(native Ext::GLEW)
|
||||
endif()
|
||||
|
@ -1576,8 +1407,6 @@ set(CoreExtra)
|
|||
set(CoreExtraLibs)
|
||||
|
||||
set(CoreExtra ${CoreExtra}
|
||||
Core/MIPS/IR/IRAnalysis.cpp
|
||||
Core/MIPS/IR/IRAnalysis.h
|
||||
Core/MIPS/IR/IRCompALU.cpp
|
||||
Core/MIPS/IR/IRCompBranch.cpp
|
||||
Core/MIPS/IR/IRCompFPU.cpp
|
||||
|
@ -1591,8 +1420,6 @@ set(CoreExtra ${CoreExtra}
|
|||
Core/MIPS/IR/IRInterpreter.h
|
||||
Core/MIPS/IR/IRJit.cpp
|
||||
Core/MIPS/IR/IRJit.h
|
||||
Core/MIPS/IR/IRNativeCommon.cpp
|
||||
Core/MIPS/IR/IRNativeCommon.h
|
||||
Core/MIPS/IR/IRPassSimplify.cpp
|
||||
Core/MIPS/IR/IRPassSimplify.h
|
||||
Core/MIPS/IR/IRRegCache.cpp
|
||||
|
@ -1632,17 +1459,6 @@ list(APPEND CoreExtra
|
|||
Core/MIPS/ARM64/Arm64RegCache.h
|
||||
Core/MIPS/ARM64/Arm64RegCacheFPU.cpp
|
||||
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
|
||||
Core/Util/DisArm64.cpp
|
||||
)
|
||||
|
@ -1663,17 +1479,6 @@ list(APPEND CoreExtra
|
|||
Core/MIPS/x86/RegCache.h
|
||||
Core/MIPS/x86/RegCacheFPU.cpp
|
||||
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/Software/DrawPixelX86.cpp
|
||||
GPU/Software/SamplerX86.cpp
|
||||
|
@ -1684,21 +1489,6 @@ list(APPEND CoreExtra
|
|||
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)
|
||||
set(CoreExtra ${CoreExtra}
|
||||
Core/AVIDump.cpp
|
||||
|
@ -1709,7 +1499,7 @@ if(NOT MOBILE_DEVICE)
|
|||
endif()
|
||||
|
||||
set(GPU_GLES
|
||||
GPU/GLES/StencilBufferGLES.cpp
|
||||
GPU/GLES/DepthBufferGLES.cpp
|
||||
GPU/GLES/GPU_GLES.cpp
|
||||
GPU/GLES/GPU_GLES.h
|
||||
GPU/GLES/FragmentTestCacheGLES.cpp
|
||||
|
@ -1790,7 +1580,6 @@ set(GPU_SOURCES
|
|||
${GPU_NEON}
|
||||
GPU/Common/Draw2D.cpp
|
||||
GPU/Common/Draw2D.h
|
||||
GPU/Common/DepthBufferCommon.cpp
|
||||
GPU/Common/TextureShaderCommon.cpp
|
||||
GPU/Common/TextureShaderCommon.h
|
||||
GPU/Common/DepalettizeShaderCommon.cpp
|
||||
|
@ -1839,10 +1628,6 @@ set(GPU_SOURCES
|
|||
GPU/Common/TextureScalerCommon.h
|
||||
GPU/Common/PostShader.cpp
|
||||
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.h
|
||||
GPU/Debugger/Debugger.cpp
|
||||
|
@ -1865,8 +1650,6 @@ set(GPU_SOURCES
|
|||
GPU/GPU.h
|
||||
GPU/GPUCommon.cpp
|
||||
GPU/GPUCommon.h
|
||||
GPU/GPUCommonHW.cpp
|
||||
GPU/GPUCommonHW.h
|
||||
GPU/GPUState.cpp
|
||||
GPU/GPUState.h
|
||||
GPU/Math3D.cpp
|
||||
|
@ -1902,8 +1685,6 @@ add_library(${CoreLibName} ${CoreLinkType}
|
|||
${CommonJIT}
|
||||
Core/Config.cpp
|
||||
Core/Config.h
|
||||
Core/ConfigSettings.cpp
|
||||
Core/ConfigSettings.h
|
||||
Core/ConfigValues.h
|
||||
Core/ControlMapper.cpp
|
||||
Core/ControlMapper.h
|
||||
|
@ -1916,8 +1697,6 @@ add_library(${CoreLibName} ${CoreLinkType}
|
|||
Core/CoreTiming.h
|
||||
Core/CwCheat.cpp
|
||||
Core/CwCheat.h
|
||||
Core/FrameTiming.cpp
|
||||
Core/FrameTiming.h
|
||||
Core/HDRemaster.cpp
|
||||
Core/HDRemaster.h
|
||||
Core/Instance.cpp
|
||||
|
@ -1926,8 +1705,6 @@ add_library(${CoreLibName} ${CoreLinkType}
|
|||
Core/KeyMap.h
|
||||
Core/KeyMapDefaults.cpp
|
||||
Core/KeyMapDefaults.h
|
||||
Core/RetroAchievements.h
|
||||
Core/RetroAchievements.cpp
|
||||
Core/ThreadEventQueue.h
|
||||
Core/TiltEventProcessor.h
|
||||
Core/TiltEventProcessor.cpp
|
||||
|
@ -1954,8 +1731,6 @@ add_library(${CoreLibName} ${CoreLinkType}
|
|||
Core/Debugger/WebSocket/GameBroadcaster.h
|
||||
Core/Debugger/WebSocket/GameSubscriber.cpp
|
||||
Core/Debugger/WebSocket/GameSubscriber.h
|
||||
Core/Debugger/WebSocket/ClientConfigSubscriber.cpp
|
||||
Core/Debugger/WebSocket/ClientConfigSubscriber.h
|
||||
Core/Debugger/WebSocket/GPUBufferSubscriber.cpp
|
||||
Core/Debugger/WebSocket/GPUBufferSubscriber.h
|
||||
Core/Debugger/WebSocket/GPURecordSubscriber.cpp
|
||||
|
@ -2201,6 +1976,8 @@ add_library(${CoreLibName} ${CoreLinkType}
|
|||
Core/HW/SasReverb.h
|
||||
Core/HW/StereoResampler.cpp
|
||||
Core/HW/StereoResampler.h
|
||||
Core/Host.cpp
|
||||
Core/Host.h
|
||||
Core/Loaders.cpp
|
||||
Core/Loaders.h
|
||||
Core/FileLoaders/CachingFileLoader.cpp
|
||||
|
@ -2237,8 +2014,6 @@ add_library(${CoreLibName} ${CoreLinkType}
|
|||
Core/MIPS/MIPSTables.h
|
||||
Core/MIPS/MIPSVFPUUtils.cpp
|
||||
Core/MIPS/MIPSVFPUUtils.h
|
||||
Core/MIPS/MIPSVFPUFallbacks.cpp
|
||||
Core/MIPS/MIPSVFPUFallbacks.h
|
||||
Core/MIPS/MIPSAsm.cpp
|
||||
Core/MIPS/MIPSAsm.h
|
||||
Core/MemFault.cpp
|
||||
|
@ -2259,14 +2034,14 @@ add_library(${CoreLibName} ${CoreLinkType}
|
|||
Core/Screenshot.h
|
||||
Core/System.cpp
|
||||
Core/System.h
|
||||
Core/TextureReplacer.cpp
|
||||
Core/TextureReplacer.h
|
||||
Core/ThreadPools.cpp
|
||||
Core/ThreadPools.h
|
||||
Core/Util/AudioFormat.cpp
|
||||
Core/Util/AudioFormat.h
|
||||
Core/Util/GameManager.cpp
|
||||
Core/Util/GameManager.h
|
||||
Core/Util/GameDB.cpp
|
||||
Core/Util/GameDB.h
|
||||
Core/Util/PortManager.cpp
|
||||
Core/Util/PortManager.h
|
||||
Core/Util/BlockAllocator.cpp
|
||||
|
@ -2325,18 +2100,9 @@ else()
|
|||
include_directories(ext/zstd/lib)
|
||||
endif()
|
||||
|
||||
include_directories(ext/libchdr/include)
|
||||
|
||||
target_link_libraries(${CoreLibName} Common native chdr kirk cityhash sfmt19937 xbrz xxhash rcheevos ${GlslangLibs}
|
||||
target_link_libraries(${CoreLibName} Common native kirk cityhash sfmt19937 xbrz xxhash ${GlslangLibs}
|
||||
${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)
|
||||
|
||||
if(FFmpeg_FOUND)
|
||||
|
@ -2483,12 +2249,10 @@ set(WindowsFiles
|
|||
Windows/Debugger/Debugger_MemoryDlg.h
|
||||
Windows/Debugger/Debugger_Lists.cpp
|
||||
Windows/Debugger/Debugger_Lists.h
|
||||
Windows/Debugger/Debugger_SymbolMap.h
|
||||
Windows/Debugger/Debugger_VFPUDlg.cpp
|
||||
Windows/Debugger/Debugger_VFPUDlg.h
|
||||
Windows/Debugger/WatchItemWindow.cpp
|
||||
Windows/Debugger/WatchItemWindow.h
|
||||
Windows/Debugger/EditSymbolsWindow.cpp
|
||||
Windows/Debugger/EditSymbolsWindow.h
|
||||
Windows/Debugger/SimpleELF.h
|
||||
Windows/GEDebugger/CtrlDisplayListView.cpp
|
||||
Windows/GEDebugger/SimpleGLWindow.cpp
|
||||
Windows/GEDebugger/TabState.cpp
|
||||
|
@ -2532,13 +2296,6 @@ set(WindowsFiles
|
|||
Windows/W32Util/ShellUtil.h
|
||||
Windows/W32Util/TabControl.cpp
|
||||
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.h
|
||||
Windows/MainWindow.cpp
|
||||
|
@ -2562,7 +2319,7 @@ set(WindowsFiles
|
|||
list(APPEND LinkCommon ${CoreLibName} ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
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)
|
||||
list(APPEND NativeAppSource ${WindowsFiles})
|
||||
endif()
|
||||
|
@ -2581,7 +2338,6 @@ set(NativeAssets
|
|||
assets/lang
|
||||
assets/shaders
|
||||
assets/themes
|
||||
assets/vfpu
|
||||
assets/Roboto-Condensed.ttf
|
||||
assets/7z.png
|
||||
assets/compat.ini
|
||||
|
@ -2605,8 +2361,8 @@ set(NativeAssets
|
|||
if(HEADLESS)
|
||||
set(HeadlessSource
|
||||
headless/Headless.cpp
|
||||
headless/HeadlessHost.cpp
|
||||
headless/HeadlessHost.h
|
||||
headless/StubHost.cpp
|
||||
headless/StubHost.h
|
||||
headless/Compare.cpp
|
||||
headless/Compare.h
|
||||
headless/SDLHeadlessHost.cpp
|
||||
|
@ -2644,7 +2400,6 @@ if(UNITTEST)
|
|||
unittest/TestIRPassSimplify.cpp
|
||||
unittest/TestX64Emitter.cpp
|
||||
unittest/TestVertexJit.cpp
|
||||
unittest/TestVFS.cpp
|
||||
unittest/TestRiscVEmitter.cpp
|
||||
unittest/TestSoftwareGPUJit.cpp
|
||||
unittest/TestThreadManager.cpp
|
||||
|
@ -2678,18 +2433,11 @@ endif()
|
|||
if(TargetBin)
|
||||
if(APPLE)
|
||||
if(NOT IOS)
|
||||
if(GOLD)
|
||||
set(ICON_PATH_ABS ${CMAKE_CURRENT_SOURCE_DIR}/icons/ppsspp_gold.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(MACOSX_BUNDLE_BUNDLE_NAME PPSSPP)
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER org.ppsspp.ppsspp)
|
||||
endif()
|
||||
|
||||
# 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 THEME_FILE assets/themes/*)
|
||||
file(GLOB_RECURSE DEBUGGER_FILES assets/debugger/*)
|
||||
file(GLOB_RECURSE VFPU_FILES assets/vfpu/*)
|
||||
|
||||
if(NOT IOS)
|
||||
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(${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(${VFPU_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/assets/vfpu")
|
||||
endif()
|
||||
|
||||
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/")
|
||||
else()
|
||||
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/")
|
||||
if(USING_QT_UI)
|
||||
file(INSTALL "${CMAKE_SOURCE_DIR}/ext/vulkan/macOS/Frameworks/libMoltenVK.dylib" DESTINATION "${CMAKE_BINARY_DIR}/PPSSPPSDL.app/Contents/Frameworks/")
|
||||
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")
|
||||
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()
|
||||
elseif(WIN32)
|
||||
|
@ -2746,7 +2492,7 @@ if(NOT ANDROID)
|
|||
file(INSTALL assets/flash0 DESTINATION assets)
|
||||
endif()
|
||||
# packaging and code signing
|
||||
if(IOS AND NOT LIBRETRO)
|
||||
if(IOS)
|
||||
set(DEPLOYMENT_TARGET 11.0)
|
||||
file(GLOB IOSAssets ios/assets/*.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()
|
||||
|
||||
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)
|
||||
configure_file(
|
||||
"${CMAKE_SOURCE_DIR}/ppsspp.desktop.in"
|
||||
|
|
|
@ -315,14 +315,6 @@ const u8* ARM64XEmitter::AlignCodePage()
|
|||
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()
|
||||
{
|
||||
FlushIcacheSection(m_lastCacheFlushEnd, m_code);
|
||||
|
@ -514,7 +506,7 @@ void ARM64XEmitter::EncodeTestBranchInst(u32 op, ARM64Reg Rt, u8 bits, const voi
|
|||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
_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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
_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);
|
||||
}
|
||||
|
||||
// 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
|
||||
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);
|
||||
}
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
void ARM64FloatEmitter::FCVT(u8 size_to, u8 size_from, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
|
@ -3586,13 +3344,6 @@ void ARM64FloatEmitter::UCVTF(ARM64Reg Rd, ARM64Reg Rn)
|
|||
|
||||
void ARM64FloatEmitter::SCVTF(ARM64Reg Rd, ARM64Reg Rn, int scale)
|
||||
{
|
||||
if (IsScalar(Rn)) {
|
||||
int imm = (IsDouble(Rn) ? 64 : 32) * 2 - scale;
|
||||
Rd = DecodeReg(Rd);
|
||||
Rn = DecodeReg(Rn);
|
||||
|
||||
Write32((1 << 30) | (0 << 29) | (0x1F << 24) | (imm << 16) | (0x1C << 11) | (1 << 10) | (Rn << 5) | Rd);
|
||||
} else {
|
||||
bool sf = Is64Bit(Rn);
|
||||
u32 type = 0;
|
||||
if (IsDouble(Rd))
|
||||
|
@ -3600,17 +3351,9 @@ void ARM64FloatEmitter::SCVTF(ARM64Reg Rd, ARM64Reg Rn, int scale)
|
|||
|
||||
EmitConversion2(sf, 0, false, type, 0, 2, 64 - scale, Rd, Rn);
|
||||
}
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::UCVTF(ARM64Reg Rd, ARM64Reg Rn, int scale)
|
||||
{
|
||||
if (IsScalar(Rn)) {
|
||||
int imm = (IsDouble(Rn) ? 64 : 32) * 2 - scale;
|
||||
Rd = DecodeReg(Rd);
|
||||
Rn = DecodeReg(Rn);
|
||||
|
||||
Write32((1 << 30) | (1 << 29) | (0x1F << 24) | (imm << 16) | (0x1C << 11) | (1 << 10) | (Rn << 5) | Rd);
|
||||
} else {
|
||||
bool sf = Is64Bit(Rn);
|
||||
u32 type = 0;
|
||||
if (IsDouble(Rd))
|
||||
|
@ -3618,7 +3361,6 @@ void ARM64FloatEmitter::UCVTF(ARM64Reg Rd, ARM64Reg Rn, int scale)
|
|||
|
||||
EmitConversion2(sf, 0, false, type, 0, 3, 64 - scale, Rd, Rn);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
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);
|
||||
}
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
_assert_msg_(shift > 0, "%s shift amount must be greater than zero!", __FUNCTION__);
|
||||
|
@ -4187,129 +3896,18 @@ 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.
|
||||
void ARM64FloatEmitter::MOVI2FDUP(ARM64Reg Rd, float value, ARM64Reg scratch, bool negate) {
|
||||
_assert_msg_(!IsSingle(Rd), "%s doesn't support singles", __FUNCTION__);
|
||||
void ARM64FloatEmitter::MOVI2FDUP(ARM64Reg Rd, float value, ARM64Reg scratch) {
|
||||
// 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;
|
||||
memcpy(&ival, &value, 4);
|
||||
uint8_t imm8;
|
||||
if (ival == 0) { // Make sure to not catch negative zero here
|
||||
// Prefer MOVI 0, which may have no latency on some CPUs.
|
||||
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);
|
||||
}
|
||||
EOR(Rd, Rd, Rd);
|
||||
} else {
|
||||
_assert_msg_(scratch != INVALID_REG, "Failed to find a way to generate FP immediate %f without scratch", value);
|
||||
if (negate) {
|
||||
ival ^= 0x80000000;
|
||||
MOVI2F(s, value, scratch);
|
||||
DUP(32, Rd, Rd, 0);
|
||||
}
|
||||
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) {
|
||||
|
|
|
@ -94,7 +94,7 @@ enum ARM64Reg
|
|||
|
||||
// R19-R28. R29 (FP), R30 (LR) are always saved and FP updated appropriately.
|
||||
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 IsSingle(ARM64Reg reg) { return (reg & 0xC0) == 0x40; }
|
||||
|
@ -290,23 +290,6 @@ public:
|
|||
}
|
||||
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)
|
||||
{
|
||||
m_destReg = Rd;
|
||||
|
@ -418,7 +401,6 @@ public:
|
|||
void ReserveCodeSpace(u32 bytes);
|
||||
const u8* AlignCode16();
|
||||
const u8* AlignCodePage();
|
||||
const u8 *NopAlignCode16();
|
||||
void FlushIcache();
|
||||
void FlushIcacheSection(const u8* start, const u8* end);
|
||||
u8* GetWritableCodePtr();
|
||||
|
@ -820,13 +802,6 @@ public:
|
|||
void FSQRT(ARM64Reg Rd, ARM64Reg Rn);
|
||||
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
|
||||
void FADD(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 EOR(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 FABS(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
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 FMLA(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 FCVTZS(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 FMUL(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void FNEG(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
|
@ -898,18 +868,6 @@ public:
|
|||
void XTN(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
|
||||
void DUP(u8 size, ARM64Reg Rd, 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 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
|
||||
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.
|
||||
void FCVTS(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.
|
||||
void SCVTF(ARM64Reg Rd, ARM64Reg Rn);
|
||||
|
@ -965,10 +909,6 @@ public:
|
|||
// Conditional select
|
||||
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
|
||||
void UZP1(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 TRN2(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
|
||||
void SSHLL(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 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 SHRN2(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift);
|
||||
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 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
|
||||
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 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 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 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);
|
||||
|
@ -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 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 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 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);
|
||||
|
@ -1041,11 +974,9 @@ private:
|
|||
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 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 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 SXTL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, bool upper);
|
||||
void UXTL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, bool upper);
|
||||
|
|
|
@ -29,18 +29,17 @@
|
|||
#if PPSSPP_ARCH(ARM)
|
||||
#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
|
||||
#endif
|
||||
#elif PPSSPP_ARCH(ARM64)
|
||||
#elif PPSSPP_ARCH(ARM64) && defined(__aarch64__)
|
||||
#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
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
@ -55,7 +54,7 @@
|
|||
std::string GetCPUBrandString();
|
||||
#else
|
||||
// No CPUID on ARM, so we'll have to read the registry
|
||||
#include "Common/CommonWindows.h"
|
||||
#include <windows.h>
|
||||
std::string GetCPUBrandString() {
|
||||
std::string cpu_string;
|
||||
|
||||
|
|
|
@ -613,14 +613,6 @@ const u8 *ARMXEmitter::AlignCode16()
|
|||
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()
|
||||
{
|
||||
ReserveCodeSpace((-(intptr_t)code) & 4095);
|
||||
|
|
|
@ -446,8 +446,6 @@ public:
|
|||
void ReserveCodeSpace(u32 bytes);
|
||||
const u8 *AlignCode16();
|
||||
const u8 *AlignCodePage();
|
||||
const u8 *NopAlignCode16();
|
||||
|
||||
void FlushIcache();
|
||||
void FlushIcacheSection(u8 *start, u8 *end);
|
||||
u8 *GetWritableCodePtr();
|
||||
|
|
|
@ -42,7 +42,7 @@ void Buffer::Append(const Buffer &other) {
|
|||
void Buffer::AppendValue(int value) {
|
||||
char buf[16];
|
||||
// This is slow.
|
||||
snprintf(buf, sizeof(buf), "%i", value);
|
||||
sprintf(buf, "%i", value);
|
||||
Append(buf);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
// Reference : https://stackoverflow.com/questions/6121792/how-to-check-if-a-cpu-supports-the-sse3-instruction-set
|
||||
#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"
|
||||
|
||||
|
|
|
@ -109,27 +109,8 @@ struct CPUInfo {
|
|||
bool RiscV_D;
|
||||
bool RiscV_C;
|
||||
bool RiscV_V;
|
||||
bool RiscV_B;
|
||||
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
|
||||
struct {
|
||||
|
|
|
@ -10,11 +10,6 @@
|
|||
#include "Common/Log.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.
|
||||
// You get memory management for free, plus, you can use all emitter functions without
|
||||
// having to prefix them with gen-> or something similar.
|
||||
|
@ -32,7 +27,7 @@ public:
|
|||
|
||||
virtual const u8 *GetCodePtr() const = 0;
|
||||
|
||||
u8 *GetBasePtr() const {
|
||||
u8 *GetBasePtr() {
|
||||
return region;
|
||||
}
|
||||
|
||||
|
@ -70,20 +65,9 @@ public:
|
|||
// Call this before you generate any code.
|
||||
void AllocCodeSpace(int 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.
|
||||
region = (u8 *)AllocateExecutableMemory(region_size);
|
||||
writableRegion = region;
|
||||
#endif // !PPSSPP_PLATFORM(SWITCH)
|
||||
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.
|
||||
void FreeCodeSpace() {
|
||||
#if !PPSSPP_PLATFORM(SWITCH)
|
||||
ProtectMemoryPages(region, region_size, MEM_PROT_READ | MEM_PROT_WRITE);
|
||||
FreeExecutableMemory(region, region_size);
|
||||
#else // !PPSSPP_PLATFORM(SWITCH)
|
||||
jitClose(&jitController);
|
||||
printf("[NXJIT]: Jit closed\n");
|
||||
#endif // PPSSPP_PLATFORM(SWITCH)
|
||||
FreeMemoryPages(region, region_size);
|
||||
region = nullptr;
|
||||
writableRegion = nullptr;
|
||||
region_size = 0;
|
||||
|
@ -197,7 +176,5 @@ private:
|
|||
const uint8_t *writeStart_ = nullptr;
|
||||
uint8_t *writableRegion = nullptr;
|
||||
size_t writeEstimated_ = 0;
|
||||
#if PPSSPP_PLATFORM(SWITCH)
|
||||
Jit jitController;
|
||||
#endif // PPSSPP_PLATFORM(SWITCH)
|
||||
};
|
||||
|
||||
|
|
|
@ -143,7 +143,6 @@
|
|||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
|
@ -171,7 +170,6 @@
|
|||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
|
@ -199,7 +197,6 @@
|
|||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
|
@ -228,7 +225,6 @@
|
|||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
|
@ -260,7 +256,6 @@
|
|||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
|
@ -296,7 +291,6 @@
|
|||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StringPooling>true</StringPooling>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
|
@ -332,7 +326,6 @@
|
|||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StringPooling>true</StringPooling>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
|
@ -368,7 +361,6 @@
|
|||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StringPooling>true</StringPooling>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
|
@ -385,13 +377,6 @@
|
|||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<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\pngconf.h" />
|
||||
<ClInclude Include="..\ext\libpng17\pngdebug.h" />
|
||||
|
@ -399,7 +384,6 @@
|
|||
<ClInclude Include="..\ext\libpng17\pnglibconf.h" />
|
||||
<ClInclude Include="..\ext\libpng17\pngpriv.h" />
|
||||
<ClInclude Include="..\ext\libpng17\pngstruct.h" />
|
||||
<ClInclude Include="..\ext\naett\naett.h" />
|
||||
<ClInclude Include="..\ext\vma\vk_mem_alloc.h" />
|
||||
<ClInclude Include="ABI.h" />
|
||||
<ClInclude Include="Arm64Emitter.h" />
|
||||
|
@ -421,7 +405,6 @@
|
|||
<ClInclude Include="Data\Encoding\Shiftjis.h" />
|
||||
<ClInclude Include="Data\Encoding\Utf16.h" />
|
||||
<ClInclude Include="Data\Encoding\Utf8.h" />
|
||||
<ClInclude Include="Data\Format\DDSLoad.h" />
|
||||
<ClInclude Include="Data\Format\IniFile.h" />
|
||||
<ClInclude Include="Data\Format\JSONReader.h" />
|
||||
<ClInclude Include="Data\Format\JSONWriter.h" />
|
||||
|
@ -435,7 +418,6 @@
|
|||
<ClInclude Include="Data\Text\Parsers.h" />
|
||||
<ClInclude Include="Data\Text\WrapText.h" />
|
||||
<ClInclude Include="FakeEmitter.h" />
|
||||
<ClInclude Include="File\AndroidContentURI.h" />
|
||||
<ClInclude Include="File\AndroidStorage.h" />
|
||||
<ClInclude Include="File\DirListing.h" />
|
||||
<ClInclude Include="File\DiskFree.h" />
|
||||
|
@ -443,21 +425,17 @@
|
|||
<ClInclude Include="File\FileUtil.h" />
|
||||
<ClInclude Include="File\Path.h" />
|
||||
<ClInclude Include="File\PathBrowser.h" />
|
||||
<ClInclude Include="File\VFS\DirectoryReader.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\D3D9\D3DCompilerLoader.h" />
|
||||
<ClInclude Include="GPU\D3D9\D3D9ShaderCompiler.h" />
|
||||
<ClInclude Include="GPU\D3D9\D3D9StateCache.h" />
|
||||
<ClInclude Include="GPU\DataFormat.h" />
|
||||
<ClInclude Include="GPU\GPUBackendCommon.h" />
|
||||
<ClInclude Include="GPU\MiscTypes.h" />
|
||||
<ClInclude Include="GPU\OpenGL\DataFormatGL.h" />
|
||||
<ClInclude Include="GPU\OpenGL\gl3stub.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\GLRenderManager.h" />
|
||||
<ClInclude Include="GPU\OpenGL\GLSLProgram.h" />
|
||||
|
@ -472,7 +450,6 @@
|
|||
<ClInclude Include="GPU\Vulkan\VulkanBarrier.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanContext.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanDebug.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanDescSet.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanFramebuffer.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanFrameData.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanImage.h" />
|
||||
|
@ -492,12 +469,10 @@
|
|||
<ClInclude Include="Math\lin\vec3.h" />
|
||||
<ClInclude Include="Math\math_util.h" />
|
||||
<ClInclude Include="Math\Statistics.h" />
|
||||
<ClInclude Include="Net\HTTPNaettRequest.h" />
|
||||
<ClInclude Include="Net\NetBuffer.h" />
|
||||
<ClInclude Include="Net\HTTPClient.h" />
|
||||
<ClInclude Include="Net\HTTPHeaders.h" />
|
||||
<ClInclude Include="Net\HTTPServer.h" />
|
||||
<ClInclude Include="Net\HTTPRequest.h" />
|
||||
<ClInclude Include="Net\Resolve.h" />
|
||||
<ClInclude Include="Net\Sinks.h" />
|
||||
<ClInclude Include="Net\URL.h" />
|
||||
|
@ -509,7 +484,6 @@
|
|||
<ClInclude Include="Render\Text\draw_text.h" />
|
||||
<ClInclude Include="Render\Text\draw_text_android.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_win.h" />
|
||||
<ClInclude Include="LogReporting.h" />
|
||||
|
@ -532,6 +506,27 @@
|
|||
<ClInclude Include="Crypto\sha256.h" />
|
||||
<ClInclude Include="DbgNew.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="Log.h" />
|
||||
<ClInclude Include="LogManager.h" />
|
||||
|
@ -545,8 +540,6 @@
|
|||
<ClInclude Include="Swap.h" />
|
||||
<ClInclude Include="SysError.h" />
|
||||
<ClInclude Include="System\Display.h" />
|
||||
<ClInclude Include="System\OSD.h" />
|
||||
<ClInclude Include="System\Request.h" />
|
||||
<ClInclude Include="System\NativeApp.h" />
|
||||
<ClInclude Include="System\System.h" />
|
||||
<ClInclude Include="Thread\Barrier.h" />
|
||||
|
@ -561,7 +554,6 @@
|
|||
<ClInclude Include="TimeUtil.h" />
|
||||
<ClInclude Include="UI\AsyncImageFileView.h" />
|
||||
<ClInclude Include="UI\Context.h" />
|
||||
<ClInclude Include="UI\IconCache.h" />
|
||||
<ClInclude Include="UI\PopupScreens.h" />
|
||||
<ClInclude Include="UI\Root.h" />
|
||||
<ClInclude Include="UI\Screen.h" />
|
||||
|
@ -582,7 +574,6 @@
|
|||
<ClInclude Include="x64Emitter.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\ext\basis_universal\basisu_transcoder.cpp" />
|
||||
<ClCompile Include="..\ext\libpng17\png.c">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
|
||||
|
@ -831,7 +822,6 @@
|
|||
<ForcedIncludeFiles Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
</ForcedIncludeFiles>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\ext\naett\naett.c" />
|
||||
<ClCompile Include="..\ext\vma\vk_mem_alloc.cpp" />
|
||||
<ClCompile Include="ABI.cpp" />
|
||||
<ClCompile Include="Arm64Emitter.cpp" />
|
||||
|
@ -847,13 +837,11 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="ArmEmitter.cpp" />
|
||||
<ClCompile Include="Buffer.cpp" />
|
||||
<ClCompile Include="Data\Collections\FastVec.h" />
|
||||
<ClCompile Include="Data\Color\RGBAUtil.cpp" />
|
||||
<ClCompile Include="Data\Convert\SmallDataConvert.cpp" />
|
||||
<ClCompile Include="Data\Encoding\Base64.cpp" />
|
||||
<ClCompile Include="Data\Encoding\Compression.cpp" />
|
||||
<ClCompile Include="Data\Encoding\Utf8.cpp" />
|
||||
<ClCompile Include="Data\Format\DDSLoad.cpp" />
|
||||
<ClCompile Include="Data\Format\IniFile.cpp" />
|
||||
<ClCompile Include="Data\Format\JSONReader.cpp" />
|
||||
<ClCompile Include="Data\Format\JSONWriter.cpp" />
|
||||
|
@ -865,7 +853,6 @@
|
|||
<ClCompile Include="Data\Text\I18n.cpp" />
|
||||
<ClCompile Include="Data\Text\Parsers.cpp" />
|
||||
<ClCompile Include="Data\Text\WrapText.cpp" />
|
||||
<ClCompile Include="File\AndroidContentURI.cpp" />
|
||||
<ClCompile Include="File\AndroidStorage.cpp" />
|
||||
<ClCompile Include="File\DirListing.cpp" />
|
||||
<ClCompile Include="File\DiskFree.cpp" />
|
||||
|
@ -873,21 +860,17 @@
|
|||
<ClCompile Include="File\FileUtil.cpp" />
|
||||
<ClCompile Include="File\Path.cpp" />
|
||||
<ClCompile Include="File\PathBrowser.cpp" />
|
||||
<ClCompile Include="File\VFS\DirectoryReader.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\thin3d_d3d11.cpp" />
|
||||
<ClCompile Include="GPU\D3D9\D3DCompilerLoader.cpp" />
|
||||
<ClCompile Include="GPU\D3D9\D3D9ShaderCompiler.cpp" />
|
||||
<ClCompile Include="GPU\D3D9\D3D9StateCache.cpp" />
|
||||
<ClCompile Include="GPU\D3D9\thin3d_d3d9.cpp" />
|
||||
<ClCompile Include="GPU\GPUBackendCommon.cpp" />
|
||||
<ClCompile Include="GPU\OpenGL\DataFormatGL.cpp" />
|
||||
<ClCompile Include="GPU\OpenGL\gl3stub.c" />
|
||||
<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\GLRenderManager.cpp" />
|
||||
<ClCompile Include="GPU\OpenGL\GLSLProgram.cpp" />
|
||||
|
@ -901,7 +884,6 @@
|
|||
<ClCompile Include="GPU\Vulkan\VulkanBarrier.cpp" />
|
||||
<ClCompile Include="GPU\Vulkan\VulkanContext.cpp" />
|
||||
<ClCompile Include="GPU\Vulkan\VulkanDebug.cpp" />
|
||||
<ClCompile Include="GPU\Vulkan\VulkanDescSet.cpp" />
|
||||
<ClCompile Include="GPU\Vulkan\VulkanFramebuffer.cpp" />
|
||||
<ClCompile Include="GPU\Vulkan\VulkanFrameData.cpp" />
|
||||
<ClCompile Include="GPU\Vulkan\VulkanImage.cpp" />
|
||||
|
@ -920,12 +902,10 @@
|
|||
<ClCompile Include="Math\lin\vec3.cpp" />
|
||||
<ClCompile Include="Math\math_util.cpp" />
|
||||
<ClCompile Include="Math\Statistics.cpp" />
|
||||
<ClCompile Include="Net\HTTPNaettRequest.cpp" />
|
||||
<ClCompile Include="Net\NetBuffer.cpp" />
|
||||
<ClCompile Include="Net\HTTPClient.cpp" />
|
||||
<ClCompile Include="Net\HTTPHeaders.cpp" />
|
||||
<ClCompile Include="Net\HTTPServer.cpp" />
|
||||
<ClCompile Include="Net\HTTPRequest.cpp" />
|
||||
<ClCompile Include="Net\Resolve.cpp" />
|
||||
<ClCompile Include="Net\Sinks.cpp" />
|
||||
<ClCompile Include="Net\URL.cpp" />
|
||||
|
@ -937,13 +917,11 @@
|
|||
<ClCompile Include="Render\Text\draw_text.cpp" />
|
||||
<ClCompile Include="Render\Text\draw_text_android.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_win.cpp" />
|
||||
<ClCompile Include="LogReporting.cpp" />
|
||||
<ClCompile Include="RiscVCPUDetect.cpp" />
|
||||
<ClCompile Include="RiscVEmitter.cpp" />
|
||||
<ClCompile Include="LoongArchCPUDetect.cpp" />
|
||||
<ClCompile Include="Serialize\Serializer.cpp" />
|
||||
<ClCompile Include="Data\Convert\ColorConv.cpp" />
|
||||
<ClCompile Include="ConsoleListener.cpp" />
|
||||
|
@ -972,6 +950,36 @@
|
|||
<ClCompile Include="Crypto\sha1.cpp" />
|
||||
<ClCompile Include="Crypto\sha256.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="MemArenaAndroid.cpp" />
|
||||
<ClCompile Include="MemArenaPosix.cpp" />
|
||||
|
@ -983,8 +991,6 @@
|
|||
<ClCompile Include="OSVersion.cpp" />
|
||||
<ClCompile Include="StringUtils.cpp" />
|
||||
<ClCompile Include="System\Display.cpp" />
|
||||
<ClCompile Include="System\OSD.cpp" />
|
||||
<ClCompile Include="System\Request.cpp" />
|
||||
<ClCompile Include="Thread\ParallelLoop.cpp" />
|
||||
<ClCompile Include="Thread\ThreadManager.cpp" />
|
||||
<ClCompile Include="Thread\ThreadUtil.cpp" />
|
||||
|
@ -992,7 +998,6 @@
|
|||
<ClCompile Include="TimeUtil.cpp" />
|
||||
<ClCompile Include="UI\AsyncImageFileView.cpp" />
|
||||
<ClCompile Include="UI\Context.cpp" />
|
||||
<ClCompile Include="UI\IconCache.cpp" />
|
||||
<ClCompile Include="UI\PopupScreens.cpp" />
|
||||
<ClCompile Include="UI\Root.cpp" />
|
||||
<ClCompile Include="UI\Screen.cpp" />
|
||||
|
@ -1023,18 +1028,6 @@
|
|||
<Project>{f761046e-6c38-4428-a5f1-38391a37bb34}</Project>
|
||||
</ProjectReference>
|
||||
</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" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
|
|
|
@ -32,6 +32,15 @@
|
|||
<ClInclude Include="ArmCommon.h" />
|
||||
<ClInclude Include="BitSet.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="DbgNew.h" />
|
||||
<ClInclude Include="OSVersion.h" />
|
||||
|
@ -167,6 +176,9 @@
|
|||
<ClInclude Include="File\VFS\VFS.h">
|
||||
<Filter>File\VFS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="File\VFS\AssetReader.h">
|
||||
<Filter>File\VFS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Data\Format\IniFile.h">
|
||||
<Filter>Data\Format</Filter>
|
||||
</ClInclude>
|
||||
|
@ -440,6 +452,9 @@
|
|||
<ClInclude Include="Render\ManagedTexture.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GPU\MiscTypes.h">
|
||||
<Filter>GPU</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GPU\Vulkan\VulkanFramebuffer.h">
|
||||
<Filter>GPU\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
|
@ -449,75 +464,6 @@
|
|||
<ClInclude Include="UI\PopupScreens.h">
|
||||
<Filter>UI</Filter>
|
||||
</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>
|
||||
<ClCompile Include="ABI.cpp" />
|
||||
|
@ -544,6 +490,15 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="MipsEmitter.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="MemArenaWin32.cpp" />
|
||||
<ClCompile Include="MemArenaAndroid.cpp" />
|
||||
|
@ -670,6 +625,9 @@
|
|||
<ClCompile Include="File\VFS\VFS.cpp">
|
||||
<Filter>File\VFS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="File\VFS\AssetReader.cpp">
|
||||
<Filter>File\VFS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Data\Format\IniFile.cpp">
|
||||
<Filter>Data\Format</Filter>
|
||||
</ClCompile>
|
||||
|
@ -878,7 +836,6 @@
|
|||
<Filter>GPU\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RiscVEmitter.cpp" />
|
||||
<ClCompile Include="LoongArchCPUDetect.cpp" />
|
||||
<ClCompile Include="GPU\Vulkan\VulkanFrameData.cpp">
|
||||
<Filter>GPU\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
|
@ -921,62 +878,17 @@
|
|||
<ClCompile Include="UI\PopupScreens.cpp">
|
||||
<Filter>UI</Filter>
|
||||
</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>
|
||||
<Filter Include="Crypto">
|
||||
<UniqueIdentifier>{1b593f03-7b28-4707-9228-4981796f5589}</UniqueIdentifier>
|
||||
</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">
|
||||
<UniqueIdentifier>{7be79ad5-3520-46a1-a370-dce2a943978c}</UniqueIdentifier>
|
||||
</Filter>
|
||||
|
@ -1073,51 +985,10 @@
|
|||
<Filter Include="VR">
|
||||
<UniqueIdentifier>{9d1c29fd-8ac7-4475-8ea6-c8c759b695fe}</UniqueIdentifier>
|
||||
</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>
|
||||
<Text Include="..\ext\libpng17\CMakeLists.txt">
|
||||
<Filter>ext\libpng17</Filter>
|
||||
</Text>
|
||||
</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>
|
|
@ -30,17 +30,12 @@
|
|||
#include <unistd.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");}
|
||||
#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)
|
||||
#define Crash() {asm ("bkpt #0");}
|
||||
#elif PPSSPP_ARCH(ARM64)
|
||||
#define Crash() {asm ("brk #0");}
|
||||
#elif PPSSPP_ARCH(RISCV64)
|
||||
#define Crash() {asm ("ebreak");}
|
||||
#else
|
||||
#include <signal.h>
|
||||
#define Crash() {kill(getpid(), SIGINT);}
|
||||
|
|
|
@ -36,39 +36,6 @@ typedef signed __int64 s64;
|
|||
|
||||
#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 short u16;
|
||||
typedef unsigned int u32;
|
||||
|
|
|
@ -119,10 +119,7 @@ void ConsoleListener::Init(bool AutoOpen, int Width, int Height, const char *Tit
|
|||
title_ = ConvertUTF8ToWString(Title);
|
||||
|
||||
if (AutoOpen)
|
||||
{
|
||||
Open();
|
||||
bHidden = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -358,7 +355,7 @@ void ConsoleListener::LogWriterThread()
|
|||
|
||||
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);
|
||||
size_t Len = next - Text;
|
||||
|
@ -367,7 +364,7 @@ void ConsoleListener::LogWriterThread()
|
|||
|
||||
if (Text[0] == '\033' && Text + 1 < End)
|
||||
{
|
||||
Level = (LogLevel)(Text[1] - '0');
|
||||
Level = (LogTypes::LOG_LEVELS) (Text[1] - '0');
|
||||
Len -= 2;
|
||||
Text += 2;
|
||||
}
|
||||
|
@ -384,7 +381,7 @@ void ConsoleListener::LogWriterThread()
|
|||
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.
|
||||
if (logPendingWritePos == (u32) -1)
|
||||
|
@ -462,7 +459,7 @@ void ConsoleListener::SendToThread(LogLevel Level, const char *Text)
|
|||
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.");
|
||||
|
||||
|
@ -479,20 +476,21 @@ void ConsoleListener::WriteToConsole(LogLevel Level, const char *Text, size_t Le
|
|||
WORD Color;
|
||||
static wchar_t tempBuf[2048];
|
||||
|
||||
switch (Level) {
|
||||
case LogLevel::LNOTICE: // light green
|
||||
switch (Level)
|
||||
{
|
||||
case NOTICE_LEVEL: // light green
|
||||
Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
|
||||
break;
|
||||
case LogLevel::LERROR: // light red
|
||||
case ERROR_LEVEL: // light red
|
||||
Color = FOREGROUND_RED | FOREGROUND_INTENSITY;
|
||||
break;
|
||||
case LogLevel::LWARNING: // light yellow
|
||||
case WARNING_LEVEL: // light yellow
|
||||
Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
|
||||
break;
|
||||
case LogLevel::LINFO: // cyan
|
||||
case INFO_LEVEL: // cyan
|
||||
Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
|
||||
break;
|
||||
case LogLevel::LDEBUG: // gray
|
||||
case DEBUG_LEVEL: // gray
|
||||
Color = FOREGROUND_INTENSITY;
|
||||
break;
|
||||
default: // off-white
|
||||
|
@ -592,7 +590,7 @@ void ConsoleListener::PixelSpace(int Left, int Top, int Width, int Height, bool
|
|||
COORD Coo = GetCoordinates(OldCursor, LBufWidth);
|
||||
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
|
||||
if (Resize) MoveWindow(GetConsoleWindow(), Left,Top, (Width + 100),Height, true);
|
||||
|
@ -614,16 +612,18 @@ void ConsoleListener::Log(const LogMessage &msg) {
|
|||
char ColorAttr[16] = "";
|
||||
char ResetAttr[16] = "";
|
||||
|
||||
if (bUseColor) {
|
||||
if (bUseColor)
|
||||
{
|
||||
strcpy(ResetAttr, "\033[0m");
|
||||
switch (msg.level) {
|
||||
case LogLevel::LNOTICE: // light green
|
||||
switch (msg.level)
|
||||
{
|
||||
case NOTICE_LEVEL: // light green
|
||||
strcpy(ColorAttr, "\033[92m");
|
||||
break;
|
||||
case LogLevel::LERROR: // light red
|
||||
case ERROR_LEVEL: // light red
|
||||
strcpy(ColorAttr, "\033[91m");
|
||||
break;
|
||||
case LogLevel::LWARNING: // light yellow
|
||||
case WARNING_LEVEL: // light yellow
|
||||
strcpy(ColorAttr, "\033[93m");
|
||||
break;
|
||||
default:
|
||||
|
@ -653,3 +653,5 @@ void ConsoleListener::ClearScreen(bool Cursor)
|
|||
if (Cursor) SetConsoleCursorPosition(hConsole, coordScreen);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -54,8 +54,8 @@ private:
|
|||
|
||||
static unsigned int WINAPI RunThread(void *lpParam);
|
||||
void LogWriterThread();
|
||||
void SendToThread(LogLevel Level, const char *Text);
|
||||
void WriteToConsole(LogLevel Level, const char *Text, size_t Len);
|
||||
void SendToThread(LogTypes::LOG_LEVELS Level, const char *Text);
|
||||
void WriteToConsole(LogTypes::LOG_LEVELS Level, const char *Text, size_t Len);
|
||||
|
||||
static int refCount;
|
||||
static HANDLE hThread;
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
/*
|
||||
* MD5 context setup
|
||||
*/
|
||||
void ppsspp_md5_starts( md5_context *ctx )
|
||||
void md5_starts( md5_context *ctx )
|
||||
{
|
||||
ctx->total[0] = 0;
|
||||
ctx->total[1] = 0;
|
||||
|
@ -73,7 +73,7 @@ void ppsspp_md5_starts( md5_context *ctx )
|
|||
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;
|
||||
|
||||
|
@ -199,7 +199,7 @@ static void ppsspp_md5_process( md5_context *ctx, unsigned char data[64] )
|
|||
/*
|
||||
* 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;
|
||||
unsigned long left;
|
||||
|
@ -220,7 +220,7 @@ void ppsspp_md5_update( md5_context *ctx, unsigned char *input, int ilen )
|
|||
{
|
||||
memcpy( (void *) (ctx->buffer + left),
|
||||
(void *) input, fill );
|
||||
ppsspp_md5_process( ctx, ctx->buffer );
|
||||
md5_process( ctx, ctx->buffer );
|
||||
input += fill;
|
||||
ilen -= fill;
|
||||
left = 0;
|
||||
|
@ -228,7 +228,7 @@ void ppsspp_md5_update( md5_context *ctx, unsigned char *input, int ilen )
|
|||
|
||||
while( ilen >= 64 )
|
||||
{
|
||||
ppsspp_md5_process( ctx, input );
|
||||
md5_process( ctx, input );
|
||||
input += 64;
|
||||
ilen -= 64;
|
||||
}
|
||||
|
@ -251,7 +251,7 @@ static const unsigned char md5_padding[64] =
|
|||
/*
|
||||
* 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 high, low;
|
||||
|
@ -267,8 +267,8 @@ void ppsspp_md5_finish( md5_context *ctx, unsigned char output[16] )
|
|||
last = ctx->total[0] & 0x3F;
|
||||
padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
|
||||
|
||||
ppsspp_md5_update( ctx, (unsigned char *) md5_padding, padn );
|
||||
ppsspp_md5_update( ctx, msglen, 8 );
|
||||
md5_update( ctx, (unsigned char *) md5_padding, padn );
|
||||
md5_update( ctx, msglen, 8 );
|
||||
|
||||
PUT_ULONG_LE( ctx->state[0], output, 0 );
|
||||
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 )
|
||||
*/
|
||||
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;
|
||||
|
||||
ppsspp_md5_starts( &ctx );
|
||||
ppsspp_md5_update( &ctx, input, ilen );
|
||||
ppsspp_md5_finish( &ctx, output );
|
||||
md5_starts( &ctx );
|
||||
md5_update( &ctx, input, ilen );
|
||||
md5_finish( &ctx, output );
|
||||
|
||||
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
|
||||
*/
|
||||
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;
|
||||
unsigned char sum[16];
|
||||
|
||||
if( keylen > 64 )
|
||||
{
|
||||
ppsspp_md5( key, keylen, sum );
|
||||
md5( key, keylen, sum );
|
||||
keylen = 16;
|
||||
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] );
|
||||
}
|
||||
|
||||
ppsspp_md5_starts( ctx );
|
||||
ppsspp_md5_update( ctx, ctx->ipad, 64 );
|
||||
md5_starts( ctx );
|
||||
md5_update( ctx, ctx->ipad, 64 );
|
||||
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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];
|
||||
|
||||
ppsspp_md5_finish( ctx, tmpbuf );
|
||||
ppsspp_md5_starts( ctx );
|
||||
ppsspp_md5_update( ctx, ctx->opad, 64 );
|
||||
ppsspp_md5_update( ctx, tmpbuf, 16 );
|
||||
ppsspp_md5_finish( ctx, output );
|
||||
md5_finish( ctx, tmpbuf );
|
||||
md5_starts( ctx );
|
||||
md5_update( ctx, ctx->opad, 64 );
|
||||
md5_update( ctx, tmpbuf, 16 );
|
||||
md5_finish( ctx, output );
|
||||
|
||||
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 )
|
||||
*/
|
||||
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] )
|
||||
{
|
||||
md5_context ctx;
|
||||
|
||||
ppsspp_md5_hmac_starts( &ctx, key, keylen );
|
||||
ppsspp_md5_hmac_update( &ctx, input, ilen );
|
||||
ppsspp_md5_hmac_finish( &ctx, output );
|
||||
md5_hmac_starts( &ctx, key, keylen );
|
||||
md5_hmac_update( &ctx, input, ilen );
|
||||
md5_hmac_finish( &ctx, output );
|
||||
|
||||
memset( &ctx, 0, sizeof( md5_context ) );
|
||||
}
|
||||
|
@ -464,7 +464,7 @@ static const unsigned char md5_hmac_test_sum[7][16] =
|
|||
/*
|
||||
* Checkup routine
|
||||
*/
|
||||
int ppsspp_md5_self_test( int verbose )
|
||||
int md5_self_test( int verbose )
|
||||
{
|
||||
int i, buflen;
|
||||
unsigned char buf[1024];
|
||||
|
|
|
@ -46,7 +46,7 @@ extern "C" {
|
|||
*
|
||||
* \param ctx context to be initialized
|
||||
*/
|
||||
void ppsspp_md5_starts( md5_context *ctx );
|
||||
void md5_starts( md5_context *ctx );
|
||||
|
||||
/**
|
||||
* \brief MD5 process buffer
|
||||
|
@ -55,7 +55,7 @@ void ppsspp_md5_starts( md5_context *ctx );
|
|||
* \param input buffer holding the 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
|
||||
|
@ -63,7 +63,7 @@ void ppsspp_md5_update( md5_context *ctx, unsigned char *input, int ilen );
|
|||
* \param ctx MD5 context
|
||||
* \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 )
|
||||
|
@ -72,7 +72,7 @@ void ppsspp_md5_finish( md5_context *ctx, unsigned char output[16] );
|
|||
* \param ilen length of the input data
|
||||
* \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 )
|
||||
|
@ -83,7 +83,7 @@ void ppsspp_md5( unsigned char *input, int ilen, unsigned char output[16] );
|
|||
* \return 0 if successful, 1 if fopen 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
|
||||
|
@ -92,7 +92,7 @@ int ppsspp_md5_file( char *path, unsigned char output[16] );
|
|||
* \param key HMAC secret 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
|
||||
|
@ -101,7 +101,7 @@ void ppsspp_md5_hmac_starts( md5_context *ctx, unsigned char *key, int keylen );
|
|||
* \param input buffer holding the 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
|
||||
|
@ -109,7 +109,7 @@ void ppsspp_md5_hmac_update( md5_context *ctx, unsigned char *input, int ilen );
|
|||
* \param ctx HMAC context
|
||||
* \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 )
|
||||
|
@ -120,7 +120,7 @@ void ppsspp_md5_hmac_finish( md5_context *ctx, unsigned char output[16] );
|
|||
* \param ilen length of the input data
|
||||
* \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 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
|
||||
*/
|
||||
int ppsspp_md5_self_test( int verbose );
|
||||
int md5_self_test( int verbose );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -30,11 +30,15 @@ template <class T, int N>
|
|||
class FixedSizeQueue {
|
||||
public:
|
||||
FixedSizeQueue() {
|
||||
// Allocate aligned memory, just because.
|
||||
//int sizeInBytes = N * sizeof(T);
|
||||
//storage_ = (T *)AllocateMemoryPages(sizeInBytes);
|
||||
storage_ = new T[N];
|
||||
clear();
|
||||
}
|
||||
|
||||
~FixedSizeQueue() {
|
||||
// FreeMemoryPages((void *)storage_, N * sizeof(T));
|
||||
delete [] storage_;
|
||||
}
|
||||
|
||||
|
@ -222,3 +226,4 @@ private:
|
|||
volatile int curReadBlock;
|
||||
volatile int curWriteBlock;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
// cache-line as the corresponding key.
|
||||
// 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 {
|
||||
public:
|
||||
DenseHashMap(int initialCapacity) : capacity_(initialCapacity) {
|
||||
|
@ -37,47 +37,26 @@ public:
|
|||
state.resize(initialCapacity);
|
||||
}
|
||||
|
||||
// Returns true if the entry was found, and writes the entry to *value.
|
||||
// Returns false and does not write to value if no entry was found.
|
||||
// Note that nulls can be stored.
|
||||
bool Get(const Key &key, Value *value) const {
|
||||
// Returns nullptr if no entry was found.
|
||||
Value Get(const Key &key) {
|
||||
uint32_t mask = capacity_ - 1;
|
||||
uint32_t pos = HashKey(key) & mask;
|
||||
// No? Let's go into search mode. Linear probing.
|
||||
uint32_t p = pos;
|
||||
while (true) {
|
||||
if (state[p] == BucketState::TAKEN && KeyEquals(key, map[p].key)) {
|
||||
*value = map[p].value;
|
||||
return true;
|
||||
} else if (state[p] == BucketState::FREE) {
|
||||
return false;
|
||||
}
|
||||
if (state[p] == BucketState::TAKEN && KeyEquals(key, map[p].key))
|
||||
return map[p].value;
|
||||
else if (state[p] == BucketState::FREE)
|
||||
return NullValue;
|
||||
p = (p + 1) & mask; // If the state is REMOVED, we just keep on walking.
|
||||
if (p == pos) {
|
||||
// We looped around the whole map.
|
||||
_assert_msg_(false, "DenseHashMap: Hit full on Get()");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return NullValue;
|
||||
}
|
||||
|
||||
// Only works if Value can be nullptr
|
||||
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!
|
||||
// Returns false if we already had the key! Which is a bit different.
|
||||
bool Insert(const Key &key, Value value) {
|
||||
// Check load factor, resize if necessary. We never shrink.
|
||||
if (count_ > capacity_ / 2) {
|
||||
|
@ -135,7 +114,6 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
// This will never crash if you call it without locking - but, the value might not be right.
|
||||
size_t size() const {
|
||||
return count_;
|
||||
}
|
||||
|
@ -212,7 +190,7 @@ private:
|
|||
|
||||
// Like the above, uses linear probing for cache-friendliness.
|
||||
// Does not perform hashing at all so expects well-distributed keys.
|
||||
template <class Value>
|
||||
template <class Value, Value NullValue>
|
||||
class PrehashMap {
|
||||
public:
|
||||
PrehashMap(int initialCapacity) : capacity_(initialCapacity) {
|
||||
|
@ -221,24 +199,22 @@ public:
|
|||
}
|
||||
|
||||
// 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 pos = hash & mask;
|
||||
// No? Let's go into search mode. Linear probing.
|
||||
uint32_t p = pos;
|
||||
while (true) {
|
||||
if (state[p] == BucketState::TAKEN && hash == map[p].hash) {
|
||||
*value = map[p].value;
|
||||
return true;
|
||||
} else if (state[p] == BucketState::FREE) {
|
||||
return false;
|
||||
}
|
||||
if (state[p] == BucketState::TAKEN && hash == map[p].hash)
|
||||
return map[p].value;
|
||||
else if (state[p] == BucketState::FREE)
|
||||
return NullValue;
|
||||
p = (p + 1) & mask; // If the state is REMOVED, we just keep on walking.
|
||||
if (p == pos) {
|
||||
_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.
|
||||
|
|
|
@ -134,68 +134,3 @@ private:
|
|||
T fastLookup_[MaxFastSize];
|
||||
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];
|
||||
};
|
||||
|
|
|
@ -617,7 +617,6 @@ void ConvertRGB565ToBGR565(u16 *dst, const u16 *src, u32 numPixels) {
|
|||
u32 i = 0;
|
||||
#endif
|
||||
|
||||
// TODO: Add a 64-bit loop too.
|
||||
const u32 *src32 = (const u32 *)src;
|
||||
u32 *dst32 = (u32 *)dst;
|
||||
for (; i < numPixels / 2; i++) {
|
||||
|
|
|
@ -219,15 +219,13 @@ int u8_strlen(const char *s)
|
|||
}
|
||||
|
||||
/* 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;
|
||||
int sz = 0;
|
||||
int i = *index;
|
||||
do {
|
||||
ch = (ch << 6) + (unsigned char)s[i++];
|
||||
ch = (ch << 6) + (unsigned char)s[(*i)++];
|
||||
sz++;
|
||||
} while (s[i] && ((s[i]) & 0xC0) == 0x80);
|
||||
*index = i;
|
||||
} while (s[*i] && ((s[*i]) & 0xC0) == 0x80);
|
||||
return ch - offsetsFromUTF8[sz - 1];
|
||||
}
|
||||
|
||||
|
@ -428,17 +426,6 @@ int u8_is_locale_utf8(const char *locale)
|
|||
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) {
|
||||
UTF8 utf(utf8string);
|
||||
int count = 0;
|
||||
|
@ -571,12 +558,6 @@ std::u16string ConvertUTF8ToUCS2(const std::string &source) {
|
|||
return dst;
|
||||
}
|
||||
|
||||
std::string CodepointToUTF8(uint32_t codePoint) {
|
||||
char temp[16]{};
|
||||
UTF8::encode(temp, codePoint);
|
||||
return std::string(temp);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
// Replacements for the Win32 wstring functions. Not to be used from emulation code!
|
||||
|
|
|
@ -26,15 +26,6 @@ int u8_strlen(const char *s);
|
|||
void u8_inc(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 {
|
||||
public:
|
||||
static const uint32_t INVALID = (uint32_t)-1;
|
||||
|
@ -98,8 +89,6 @@ bool UTF8StringHasNonASCII(const char *utf8string);
|
|||
// Removes overlong encodings and similar.
|
||||
std::string SanitizeUTF8(const std::string &utf8string);
|
||||
|
||||
std::string CodepointToUTF8(uint32_t codePoint);
|
||||
|
||||
|
||||
// UTF8 to Win32 UTF-16
|
||||
// Should be used when calling Win32 api calls
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
#include "Common/Data/Format/DDSLoad.h"
|
||||
|
||||
bool DetectDDSParams(const DDSHeader *header, DDSLoadInfo *info) {
|
||||
return false;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -5,9 +5,6 @@
|
|||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
// Hm, what's this for?
|
||||
#ifndef _MSC_VER
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
@ -20,17 +17,17 @@
|
|||
#include <vector>
|
||||
|
||||
#include "Common/Data/Format/IniFile.h"
|
||||
#include "Common/Data/Text/Parsers.h"
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
#include "Common/File/FileUtil.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/Math/math_util.h"
|
||||
#include "Common/Data/Text/Parsers.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Common/Data/Encoding/Utf8.h"
|
||||
#endif
|
||||
|
||||
#include "Common/StringUtils.h"
|
||||
|
||||
// This unescapes # signs.
|
||||
// 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) {
|
||||
static bool ParseLineKey(const std::string &line, size_t &pos, std::string *keyOut) {
|
||||
std::string key = "";
|
||||
|
||||
while (pos < line.size()) {
|
||||
|
@ -45,8 +42,7 @@ static bool ParseLineKey(std::string_view line, size_t &pos, std::string *keyOut
|
|||
}
|
||||
|
||||
// Escaped.
|
||||
key += line.substr(pos, next - pos - 1);
|
||||
key.push_back('#');
|
||||
key += line.substr(pos, next - pos - 1) + "#";
|
||||
pos = next + 1;
|
||||
} else if (line[next] == '=') {
|
||||
// Hurray, done.
|
||||
|
@ -62,11 +58,11 @@ static bool ParseLineKey(std::string_view line, size_t &pos, std::string *keyOut
|
|||
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_view strippedLine = StripSpaces(line.substr(pos));
|
||||
if (strippedLine.size() >= 2 && strippedLine[0] == '"' && strippedLine[strippedLine.size() - 1] == '"') {
|
||||
std::string strippedLine = StripSpaces(line.substr(pos));
|
||||
if (strippedLine[0] == '"' && strippedLine[strippedLine.size()-1] == '"') {
|
||||
// Don't remove comment if is surrounded by " "
|
||||
value += line.substr(pos);
|
||||
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;
|
||||
} else {
|
||||
// Escaped.
|
||||
value += line.substr(pos, next - pos - 1);
|
||||
value.push_back('#');
|
||||
value += line.substr(pos, next - pos - 1) + "#";
|
||||
pos = next + 1;
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +94,7 @@ static bool ParseLineValue(std::string_view line, size_t &pos, std::string *valu
|
|||
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.
|
||||
if (commentOut) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
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:
|
||||
// 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;
|
||||
}
|
||||
|
||||
static std::string EscapeHash(std::string_view value) {
|
||||
static std::string EscapeComments(const std::string &value) {
|
||||
std::string result = "";
|
||||
|
||||
for (size_t pos = 0; pos < value.size(); ) {
|
||||
|
@ -153,8 +149,7 @@ static std::string EscapeHash(std::string_view value) {
|
|||
result += value.substr(pos);
|
||||
pos = value.npos;
|
||||
} else {
|
||||
result += value.substr(pos, next - pos);
|
||||
result += "\\#";
|
||||
result += value.substr(pos, next - pos) + "\\#";
|
||||
pos = next + 1;
|
||||
}
|
||||
}
|
||||
|
@ -162,56 +157,34 @@ static std::string EscapeHash(std::string_view value) {
|
|||
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() {
|
||||
lines_.clear();
|
||||
lines.clear();
|
||||
}
|
||||
|
||||
bool Section::GetKeys(std::vector<std::string> &keys) const {
|
||||
keys.clear();
|
||||
for (auto liter = lines_.begin(); liter != lines_.end(); ++liter) {
|
||||
if (!liter->Key().empty())
|
||||
keys.push_back(std::string(liter->Key()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ParsedIniLine *Section::GetLine(const char *key) {
|
||||
for (auto &line : lines_) {
|
||||
if (equalsNoCase(line.Key(), key))
|
||||
std::string* Section::GetLine(const char* key, std::string* valueOut, std::string* commentOut)
|
||||
{
|
||||
for (std::vector<std::string>::iterator iter = lines.begin(); iter != lines.end(); ++iter)
|
||||
{
|
||||
std::string& line = *iter;
|
||||
std::string lineKey;
|
||||
ParseLine(line, &lineKey, valueOut, commentOut);
|
||||
if (!strcasecmp(lineKey.c_str(), key))
|
||||
return &line;
|
||||
}
|
||||
return nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const ParsedIniLine *Section::GetLine(const char* key) const {
|
||||
for (auto &line : lines_) {
|
||||
if (equalsNoCase(line.Key(), key))
|
||||
const std::string* Section::GetLine(const char* key, std::string* valueOut, std::string* commentOut) const
|
||||
{
|
||||
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 nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
_dbg_assert_(!my_isnanorinf(newValue));
|
||||
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());
|
||||
}
|
||||
|
||||
void Section::Set(const char* key, const char* newValue) {
|
||||
ParsedIniLine *line = GetLine(key);
|
||||
if (line) {
|
||||
line->SetValue(newValue);
|
||||
} else {
|
||||
void Section::Set(const char* key, const char* newValue)
|
||||
{
|
||||
std::string value, commented;
|
||||
std::string* line = GetLine(key, &value, &commented);
|
||||
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.
|
||||
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);
|
||||
}
|
||||
|
||||
bool Section::Get(const char* key, std::string* value, const char* defaultValue) const {
|
||||
const ParsedIniLine *line = GetLine(key);
|
||||
if (!line) {
|
||||
if (defaultValue) {
|
||||
bool Section::Get(const char* key, std::string* value, const char* defaultValue) const
|
||||
{
|
||||
const std::string* line = GetLine(key, value, 0);
|
||||
if (!line)
|
||||
{
|
||||
if (defaultValue)
|
||||
{
|
||||
*value = defaultValue;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
*value = line->Value();
|
||||
}
|
||||
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) {
|
||||
lines_.emplace_back(ParsedIniLine::CommentOnly("# " + comment));
|
||||
lines.emplace_back("# " + comment);
|
||||
}
|
||||
|
||||
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;
|
||||
bool retval = Get(key, &temp, 0);
|
||||
if (retval && TryParse(temp, value))
|
||||
if (retval && TryParse(temp.c_str(), value))
|
||||
return true;
|
||||
*value = defaultValue;
|
||||
return false;
|
||||
|
@ -371,7 +350,7 @@ bool Section::Get(const char* key, bool* value, bool defaultValue) const
|
|||
{
|
||||
std::string temp;
|
||||
bool retval = Get(key, &temp, 0);
|
||||
if (retval && TryParse(temp, value))
|
||||
if (retval && TryParse(temp.c_str(), value))
|
||||
return true;
|
||||
*value = defaultValue;
|
||||
return false;
|
||||
|
@ -381,7 +360,7 @@ bool Section::Get(const char* key, float* value, float defaultValue) const
|
|||
{
|
||||
std::string temp;
|
||||
bool retval = Get(key, &temp, 0);
|
||||
if (retval && TryParse(temp, value))
|
||||
if (retval && TryParse(temp.c_str(), value))
|
||||
return true;
|
||||
*value = defaultValue;
|
||||
return false;
|
||||
|
@ -391,35 +370,46 @@ bool Section::Get(const char* key, double* value, double defaultValue) const
|
|||
{
|
||||
std::string temp;
|
||||
bool retval = Get(key, &temp, 0);
|
||||
if (retval && TryParse(temp, value))
|
||||
if (retval && TryParse(temp.c_str(), value))
|
||||
return true;
|
||||
*value = defaultValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Section::Exists(const char *key) const {
|
||||
for (auto &line : lines_) {
|
||||
if (equalsNoCase(key, line.Key()))
|
||||
bool Section::Exists(const char *key) const
|
||||
{
|
||||
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 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;
|
||||
for (auto &line : lines_) {
|
||||
if (!line.Key().empty()) {
|
||||
outMap[std::string(line.Key())] = line.Value();
|
||||
for (std::vector<std::string>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter)
|
||||
{
|
||||
std::string lineKey, lineValue;
|
||||
if (ParseLine(*iter, &lineKey, &lineValue, NULL)) {
|
||||
outMap[lineKey] = lineValue;
|
||||
}
|
||||
}
|
||||
return outMap;
|
||||
}
|
||||
|
||||
bool Section::Delete(const char *key) {
|
||||
ParsedIniLine *line = GetLine(key);
|
||||
for (auto liter = lines_.begin(); liter != lines_.end(); ++liter) {
|
||||
if (line == &*liter) {
|
||||
lines_.erase(liter);
|
||||
|
||||
bool Section::Delete(const char *key)
|
||||
{
|
||||
std::string* line = GetLine(key, 0, 0);
|
||||
for (std::vector<std::string>::iterator liter = lines.begin(); liter != lines.end(); ++liter)
|
||||
{
|
||||
if (line == &*liter)
|
||||
{
|
||||
lines.erase(liter);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -428,36 +418,42 @@ bool Section::Delete(const char *key) {
|
|||
|
||||
// IniFile
|
||||
|
||||
const Section* IniFile::GetSection(const char* sectionName) const {
|
||||
for (const auto &iter : sections)
|
||||
const Section* IniFile::GetSection(const char* sectionName) const
|
||||
{
|
||||
for (std::vector<Section>::const_iterator iter = sections.begin(); iter != sections.end(); ++iter)
|
||||
if (!strcasecmp(iter->name().c_str(), sectionName))
|
||||
return iter.get();
|
||||
return nullptr;
|
||||
return (&(*iter));
|
||||
return 0;
|
||||
}
|
||||
|
||||
Section* IniFile::GetSection(const char* sectionName) {
|
||||
for (const auto &iter : sections)
|
||||
Section* IniFile::GetSection(const char* sectionName)
|
||||
{
|
||||
for (std::vector<Section>::iterator iter = sections.begin(); iter != sections.end(); ++iter)
|
||||
if (!strcasecmp(iter->name().c_str(), sectionName))
|
||||
return iter.get();
|
||||
return nullptr;
|
||||
return (&(*iter));
|
||||
return 0;
|
||||
}
|
||||
|
||||
Section* IniFile::GetOrCreateSection(const char* sectionName) {
|
||||
Section* IniFile::GetOrCreateSection(const char* sectionName)
|
||||
{
|
||||
Section* section = GetSection(sectionName);
|
||||
if (!section) {
|
||||
sections.push_back(std::unique_ptr<Section>(new Section(sectionName)));
|
||||
section = sections.back().get();
|
||||
if (!section)
|
||||
{
|
||||
sections.push_back(Section(sectionName));
|
||||
section = §ions[sections.size() - 1];
|
||||
}
|
||||
return section;
|
||||
}
|
||||
|
||||
bool IniFile::DeleteSection(const char* sectionName) {
|
||||
bool IniFile::DeleteSection(const char* sectionName)
|
||||
{
|
||||
Section* s = GetSection(sectionName);
|
||||
if (!s)
|
||||
return false;
|
||||
|
||||
for (auto iter = sections.begin(); iter != sections.end(); ++iter) {
|
||||
if (iter->get() == s) {
|
||||
for (std::vector<Section>::iterator iter = sections.begin(); iter != sections.end(); ++iter)
|
||||
{
|
||||
if (&(*iter) == s)
|
||||
{
|
||||
sections.erase(iter);
|
||||
return true;
|
||||
}
|
||||
|
@ -465,21 +461,35 @@ bool IniFile::DeleteSection(const char* sectionName) {
|
|||
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);
|
||||
if (!section)
|
||||
return false;
|
||||
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);
|
||||
if (!section)
|
||||
return false;
|
||||
ParsedIniLine *line = section->GetLine(key);
|
||||
for (auto liter = section->lines_.begin(); liter != section->lines_.end(); ++liter) {
|
||||
if (line == &(*liter)) {
|
||||
section->lines_.erase(liter);
|
||||
std::string* line = section->GetLine(key, 0, 0);
|
||||
for (std::vector<std::string>::iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter)
|
||||
{
|
||||
if (line == &(*liter))
|
||||
{
|
||||
section->lines.erase(liter);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -487,12 +497,54 @@ bool IniFile::DeleteKey(const char* sectionName, const char* key) {
|
|||
}
|
||||
|
||||
// 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);
|
||||
if (!section)
|
||||
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()
|
||||
{
|
||||
|
@ -502,7 +554,7 @@ void IniFile::SortSections()
|
|||
bool IniFile::Load(const Path &path)
|
||||
{
|
||||
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
|
||||
|
||||
// Open file
|
||||
|
@ -515,9 +567,9 @@ bool IniFile::Load(const Path &path)
|
|||
return success;
|
||||
}
|
||||
|
||||
bool IniFile::LoadFromVFS(VFSInterface &vfs, const std::string &filename) {
|
||||
bool IniFile::LoadFromVFS(const std::string &filename) {
|
||||
size_t size;
|
||||
uint8_t *data = vfs.ReadFile(filename.c_str(), &size);
|
||||
uint8_t *data = VFSReadFile(filename.c_str(), &size);
|
||||
if (!data)
|
||||
return false;
|
||||
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) {
|
||||
// Maximum number of letters in a line
|
||||
static const int MAX_BYTES = 1024*32;
|
||||
char *templine = new char[MAX_BYTES]; // avoid using up massive stack space
|
||||
|
||||
while (!(in.eof() || in.fail()))
|
||||
{
|
||||
char templine[MAX_BYTES];
|
||||
in.getline(templine, MAX_BYTES);
|
||||
std::string line = templine;
|
||||
|
||||
|
@ -558,23 +610,20 @@ bool IniFile::Load(std::istream &in) {
|
|||
if (sectionNameEnd != std::string::npos) {
|
||||
// New section!
|
||||
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()) {
|
||||
sections.back()->comment = line.substr(sectionNameEnd + 1);
|
||||
sections[sections.size() - 1].comment = line.substr(sectionNameEnd + 1);
|
||||
}
|
||||
} else {
|
||||
if (sections.empty()) {
|
||||
sections.push_back(std::unique_ptr<Section>(new Section("")));
|
||||
sections.push_back(Section(""));
|
||||
}
|
||||
ParsedIniLine parsedLine;
|
||||
parsedLine.ParseFrom(line);
|
||||
sections.back()->lines_.push_back(parsedLine);
|
||||
sections[sections.size() - 1].lines.push_back(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] templine;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -589,14 +638,13 @@ bool IniFile::Save(const Path &filename)
|
|||
// TODO: Do we still need this? It's annoying.
|
||||
fprintf(file, "\xEF\xBB\xBF");
|
||||
|
||||
for (const auto §ion : sections) {
|
||||
if (!section->name().empty() && (!section->lines_.empty() || !section->comment.empty())) {
|
||||
fprintf(file, "[%s]%s\n", section->name().c_str(), section->comment.c_str());
|
||||
for (const Section §ion : sections) {
|
||||
if (!section.name().empty() && (!section.lines.empty() || !section.comment.empty())) {
|
||||
fprintf(file, "[%s]%s\n", section.name().c_str(), section.comment.c_str());
|
||||
}
|
||||
for (const auto &line : section->lines_) {
|
||||
std::string buffer;
|
||||
line.Reconstruct(&buffer);
|
||||
fprintf(file, "%s\n", buffer.c_str());
|
||||
|
||||
for (const std::string &s : section.lines) {
|
||||
fprintf(file, "%s\n", s.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,50 +5,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
#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 {
|
||||
friend class IniFile;
|
||||
|
||||
|
@ -63,8 +25,8 @@ public:
|
|||
|
||||
std::map<std::string, std::string> ToMap() const;
|
||||
|
||||
ParsedIniLine *GetLine(const char *key);
|
||||
const ParsedIniLine *GetLine(const char *key) const;
|
||||
std::string *GetLine(const char* key, std::string* valueOut, std::string* commentOut);
|
||||
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 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, 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 {
|
||||
return name_ < other.name_;
|
||||
}
|
||||
|
@ -117,7 +76,7 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
std::vector<ParsedIniLine> lines_;
|
||||
std::vector<std::string> lines;
|
||||
std::string name_;
|
||||
std::string comment;
|
||||
};
|
||||
|
@ -125,10 +84,12 @@ protected:
|
|||
class IniFile {
|
||||
public:
|
||||
bool Load(const Path &path);
|
||||
bool Load(const std::string &filename) { return Load(Path(filename)); }
|
||||
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 std::string &filename) { return Save(Path(filename)); }
|
||||
|
||||
// Returns true if key exists in section
|
||||
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;
|
||||
|
||||
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 DeleteSection(const char* sectionName);
|
||||
|
||||
void SortSections();
|
||||
|
||||
std::vector<std::unique_ptr<Section>> &Sections() { return sections; }
|
||||
std::vector<Section> &Sections() { return sections; }
|
||||
|
||||
bool HasSection(const char *section) { return GetSection(section) != 0; }
|
||||
|
||||
Section* GetOrCreateSection(const char* section);
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<Section>> sections;
|
||||
std::vector<Section> sections;
|
||||
|
||||
const Section* GetSection(const char* section) const;
|
||||
Section* GetSection(const char* section);
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace json {
|
|||
|
||||
JsonReader::JsonReader(const std::string &filename) {
|
||||
size_t buf_size;
|
||||
buffer_ = (char *)g_VFS.ReadFile(filename.c_str(), &buf_size);
|
||||
buffer_ = (char *)VFSReadFile(filename.c_str(), &buf_size);
|
||||
if (buffer_) {
|
||||
parse();
|
||||
} else {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
//
|
||||
// Zero dependencies apart from stdlib (if you remove the vhjson usage.)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
|
|
@ -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);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Common/BitSet.h"
|
||||
#ifndef _PNG_LOAD_H
|
||||
#define _PNG_LOAD_H
|
||||
|
||||
// *image_data_ptr should be deleted with free()
|
||||
// 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 *pheight, unsigned char **image_data_ptr);
|
||||
|
||||
// PNG peeker - just read the start of a PNG straight into this struct, in order to
|
||||
// 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); }
|
||||
};
|
||||
#endif // _PNG_LOAD_H
|
||||
|
|
|
@ -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) {
|
||||
size_t size;
|
||||
uint8_t *buffer = g_VFS.ReadFile(filename, &size);
|
||||
uint8_t *buffer = VFSReadFile(filename, &size);
|
||||
if (!buffer) {
|
||||
ERROR_LOG(IO, "Couldn't read data for '%s'", filename);
|
||||
return 0;
|
||||
|
|
|
@ -1,88 +1,38 @@
|
|||
#include <cstring>
|
||||
|
||||
#include "Common/Data/Text/I18n.h"
|
||||
#include "Common/Data/Format/IniFile.h"
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
#include "Common/Log.h"
|
||||
|
||||
#include "Common/StringUtils.h"
|
||||
|
||||
static const char * const g_categoryNames[(size_t)I18NCat::CATEGORY_COUNT] = {
|
||||
"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 i18nrepo;
|
||||
|
||||
I18NRepo g_i18nrepo;
|
||||
I18NRepo::~I18NRepo() {
|
||||
Clear();
|
||||
}
|
||||
|
||||
std::string I18NRepo::LanguageID() {
|
||||
return languageID_;
|
||||
}
|
||||
|
||||
I18NRepo::I18NRepo() {
|
||||
Clear();
|
||||
}
|
||||
|
||||
void I18NRepo::Clear() {
|
||||
std::lock_guard<std::mutex> guard(catsLock_);
|
||||
for (auto &iter : cats_) {
|
||||
// Initialize with empty categories, so that early lookups don't crash.
|
||||
iter = std::shared_ptr<I18NCategory>(new I18NCategory());
|
||||
for (auto iter = cats_.begin(); iter != cats_.end(); ++iter) {
|
||||
iter->second.reset();
|
||||
}
|
||||
}
|
||||
|
||||
I18NCategory::I18NCategory(const Section §ion) {
|
||||
std::map<std::string, std::string> sectionMap = section.ToMap();
|
||||
SetMap(sectionMap);
|
||||
}
|
||||
|
||||
void I18NCategory::Clear() {
|
||||
map_.clear();
|
||||
missedKeyLog_.clear();
|
||||
cats_.clear();
|
||||
}
|
||||
|
||||
const char *I18NCategory::T(const char *key, const char *def) {
|
||||
if (!key) {
|
||||
return "ERROR";
|
||||
}
|
||||
|
||||
// Replace the \n's with \\n's so that key values with newlines will be found correctly.
|
||||
std::string modifiedKey = key;
|
||||
modifiedKey = ReplaceAll(modifiedKey, "\n", "\\n");
|
||||
|
||||
auto iter = map_.find(modifiedKey);
|
||||
if (iter != map_.end()) {
|
||||
// INFO_LOG(SYSTEM, "translation key found in %s: %s", name_.c_str(), key);
|
||||
return iter->second.text.c_str();
|
||||
} else {
|
||||
std::lock_guard<std::mutex> guard(missedKeyLock_);
|
||||
|
@ -90,6 +40,7 @@ const char *I18NCategory::T(const char *key, const char *def) {
|
|||
missedKeyLog_[key] = def;
|
||||
else
|
||||
missedKeyLog_[key] = modifiedKey;
|
||||
// INFO_LOG(SYSTEM, "Missed translation key in %s: %s", name_.c_str(), 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()) {
|
||||
std::string text = ReplaceAll(iter->second, "\\n", "\n");
|
||||
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_);
|
||||
if (category != I18NCat::NONE)
|
||||
return cats_[(size_t)category];
|
||||
else
|
||||
return nullptr;
|
||||
auto iter = cats_.find(category);
|
||||
if (iter != cats_.end()) {
|
||||
return iter->second;
|
||||
} else {
|
||||
I18NCategory *c = new I18NCategory(this, category);
|
||||
cats_[category].reset(c);
|
||||
return cats_[category];
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
File::FileInfo info;
|
||||
if (!g_VFS.GetFileInfo(GetIniPath(languageID).ToString().c_str(), &info))
|
||||
if (!VFSGetFileInfo(GetIniPath(languageID).ToString().c_str(), &info))
|
||||
return false;
|
||||
if (!info.exists)
|
||||
return false;
|
||||
|
@ -135,19 +91,17 @@ bool I18NRepo::LoadIni(const std::string &languageID, const Path &overridePath)
|
|||
iniPath = GetIniPath(languageID);
|
||||
}
|
||||
|
||||
if (!ini.LoadFromVFS(g_VFS, iniPath.ToString()))
|
||||
if (!ini.LoadFromVFS(iniPath.ToString()))
|
||||
return false;
|
||||
|
||||
Clear();
|
||||
|
||||
const std::vector<std::unique_ptr<Section>> §ions = ini.Sections();
|
||||
const std::vector<Section> §ions = ini.Sections();
|
||||
|
||||
std::lock_guard<std::mutex> guard(catsLock_);
|
||||
for (auto §ion : sections) {
|
||||
for (size_t i = 0; i < (size_t)I18NCat::CATEGORY_COUNT; i++) {
|
||||
if (!strcmp(section->name().c_str(), g_categoryNames[i])) {
|
||||
cats_[i].reset(new I18NCategory(*section.get()));
|
||||
}
|
||||
for (auto iter = sections.begin(); iter != sections.end(); ++iter) {
|
||||
if (iter->name() != "") {
|
||||
cats_[iter->name()].reset(LoadSection(&(*iter), iter->name().c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,21 +109,53 @@ bool I18NRepo::LoadIni(const std::string &languageID, const Path &overridePath)
|
|||
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_);
|
||||
for (size_t i = 0; i < (size_t)I18NCat::CATEGORY_COUNT; i++) {
|
||||
auto &cat = cats_[i];
|
||||
for (auto &key : cat->Missed()) {
|
||||
INFO_LOG(SYSTEM, "Missing translation [%s]: %s (%s)", g_categoryNames[i], key.first.c_str(), key.second.c_str());
|
||||
for (auto &cat : cats_) {
|
||||
for (auto &key : cat.second->Missed()) {
|
||||
ret[cat.first].push_back(key.first);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
I18NCategory *I18NRepo::LoadSection(const Section *section, const char *name) {
|
||||
I18NCategory *cat = new I18NCategory(this, name);
|
||||
std::map<std::string, std::string> sectionMap = section->ToMap();
|
||||
cat->SetMap(sectionMap);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<I18NCategory> GetI18NCategory(I18NCat category) {
|
||||
if (category == I18NCat::NONE) {
|
||||
return std::shared_ptr<I18NCategory>();
|
||||
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);
|
||||
}
|
||||
std::shared_ptr<I18NCategory> cat = g_i18nrepo.GetCategory(category);
|
||||
_dbg_assert_(cat);
|
||||
return cat;
|
||||
|
||||
cat->ClearMissed();
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
// As usual, everything is UTF-8. Nothing else allowed.
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/File/Path.h"
|
||||
|
@ -23,45 +23,6 @@ class I18NRepo;
|
|||
class IniFile;
|
||||
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 {
|
||||
I18NEntry(const std::string &t) : text(t), readFlag(false) {}
|
||||
I18NEntry() : readFlag(false) {}
|
||||
|
@ -78,10 +39,9 @@ struct I18NCandidate {
|
|||
|
||||
class I18NCategory {
|
||||
public:
|
||||
I18NCategory() {}
|
||||
explicit I18NCategory(const Section §ion);
|
||||
|
||||
const char *T(const char *key, const char *def = nullptr);
|
||||
// NOTE: Name must be a global constant string - it is not copied.
|
||||
I18NCategory(const char *name) : name_(name) {}
|
||||
const char *T(const char *key, const char *def = 0);
|
||||
const char *T(const std::string &key) {
|
||||
return T(key.c_str(), nullptr);
|
||||
}
|
||||
|
@ -93,55 +53,76 @@ public:
|
|||
|
||||
const std::map<std::string, I18NEntry> &GetMap() { return map_; }
|
||||
void ClearMissed() { missedKeyLog_.clear(); }
|
||||
void Clear();
|
||||
const char *GetName() const { return name_.c_str(); }
|
||||
|
||||
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);
|
||||
|
||||
std::string name_;
|
||||
|
||||
std::map<std::string, I18NEntry> map_;
|
||||
mutable std::mutex missedKeyLock_;
|
||||
std::map<std::string, std::string> missedKeyLog_;
|
||||
|
||||
// Noone else can create these.
|
||||
friend class I18NRepo;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(I18NCategory);
|
||||
};
|
||||
|
||||
class I18NRepo {
|
||||
public:
|
||||
I18NRepo();
|
||||
I18NRepo() {}
|
||||
~I18NRepo();
|
||||
|
||||
bool IniExists(const std::string &languageID) const;
|
||||
bool LoadIni(const std::string &languageID, const Path &overridePath = Path()); // NOT the filename!
|
||||
void SaveIni(const std::string &languageID);
|
||||
|
||||
std::string LanguageID();
|
||||
|
||||
std::shared_ptr<I18NCategory> GetCategory(I18NCat category);
|
||||
|
||||
// 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.
|
||||
// def can (and usually is) set to nullptr.
|
||||
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);
|
||||
std::shared_ptr<I18NCategory> GetCategory(const char *categoryName);
|
||||
bool HasCategory(const char *categoryName) const {
|
||||
std::lock_guard<std::mutex> guard(catsLock_);
|
||||
return cats_.find(categoryName) != cats_.end();
|
||||
}
|
||||
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:
|
||||
Path GetIniPath(const std::string &languageID) const;
|
||||
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_;
|
||||
std::shared_ptr<I18NCategory> cats_[(size_t)I18NCat::CATEGORY_COUNT];
|
||||
std::map<std::string, std::shared_ptr<I18NCategory>> cats_;
|
||||
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.
|
||||
|
||||
std::shared_ptr<I18NCategory> GetI18NCategory(I18NCat cat);
|
||||
|
||||
inline const char *T(I18NCat category, const char *key, const char *def = nullptr) {
|
||||
return g_i18nrepo.T(category, key, def);
|
||||
inline std::shared_ptr<I18NCategory> GetI18NCategory(const char *categoryName) {
|
||||
if (!categoryName)
|
||||
return nullptr;
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ void NiceSizeFormat(uint64_t size, char *out, size_t bufSize) {
|
|||
if (s == 0)
|
||||
snprintf(out, bufSize, "%d B", (int)size);
|
||||
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) {
|
||||
|
@ -43,7 +43,7 @@ bool Version::ParseVersionString(std::string str) {
|
|||
|
||||
std::string Version::ToString() const {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -177,18 +177,20 @@ void WordWrapper::AppendWord(int endIndex, int lastChar, bool addNewline) {
|
|||
}
|
||||
|
||||
void WordWrapper::Wrap() {
|
||||
out_.clear();
|
||||
|
||||
// First, let's check if it fits as-is.
|
||||
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 it fits, we don't need to go through each character.
|
||||
out_ = str_;
|
||||
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) {
|
||||
ellipsisWidth_ = MeasureWidth("...", 3);
|
||||
}
|
||||
|
|
|
@ -18,14 +18,12 @@
|
|||
#include "ppsspp_config.h"
|
||||
#if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64)
|
||||
#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
|
||||
#elif PPSSPP_ARCH(MIPS) || PPSSPP_ARCH(MIPS64)
|
||||
#define REAL_CPUDETECT_AVAIL 1
|
||||
#elif PPSSPP_ARCH(RISCV64)
|
||||
#define REAL_CPUDETECT_AVAIL 1
|
||||
#elif PPSSPP_ARCH(LOONGARCH64)
|
||||
#define REAL_CPUDETECT_AVAIL 1
|
||||
#endif
|
||||
|
||||
#ifndef REAL_CPUDETECT_AVAIL
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
};
|
|
@ -1,5 +1,3 @@
|
|||
#include <inttypes.h>
|
||||
|
||||
#include "Common/File/AndroidStorage.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/Log.h"
|
||||
|
@ -61,16 +59,16 @@ void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj) {
|
|||
_dbg_assert_(computeRecursiveDirectorySize);
|
||||
}
|
||||
|
||||
bool Android_IsContentUri(std::string_view filename) {
|
||||
bool Android_IsContentUri(const std::string &filename) {
|
||||
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) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string fname(filename);
|
||||
std::string fname = filename;
|
||||
// PPSSPP adds an ending slash to directories before looking them up.
|
||||
// TODO: Fix that in the caller (or don't call this for directories).
|
||||
if (fname.back() == '/')
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "Common/File/DirListing.h"
|
||||
|
||||
|
@ -40,8 +39,8 @@ extern std::string g_externalDir;
|
|||
|
||||
void Android_StorageSetNativeActivity(jobject nativeActivity);
|
||||
|
||||
bool Android_IsContentUri(std::string_view uri);
|
||||
int Android_OpenContentUriFd(std::string_view uri, const Android_OpenContentUriMode mode);
|
||||
bool Android_IsContentUri(const std::string &uri);
|
||||
int Android_OpenContentUriFd(const std::string &uri, const Android_OpenContentUriMode mode);
|
||||
StorageError Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName);
|
||||
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);
|
||||
|
@ -64,8 +63,8 @@ void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj);
|
|||
|
||||
// Stub out the Android Storage wrappers, so that we can avoid ifdefs everywhere.
|
||||
|
||||
inline bool Android_IsContentUri(std::string_view uri) { return false; }
|
||||
inline int Android_OpenContentUriFd(std::string_view uri, const Android_OpenContentUriMode mode) { return -1; }
|
||||
inline bool Android_IsContentUri(const std::string &uri) { return false; }
|
||||
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_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; }
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include <direct.h>
|
||||
#if PPSSPP_PLATFORM(UWP)
|
||||
#include <fileapifromapp.h>
|
||||
#include <UWP/UWPHelpers/StorageManager.h>
|
||||
#endif
|
||||
#else
|
||||
#include <strings.h>
|
||||
|
@ -184,7 +183,7 @@ bool GetFilesInDir(const Path &directory, std::vector<FileInfo> *files, const ch
|
|||
std::string tmp;
|
||||
while (*filter) {
|
||||
if (*filter == ':') {
|
||||
filters.insert(tmp);
|
||||
filters.insert(std::move(tmp));
|
||||
tmp.clear();
|
||||
} else {
|
||||
tmp.push_back(*filter);
|
||||
|
@ -192,7 +191,7 @@ bool GetFilesInDir(const Path &directory, std::vector<FileInfo> *files, const ch
|
|||
filter++;
|
||||
}
|
||||
if (!tmp.empty())
|
||||
filters.insert(tmp);
|
||||
filters.insert(std::move(tmp));
|
||||
}
|
||||
|
||||
#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);
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
do {
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "Common/File/Path.h"
|
||||
|
||||
|
|
|
@ -23,19 +23,9 @@
|
|||
#include "Common/File/AndroidStorage.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) {
|
||||
#ifdef _WIN32
|
||||
ULARGE_INTEGER free;
|
||||
#if PPSSPP_PLATFORM(UWP)
|
||||
if (GetDriveFreeSpace(path, space)) {
|
||||
return true;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (GetDiskFreeSpaceExW(path.ToWString().c_str(), &free, nullptr, nullptr)) {
|
||||
space = free.QuadPart;
|
||||
return true;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "ppsspp_config.h"
|
||||
|
||||
#include "android/jni/app-android.h"
|
||||
#include "android/jni/AndroidContentURI.h"
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <unistd.h>
|
||||
|
@ -32,14 +33,12 @@
|
|||
#define _POSIX_THREAD_SAFE_FUNCTIONS 200112L
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
|
||||
#include "Common/Log.h"
|
||||
#include "Common/LogReporting.h"
|
||||
#include "Common/File/AndroidContentURI.h"
|
||||
#include "Common/File/FileUtil.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/SysError.h"
|
||||
|
@ -54,7 +53,6 @@
|
|||
#include <direct.h> // getcwd
|
||||
#if PPSSPP_PLATFORM(UWP)
|
||||
#include <fileapifromapp.h>
|
||||
#include "UWP/UWPHelpers/StorageManager.h"
|
||||
#endif
|
||||
#else
|
||||
#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.
|
||||
Android_OpenContentUriMode openMode = 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);
|
||||
int descriptor = Android_OpenContentUriFd(path.ToString(), Android_OpenContentUriMode::READ_WRITE_TRUNCATE);
|
||||
if (descriptor < 0) {
|
||||
INFO_LOG(COMMON, "Opening '%s' for write failed", path.ToString().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
FILE *f = fdopen(descriptor, fmode);
|
||||
FILE *f = fdopen(descriptor, "wb");
|
||||
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);
|
||||
}
|
||||
return f;
|
||||
|
@ -165,18 +157,7 @@ FILE *OpenCFile(const Path &path, const char *mode) {
|
|||
}
|
||||
|
||||
#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());
|
||||
#endif
|
||||
#else
|
||||
return fopen(path.c_str(), mode);
|
||||
#endif
|
||||
|
@ -592,7 +573,7 @@ bool CreateFullPath(const Path &path) {
|
|||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string_view> parts;
|
||||
std::vector<std::string> parts;
|
||||
SplitString(diff, '/', parts);
|
||||
|
||||
// Probably not necessary sanity check, ported from the old code.
|
||||
|
@ -602,7 +583,7 @@ bool CreateFullPath(const Path &path) {
|
|||
}
|
||||
|
||||
Path curPath = root;
|
||||
for (auto part : parts) {
|
||||
for (auto &part : parts) {
|
||||
curPath /= part;
|
||||
if (!File::Exists(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());
|
||||
|
||||
#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 destw = destFilename.ToWString();
|
||||
if (_wrename(srcw.c_str(), destw.c_str()) == 0)
|
||||
return true;
|
||||
#endif
|
||||
#else
|
||||
if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
|
||||
return true;
|
||||
|
@ -969,7 +945,7 @@ bool OpenFileInEditor(const Path &fileName) {
|
|||
|
||||
#if PPSSPP_PLATFORM(WINDOWS)
|
||||
#if PPSSPP_PLATFORM(UWP)
|
||||
OpenFile(fileName.ToString());
|
||||
// Do nothing.
|
||||
#else
|
||||
ShellExecuteW(nullptr, L"open", fileName.ToWString().c_str(), nullptr, nullptr, SW_SHOW);
|
||||
#endif
|
||||
|
@ -1181,7 +1157,6 @@ uint8_t *ReadLocalFile(const Path &filename, size_t *size) {
|
|||
return nullptr;
|
||||
}
|
||||
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];
|
||||
if (fread(contents, 1, f_size, file) != f_size) {
|
||||
delete[] contents;
|
||||
|
|
|
@ -4,17 +4,13 @@
|
|||
#include <cstring>
|
||||
|
||||
#include "Common/File/Path.h"
|
||||
#include "Common/File/AndroidContentURI.h"
|
||||
#include "Common/File/FileUtil.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/Data/Encoding/Utf8.h"
|
||||
|
||||
#include "android/jni/app-android.h"
|
||||
|
||||
#if PPSSPP_PLATFORM(UWP) && !defined(__LIBRETRO__)
|
||||
#include "UWP/UWPHelpers/StorageManager.h"
|
||||
#endif
|
||||
#include "android/jni/AndroidContentURI.h"
|
||||
|
||||
#if HOST_IS_CASE_SENSITIVE
|
||||
#include <dirent.h>
|
||||
|
@ -22,7 +18,7 @@
|
|||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
Path::Path(std::string_view str) {
|
||||
Path::Path(const std::string &str) {
|
||||
Init(str);
|
||||
}
|
||||
|
||||
|
@ -33,7 +29,7 @@ Path::Path(const std::wstring &str) {
|
|||
}
|
||||
#endif
|
||||
|
||||
void Path::Init(std::string_view str) {
|
||||
void Path::Init(const std::string &str) {
|
||||
if (str.empty()) {
|
||||
type_ = PathType::UNDEFINED;
|
||||
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
|
||||
// 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) {
|
||||
AndroidContentURI uri(path_);
|
||||
return Path(uri.WithComponent(subdir).ToString());
|
||||
|
@ -104,18 +100,18 @@ Path Path::operator /(std::string_view subdir) const {
|
|||
return Path(fullPath);
|
||||
}
|
||||
|
||||
void Path::operator /=(std::string_view subdir) {
|
||||
void Path::operator /=(const std::string &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) {
|
||||
AndroidContentURI uri(path_);
|
||||
return Path(uri.WithExtraExtension(ext).ToString());
|
||||
}
|
||||
|
||||
_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 {
|
||||
|
@ -135,11 +131,6 @@ Path Path::WithReplacedExtension(const std::string &oldExtension, const std::str
|
|||
}
|
||||
|
||||
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] == '.');
|
||||
if (path_.empty()) {
|
||||
return Path(*this);
|
||||
|
@ -161,7 +152,7 @@ std::string Path::GetFilename() const {
|
|||
return path_;
|
||||
}
|
||||
|
||||
std::string GetExtFromString(std::string_view str) {
|
||||
static std::string GetExtFromString(const std::string &str) {
|
||||
size_t pos = str.rfind(".");
|
||||
if (pos == std::string::npos) {
|
||||
return "";
|
||||
|
@ -171,7 +162,7 @@ std::string GetExtFromString(std::string_view str) {
|
|||
// Don't want to detect "df/file" from "/as.df/file"
|
||||
return "";
|
||||
}
|
||||
std::string ext(str.substr(pos));
|
||||
std::string ext = str.substr(pos);
|
||||
for (size_t i = 0; i < ext.size(); i++) {
|
||||
ext[i] = tolower(ext[i]);
|
||||
}
|
||||
|
@ -181,7 +172,7 @@ std::string GetExtFromString(std::string_view str) {
|
|||
std::string Path::GetFileExtension() const {
|
||||
if (type_ == PathType::CONTENT_URI) {
|
||||
AndroidContentURI uri(path_);
|
||||
return uri.GetFileExtension();
|
||||
return GetExtFromString(uri.FilePath());
|
||||
}
|
||||
return GetExtFromString(path_);
|
||||
}
|
||||
|
@ -262,49 +253,14 @@ std::wstring Path::ToWString() const {
|
|||
}
|
||||
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
|
||||
|
||||
std::string Path::ToVisualString(const char *relativeRoot) const {
|
||||
std::string Path::ToVisualString() const {
|
||||
if (type_ == PathType::CONTENT_URI) {
|
||||
return AndroidContentURI(path_).ToVisualString();
|
||||
#if PPSSPP_PLATFORM(WINDOWS)
|
||||
} else if (type_ == PathType::NATIVE) {
|
||||
#if PPSSPP_PLATFORM(UWP) && !defined(__LIBRETRO__)
|
||||
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
|
||||
} else {
|
||||
return path_;
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include "ppsspp_config.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
|
@ -37,11 +36,11 @@ enum class PathType {
|
|||
|
||||
class Path {
|
||||
private:
|
||||
void Init(std::string_view str);
|
||||
void Init(const std::string &str);
|
||||
|
||||
public:
|
||||
Path() : type_(PathType::UNDEFINED) {}
|
||||
explicit Path(std::string_view str);
|
||||
explicit Path(const std::string &str);
|
||||
|
||||
#if PPSSPP_PLATFORM(WINDOWS)
|
||||
explicit Path(const std::wstring &str);
|
||||
|
@ -72,34 +71,28 @@ public:
|
|||
bool IsAbsolute() const;
|
||||
|
||||
// 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.
|
||||
void operator /=(std::string_view subdir);
|
||||
void operator /=(const std::string &subdir);
|
||||
|
||||
// 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 &newExtension) const;
|
||||
|
||||
// Removes the last component.
|
||||
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.
|
||||
// Removes the last component.
|
||||
std::string GetDirectory() const;
|
||||
|
||||
const std::string &ToString() const;
|
||||
|
||||
#if PPSSPP_PLATFORM(WINDOWS)
|
||||
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
|
||||
|
||||
// Pass in a relative root to turn the path into a relative path - if it is one!
|
||||
std::string ToVisualString(const char *relativeRoot = nullptr) const;
|
||||
std::string ToVisualString() const;
|
||||
|
||||
bool CanNavigateUp() const;
|
||||
Path NavigateUp() const;
|
||||
|
@ -139,8 +132,6 @@ private:
|
|||
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.
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#if PPSSPP_PLATFORM(ANDROID)
|
||||
#include "android/jni/app-android.h"
|
||||
#include "android/jni/AndroidContentURI.h"
|
||||
#endif
|
||||
|
||||
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");
|
||||
if (http.Resolve(baseURL.Host().c_str(), baseURL.Port())) {
|
||||
if (http.Connect(2, 20.0, cancel)) {
|
||||
net::RequestProgress progress(cancel);
|
||||
http::RequestProgress progress(cancel);
|
||||
code = http.GET(req, &result, responseHeaders, &progress);
|
||||
http.Disconnect();
|
||||
}
|
||||
|
@ -78,7 +79,7 @@ bool LoadRemoteFileList(const Path &url, const std::string &userAgent, bool *can
|
|||
return false;
|
||||
}
|
||||
|
||||
for (auto &item : items) {
|
||||
for (std::string item : items) {
|
||||
// Apply some workarounds.
|
||||
if (item.empty())
|
||||
continue;
|
||||
|
@ -210,7 +211,7 @@ std::string PathBrowser::GetFriendlyPath() const {
|
|||
bool PathBrowser::GetListing(std::vector<File::FileInfo> &fileInfo, const char *filter, bool *cancel) {
|
||||
std::unique_lock<std::mutex> guard(pendingLock_);
|
||||
while (!IsListingReady() && (!cancel || !*cancel)) {
|
||||
// In case cancel changes, just sleep. TODO: Replace with condition variable.
|
||||
// In case cancel changes, just sleep.
|
||||
guard.unlock();
|
||||
sleep_ms(50);
|
||||
guard.lock();
|
||||
|
@ -221,6 +222,14 @@ bool PathBrowser::GetListing(std::vector<File::FileInfo> &fileInfo, const char *
|
|||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
204
Common/File/VFS/AssetReader.cpp
Normal file
204
Common/File/VFS/AssetReader.cpp
Normal 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);
|
||||
}
|
65
Common/File/VFS/AssetReader.h
Normal file
65
Common/File/VFS/AssetReader.h
Normal 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_;
|
||||
};
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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_;
|
||||
};
|
|
@ -1,34 +1,35 @@
|
|||
#include <cstring>
|
||||
|
||||
#include "Common/Log.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/StringUtils.h"
|
||||
|
||||
VFS g_VFS;
|
||||
struct VFSEntry {
|
||||
const char *prefix;
|
||||
AssetReader *reader;
|
||||
};
|
||||
|
||||
void VFS::Register(const char *prefix, VFSBackend *reader) {
|
||||
if (reader) {
|
||||
entries_.push_back(VFSEntry{ prefix, reader });
|
||||
static VFSEntry entries[16];
|
||||
static int num_entries = 0;
|
||||
|
||||
void VFSRegister(const char *prefix, AssetReader *reader) {
|
||||
entries[num_entries].prefix = prefix;
|
||||
entries[num_entries].reader = reader;
|
||||
DEBUG_LOG(IO, "Registered VFS for prefix %s: %s", prefix, reader->toString().c_str());
|
||||
} else {
|
||||
ERROR_LOG(IO, "Trying to register null VFS backend for prefix %s", prefix);
|
||||
}
|
||||
num_entries++;
|
||||
}
|
||||
|
||||
void VFS::Clear() {
|
||||
for (auto &entry : entries_) {
|
||||
delete entry.reader;
|
||||
void VFSShutdown() {
|
||||
for (int i = 0; i < num_entries; i++) {
|
||||
delete entries[i].reader;
|
||||
}
|
||||
entries_.clear();
|
||||
num_entries = 0;
|
||||
}
|
||||
|
||||
// TODO: Use Path more.
|
||||
static bool IsLocalAbsolutePath(const char *path) {
|
||||
bool isUnixLocal = path[0] == '/';
|
||||
#ifdef _WIN32
|
||||
bool isWindowsLocal = (isalpha(path[0]) && path[1] == ':') || startsWith(path, "\\\\") || startsWith(path, "//");
|
||||
bool isWindowsLocal = isalpha(path[0]) && path[1] == ':';
|
||||
#else
|
||||
bool isWindowsLocal = false;
|
||||
#endif
|
||||
|
@ -37,7 +38,7 @@ static bool IsLocalAbsolutePath(const char *path) {
|
|||
}
|
||||
|
||||
// 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)) {
|
||||
// Local path, not VFS.
|
||||
// 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);
|
||||
bool fileSystemFound = false;
|
||||
for (const auto &entry : entries_) {
|
||||
int prefix_len = (int)strlen(entry.prefix);
|
||||
for (int i = 0; i < num_entries; i++) {
|
||||
int prefix_len = (int)strlen(entries[i].prefix);
|
||||
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;
|
||||
// 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)
|
||||
return data;
|
||||
else
|
||||
|
@ -66,7 +67,7 @@ uint8_t *VFS::ReadFile(const char *filename, size_t *size) {
|
|||
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)) {
|
||||
// Local path, not VFS.
|
||||
// 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);
|
||||
bool fileSystemFound = false;
|
||||
for (const auto &entry : entries_) {
|
||||
int prefix_len = (int)strlen(entry.prefix);
|
||||
for (int i = 0; i < num_entries; i++) {
|
||||
int prefix_len = (int)strlen(entries[i].prefix);
|
||||
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;
|
||||
if (entry.reader->GetFileListing(path + prefix_len, listing, filter)) {
|
||||
if (entries[i].reader->GetFileListing(path + prefix_len, listing, filter)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +94,7 @@ bool VFS::GetFileListing(const char *path, std::vector<File::FileInfo> *listing,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool VFS::GetFileInfo(const char *path, File::FileInfo *info) {
|
||||
bool VFSGetFileInfo(const char *path, File::FileInfo *info) {
|
||||
if (IsLocalAbsolutePath(path)) {
|
||||
// Local path, not VFS.
|
||||
// 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;
|
||||
int fn_len = (int)strlen(path);
|
||||
for (const auto &entry : entries_) {
|
||||
int prefix_len = (int)strlen(entry.prefix);
|
||||
for (int i = 0; i < num_entries; i++) {
|
||||
int prefix_len = (int)strlen(entries[i].prefix);
|
||||
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;
|
||||
if (entry.reader->GetFileInfo(path + prefix_len, info))
|
||||
if (entries[i].reader->GetFileInfo(path + prefix_len, info))
|
||||
return true;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
}
|
||||
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.
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,83 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
#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
|
||||
// platforms as well with the appropriate directory set-up.
|
||||
|
||||
// Note that this is kinda similar in concept to Core/MetaFileSystem.h, but that one
|
||||
// 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.
|
||||
class AssetReader;
|
||||
|
||||
// VFSBackend instances can be used on their own, without the VFS, to serve as an abstraction of
|
||||
// a single directory or ZIP file.
|
||||
|
||||
// The VFSFileReference level of abstraction is there to hold things like zip file indices,
|
||||
// for fast re-open etc.
|
||||
|
||||
class VFSFileReference {
|
||||
public:
|
||||
virtual ~VFSFileReference() {}
|
||||
};
|
||||
|
||||
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();
|
||||
void VFSRegister(const char *prefix, AssetReader *reader);
|
||||
void VFSShutdown();
|
||||
|
||||
// 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;
|
||||
uint8_t *VFSReadFile(const char *filename, size_t *size);
|
||||
bool VFSGetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter = 0);
|
||||
bool VFSGetFileInfo(const char *filename, File::FileInfo *fileInfo);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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_;
|
||||
};
|
402
Common/GL/GLInterface/EGL.cpp
Normal file
402
Common/GL/GLInterface/EGL.cpp
Normal 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;
|
||||
}
|
||||
}
|
42
Common/GL/GLInterface/EGL.h
Normal file
42
Common/GL/GLInterface/EGL.h
Normal 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();
|
||||
};
|
31
Common/GL/GLInterface/EGLAndroid.cpp
Normal file
31
Common/GL/GLInterface/EGLAndroid.cpp
Normal 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() {
|
||||
}
|
24
Common/GL/GLInterface/EGLAndroid.h
Normal file
24
Common/GL/GLInterface/EGLAndroid.h
Normal 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_;
|
||||
};
|
21
Common/GL/GLInterface/EGLSwitch.cpp
Normal file
21
Common/GL/GLInterface/EGLSwitch.cpp
Normal 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
|
24
Common/GL/GLInterface/EGLSwitch.h
Normal file
24
Common/GL/GLInterface/EGLSwitch.h
Normal 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;
|
||||
};
|
44
Common/GL/GLInterface/GLInterface.cpp
Normal file
44
Common/GL/GLInterface/GLInterface.cpp
Normal 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
|
||||
}
|
50
Common/GL/GLInterfaceBase.h
Normal file
50
Common/GL/GLInterfaceBase.h
Normal 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();
|
|
@ -11,6 +11,7 @@ static HMODULE g_D3DCompileModule;
|
|||
|
||||
LPCREATEDXGIFACTORY ptr_CreateDXGIFactory;
|
||||
LPD3D11CREATEDEVICE ptr_D3D11CreateDevice;
|
||||
LPD3D11CREATEDEVICEANDSWAPCHAIN ptr_D3D11CreateDeviceAndSwapChain;
|
||||
pD3DCompile ptr_D3DCompile;
|
||||
|
||||
LoadD3D11Error LoadD3D11() {
|
||||
|
@ -21,6 +22,7 @@ LoadD3D11Error LoadD3D11() {
|
|||
g_D3D11Module = LoadLibrary(L"d3d11.dll");
|
||||
if (g_D3D11Module) {
|
||||
ptr_D3D11CreateDevice = (LPD3D11CREATEDEVICE)GetProcAddress(g_D3D11Module, "D3D11CreateDevice");
|
||||
ptr_D3D11CreateDeviceAndSwapChain = (LPD3D11CREATEDEVICEANDSWAPCHAIN)GetProcAddress(g_D3D11Module, "D3D11CreateDeviceAndSwapChain");
|
||||
} else {
|
||||
return LoadD3D11Error::FAIL_NO_D3D11;
|
||||
}
|
||||
|
|
|
@ -14,10 +14,12 @@
|
|||
#endif
|
||||
|
||||
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 **);
|
||||
|
||||
extern LPCREATEDXGIFACTORY ptr_CreateDXGIFactory;
|
||||
extern LPD3D11CREATEDEVICE ptr_D3D11CreateDevice;
|
||||
extern LPD3D11CREATEDEVICEANDSWAPCHAIN ptr_D3D11CreateDeviceAndSwapChain;
|
||||
extern pD3DCompile ptr_D3DCompile;
|
||||
|
||||
enum class LoadD3D11Error {
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "Common/Data/Convert/ColorConv.h"
|
||||
#include "Common/Data/Convert/SmallDataConvert.h"
|
||||
#include "Common/Data/Encoding/Utf8.h"
|
||||
#include "Common/TimeUtil.h"
|
||||
#include "Common/Log.h"
|
||||
|
||||
#include <map>
|
||||
|
@ -63,7 +62,7 @@ public:
|
|||
|
||||
class D3D11DrawContext : public DrawContext {
|
||||
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();
|
||||
|
||||
const DeviceCaps &GetDeviceCaps() const override {
|
||||
|
@ -89,11 +88,10 @@ public:
|
|||
Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) 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;
|
||||
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.
|
||||
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 BindNativeTexture(int index, void *nativeTexture) 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 BindPipeline(Pipeline *pipeline) override;
|
||||
|
||||
|
@ -114,7 +112,7 @@ public:
|
|||
|
||||
// Raster state
|
||||
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 {
|
||||
if (memcmp(blendFactor_, color, sizeof(float) * 4)) {
|
||||
memcpy(blendFactor_, color, sizeof(float) * 4);
|
||||
|
@ -128,17 +126,14 @@ public:
|
|||
stencilDirty_ = true;
|
||||
}
|
||||
|
||||
void EndFrame() override;
|
||||
|
||||
void Draw(int vertexCount, int offset) override;
|
||||
void DrawIndexed(int vertexCount, int offset) override;
|
||||
void DrawUP(const void *vdata, int vertexCount) override;
|
||||
void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override;
|
||||
|
||||
void BeginFrame(DebugFlags debugFlags) override;
|
||||
void EndFrame() override;
|
||||
void Present(PresentMode presentMode, int vblanks) override;
|
||||
|
||||
int GetFrameCount() override { return frameCount_; }
|
||||
void BeginFrame() override;
|
||||
|
||||
std::string GetInfoString(InfoField info) const override {
|
||||
switch (info) {
|
||||
|
@ -179,10 +174,9 @@ private:
|
|||
|
||||
HWND hWnd_;
|
||||
ID3D11Device *device_;
|
||||
ID3D11Device1 *device1_;
|
||||
ID3D11DeviceContext *context_;
|
||||
ID3D11Device1 *device1_;
|
||||
ID3D11DeviceContext1 *context1_;
|
||||
IDXGISwapChain *swapChain_;
|
||||
|
||||
ID3D11Texture2D *bbRenderTargetTex_ = nullptr; // NOT OWNED
|
||||
ID3D11RenderTargetView *bbRenderTargetView_ = nullptr;
|
||||
|
@ -214,15 +208,14 @@ private:
|
|||
ID3D11GeometryShader *curGS_ = nullptr;
|
||||
D3D11_PRIMITIVE_TOPOLOGY curTopology_ = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
|
||||
|
||||
ID3D11Buffer *nextVertexBuffer_ = nullptr;
|
||||
UINT nextVertexBufferOffset_ = 0;
|
||||
ID3D11Buffer *nextVertexBuffers_[4]{};
|
||||
int nextVertexBufferOffsets_[4]{};
|
||||
|
||||
bool dirtyIndexBuffer_ = false;
|
||||
ID3D11Buffer *nextIndexBuffer_ = nullptr;
|
||||
UINT nextIndexBufferOffset_ = 0;
|
||||
int nextIndexBufferOffset_ = 0;
|
||||
|
||||
InvalidationCallback invalidationCallback_;
|
||||
int frameCount_ = FRAME_TIME_HISTORY_LENGTH;
|
||||
|
||||
// Dynamic state
|
||||
float blendFactor_[4]{};
|
||||
|
@ -243,14 +236,13 @@ private:
|
|||
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),
|
||||
device_(device),
|
||||
context_(deviceContext1),
|
||||
device1_(device1),
|
||||
context1_(deviceContext1),
|
||||
featureLevel_(featureLevel),
|
||||
swapChain_(swapChain),
|
||||
deviceList_(deviceList) {
|
||||
|
||||
// We no longer support Windows Phone.
|
||||
|
@ -281,10 +273,6 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de
|
|||
caps_.blendMinMaxSupported = true;
|
||||
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{};
|
||||
HRESULT result = device_->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options, sizeof(options));
|
||||
if (SUCCEEDED(result)) {
|
||||
|
@ -350,13 +338,6 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de
|
|||
const size_t UP_MAX_BYTES = 65536 * 24;
|
||||
|
||||
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() {
|
||||
|
@ -427,43 +408,32 @@ void D3D11DrawContext::HandleEvent(Event ev, int width, int height, void *param1
|
|||
curRTHeight_ = height;
|
||||
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() {
|
||||
// Fake a submit time.
|
||||
frameTimeHistory_[frameCount_].firstSubmit = time_now_d();
|
||||
curPipeline_ = nullptr;
|
||||
}
|
||||
|
||||
void D3D11DrawContext::Present(PresentMode presentMode, int vblanks) {
|
||||
frameTimeHistory_[frameCount_].queuePresent = time_now_d();
|
||||
|
||||
int interval = vblanks;
|
||||
if (presentMode != PresentMode::FIFO) {
|
||||
interval = 0;
|
||||
}
|
||||
// Safety for libretro
|
||||
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 };
|
||||
void D3D11DrawContext::SetViewports(int count, Viewport *viewports) {
|
||||
D3D11_VIEWPORT vp[4];
|
||||
for (int i = 0; i < count; i++) {
|
||||
DisplayRect<float> rc{ viewports[i].TopLeftX , viewports[i].TopLeftY, viewports[i].Width, viewports[i].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);
|
||||
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;
|
||||
}
|
||||
context_->RSSetViewports(count, vp);
|
||||
}
|
||||
|
||||
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::D32F: return DXGI_FORMAT_D32_FLOAT;
|
||||
case DataFormat::D32F_S8: return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
|
||||
case DataFormat::BC1_RGBA_UNORM_BLOCK: return DXGI_FORMAT_BC1_UNORM;
|
||||
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;
|
||||
case DataFormat::ETC1:
|
||||
default:
|
||||
return DXGI_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
@ -725,7 +690,7 @@ public:
|
|||
D3D11InputLayout() {}
|
||||
InputLayoutDesc desc;
|
||||
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) {
|
||||
|
@ -752,13 +717,15 @@ InputLayout *D3D11DrawContext::CreateInputLayout(const InputLayoutDesc &desc) {
|
|||
D3D11_INPUT_ELEMENT_DESC el;
|
||||
el.AlignedByteOffset = desc.attributes[i].offset;
|
||||
el.Format = dataFormatToD3D11(desc.attributes[i].format);
|
||||
el.InstanceDataStepRate = 0;
|
||||
el.InputSlot = 0;
|
||||
el.InstanceDataStepRate = desc.bindings[desc.attributes[i].binding].instanceRate ? 1 : 0;
|
||||
el.InputSlot = desc.attributes[i].binding;
|
||||
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->stride = desc.stride;
|
||||
for (size_t i = 0; i < desc.bindings.size(); i++) {
|
||||
inputLayout->strides.push_back(desc.bindings[i].stride);
|
||||
}
|
||||
return inputLayout;
|
||||
}
|
||||
|
||||
|
@ -821,83 +788,35 @@ public:
|
|||
width_ = desc.width;
|
||||
height_ = desc.height;
|
||||
depth_ = desc.depth;
|
||||
format_ = desc.format;
|
||||
mipLevels_ = desc.mipLevels;
|
||||
}
|
||||
~D3D11Texture() {
|
||||
if (tex_)
|
||||
tex_->Release();
|
||||
if (stagingTex_)
|
||||
stagingTex_->Release();
|
||||
if (view_)
|
||||
view_->Release();
|
||||
if (tex)
|
||||
tex->Release();
|
||||
if (stagingTex)
|
||||
stagingTex->Release();
|
||||
if (view)
|
||||
view->Release();
|
||||
}
|
||||
|
||||
bool Create(ID3D11DeviceContext *context, ID3D11Device *device, const TextureDesc &desc, bool generateMips);
|
||||
|
||||
bool CreateStagingTexture(ID3D11Device *device);
|
||||
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;
|
||||
ID3D11Texture2D *tex = nullptr;
|
||||
ID3D11Texture2D *stagingTex = nullptr;
|
||||
ID3D11ShaderResourceView *view = nullptr;
|
||||
};
|
||||
|
||||
bool D3D11Texture::FillLevel(ID3D11DeviceContext *context, int level, int w, int h, int d, const uint8_t *const *data, TextureCallback initDataCallback) {
|
||||
D3D11_MAPPED_SUBRESOURCE mapped;
|
||||
HRESULT hr = context->Map(stagingTex_, level, D3D11_MAP_WRITE, 0, &mapped);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
tex_->Release();
|
||||
tex_ = nullptr;
|
||||
return false;
|
||||
Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) {
|
||||
if (!(GetDataFormatSupport(desc.format) & FMT_TEXTURE)) {
|
||||
// D3D11 does not support this format as a texture format.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!initDataCallback((uint8_t *)mapped.pData, data[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(format_);
|
||||
const void *src = data[level] + byteStride * (y + h * s);
|
||||
memcpy(dest, src, byteStride);
|
||||
}
|
||||
}
|
||||
}
|
||||
context->Unmap(stagingTex_, level);
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
|
||||
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{};
|
||||
descColor.Width = desc.width;
|
||||
descColor.Height = desc.height;
|
||||
|
@ -906,16 +825,25 @@ bool D3D11Texture::Create(ID3D11DeviceContext *context, ID3D11Device *device, co
|
|||
descColor.Format = dataFormatToD3D11(desc.format);
|
||||
descColor.SampleDesc.Count = 1;
|
||||
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.BindFlags = generateMips ? (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET) : D3D11_BIND_SHADER_RESOURCE;
|
||||
descColor.MiscFlags = generateMips ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 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 initData[12]{};
|
||||
std::vector<uint8_t> initDataBuffer[12];
|
||||
|
@ -935,39 +863,62 @@ bool D3D11Texture::Create(ID3D11DeviceContext *context, ID3D11Device *device, co
|
|||
initDataParam = initData;
|
||||
}
|
||||
|
||||
HRESULT hr = device->CreateTexture2D(&descColor, initDataParam, &tex_);
|
||||
HRESULT hr = device_->CreateTexture2D(&descColor, initDataParam, &tex->tex);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
tex_ = nullptr;
|
||||
return false;
|
||||
delete tex;
|
||||
return nullptr;
|
||||
}
|
||||
hr = device->CreateShaderResourceView(tex_, nullptr, &view_);
|
||||
hr = device_->CreateShaderResourceView(tex->tex, nullptr, &tex->view);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
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 (desc.initDataCallback) {
|
||||
if (!FillLevel(context, 0, desc.width, desc.height, desc.depth, desc.initData.data(), desc.initDataCallback)) {
|
||||
tex_->Release();
|
||||
return false;
|
||||
if (!populateLevelCallback(0, desc.width, desc.height, desc.depth)) {
|
||||
delete tex;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
context->CopyResource(tex_, stagingTex_);
|
||||
stagingTex_->Release();
|
||||
stagingTex_ = nullptr;
|
||||
context_->CopyResource(tex->stagingTex, tex->stagingTex);
|
||||
tex->stagingTex->Release();
|
||||
tex->stagingTex = nullptr;
|
||||
} else {
|
||||
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) {
|
||||
int w = desc.width;
|
||||
int h = desc.height;
|
||||
int d = desc.depth;
|
||||
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) {
|
||||
return false;
|
||||
delete tex;
|
||||
return nullptr;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -978,62 +929,13 @@ bool D3D11Texture::Create(ID3D11DeviceContext *context, ID3D11Device *device, co
|
|||
d = (d + 1) / 2;
|
||||
}
|
||||
|
||||
context->CopyResource(tex_, stagingTex_);
|
||||
stagingTex_->Release();
|
||||
stagingTex_ = nullptr;
|
||||
context_->CopyResource(tex->tex, tex->stagingTex);
|
||||
tex->stagingTex->Release();
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
if (language != ShaderLanguage::HLSL_D3D11) {
|
||||
ERROR_LOG(G3D, "Unsupported shader language");
|
||||
|
@ -1251,7 +1153,8 @@ void D3D11DrawContext::ApplyCurrentState() {
|
|||
}
|
||||
|
||||
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_) {
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
D3D11Buffer *buf = (D3D11Buffer *)buffer;
|
||||
nextVertexBuffer_ = buf->buf;
|
||||
nextVertexBufferOffset_ = offset;
|
||||
for (int i = 0; i < count; i++) {
|
||||
D3D11Buffer *buf = (D3D11Buffer *)buffers[i];
|
||||
nextVertexBuffers_[start + i] = buf->buf;
|
||||
nextVertexBufferOffsets_[start + i] = offsets ? offsets[i] : 0;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
BindVertexBuffer(upBuffer_, 0);
|
||||
BindVertexBuffers(0, 1, &upBuffer_, nullptr);
|
||||
int offset = 0;
|
||||
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.
|
||||
_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{};
|
||||
descColor.Width = desc.width;
|
||||
descColor.Height = desc.height;
|
||||
|
@ -1498,7 +1414,7 @@ void D3D11DrawContext::BindTextures(int start, int count, Texture **textures, Te
|
|||
_assert_(start + count <= ARRAY_SIZE(views));
|
||||
for (int i = 0; i < count; i++) {
|
||||
D3D11Texture *tex = (D3D11Texture *)textures[i];
|
||||
views[i] = tex ? tex->View() : nullptr;
|
||||
views[i] = tex ? tex->view : nullptr;
|
||||
}
|
||||
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) {
|
||||
FrameTimeData &frameTimeData = frameTimeHistory_.Add(frameCount_);
|
||||
frameTimeData.afterFenceWait = time_now_d();
|
||||
frameTimeData.frameBegin = frameTimeData.afterFenceWait;
|
||||
|
||||
void D3D11DrawContext::BeginFrame() {
|
||||
context_->OMSetRenderTargets(1, &curRenderTargetView_, curDepthStencilView_);
|
||||
|
||||
if (curBlend_ != nullptr) {
|
||||
|
@ -1559,7 +1471,7 @@ void D3D11DrawContext::BeginFrame(DebugFlags debugFlags) {
|
|||
context_->IASetPrimitiveTopology(curTopology_);
|
||||
}
|
||||
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_);
|
||||
if (curPipeline_->dynamicUniforms) {
|
||||
context_->VSSetConstantBuffers(0, 1, &curPipeline_->dynamicUniforms);
|
||||
|
@ -1623,7 +1535,7 @@ bool D3D11DrawContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1,
|
|||
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;
|
||||
|
||||
if (fb) {
|
||||
|
@ -1862,7 +1774,7 @@ uint64_t D3D11DrawContext::GetNativeObject(NativeObject obj, void *srcObject) {
|
|||
case NativeObject::FEATURE_LEVEL:
|
||||
return (uint64_t)(uintptr_t)featureLevel_;
|
||||
case NativeObject::TEXTURE_VIEW:
|
||||
return (uint64_t)(((D3D11Texture *)srcObject)->View());
|
||||
return (uint64_t)(((D3D11Texture *)srcObject)->view);
|
||||
default:
|
||||
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) {
|
||||
return new D3D11DrawContext(device, context, device1, context1, swapChain, featureLevel, hWnd, adapterNames, 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, featureLevel, hWnd, adapterNames);
|
||||
}
|
||||
|
||||
} // namespace Draw
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include "Common/GPU/D3D9/D3D9StateCache.h"
|
||||
#include "Common/OSVersion.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/TimeUtil.h"
|
||||
|
||||
#include "Common/Log.h"
|
||||
|
||||
|
@ -114,7 +113,7 @@ static const D3DSTENCILOP stencilOpToD3D9[] = {
|
|||
D3DSTENCILOP_DECR,
|
||||
};
|
||||
|
||||
static D3DFORMAT FormatToD3DFMT(DataFormat fmt) {
|
||||
D3DFORMAT FormatToD3DFMT(DataFormat 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::R8G8B8A8_UNORM: return D3DFMT_A8R8G8B8;
|
||||
|
@ -126,9 +125,6 @@ static D3DFORMAT FormatToD3DFMT(DataFormat fmt) {
|
|||
case DataFormat::A1R5G5B5_UNORM_PACK16: return D3DFMT_A1R5G5B5;
|
||||
case DataFormat::D24_S8: return D3DFMT_D24S8;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -231,14 +227,14 @@ public:
|
|||
decl_->Release();
|
||||
}
|
||||
}
|
||||
int GetStride() const { return stride_; }
|
||||
int GetStride(int binding) const { return stride_[binding]; }
|
||||
void Apply(LPDIRECT3DDEVICE9 device) {
|
||||
device->SetVertexDeclaration(decl_);
|
||||
}
|
||||
|
||||
private:
|
||||
LPDIRECT3DVERTEXDECLARATION9 decl_;
|
||||
int stride_;
|
||||
int stride_[4];
|
||||
};
|
||||
|
||||
class D3D9ShaderModule : public ShaderModule {
|
||||
|
@ -309,14 +305,14 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
}
|
||||
void UpdateTextureLevels(const uint8_t * const *data, int numLevels, TextureCallback initDataCallback);
|
||||
|
||||
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);
|
||||
LPDIRECT3DDEVICE9 device_;
|
||||
LPDIRECT3DDEVICE9EX deviceEx_;
|
||||
TextureType type_;
|
||||
DataFormat format_;
|
||||
D3DFORMAT d3dfmt_;
|
||||
LPDIRECT3DTEXTURE9 tex_ = nullptr;
|
||||
LPDIRECT3DVOLUMETEXTURE9 volTex_ = nullptr;
|
||||
|
@ -375,30 +371,26 @@ bool D3D9Texture::Create(const TextureDesc &desc) {
|
|||
break;
|
||||
}
|
||||
if (FAILED(hr)) {
|
||||
ERROR_LOG(G3D, "D3D9 Texture creation failed");
|
||||
ERROR_LOG(G3D, "Texture creation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (desc.initData.size()) {
|
||||
// In D3D9, after setting D3DUSAGE_AUTOGENMIPS, we can only access the top layer. The rest will be
|
||||
// automatically generated.
|
||||
int numLevels = desc.generateMips ? 1 : (int)desc.initData.size();
|
||||
UpdateTextureLevels(desc.initData.data(), numLevels, desc.initDataCallback);
|
||||
}
|
||||
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);
|
||||
int maxLevel = desc.generateMips ? 1 : (int)desc.initData.size();
|
||||
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;
|
||||
}
|
||||
|
||||
// Just switches R and G.
|
||||
inline uint32_t Shuffle8888(uint32_t x) {
|
||||
|
@ -533,13 +525,12 @@ public:
|
|||
Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) 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 {
|
||||
// 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 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.
|
||||
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
|
||||
|
@ -560,9 +551,12 @@ public:
|
|||
s->Apply(device_, start + i);
|
||||
}
|
||||
}
|
||||
void BindVertexBuffer(Buffer *vertexBuffer, int offset) override {
|
||||
curVBuffer_ = (D3D9Buffer *)vertexBuffer;
|
||||
curVBufferOffset_ = offset;
|
||||
void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) override {
|
||||
_assert_(start + count <= ARRAY_SIZE(curVBuffers_));
|
||||
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 {
|
||||
curIBuffer_ = (D3D9Buffer *)indexBuffer;
|
||||
|
@ -573,17 +567,13 @@ public:
|
|||
curPipeline_ = (D3D9Pipeline *)pipeline;
|
||||
}
|
||||
|
||||
void BeginFrame(Draw::DebugFlags debugFlags) 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;
|
||||
|
||||
// Raster state
|
||||
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 SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override;
|
||||
|
||||
|
@ -638,12 +628,11 @@ private:
|
|||
D3DCAPS9 d3dCaps_;
|
||||
char shadeLangVersion_[64]{};
|
||||
DeviceCaps caps_{};
|
||||
int frameCount_ = FRAME_TIME_HISTORY_LENGTH;
|
||||
|
||||
// Bound state
|
||||
AutoRef<D3D9Pipeline> curPipeline_;
|
||||
AutoRef<D3D9Buffer> curVBuffer_;
|
||||
int curVBufferOffset_ = 0;
|
||||
AutoRef<D3D9Buffer> curVBuffers_[4];
|
||||
int curVBufferOffsets_[4]{};
|
||||
AutoRef<D3D9Buffer> curIBuffer_;
|
||||
int curIBufferOffset_ = 0;
|
||||
AutoRef<Framebuffer> curRenderTarget_;
|
||||
|
@ -755,10 +744,10 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID
|
|||
}
|
||||
|
||||
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 {
|
||||
WARN_LOG(G3D, "Direct3D9: Failed to get the device caps!");
|
||||
truncate_cpy(shadeLangVersion_, "N/A");
|
||||
strcpy(shadeLangVersion_, "N/A");
|
||||
}
|
||||
|
||||
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_.clipPlanesSupported = caps.MaxUserClipPlanes;
|
||||
caps_.presentInstantModeChange = false;
|
||||
caps_.presentMaxInterval = 1;
|
||||
caps_.presentModesSupported = PresentMode::FIFO;
|
||||
|
||||
if ((caps.RasterCaps & D3DPRASTERCAPS_ANISOTROPY) != 0 && caps.MaxAnisotropy > 1) {
|
||||
caps_.anisoSupported = true;
|
||||
|
@ -941,12 +927,6 @@ Texture *D3D9Context::CreateTexture(const TextureDesc &desc) {
|
|||
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) {
|
||||
_assert_(start + count <= MAX_BOUND_TEXTURES);
|
||||
for (int i = start; i < start + count; i++) {
|
||||
|
@ -964,31 +944,10 @@ void D3D9Context::BindNativeTexture(int index, void *nativeTexture) {
|
|||
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() {
|
||||
frameTimeHistory_[frameCount_].firstSubmit = time_now_d();
|
||||
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) {
|
||||
*index = 0;
|
||||
switch (semantic) {
|
||||
|
@ -1025,7 +984,7 @@ D3D9InputLayout::D3D9InputLayout(LPDIRECT3DDEVICE9 device, const InputLayoutDesc
|
|||
D3DVERTEXELEMENT9 *elements = new D3DVERTEXELEMENT9[desc.attributes.size() + 1];
|
||||
size_t 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].Method = D3DDECLMETHOD_DEFAULT;
|
||||
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.
|
||||
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_);
|
||||
if (FAILED(hr)) {
|
||||
|
@ -1169,7 +1130,7 @@ inline int D3DPrimCount(D3DPRIMITIVETYPE prim, int size) {
|
|||
}
|
||||
|
||||
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_->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_);
|
||||
ApplyDynamicState();
|
||||
|
@ -1180,7 +1141,7 @@ void D3D9Context::DrawIndexed(int vertexCount, int offset) {
|
|||
curPipeline_->inputLayout->Apply(device_);
|
||||
curPipeline_->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_);
|
||||
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_->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_);
|
||||
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) {
|
||||
|
@ -1212,12 +1173,12 @@ void D3D9Context::SetScissorRect(int left, int top, int width, int height) {
|
|||
dxstate.scissorTest.set(true);
|
||||
}
|
||||
|
||||
void D3D9Context::SetViewport(const Viewport &viewport) {
|
||||
int x = (int)viewport.TopLeftX;
|
||||
int y = (int)viewport.TopLeftY;
|
||||
int w = (int)viewport.Width;
|
||||
int h = (int)viewport.Height;
|
||||
dxstate.viewport.set(x, y, w, h, viewport.MinDepth, viewport.MaxDepth);
|
||||
void D3D9Context::SetViewports(int count, Viewport *viewports) {
|
||||
int x = (int)viewports[0].TopLeftX;
|
||||
int y = (int)viewports[0].TopLeftY;
|
||||
int w = (int)viewports[0].Width;
|
||||
int h = (int)viewports[0].Height;
|
||||
dxstate.viewport.set(x, y, w, h, viewports[0].MinDepth, viewports[0].MaxDepth);
|
||||
}
|
||||
|
||||
void D3D9Context::SetBlendFactor(float color[4]) {
|
||||
|
@ -1303,7 +1264,18 @@ Framebuffer *D3D9Context::CreateFramebuffer(const FramebufferDesc &desc) {
|
|||
D3D9Framebuffer *fbo = new D3D9Framebuffer(desc.width, desc.height);
|
||||
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)) {
|
||||
ERROR_LOG(G3D, "Failed to create render target");
|
||||
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));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (fb) {
|
||||
|
@ -1628,7 +1600,6 @@ uint32_t D3D9Context::GetDataFormatSupport(DataFormat fmt) const {
|
|||
case DataFormat::BC1_RGBA_UNORM_BLOCK:
|
||||
case DataFormat::BC2_UNORM_BLOCK:
|
||||
case DataFormat::BC3_UNORM_BLOCK:
|
||||
// DXT1, DXT3, DXT5.
|
||||
return FMT_TEXTURE;
|
||||
default:
|
||||
return 0;
|
||||
|
|
|
@ -46,20 +46,22 @@ enum class DataFormat : uint8_t {
|
|||
// Block compression formats.
|
||||
// These are modern names for DXT and friends, now patent free.
|
||||
// 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.
|
||||
BC2_UNORM_BLOCK, // 4-bit straight alpha + DXT1 color. 128 bits per block. Usually not worth using
|
||||
BC3_UNORM_BLOCK, // 3-bit alpha with 2 ref values (+ magic) + DXT1 color. 128 bits per block.
|
||||
BC4_UNORM_BLOCK, // 1-channel, same storage as BC3 alpha. 64 bits per block.
|
||||
BC5_UNORM_BLOCK, // 2-channel RG, each has same storage as BC3 alpha. 128 bits per block.
|
||||
BC7_UNORM_BLOCK, // Highly advanced RGBA, very expensive to compress, very good quality. 128 bits per block.
|
||||
BC1_RGBA_UNORM_BLOCK,
|
||||
BC1_RGBA_SRGB_BLOCK,
|
||||
BC2_UNORM_BLOCK, // 4-bit straight alpha + DXT1 color. Usually not worth using
|
||||
BC2_SRGB_BLOCK,
|
||||
BC3_UNORM_BLOCK, // 3-bit alpha with 2 ref values (+ magic) + DXT1 color
|
||||
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.
|
||||
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,
|
||||
ETC1,
|
||||
|
||||
S8,
|
||||
D16,
|
||||
|
@ -74,7 +76,6 @@ bool DataFormatIsDepthStencil(DataFormat fmt);
|
|||
inline bool DataFormatIsColor(DataFormat fmt) {
|
||||
return !DataFormatIsDepthStencil(fmt);
|
||||
}
|
||||
bool DataFormatIsBlockCompressed(DataFormat fmt, int *blockSize);
|
||||
|
||||
// Limited format support for now.
|
||||
const char *DataFormatToString(DataFormat fmt);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
#include "Common/Common.h"
|
||||
|
||||
// Flags and structs shared between backends that haven't found a good home.
|
||||
|
||||
enum class InvalidationFlags {
|
||||
CACHED_RENDER_STATE = 1,
|
||||
};
|
||||
|
@ -16,22 +14,3 @@ enum class InvalidationCallbackFlags {
|
|||
ENUM_CLASS_BITOPS(InvalidationCallbackFlags);
|
||||
|
||||
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;
|
||||
|
|
|
@ -55,7 +55,7 @@ bool Thin3DFormatToGLFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuin
|
|||
internalFormat = GL_RGB;
|
||||
format = GL_RGB;
|
||||
type = GL_UNSIGNED_BYTE;
|
||||
alignment = 3;
|
||||
alignment = 1;
|
||||
break;
|
||||
|
||||
case DataFormat::R4G4B4A4_UNORM_PACK16:
|
||||
|
@ -86,75 +86,6 @@ bool Thin3DFormatToGLFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuin
|
|||
alignment = 16;
|
||||
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:
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -182,9 +182,7 @@ bool CheckGLExtensions() {
|
|||
gl_extensions.gpuVendor = GPU_VENDOR_IMGTEC;
|
||||
} else if (vendor == "Qualcomm") {
|
||||
gl_extensions.gpuVendor = GPU_VENDOR_QUALCOMM;
|
||||
if (1 != sscanf(renderer, "Adreno (TM) %d", &gl_extensions.modelNumber)) {
|
||||
gl_extensions.modelNumber = 300; // or what should we default to?
|
||||
}
|
||||
sscanf(renderer, "Adreno (TM) %d", &gl_extensions.modelNumber);
|
||||
} else if (vendor == "Broadcom") {
|
||||
gl_extensions.gpuVendor = GPU_VENDOR_BROADCOM;
|
||||
// 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_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_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) {
|
||||
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;
|
||||
|
@ -583,40 +575,6 @@ bool CheckGLExtensions() {
|
|||
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();
|
||||
|
||||
int error = glGetError();
|
||||
|
@ -628,13 +586,11 @@ bool CheckGLExtensions() {
|
|||
}
|
||||
|
||||
void SetGLCoreContext(bool flag) {
|
||||
if (!extensionsDone) {
|
||||
_assert_msg_(!extensionsDone, "SetGLCoreContext() after CheckGLExtensions()");
|
||||
|
||||
useCoreContext = flag;
|
||||
// For convenience, it'll get reset later.
|
||||
gl_extensions.IsCoreContext = useCoreContext;
|
||||
} else {
|
||||
_assert_(flag == useCoreContext);
|
||||
}
|
||||
}
|
||||
|
||||
void ResetGLExtensions() {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
// TODO: Replace with thin3d's vendor enum.
|
||||
enum {
|
||||
|
@ -18,7 +17,6 @@ enum {
|
|||
GPU_VENDOR_BROADCOM = 7, // Raspberry PI etc
|
||||
GPU_VENDOR_VIVANTE = 8,
|
||||
GPU_VENDOR_APPLE = 9,
|
||||
GPU_VENDOR_MESA = 10,
|
||||
GPU_VENDOR_UNKNOWN = 0,
|
||||
};
|
||||
|
||||
|
@ -54,7 +52,6 @@ struct GLExtensions {
|
|||
bool OES_copy_image;
|
||||
bool OES_texture_float;
|
||||
bool OES_texture_3D;
|
||||
bool OES_texture_compression_astc;
|
||||
|
||||
// ARB
|
||||
bool ARB_framebuffer_object;
|
||||
|
@ -76,14 +73,8 @@ struct GLExtensions {
|
|||
bool ARB_texture_non_power_of_two;
|
||||
bool ARB_stencil_texturing;
|
||||
bool ARB_shader_stencil_export;
|
||||
bool ARB_texture_compression_bptc;
|
||||
bool ARB_texture_compression_rgtc;
|
||||
|
||||
// KHR
|
||||
bool KHR_texture_compression_astc_ldr;
|
||||
|
||||
// EXT
|
||||
bool EXT_texture_compression_s3tc;
|
||||
bool EXT_swap_control_tear;
|
||||
bool EXT_discard_framebuffer;
|
||||
bool EXT_unpack_subimage; // always supported on desktop and ES3
|
||||
|
@ -124,12 +115,6 @@ struct GLExtensions {
|
|||
|
||||
int maxVertexTextureUnits;
|
||||
|
||||
bool supportsETC2;
|
||||
bool supportsBC123;
|
||||
bool supportsBC45;
|
||||
bool supportsBC7;
|
||||
bool supportsASTC;
|
||||
|
||||
// greater-or-equal than
|
||||
bool VersionGEThan(int major, int minor, int sub = 0);
|
||||
int GLSLVersion();
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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());
|
||||
}
|
|
@ -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_;
|
||||
};
|
|
@ -73,19 +73,24 @@ void GLQueueRunner::CreateDeviceObjects() {
|
|||
populate(GL_SHADING_LANGUAGE_VERSION);
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLQueueRunner::DestroyDeviceObjects() {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
if (!nameCache_.empty()) {
|
||||
glDeleteTextures((GLsizei)nameCache_.size(), &nameCache_[0]);
|
||||
nameCache_.clear();
|
||||
}
|
||||
if (gl_extensions.ARB_vertex_array_object) {
|
||||
glDeleteVertexArrays(1, &globalVAO_);
|
||||
}
|
||||
delete[] readbackBuffer_;
|
||||
readbackBuffer_ = nullptr;
|
||||
readbackBufferSize_ = 0;
|
||||
delete[] tempBuffer_;
|
||||
tempBuffer_ = nullptr;
|
||||
tempBufferSize_ = 0;
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
|
@ -107,20 +112,20 @@ static std::string GetInfoLog(GLuint name, Getiv getiv, GetLog getLog) {
|
|||
return infoLog;
|
||||
}
|
||||
|
||||
static int GetStereoBufferIndex(const char *uniformName) {
|
||||
int GLQueueRunner::GetStereoBufferIndex(const char *uniformName) {
|
||||
if (!uniformName) return -1;
|
||||
else if (strcmp(uniformName, "u_view") == 0) return 0;
|
||||
else if (strcmp(uniformName, "u_proj_lens") == 0) 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";
|
||||
else if (strcmp(uniformName, "u_proj_lens") == 0) return "ProjectionMatrix";
|
||||
else return "undefined";
|
||||
}
|
||||
|
||||
void GLQueueRunner::RunInitSteps(const FastVec<GLRInitStep> &steps, bool skipGLCalls) {
|
||||
void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool skipGLCalls) {
|
||||
if (skipGLCalls) {
|
||||
// Some bookkeeping still needs to be done.
|
||||
for (size_t i = 0; i < steps.size(); i++) {
|
||||
|
@ -325,8 +330,8 @@ void GLQueueRunner::RunInitSteps(const FastVec<GLRInitStep> &steps, bool skipGLC
|
|||
glCompileShader(shader);
|
||||
GLint success = 0;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
std::string infoLog = GetInfoLog(shader, glGetShaderiv, glGetShaderInfoLog);
|
||||
if (!success) {
|
||||
std::string errorString = StringFromFormat(
|
||||
"Error in shader compilation for: %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(),
|
||||
infoLog.c_str(),
|
||||
LineNumberString(code).c_str());
|
||||
std::vector<std::string_view> lines;
|
||||
std::vector<std::string> lines;
|
||||
SplitString(errorString, '\n', lines);
|
||||
for (auto line : lines) {
|
||||
ERROR_LOG(G3D, "%.*s", (int)line.size(), line.data());
|
||||
for (auto &line : lines) {
|
||||
ERROR_LOG(G3D, "%s", line.c_str());
|
||||
}
|
||||
if (errorCallback_) {
|
||||
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.
|
||||
|
||||
int blockSize = 0;
|
||||
bool bc = Draw::DataFormatIsBlockCompressed(step.texture_image.format, &blockSize);
|
||||
|
||||
GLenum internalFormat, format, type;
|
||||
int alignment;
|
||||
Thin3DFormatToGLFormatAndType(step.texture_image.format, internalFormat, format, type, alignment);
|
||||
if (step.texture_image.depth == 1) {
|
||||
if (bc) {
|
||||
int dataSize = ((step.texture_image.width + 3) & ~3) * ((step.texture_image.height + 3) & ~3) * blockSize / 16;
|
||||
glCompressedTexImage2D(tex->target, step.texture_image.level, internalFormat,
|
||||
step.texture_image.width, step.texture_image.height, 0, dataSize, 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 {
|
||||
glTexImage3D(tex->target,
|
||||
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_T, tex.wrapT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex.magFilter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex.minFilter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex.magFilter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex.minFilter);
|
||||
if (!gl_extensions.IsGLES || gl_extensions.GLES3) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
}
|
||||
|
@ -521,7 +517,22 @@ void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) {
|
|||
|
||||
// Color texture is same everywhere
|
||||
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:
|
||||
if (!fbo->z_stencil_) {
|
||||
|
@ -653,7 +664,7 @@ retry_depth:
|
|||
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 (keepSteps) {
|
||||
return;
|
||||
|
@ -702,7 +713,7 @@ void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, GLFrameData &f
|
|||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
size_t renderCount = 0;
|
||||
for (size_t i = 0; i < steps.size(); i++) {
|
||||
GLRStep &step = *steps[i];
|
||||
const GLRStep &step = *steps[i];
|
||||
|
||||
#if !defined(USING_GLES2)
|
||||
if (useDebugGroups_)
|
||||
|
@ -713,10 +724,11 @@ void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, GLFrameData &f
|
|||
case GLRStepType::RENDER:
|
||||
renderCount++;
|
||||
if (IsVREnabled()) {
|
||||
PreprocessStepVR(&step);
|
||||
PerformRenderPass(step, renderCount == 1, renderCount == totalRenderCount, frameData.profile);
|
||||
GLRStep vrStep = step;
|
||||
PreprocessStepVR(&vrStep);
|
||||
PerformRenderPass(vrStep, renderCount == 1, renderCount == totalRenderCount);
|
||||
} else {
|
||||
PerformRenderPass(step, renderCount == 1, renderCount == totalRenderCount, frameData.profile);
|
||||
PerformRenderPass(step, renderCount == 1, renderCount == totalRenderCount);
|
||||
}
|
||||
break;
|
||||
case GLRStepType::COPY:
|
||||
|
@ -742,17 +754,19 @@ void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, GLFrameData &f
|
|||
if (useDebugGroups_)
|
||||
glPopDebugGroup();
|
||||
#endif
|
||||
if (frameData.profile.enabled) {
|
||||
frameData.profile.passesString += StepToString(step);
|
||||
}
|
||||
|
||||
if (!keepSteps) {
|
||||
delete steps[i];
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
void GLQueueRunner::LogSteps(const std::vector<GLRStep *> &steps) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
void GLQueueRunner::PerformBlit(const GLRStep &step) {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
// 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) {
|
||||
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) {
|
||||
void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last) {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
|
||||
PerformBindFramebufferAsRenderTarget(step);
|
||||
|
@ -812,9 +813,10 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
|||
glDisable(GL_COLOR_LOGIC_OP);
|
||||
}
|
||||
#endif
|
||||
if (gl_extensions.ARB_vertex_array_object) {
|
||||
glBindVertexArray(globalVAO_);
|
||||
}
|
||||
|
||||
if (first && gl_extensions.ARB_vertex_array_object) {
|
||||
glBindVertexArray(globalVAO_);
|
||||
}
|
||||
|
||||
GLRProgram *curProgram = nullptr;
|
||||
|
@ -840,43 +842,12 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
|||
bool clipDistanceEnabled[8]{};
|
||||
GLuint blendEqColor = (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]{};
|
||||
|
||||
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();
|
||||
auto &commands = step.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) {
|
||||
case GLRRenderCommand::DEPTH:
|
||||
if (c.depth.enabled) {
|
||||
|
@ -892,39 +863,28 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
|||
glDepthFunc(c.depth.func);
|
||||
depthFunc = c.depth.func;
|
||||
}
|
||||
} else if (/* !c.depth.enabled && */ depthEnabled) {
|
||||
} else if (!c.depth.enabled && depthEnabled) {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
depthEnabled = false;
|
||||
}
|
||||
break;
|
||||
case GLRRenderCommand::STENCIL:
|
||||
if (c.stencil.enabled) {
|
||||
case GLRRenderCommand::STENCILFUNC:
|
||||
if (c.stencilFunc.enabled) {
|
||||
if (!stencilEnabled) {
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
stencilEnabled = true;
|
||||
}
|
||||
if (c.stencil.func != stencilFunc || c.stencil.ref != stencilRef || c.stencil.compareMask != stencilCompareMask) {
|
||||
glStencilFunc(c.stencil.func, c.stencil.ref, c.stencil.compareMask);
|
||||
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) {
|
||||
glStencilFunc(c.stencilFunc.func, c.stencilFunc.ref, c.stencilFunc.compareMask);
|
||||
} else if (stencilEnabled) {
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
stencilEnabled = false;
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
break;
|
||||
case GLRRenderCommand::STENCILOP:
|
||||
glStencilOp(c.stencilOp.sFail, c.stencilOp.zFail, c.stencilOp.pass);
|
||||
glStencilMask(c.stencilOp.writeMask);
|
||||
break;
|
||||
case GLRRenderCommand::BLEND:
|
||||
if (c.blend.enabled) {
|
||||
if (!blendEnabled) {
|
||||
|
@ -936,14 +896,8 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
|||
blendEqColor = c.blend.funcColor;
|
||||
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);
|
||||
blendSrcColor = c.blend.srcColor;
|
||||
blendDstColor = c.blend.dstColor;
|
||||
blendSrcAlpha = c.blend.srcAlpha;
|
||||
blendDstAlpha = c.blend.dstAlpha;
|
||||
}
|
||||
} else if (/* !c.blend.enabled && */ blendEnabled) {
|
||||
} else if (!c.blend.enabled && blendEnabled) {
|
||||
glDisable(GL_BLEND);
|
||||
blendEnabled = false;
|
||||
}
|
||||
|
@ -963,7 +917,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
|||
if (logicOp != c.logic.logicOp) {
|
||||
glLogicOp(c.logic.logicOp);
|
||||
}
|
||||
} else if (/* !c.logic.enabled && */ logicEnabled) {
|
||||
} else if (!c.logic.enabled && logicEnabled) {
|
||||
glDisable(GL_COLOR_LOGIC_OP);
|
||||
logicEnabled = false;
|
||||
}
|
||||
|
@ -1020,17 +974,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
|||
y = curFBHeight_ - y - c.viewport.vp.h;
|
||||
|
||||
// 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);
|
||||
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 (gl_extensions.IsGLES) {
|
||||
glDepthRangef(c.viewport.vp.minZ, c.viewport.vp.maxZ);
|
||||
|
@ -1040,7 +984,6 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
|||
#else
|
||||
glDepthRangef(c.viewport.vp.minZ, c.viewport.vp.maxZ);
|
||||
#endif
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
break;
|
||||
}
|
||||
|
@ -1049,30 +992,30 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
|||
int y = c.scissor.rc.y;
|
||||
if (!curFB_)
|
||||
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);
|
||||
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();
|
||||
break;
|
||||
}
|
||||
case GLRRenderCommand::UNIFORM4F:
|
||||
{
|
||||
_dbg_assert_(curProgram);
|
||||
int loc = c.uniform4.loc ? *c.uniform4.loc : -1;
|
||||
if (c.uniform4.name) {
|
||||
loc = curProgram->GetUniformLoc(c.uniform4.name);
|
||||
}
|
||||
if (loc >= 0) {
|
||||
_dbg_assert_(c.uniform4.count >=1 && c.uniform4.count <=4);
|
||||
switch (c.uniform4.count) {
|
||||
case 1: glUniform1f(loc, c.uniform4.v[0]); 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;
|
||||
case 1:
|
||||
glUniform1f(loc, c.uniform4.v[0]);
|
||||
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();
|
||||
|
@ -1086,7 +1029,6 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
|||
loc = curProgram->GetUniformLoc(c.uniform4.name);
|
||||
}
|
||||
if (loc >= 0) {
|
||||
_dbg_assert_(c.uniform4.count >=1 && c.uniform4.count <=4);
|
||||
switch (c.uniform4.count) {
|
||||
case 1: glUniform1uiv(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);
|
||||
}
|
||||
if (loc >= 0) {
|
||||
_dbg_assert_(c.uniform4.count >=1 && c.uniform4.count <=4);
|
||||
switch (c.uniform4.count) {
|
||||
case 1: glUniform1iv(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);
|
||||
if (IsMultiviewSupported()) {
|
||||
int layout = GetStereoBufferIndex(c.uniformStereoMatrix4.name);
|
||||
int layout = GetStereoBufferIndex(c.uniformMatrix4.name);
|
||||
if (layout >= 0) {
|
||||
int size = 2 * 16 * sizeof(float);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, layout, *c.uniformStereoMatrix4.loc);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, *c.uniformStereoMatrix4.loc);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, layout, *c.uniformMatrix4.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);
|
||||
memcpy(matrices, c.uniformStereoMatrix4.mData, size);
|
||||
memcpy(matrices, c.uniformMatrix4.m, size);
|
||||
glUnmapBuffer(GL_UNIFORM_BUFFER);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
delete[] c.uniformStereoMatrix4.mData; // We only playback once.
|
||||
} else {
|
||||
int loc = c.uniformStereoMatrix4.loc ? *c.uniformStereoMatrix4.loc : -1;
|
||||
if (c.uniformStereoMatrix4.name) {
|
||||
loc = curProgram->GetUniformLoc(c.uniformStereoMatrix4.name);
|
||||
int loc = c.uniformMatrix4.loc ? *c.uniformMatrix4.loc : -1;
|
||||
if (c.uniformMatrix4.name) {
|
||||
loc = curProgram->GetUniformLoc(c.uniformMatrix4.name);
|
||||
}
|
||||
if (loc >= 0) {
|
||||
if (GetVRFBOIndex() == 0) {
|
||||
glUniformMatrix4fv(loc, 1, false, c.uniformStereoMatrix4.mData);
|
||||
glUniformMatrix4fv(loc, 1, false, c.uniformMatrix4.m);
|
||||
} 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();
|
||||
break;
|
||||
|
@ -1236,37 +1171,52 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
|||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
break;
|
||||
}
|
||||
case GLRRenderCommand::DRAW:
|
||||
case GLRRenderCommand::BIND_VERTEX_BUFFER:
|
||||
{
|
||||
GLRInputLayout *layout = c.draw.inputLayout;
|
||||
GLuint buf = c.draw.vertexBuffer->buffer_;
|
||||
_dbg_assert_(!c.draw.vertexBuffer->Mapped());
|
||||
// TODO: Add fast path for glBindVertexBuffer
|
||||
GLRInputLayout *layout = c.bindVertexBuffer.inputLayout;
|
||||
GLuint buf = c.bindVertexBuffer.buffer ? c.bindVertexBuffer.buffer->buffer_ : 0;
|
||||
_dbg_assert_(!c.bindVertexBuffer.buffer || !c.bindVertexBuffer.buffer->Mapped());
|
||||
if (buf != curArrayBuffer) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buf);
|
||||
curArrayBuffer = buf;
|
||||
}
|
||||
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_;
|
||||
}
|
||||
for (size_t i = 0; i < layout->entries.size(); 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) {
|
||||
GLuint buf = c.draw.indexBuffer->buffer_;
|
||||
_dbg_assert_(!c.draw.indexBuffer->Mapped());
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
break;
|
||||
}
|
||||
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) {
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 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 {
|
||||
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();
|
||||
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?
|
||||
// Also, should this not be an init command?
|
||||
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();
|
||||
break;
|
||||
case GLRRenderCommand::TEXTURESAMPLER:
|
||||
|
@ -1322,7 +1282,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
|||
}
|
||||
case GLRRenderCommand::TEXTURELOD:
|
||||
{
|
||||
GLint slot = c.textureLod.slot;
|
||||
GLint slot = c.textureSampler.slot;
|
||||
if (slot != activeSlot) {
|
||||
glActiveTexture(GL_TEXTURE0 + slot);
|
||||
activeSlot = slot;
|
||||
|
@ -1378,15 +1338,9 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
|||
glEnable(GL_CULL_FACE);
|
||||
cullEnabled = true;
|
||||
}
|
||||
if (frontFace != c.raster.frontFace) {
|
||||
glFrontFace(c.raster.frontFace);
|
||||
frontFace = c.raster.frontFace;
|
||||
}
|
||||
if (cullFace != c.raster.cullFace) {
|
||||
glCullFace(c.raster.cullFace);
|
||||
cullFace = c.raster.cullFace;
|
||||
}
|
||||
} else if (/* !c.raster.cullEnable && */ cullEnabled) {
|
||||
} else if (!c.raster.cullEnable && cullEnabled) {
|
||||
glDisable(GL_CULL_FACE);
|
||||
cullEnabled = false;
|
||||
}
|
||||
|
@ -1395,7 +1349,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
|||
glEnable(GL_DITHER);
|
||||
ditherEnabled = true;
|
||||
}
|
||||
} else if (/* !c.raster.ditherEnable && */ ditherEnabled) {
|
||||
} else if (!c.raster.ditherEnable && ditherEnabled) {
|
||||
glDisable(GL_DITHER);
|
||||
ditherEnabled = false;
|
||||
}
|
||||
|
@ -1405,7 +1359,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
|||
glEnable(GL_DEPTH_CLAMP);
|
||||
depthClampEnabled = true;
|
||||
}
|
||||
} else if (/* !c.raster.depthClampEnable && */ depthClampEnabled) {
|
||||
} else if (!c.raster.depthClampEnable && depthClampEnabled) {
|
||||
glDisable(GL_DEPTH_CLAMP);
|
||||
depthClampEnabled = false;
|
||||
}
|
||||
|
@ -1542,24 +1496,26 @@ void GLQueueRunner::PerformReadback(const GLRStep &pass) {
|
|||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
|
||||
// Always read back in 8888 format for the color aspect.
|
||||
GLuint internalFormat = GL_RGBA;
|
||||
GLuint format = GL_RGBA;
|
||||
GLuint type = GL_UNSIGNED_BYTE;
|
||||
int srcAlignment = 4;
|
||||
int dstAlignment = (int)DataFormatSizeInBytes(pass.readback.dstFormat);
|
||||
|
||||
#ifndef USING_GLES2
|
||||
if (pass.readback.aspectMask & GL_DEPTH_BUFFER_BIT) {
|
||||
internalFormat = GL_DEPTH_COMPONENT;
|
||||
format = GL_DEPTH_COMPONENT;
|
||||
type = GL_FLOAT;
|
||||
srcAlignment = 4;
|
||||
} else if (pass.readback.aspectMask & GL_STENCIL_BUFFER_BIT) {
|
||||
internalFormat = GL_STENCIL_INDEX;
|
||||
format = GL_STENCIL_INDEX;
|
||||
type = GL_UNSIGNED_BYTE;
|
||||
srcAlignment = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
readbackAspectMask_ = pass.readback.aspectMask;
|
||||
|
||||
int pixelStride = pass.readback.srcRect.w;
|
||||
// Apply the correct alignment.
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, srcAlignment);
|
||||
|
@ -1570,20 +1526,31 @@ void GLQueueRunner::PerformReadback(const GLRStep &pass) {
|
|||
|
||||
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_) {
|
||||
delete[] readbackBuffer_;
|
||||
readbackBuffer_ = new uint8_t[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
|
||||
LogReadPixelsError(glGetError());
|
||||
#endif
|
||||
if (!gl_extensions.IsGLES || gl_extensions.GLES3) {
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -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_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_) {
|
||||
delete[] readbackBuffer_;
|
||||
readbackBuffer_ = new uint8_t[size];
|
||||
|
@ -1663,7 +1630,7 @@ void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) {
|
|||
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
|
||||
// don't usually provide very optimized conversion implementations, though some do.
|
||||
// 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.
|
||||
return;
|
||||
}
|
||||
|
||||
// Always read back in 8888 format for the color aspect.
|
||||
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;
|
||||
if (convert) {
|
||||
// srcStride is width because we read back "packed" (with no gaps) from GL.
|
||||
ConvertFromRGBA8888(pixels, readbackBuffer_, pixelStride, width, width, height, destFormat);
|
||||
} else {
|
||||
for (int y = 0; y < height; y++) {
|
||||
memcpy(pixels + y * pixelStride * bpp, readbackBuffer_ + y * width * bpp, width * bpp);
|
||||
}
|
||||
}
|
||||
|
||||
GLuint GLQueueRunner::AllocTextureName() {
|
||||
if (nameCache_.empty()) {
|
||||
nameCache_.resize(TEXCACHE_NAME_CACHE_SIZE);
|
||||
glGenTextures(TEXCACHE_NAME_CACHE_SIZE, &nameCache_[0]);
|
||||
}
|
||||
u32 name = nameCache_.back();
|
||||
nameCache_.pop_back();
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
return name;
|
||||
}
|
||||
|
||||
// On PC, we always use GL_DEPTH24_STENCIL8.
|
||||
|
@ -1812,7 +1773,7 @@ void GLQueueRunner::fbo_unbind() {
|
|||
glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO);
|
||||
#endif
|
||||
|
||||
#if PPSSPP_PLATFORM(IOS) && !defined(__LIBRETRO__)
|
||||
#if PPSSPP_PLATFORM(IOS)
|
||||
bindDefaultFBO();
|
||||
#endif
|
||||
|
||||
|
@ -1853,74 +1814,3 @@ GLRFramebuffer::~GLRFramebuffer() {
|
|||
glDeleteRenderbuffers(1, &stencil_buffer);
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,11 @@
|
|||
#include <unordered_map>
|
||||
|
||||
#include "Common/GPU/OpenGL/GLCommon.h"
|
||||
#include "Common/GPU/OpenGL/GLFrameData.h"
|
||||
#include "Common/GPU/DataFormat.h"
|
||||
#include "Common/GPU/Shader.h"
|
||||
#include "Common/GPU/thin3d.h"
|
||||
#include "Common/Data/Collections/TinySet.h"
|
||||
#include "Common/Data/Collections/FastVec.h"
|
||||
|
||||
|
||||
struct GLRViewport {
|
||||
float x, y, w, h, minZ, maxZ;
|
||||
|
@ -25,7 +24,7 @@ struct GLOffset2D {
|
|||
int x, y;
|
||||
};
|
||||
|
||||
enum class GLRAllocType : uint8_t {
|
||||
enum class GLRAllocType {
|
||||
NONE,
|
||||
NEW,
|
||||
ALIGNED,
|
||||
|
@ -40,7 +39,8 @@ class GLRInputLayout;
|
|||
|
||||
enum class GLRRenderCommand : uint8_t {
|
||||
DEPTH,
|
||||
STENCIL,
|
||||
STENCILFUNC,
|
||||
STENCILOP,
|
||||
BLEND,
|
||||
BLENDCOLOR,
|
||||
LOGICOP,
|
||||
|
@ -60,8 +60,10 @@ enum class GLRRenderCommand : uint8_t {
|
|||
BINDTEXTURE,
|
||||
BIND_FB_TEXTURE,
|
||||
BIND_VERTEX_BUFFER,
|
||||
BIND_BUFFER,
|
||||
GENMIPS,
|
||||
DRAW,
|
||||
DRAW_INDEXED,
|
||||
TEXTURE_SUBIMAGE,
|
||||
};
|
||||
|
||||
|
@ -69,7 +71,6 @@ enum class GLRRenderCommand : uint8_t {
|
|||
// type field, smashed right after each other?)
|
||||
// Also, all GLenums are really only 16 bits.
|
||||
struct GLRRenderData {
|
||||
GLRRenderData(GLRRenderCommand _cmd) : cmd(_cmd) {}
|
||||
GLRRenderCommand cmd;
|
||||
union {
|
||||
struct {
|
||||
|
@ -99,23 +100,26 @@ struct GLRRenderData {
|
|||
GLenum func;
|
||||
uint8_t ref;
|
||||
uint8_t compareMask;
|
||||
} stencilFunc;
|
||||
struct {
|
||||
GLenum sFail;
|
||||
GLenum zFail;
|
||||
GLenum pass;
|
||||
uint8_t writeMask;
|
||||
} stencil;
|
||||
} stencilOp; // also write mask
|
||||
struct {
|
||||
GLRInputLayout *inputLayout;
|
||||
GLRBuffer *vertexBuffer;
|
||||
GLRBuffer *indexBuffer;
|
||||
uint32_t vertexOffset;
|
||||
uint32_t indexOffset;
|
||||
GLenum mode; // primitive
|
||||
GLint buffer;
|
||||
GLint first;
|
||||
GLint count;
|
||||
GLint indexType;
|
||||
GLint instances;
|
||||
} draw;
|
||||
struct {
|
||||
GLenum mode; // primitive
|
||||
GLint count;
|
||||
GLint instances;
|
||||
GLint indexType;
|
||||
void *indices;
|
||||
} drawIndexed;
|
||||
struct {
|
||||
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.
|
||||
|
@ -125,13 +129,8 @@ struct GLRRenderData {
|
|||
struct {
|
||||
const char *name; // if null, use loc
|
||||
const GLint *loc;
|
||||
float m[16];
|
||||
float m[32];
|
||||
} uniformMatrix4;
|
||||
struct {
|
||||
const char *name; // if null, use loc
|
||||
const GLint *loc;
|
||||
float *mData; // new'd, 32 entries
|
||||
} uniformStereoMatrix4;
|
||||
struct {
|
||||
uint32_t clearColor;
|
||||
float clearZ;
|
||||
|
@ -171,6 +170,11 @@ struct GLRRenderData {
|
|||
struct {
|
||||
GLRProgram *program;
|
||||
} program;
|
||||
struct {
|
||||
GLRInputLayout *inputLayout;
|
||||
GLRBuffer *buffer;
|
||||
size_t offset;
|
||||
} bindVertexBuffer;
|
||||
struct {
|
||||
int slot;
|
||||
GLenum wrapS;
|
||||
|
@ -223,6 +227,7 @@ struct GLRInitStep {
|
|||
union {
|
||||
struct {
|
||||
GLRTexture *texture;
|
||||
GLenum target;
|
||||
} create_texture;
|
||||
struct {
|
||||
GLRShader *shader;
|
||||
|
@ -290,17 +295,16 @@ enum class GLRRenderPassAction {
|
|||
|
||||
class GLRFramebuffer;
|
||||
|
||||
enum GLRAspect {
|
||||
enum {
|
||||
GLR_ASPECT_COLOR = 1,
|
||||
GLR_ASPECT_DEPTH = 2,
|
||||
GLR_ASPECT_STENCIL = 3,
|
||||
};
|
||||
const char *GLRAspectToString(GLRAspect aspect);
|
||||
|
||||
struct GLRStep {
|
||||
GLRStep(GLRStepType _type) : stepType(_type) {}
|
||||
GLRStepType stepType;
|
||||
FastVec<GLRRenderData> commands;
|
||||
std::vector<GLRRenderData> commands;
|
||||
TinySet<const GLRFramebuffer *, 8> dependencies;
|
||||
const char *tag;
|
||||
union {
|
||||
|
@ -309,6 +313,8 @@ struct GLRStep {
|
|||
GLRRenderPassAction color;
|
||||
GLRRenderPassAction depth;
|
||||
GLRRenderPassAction stencil;
|
||||
// Note: not accurate.
|
||||
int numDraws;
|
||||
} render;
|
||||
struct {
|
||||
GLRFramebuffer *src;
|
||||
|
@ -352,14 +358,22 @@ public:
|
|||
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 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) {
|
||||
targetWidth_ = width;
|
||||
|
@ -379,7 +393,7 @@ private:
|
|||
void InitCreateFramebuffer(const GLRInitStep &step);
|
||||
|
||||
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 PerformBlit(const GLRStep &pass);
|
||||
void PerformReadback(const GLRStep &pass);
|
||||
|
@ -390,8 +404,6 @@ private:
|
|||
GLenum fbo_get_fb_target(bool read, GLuint **cached);
|
||||
void fbo_unbind();
|
||||
|
||||
std::string StepToString(const GLRStep &step) const;
|
||||
|
||||
GLRFramebuffer *curFB_ = nullptr;
|
||||
|
||||
GLuint globalVAO_ = 0;
|
||||
|
@ -407,7 +419,9 @@ private:
|
|||
// We size it generously.
|
||||
uint8_t *readbackBuffer_ = nullptr;
|
||||
int readbackBufferSize_ = 0;
|
||||
uint32_t readbackAspectMask_ = 0;
|
||||
// Temp buffer for color conversion
|
||||
uint8_t *tempBuffer_ = nullptr;
|
||||
int tempBufferSize_ = 0;
|
||||
|
||||
float maxAnisotropyLevel_ = 0.0f;
|
||||
|
||||
|
@ -415,6 +429,10 @@ private:
|
|||
GLuint currentDrawHandle_ = 0;
|
||||
GLuint currentReadHandle_ = 0;
|
||||
|
||||
GLuint AllocTextureName();
|
||||
|
||||
// Texture name cache. Ripped straight from TextureCacheGLES.
|
||||
std::vector<GLuint> nameCache_;
|
||||
std::unordered_map<int, std::string> glStrings_;
|
||||
|
||||
bool sawOutOfMemory_ = false;
|
||||
|
@ -423,5 +441,3 @@ private:
|
|||
ErrorCallbackFn errorCallback_ = nullptr;
|
||||
void *errorCallbackUserData_ = nullptr;
|
||||
};
|
||||
|
||||
const char *RenderCommandToString(GLRRenderCommand cmd);
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "Common/Log.h"
|
||||
#include "Common/TimeUtil.h"
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/Math/math_util.h"
|
||||
|
||||
#if 0 // def _DEBUG
|
||||
|
@ -17,7 +16,12 @@
|
|||
#define VLOG(...)
|
||||
#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) {
|
||||
if (caps.textureNPOTFullySupported) {
|
||||
|
@ -37,9 +41,76 @@ GLRTexture::~GLRTexture() {
|
|||
}
|
||||
}
|
||||
|
||||
GLRenderManager::GLRenderManager(HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory) : frameTimeHistory_(frameTimeHistory) {
|
||||
// size_t sz = sizeof(GLRRenderData);
|
||||
// _dbg_assert_(sz == 88);
|
||||
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 = 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() {
|
||||
|
@ -129,25 +200,25 @@ bool GLRenderManager::ThreadFrame() {
|
|||
return false;
|
||||
}
|
||||
|
||||
GLRRenderThreadTask *task = nullptr;
|
||||
GLRRenderThreadTask task;
|
||||
|
||||
// In case of syncs or other partial completion, we keep going until we complete a frame.
|
||||
while (true) {
|
||||
// Pop a task of the queue and execute it.
|
||||
// NOTE: We need to actually wait for a task, we can't just bail!
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(pushMutex_);
|
||||
while (renderThreadQueue_.empty()) {
|
||||
pushCondVar_.wait(lock);
|
||||
}
|
||||
task = std::move(renderThreadQueue_.front());
|
||||
task = renderThreadQueue_.front();
|
||||
renderThreadQueue_.pop();
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (task->runType == GLRRunType::EXIT) {
|
||||
delete task;
|
||||
if (task.runType == GLRRunType::EXIT) {
|
||||
// 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.
|
||||
std::unique_lock<std::mutex> lock(syncMutex_);
|
||||
|
@ -157,13 +228,11 @@ bool GLRenderManager::ThreadFrame() {
|
|||
}
|
||||
|
||||
// Render the scene.
|
||||
VLOG(" PULL: Frame %d RUN (%0.3f)", task->frame, time_now_d());
|
||||
if (Run(*task)) {
|
||||
VLOG(" PULL: Frame %d RUN (%0.3f)", task.frame, time_now_d());
|
||||
if (Run(task)) {
|
||||
// Swap requested, so we just bail the loop.
|
||||
delete task;
|
||||
break;
|
||||
}
|
||||
delete task;
|
||||
};
|
||||
|
||||
return true;
|
||||
|
@ -176,21 +245,15 @@ void GLRenderManager::StopThread() {
|
|||
run_ = false;
|
||||
|
||||
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();
|
||||
} else {
|
||||
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) {
|
||||
_assert_(insideFrame_);
|
||||
#ifdef _DEBUG
|
||||
|
@ -198,7 +261,7 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende
|
|||
#endif
|
||||
|
||||
// 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) {
|
||||
// We don't move to a new step, this bind was unnecessary and we can safely skip it.
|
||||
curRenderStep_ = steps_.back();
|
||||
|
@ -206,7 +269,7 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende
|
|||
}
|
||||
}
|
||||
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 };
|
||||
|
@ -215,11 +278,13 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende
|
|||
step->render.color = color;
|
||||
step->render.depth = depth;
|
||||
step->render.stencil = stencil;
|
||||
step->render.numDraws = 0;
|
||||
step->tag = tag;
|
||||
steps_.push_back(step);
|
||||
|
||||
GLuint clearMask = 0;
|
||||
GLRRenderData data(GLRRenderCommand::CLEAR);
|
||||
GLRRenderData data;
|
||||
data.cmd = GLRRenderCommand::CLEAR;
|
||||
if (color == GLRRenderPassAction::CLEAR) {
|
||||
clearMask |= GL_COLOR_BUFFER_BIT;
|
||||
data.clear.clearColor = clearColor;
|
||||
|
@ -296,7 +361,7 @@ void GLRenderManager::BlitFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLR
|
|||
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);
|
||||
|
||||
GLRStep *step = new GLRStep{ GLRStepType::READBACK };
|
||||
|
@ -323,7 +388,7 @@ bool GLRenderManager::CopyFramebufferToMemory(GLRFramebuffer *src, int aspectBit
|
|||
} else {
|
||||
return false;
|
||||
}
|
||||
queueRunner_.CopyFromReadbackBuffer(src, w, h, srcFormat, destFormat, pixelStride, pixels);
|
||||
queueRunner_.CopyReadbackBuffer(w, h, srcFormat, destFormat, pixelStride, pixels);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -340,28 +405,20 @@ void GLRenderManager::CopyImageToMemorySync(GLRTexture *texture, int mipLevel, i
|
|||
curRenderStep_ = nullptr;
|
||||
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
|
||||
curProgram_ = nullptr;
|
||||
#endif
|
||||
|
||||
int curFrame = GetCurFrame();
|
||||
|
||||
FrameTimeData &frameTimeData = frameTimeHistory_.Add(frameIdGen_);
|
||||
frameTimeData.frameBegin = time_now_d();
|
||||
frameTimeData.afterFenceWait = frameTimeData.frameBegin;
|
||||
|
||||
GLFrameData &frameData = frameData_[curFrame];
|
||||
frameData.frameId = frameIdGen_;
|
||||
frameData.profile.enabled = enableProfiling;
|
||||
|
||||
frameIdGen_++;
|
||||
FrameData &frameData = frameData_[curFrame];
|
||||
{
|
||||
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());
|
||||
std::unique_lock<std::mutex> lock(frameData.fenceMutex);
|
||||
while (!frameData.readyForFence) {
|
||||
frameData.fenceCondVar.wait(lock);
|
||||
}
|
||||
|
@ -378,99 +435,36 @@ void GLRenderManager::BeginFrame(bool enableProfiling) {
|
|||
void GLRenderManager::Finish() {
|
||||
curRenderStep_ = nullptr; // EndCurRenderStep is this simple here.
|
||||
|
||||
int curFrame = curFrame_;
|
||||
GLFrameData &frameData = frameData_[curFrame];
|
||||
|
||||
frameTimeHistory_[frameData.frameId].firstSubmit = time_now_d();
|
||||
int curFrame = GetCurFrame();
|
||||
FrameData &frameData = frameData_[curFrame];
|
||||
|
||||
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);
|
||||
GLRRenderThreadTask *task = new GLRRenderThreadTask(GLRRunType::SUBMIT);
|
||||
task->frame = curFrame;
|
||||
GLRRenderThreadTask task;
|
||||
task.frame = curFrame;
|
||||
task.runType = GLRRunType::PRESENT;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(pushMutex_);
|
||||
renderThreadQueue_.push(task);
|
||||
renderThreadQueue_.back()->initSteps = std::move(initSteps_);
|
||||
renderThreadQueue_.back()->steps = std::move(steps_);
|
||||
renderThreadQueue_.back().initSteps = std::move(initSteps_);
|
||||
renderThreadQueue_.back().steps = std::move(steps_);
|
||||
initSteps_.clear();
|
||||
steps_.clear();
|
||||
pushCondVar_.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void GLRenderManager::Present() {
|
||||
GLRRenderThreadTask *presentTask = new GLRRenderThreadTask(GLRRunType::PRESENT);
|
||||
presentTask->frame = curFrame_;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(pushMutex_);
|
||||
renderThreadQueue_.push(presentTask);
|
||||
pushCondVar_.notify_one();
|
||||
}
|
||||
|
||||
int newCurFrame = curFrame_ + 1;
|
||||
if (newCurFrame >= inflightFrames_) {
|
||||
newCurFrame = 0;
|
||||
}
|
||||
curFrame_ = newCurFrame;
|
||||
curFrame_++;
|
||||
if (curFrame_ >= inflightFrames_)
|
||||
curFrame_ = 0;
|
||||
|
||||
insideFrame_ = false;
|
||||
}
|
||||
|
||||
// Render thread. Returns true if the caller should handle a swap.
|
||||
bool GLRenderManager::Run(GLRRenderThreadTask &task) {
|
||||
_dbg_assert_(task.frame >= 0);
|
||||
|
||||
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;
|
||||
}
|
||||
FrameData &frameData = frameData_[task.frame];
|
||||
|
||||
if (!frameData.hasBegun) {
|
||||
frameData.hasBegun = true;
|
||||
|
@ -490,23 +484,15 @@ bool GLRenderManager::Run(GLRRenderThreadTask &task) {
|
|||
}
|
||||
}
|
||||
|
||||
if (frameData.profile.enabled) {
|
||||
frameData.profile.cpuStartTime = time_now_d();
|
||||
}
|
||||
|
||||
if (IsVREnabled()) {
|
||||
int passes = GetVRPassesCount();
|
||||
for (int i = 0; i < passes; i++) {
|
||||
PreVRFrameRender(i);
|
||||
queueRunner_.RunSteps(task.steps, frameData, skipGLCalls_, i < passes - 1, true);
|
||||
queueRunner_.RunSteps(task.steps, skipGLCalls_, i < passes - 1, true);
|
||||
PostVRFrameRender();
|
||||
}
|
||||
} else {
|
||||
queueRunner_.RunSteps(task.steps, frameData, skipGLCalls_, false, false);
|
||||
}
|
||||
|
||||
if (frameData.profile.enabled) {
|
||||
frameData.profile.cpuEndTime = time_now_d();
|
||||
queueRunner_.RunSteps(task.steps, skipGLCalls_, false, false);
|
||||
}
|
||||
|
||||
if (!skipGLCalls_) {
|
||||
|
@ -515,8 +501,43 @@ bool GLRenderManager::Run(GLRRenderThreadTask &task) {
|
|||
}
|
||||
}
|
||||
|
||||
bool swapRequest = false;
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
// glBufferStorage. Then we need to use fences.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(syncMutex_);
|
||||
std::unique_lock<std::mutex> lock(syncMutex_);
|
||||
syncDone_ = true;
|
||||
syncCondVar_.notify_one();
|
||||
}
|
||||
|
@ -535,20 +556,21 @@ bool GLRenderManager::Run(GLRRenderThreadTask &task) {
|
|||
_assert_(false);
|
||||
}
|
||||
VLOG(" PULL: ::Run(): Done running tasks");
|
||||
return false;
|
||||
return swapRequest;
|
||||
}
|
||||
|
||||
void GLRenderManager::FlushSync() {
|
||||
{
|
||||
VLOG("PUSH: Frame[%d].readyForRun = true (sync)", curFrame_);
|
||||
|
||||
GLRRenderThreadTask *task = new GLRRenderThreadTask(GLRRunType::SYNC);
|
||||
task->frame = curFrame_;
|
||||
GLRRenderThreadTask task;
|
||||
task.frame = curFrame_;
|
||||
task.runType = GLRRunType::SYNC;
|
||||
|
||||
std::unique_lock<std::mutex> lock(pushMutex_);
|
||||
renderThreadQueue_.push(task);
|
||||
renderThreadQueue_.back()->initSteps = std::move(initSteps_);
|
||||
renderThreadQueue_.back()->steps = std::move(steps_);
|
||||
renderThreadQueue_.back().initSteps = std::move(initSteps_);
|
||||
renderThreadQueue_.back().steps = std::move(steps_);
|
||||
pushCondVar_.notify_one();
|
||||
steps_.clear();
|
||||
}
|
||||
|
@ -563,3 +585,254 @@ void GLRenderManager::FlushSync() {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -10,13 +10,11 @@
|
|||
#include <queue>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "Common/GPU/OpenGL/GLCommon.h"
|
||||
#include "Common/GPU/MiscTypes.h"
|
||||
#include "Common/Data/Convert/SmallDataConvert.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/GPU/OpenGL/GLQueueRunner.h"
|
||||
#include "Common/GPU/OpenGL/GLFrameData.h"
|
||||
#include "Common/GPU/OpenGL/GLCommon.h"
|
||||
#include "Common/GPU/OpenGL/GLMemory.h"
|
||||
#include "GLQueueRunner.h"
|
||||
|
||||
class GLRInputLayout;
|
||||
class GLPushBuffer;
|
||||
|
@ -53,13 +51,11 @@ public:
|
|||
|
||||
class GLRFramebuffer {
|
||||
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),
|
||||
width(_width), height(_height), z_stencil_(z_stencil) {
|
||||
}
|
||||
~GLRFramebuffer();
|
||||
colorFormat(_colorFormat), width(_width), height(_height), z_stencil_(z_stencil) {}
|
||||
|
||||
const char *Tag() const { return tag_.c_str(); }
|
||||
~GLRFramebuffer();
|
||||
|
||||
GLuint handle = 0;
|
||||
GLRTexture color_texture;
|
||||
|
@ -68,14 +64,13 @@ public:
|
|||
GLRTexture z_stencil_texture;
|
||||
GLuint z_buffer = 0;
|
||||
GLuint stencil_buffer = 0;
|
||||
Draw::DataFormat colorFormat;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
GLuint colorDepth = 0;
|
||||
bool z_stencil_;
|
||||
|
||||
private:
|
||||
std::string tag_;
|
||||
bool z_stencil_;
|
||||
};
|
||||
|
||||
// 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_;
|
||||
};
|
||||
|
||||
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 {
|
||||
public:
|
||||
struct Entry {
|
||||
|
@ -190,15 +357,14 @@ public:
|
|||
int count;
|
||||
GLenum type;
|
||||
GLboolean normalized;
|
||||
int stride;
|
||||
intptr_t offset;
|
||||
};
|
||||
std::vector<Entry> entries;
|
||||
int stride;
|
||||
int semanticsMask_ = 0;
|
||||
};
|
||||
|
||||
enum class GLRRunType {
|
||||
SUBMIT,
|
||||
PRESENT,
|
||||
SYNC,
|
||||
EXIT,
|
||||
|
@ -207,19 +373,33 @@ enum class GLRRunType {
|
|||
class GLRenderManager;
|
||||
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 {
|
||||
GLRRenderThreadTask(GLRRunType _runType) : runType(_runType) {}
|
||||
|
||||
std::vector<GLRStep *> steps;
|
||||
FastVec<GLRInitStep> initSteps;
|
||||
std::vector<GLRInitStep> initSteps;
|
||||
|
||||
int frame = -1;
|
||||
int frame;
|
||||
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
|
||||
|
@ -227,11 +407,9 @@ struct GLRRenderThreadTask {
|
|||
// directly in the destructor.
|
||||
class GLRenderManager {
|
||||
public:
|
||||
GLRenderManager(HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory);
|
||||
GLRenderManager() {}
|
||||
~GLRenderManager();
|
||||
|
||||
GLRenderManager(GLRenderManager &) = delete;
|
||||
GLRenderManager &operator=(GLRenderManager &) = delete;
|
||||
|
||||
void SetInvalidationCallback(InvalidationCallback callback) {
|
||||
invalidationCallback_ = callback;
|
||||
|
@ -249,52 +427,47 @@ public:
|
|||
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.
|
||||
void BeginFrame(bool enableProfiling);
|
||||
void BeginFrame();
|
||||
// Can run on a different thread!
|
||||
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.
|
||||
// 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.
|
||||
GLRTexture *CreateTexture(GLenum target, int width, int height, int depth, int numMips) {
|
||||
_dbg_assert_(target != 0);
|
||||
GLRInitStep &step = initSteps_.push_uninitialized();
|
||||
step.stepType = GLRInitStepType::CREATE_TEXTURE;
|
||||
GLRInitStep step { GLRInitStepType::CREATE_TEXTURE };
|
||||
step.create_texture.texture = new GLRTexture(caps_, width, height, depth, numMips);
|
||||
step.create_texture.texture->target = target;
|
||||
initSteps_.push_back(step);
|
||||
return step.create_texture.texture;
|
||||
}
|
||||
|
||||
GLRBuffer *CreateBuffer(GLuint target, size_t size, GLuint usage) {
|
||||
GLRInitStep &step = initSteps_.push_uninitialized();
|
||||
step.stepType = GLRInitStepType::CREATE_BUFFER;
|
||||
GLRInitStep step{ GLRInitStepType::CREATE_BUFFER };
|
||||
step.create_buffer.buffer = new GLRBuffer(target, size);
|
||||
step.create_buffer.size = (int)size;
|
||||
step.create_buffer.usage = usage;
|
||||
initSteps_.push_back(step);
|
||||
return step.create_buffer.buffer;
|
||||
}
|
||||
|
||||
GLRShader *CreateShader(GLuint stage, const std::string &code, const std::string &desc) {
|
||||
GLRInitStep &step = initSteps_.push_uninitialized();
|
||||
step.stepType = GLRInitStepType::CREATE_SHADER;
|
||||
GLRInitStep step{ GLRInitStepType::CREATE_SHADER };
|
||||
step.create_shader.shader = new GLRShader();
|
||||
step.create_shader.shader->desc = desc;
|
||||
step.create_shader.stage = stage;
|
||||
step.create_shader.code = new char[code.size() + 1];
|
||||
memcpy(step.create_shader.code, code.data(), code.size() + 1);
|
||||
initSteps_.push_back(step);
|
||||
return step.create_shader.shader;
|
||||
}
|
||||
|
||||
GLRFramebuffer *CreateFramebuffer(int width, int height, bool z_stencil, const char *tag) {
|
||||
_dbg_assert_(width > 0 && height > 0 && tag != nullptr);
|
||||
|
||||
GLRInitStep &step = initSteps_.push_uninitialized();
|
||||
step.stepType = GLRInitStepType::CREATE_FRAMEBUFFER;
|
||||
step.create_framebuffer.framebuffer = new GLRFramebuffer(caps_, width, height, z_stencil, tag);
|
||||
GLRFramebuffer *CreateFramebuffer(Draw::DataFormat colorFormat, int width, int height, bool z_stencil) {
|
||||
GLRInitStep step{ GLRInitStepType::CREATE_FRAMEBUFFER };
|
||||
step.create_framebuffer.framebuffer = new GLRFramebuffer(caps_, colorFormat, width, height, z_stencil);
|
||||
initSteps_.push_back(step);
|
||||
return step.create_framebuffer.framebuffer;
|
||||
}
|
||||
|
||||
|
@ -303,8 +476,7 @@ public:
|
|||
GLRProgram *CreateProgram(
|
||||
std::vector<GLRShader *> shaders, std::vector<GLRProgram::Semantic> semantics, std::vector<GLRProgram::UniformLocQuery> queries,
|
||||
std::vector<GLRProgram::Initializer> initializers, GLRProgramLocData *locData, const GLRProgramFlags &flags) {
|
||||
GLRInitStep &step = initSteps_.push_uninitialized();
|
||||
step.stepType = GLRInitStepType::CREATE_PROGRAM;
|
||||
GLRInitStep step{ GLRInitStepType::CREATE_PROGRAM };
|
||||
_assert_(shaders.size() <= ARRAY_SIZE(step.create_program.shaders));
|
||||
step.create_program.program = new GLRProgram();
|
||||
step.create_program.program->semantics_ = semantics;
|
||||
|
@ -328,53 +500,46 @@ public:
|
|||
}
|
||||
#endif
|
||||
step.create_program.num_shaders = (int)shaders.size();
|
||||
initSteps_.push_back(step);
|
||||
return step.create_program.program;
|
||||
}
|
||||
|
||||
GLRInputLayout *CreateInputLayout(const std::vector<GLRInputLayout::Entry> &entries, int stride) {
|
||||
GLRInitStep &step = initSteps_.push_uninitialized();
|
||||
step.stepType = GLRInitStepType::CREATE_INPUT_LAYOUT;
|
||||
GLRInputLayout *CreateInputLayout(const std::vector<GLRInputLayout::Entry> &entries) {
|
||||
GLRInitStep step{ GLRInitStepType::CREATE_INPUT_LAYOUT };
|
||||
step.create_input_layout.inputLayout = new GLRInputLayout();
|
||||
step.create_input_layout.inputLayout->entries = entries;
|
||||
step.create_input_layout.inputLayout->stride = stride;
|
||||
for (auto &iter : step.create_input_layout.inputLayout->entries) {
|
||||
step.create_input_layout.inputLayout->semanticsMask_ |= 1 << iter.location;
|
||||
}
|
||||
initSteps_.push_back(step);
|
||||
return step.create_input_layout.inputLayout;
|
||||
}
|
||||
|
||||
GLPushBuffer *CreatePushBuffer(int frame, GLuint target, size_t size, const char *tag) {
|
||||
GLPushBuffer *push = new GLPushBuffer(this, target, size, tag);
|
||||
GLPushBuffer *CreatePushBuffer(int frame, GLuint target, size_t size) {
|
||||
GLPushBuffer *push = new GLPushBuffer(this, target, size);
|
||||
RegisterPushBuffer(frame, push);
|
||||
return push;
|
||||
}
|
||||
|
||||
void DeleteShader(GLRShader *shader) {
|
||||
_dbg_assert_(shader != nullptr);
|
||||
deleter_.shaders.push_back(shader);
|
||||
}
|
||||
void DeleteProgram(GLRProgram *program) {
|
||||
_dbg_assert_(program != nullptr);
|
||||
deleter_.programs.push_back(program);
|
||||
}
|
||||
void DeleteBuffer(GLRBuffer *buffer) {
|
||||
_dbg_assert_(buffer != nullptr);
|
||||
deleter_.buffers.push_back(buffer);
|
||||
}
|
||||
void DeleteTexture(GLRTexture *texture) {
|
||||
_dbg_assert_(texture != nullptr);
|
||||
deleter_.textures.push_back(texture);
|
||||
}
|
||||
void DeleteInputLayout(GLRInputLayout *inputLayout) {
|
||||
_dbg_assert_(inputLayout != nullptr);
|
||||
deleter_.inputLayouts.push_back(inputLayout);
|
||||
}
|
||||
void DeleteFramebuffer(GLRFramebuffer *framebuffer) {
|
||||
_dbg_assert_(framebuffer != nullptr);
|
||||
deleter_.framebuffers.push_back(framebuffer);
|
||||
}
|
||||
void DeletePushBuffer(GLPushBuffer *pushbuffer) {
|
||||
_dbg_assert_(pushbuffer != nullptr);
|
||||
deleter_.pushBuffers.push_back(pushbuffer);
|
||||
}
|
||||
|
||||
|
@ -386,13 +551,9 @@ public:
|
|||
pushbuffer->End();
|
||||
}
|
||||
|
||||
bool IsInRenderPass() const {
|
||||
return curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER;
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// 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.
|
||||
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 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) {
|
||||
// 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.
|
||||
GLRInitStep &step = initSteps_.push_uninitialized();
|
||||
step.stepType = GLRInitStepType::BUFFER_SUBDATA;
|
||||
GLRInitStep step{ GLRInitStepType::BUFFER_SUBDATA };
|
||||
_dbg_assert_(offset >= 0);
|
||||
_dbg_assert_(offset <= buffer->size_ - size);
|
||||
step.buffer_subdata.buffer = buffer;
|
||||
|
@ -428,12 +588,12 @@ public:
|
|||
step.buffer_subdata.size = (int)size;
|
||||
step.buffer_subdata.data = data;
|
||||
step.buffer_subdata.deleteData = deleteData;
|
||||
initSteps_.push_back(step);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
GLRInitStep &step = initSteps_.push_uninitialized();
|
||||
step.stepType = GLRInitStepType::TEXTURE_IMAGE;
|
||||
GLRInitStep step{ GLRInitStepType::TEXTURE_IMAGE };
|
||||
step.texture_image.texture = texture;
|
||||
step.texture_image.data = data;
|
||||
step.texture_image.format = format;
|
||||
|
@ -443,11 +603,12 @@ public:
|
|||
step.texture_image.depth = depth;
|
||||
step.texture_image.allocType = allocType;
|
||||
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) {
|
||||
_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.data = data;
|
||||
_data.texture_subimage.format = format;
|
||||
|
@ -462,59 +623,80 @@ public:
|
|||
}
|
||||
|
||||
void FinalizeTexture(GLRTexture *texture, int loadedLevels, bool genMips) {
|
||||
GLRInitStep &step = initSteps_.push_uninitialized();
|
||||
step.stepType = GLRInitStepType::TEXTURE_FINALIZE;
|
||||
GLRInitStep step{ GLRInitStepType::TEXTURE_FINALIZE };
|
||||
step.texture_finalize.texture = texture;
|
||||
step.texture_finalize.loadedLevels = loadedLevels;
|
||||
step.texture_finalize.genMips = genMips;
|
||||
initSteps_.push_back(step);
|
||||
}
|
||||
|
||||
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_(slot < MAX_GL_TEXTURE_SLOTS);
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::BINDTEXTURE;
|
||||
GLRRenderData data{ GLRRenderCommand::BINDTEXTURE };
|
||||
data.texture.slot = slot;
|
||||
data.texture.texture = tex;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void BindProgram(GLRProgram *program) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::BINDPROGRAM;
|
||||
GLRRenderData data{ GLRRenderCommand::BINDPROGRAM };
|
||||
_dbg_assert_(program != nullptr);
|
||||
data.program.program = program;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
#ifdef _DEBUG
|
||||
curProgram_ = program;
|
||||
#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) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::DEPTH;
|
||||
GLRRenderData data{ GLRRenderCommand::DEPTH };
|
||||
data.depth.enabled = enabled;
|
||||
data.depth.write = write;
|
||||
data.depth.func = func;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetViewport(const GLRViewport &vp) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::VIEWPORT;
|
||||
GLRRenderData data{ GLRRenderCommand::VIEWPORT };
|
||||
data.viewport.vp = vp;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetScissor(const GLRect2D &rc) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::SCISSOR;
|
||||
GLRRenderData data{ GLRRenderCommand::SCISSOR };
|
||||
data.scissor.rc = rc;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetUniformI(const GLint *loc, int count, const int *udata) {
|
||||
|
@ -522,12 +704,11 @@ public:
|
|||
#ifdef _DEBUG
|
||||
_dbg_assert_(curProgram_);
|
||||
#endif
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::UNIFORM4I;
|
||||
data.uniform4.name = nullptr;
|
||||
GLRRenderData data{ GLRRenderCommand::UNIFORM4I };
|
||||
data.uniform4.loc = loc;
|
||||
data.uniform4.count = count;
|
||||
memcpy(data.uniform4.v, udata, sizeof(int) * count);
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetUniformI1(const GLint *loc, int udata) {
|
||||
|
@ -535,12 +716,11 @@ public:
|
|||
#ifdef _DEBUG
|
||||
_dbg_assert_(curProgram_);
|
||||
#endif
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::UNIFORM4I;
|
||||
data.uniform4.name = nullptr;
|
||||
GLRRenderData data{ GLRRenderCommand::UNIFORM4I };
|
||||
data.uniform4.loc = loc;
|
||||
data.uniform4.count = 1;
|
||||
memcpy(data.uniform4.v, &udata, sizeof(udata));
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetUniformUI(const GLint *loc, int count, const uint32_t *udata) {
|
||||
|
@ -548,12 +728,11 @@ public:
|
|||
#ifdef _DEBUG
|
||||
_dbg_assert_(curProgram_);
|
||||
#endif
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::UNIFORM4UI;
|
||||
data.uniform4.name = nullptr;
|
||||
GLRRenderData data{ GLRRenderCommand::UNIFORM4UI };
|
||||
data.uniform4.loc = loc;
|
||||
data.uniform4.count = count;
|
||||
memcpy(data.uniform4.v, udata, sizeof(uint32_t) * count);
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetUniformUI1(const GLint *loc, uint32_t udata) {
|
||||
|
@ -561,12 +740,11 @@ public:
|
|||
#ifdef _DEBUG
|
||||
_dbg_assert_(curProgram_);
|
||||
#endif
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::UNIFORM4UI;
|
||||
data.uniform4.name = nullptr;
|
||||
GLRRenderData data{ GLRRenderCommand::UNIFORM4UI };
|
||||
data.uniform4.loc = loc;
|
||||
data.uniform4.count = 1;
|
||||
memcpy(data.uniform4.v, &udata, sizeof(udata));
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetUniformF(const GLint *loc, int count, const float *udata) {
|
||||
|
@ -574,12 +752,11 @@ public:
|
|||
#ifdef _DEBUG
|
||||
_dbg_assert_(curProgram_);
|
||||
#endif
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::UNIFORM4F;
|
||||
data.uniform4.name = nullptr;
|
||||
GLRRenderData data{ GLRRenderCommand::UNIFORM4F };
|
||||
data.uniform4.loc = loc;
|
||||
data.uniform4.count = count;
|
||||
memcpy(data.uniform4.v, udata, sizeof(float) * count);
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetUniformF1(const GLint *loc, const float udata) {
|
||||
|
@ -587,12 +764,11 @@ public:
|
|||
#ifdef _DEBUG
|
||||
_dbg_assert_(curProgram_);
|
||||
#endif
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::UNIFORM4F;
|
||||
data.uniform4.name = nullptr;
|
||||
GLRRenderData data{ GLRRenderCommand::UNIFORM4F };
|
||||
data.uniform4.loc = loc;
|
||||
data.uniform4.count = 1;
|
||||
memcpy(data.uniform4.v, &udata, sizeof(float));
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetUniformF(const char *name, int count, const float *udata) {
|
||||
|
@ -600,12 +776,11 @@ public:
|
|||
#ifdef _DEBUG
|
||||
_dbg_assert_(curProgram_);
|
||||
#endif
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::UNIFORM4F;
|
||||
GLRRenderData data{ GLRRenderCommand::UNIFORM4F };
|
||||
data.uniform4.name = name;
|
||||
data.uniform4.loc = nullptr;
|
||||
data.uniform4.count = count;
|
||||
memcpy(data.uniform4.v, udata, sizeof(float) * count);
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetUniformM4x4(const GLint *loc, const float *udata) {
|
||||
|
@ -613,11 +788,10 @@ public:
|
|||
#ifdef _DEBUG
|
||||
_dbg_assert_(curProgram_);
|
||||
#endif
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::UNIFORMMATRIX;
|
||||
data.uniformMatrix4.name = nullptr;
|
||||
GLRRenderData data{ GLRRenderCommand::UNIFORMMATRIX };
|
||||
data.uniformMatrix4.loc = loc;
|
||||
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) {
|
||||
|
@ -625,13 +799,12 @@ public:
|
|||
#ifdef _DEBUG
|
||||
_dbg_assert_(curProgram_);
|
||||
#endif
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::UNIFORMSTEREOMATRIX;
|
||||
data.uniformStereoMatrix4.name = name;
|
||||
data.uniformStereoMatrix4.loc = loc;
|
||||
data.uniformStereoMatrix4.mData = new float[32];
|
||||
memcpy(&data.uniformStereoMatrix4.mData[0], left, sizeof(float) * 16);
|
||||
memcpy(&data.uniformStereoMatrix4.mData[16], right, sizeof(float) * 16);
|
||||
GLRRenderData data{ GLRRenderCommand::UNIFORMSTEREOMATRIX };
|
||||
data.uniformMatrix4.name = name;
|
||||
data.uniformMatrix4.loc = loc;
|
||||
memcpy(&data.uniformMatrix4.m[0], left, sizeof(float) * 16);
|
||||
memcpy(&data.uniformMatrix4.m[16], right, sizeof(float) * 16);
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetUniformM4x4(const char *name, const float *udata) {
|
||||
|
@ -639,19 +812,17 @@ public:
|
|||
#ifdef _DEBUG
|
||||
_dbg_assert_(curProgram_);
|
||||
#endif
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::UNIFORMMATRIX;
|
||||
GLRRenderData data{ GLRRenderCommand::UNIFORMMATRIX };
|
||||
data.uniformMatrix4.name = name;
|
||||
data.uniformMatrix4.loc = nullptr;
|
||||
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) {
|
||||
// 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.
|
||||
_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::BLEND;
|
||||
GLRRenderData data{ GLRRenderCommand::BLEND };
|
||||
data.blend.mask = colorMask;
|
||||
data.blend.enabled = blendEnabled;
|
||||
data.blend.srcColor = srcColor;
|
||||
|
@ -660,88 +831,96 @@ public:
|
|||
data.blend.dstAlpha = dstAlpha;
|
||||
data.blend.funcColor = funcColor;
|
||||
data.blend.funcAlpha = funcAlpha;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetNoBlendAndMask(int colorMask) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::BLEND;
|
||||
GLRRenderData data{ GLRRenderCommand::BLEND };
|
||||
data.blend.mask = colorMask;
|
||||
data.blend.enabled = false;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
#ifndef USING_GLES2
|
||||
void SetLogicOp(bool enabled, GLenum logicOp) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::LOGICOP;
|
||||
GLRRenderData data{ GLRRenderCommand::LOGICOP };
|
||||
data.logic.enabled = enabled;
|
||||
data.logic.logicOp = logicOp;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
#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);
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::STENCIL;
|
||||
data.stencil.enabled = enabled;
|
||||
data.stencil.func = func;
|
||||
data.stencil.ref = refValue;
|
||||
data.stencil.compareMask = compareMask;
|
||||
data.stencil.writeMask = writeMask;
|
||||
data.stencil.sFail = sFail;
|
||||
data.stencil.zFail = zFail;
|
||||
data.stencil.pass = pass;
|
||||
GLRRenderData data{ GLRRenderCommand::STENCILFUNC };
|
||||
data.stencilFunc.enabled = enabled;
|
||||
data.stencilFunc.func = func;
|
||||
data.stencilFunc.ref = refValue;
|
||||
data.stencilFunc.compareMask = compareMask;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetStencilOp(uint8_t writeMask, GLenum sFail, GLenum zFail, GLenum 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() {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::STENCIL;
|
||||
data.stencil.enabled = false;
|
||||
GLRRenderData data;
|
||||
data.cmd = GLRRenderCommand::STENCILFUNC;
|
||||
data.stencilFunc.enabled = false;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetBlendFactor(const float color[4]) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::BLENDCOLOR;
|
||||
GLRRenderData data{ GLRRenderCommand::BLENDCOLOR };
|
||||
CopyFloat4(data.blendColor.color, color);
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetRaster(GLboolean cullEnable, GLenum frontFace, GLenum cullFace, GLboolean ditherEnable, GLboolean depthClamp) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::RASTER;
|
||||
GLRRenderData data{ GLRRenderCommand::RASTER };
|
||||
data.raster.cullEnable = cullEnable;
|
||||
data.raster.frontFace = frontFace;
|
||||
data.raster.cullFace = cullFace;
|
||||
data.raster.ditherEnable = ditherEnable;
|
||||
data.raster.depthClampEnable = depthClamp;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
_dbg_assert_(slot < MAX_GL_TEXTURE_SLOTS);
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::TEXTURESAMPLER;
|
||||
GLRRenderData data{ GLRRenderCommand::TEXTURESAMPLER };
|
||||
data.textureSampler.slot = slot;
|
||||
data.textureSampler.wrapS = wrapS;
|
||||
data.textureSampler.wrapT = wrapT;
|
||||
data.textureSampler.magFilter = magFilter;
|
||||
data.textureSampler.minFilter = minFilter;
|
||||
data.textureSampler.anisotropy = anisotropy;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetTextureLod(int slot, float minLod, float maxLod, float lodBias) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
_dbg_assert_(slot < MAX_GL_TEXTURE_SLOTS);
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::TEXTURELOD;
|
||||
GLRRenderData data{ GLRRenderCommand::TEXTURELOD};
|
||||
data.textureLod.slot = slot;
|
||||
data.textureLod.minLod = minLod;
|
||||
data.textureLod.maxLod = maxLod;
|
||||
data.textureLod.lodBias = lodBias;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (!clearMask)
|
||||
return;
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::CLEAR;
|
||||
GLRRenderData data{ GLRRenderCommand::CLEAR };
|
||||
data.clear.clearMask = clearMask;
|
||||
data.clear.clearColor = clearColor;
|
||||
data.clear.clearZ = clearZ;
|
||||
|
@ -760,36 +938,30 @@ public:
|
|||
data.clear.scissorY = scissorY;
|
||||
data.clear.scissorW = scissorW;
|
||||
data.clear.scissorH = scissorH;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void Draw(GLRInputLayout *inputLayout, GLRBuffer *vertexBuffer, uint32_t vertexOffset, GLenum mode, int first, int count) {
|
||||
_dbg_assert_(vertexBuffer && curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::DRAW;
|
||||
data.draw.inputLayout = inputLayout;
|
||||
data.draw.vertexOffset = vertexOffset;
|
||||
data.draw.vertexBuffer = vertexBuffer;
|
||||
data.draw.indexBuffer = nullptr;
|
||||
void Draw(GLenum mode, int first, int count) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
GLRRenderData data{ GLRRenderCommand::DRAW };
|
||||
data.draw.mode = mode;
|
||||
data.draw.first = first;
|
||||
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(GLRInputLayout *inputLayout, GLRBuffer *vertexBuffer, uint32_t vertexOffset, GLRBuffer *indexBuffer, uint32_t indexOffset, GLenum mode, int count, GLenum indexType, int instances = 1) {
|
||||
_dbg_assert_(vertexBuffer && indexBuffer && curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
GLRRenderData &data = curRenderStep_->commands.push_uninitialized();
|
||||
data.cmd = GLRRenderCommand::DRAW;
|
||||
data.draw.inputLayout = inputLayout;
|
||||
data.draw.vertexOffset = vertexOffset;
|
||||
data.draw.vertexBuffer = vertexBuffer;
|
||||
data.draw.indexBuffer = indexBuffer;
|
||||
data.draw.indexOffset = indexOffset;
|
||||
data.draw.mode = mode;
|
||||
data.draw.count = count;
|
||||
data.draw.indexType = indexType;
|
||||
data.draw.instances = instances;
|
||||
void DrawIndexed(GLenum mode, int count, GLenum indexType, void *indices, int instances = 1) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
GLRRenderData data{ GLRRenderCommand::DRAW_INDEXED };
|
||||
data.drawIndexed.mode = mode;
|
||||
data.drawIndexed.count = count;
|
||||
data.drawIndexed.indexType = indexType;
|
||||
data.drawIndexed.instances = instances;
|
||||
data.drawIndexed.indices = indices;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
curRenderStep_->render.numDraws++;
|
||||
}
|
||||
|
||||
enum { MAX_INFLIGHT_FRAMES = 3 };
|
||||
|
@ -820,8 +992,9 @@ public:
|
|||
_dbg_assert_(foundCount == 1);
|
||||
}
|
||||
|
||||
void SetSwapFunction(std::function<void()> swapFunction) {
|
||||
void SetSwapFunction(std::function<void()> swapFunction, bool retainControl) {
|
||||
swapFunction_ = swapFunction;
|
||||
retainControl_ = retainControl;
|
||||
}
|
||||
|
||||
void SetSwapIntervalFunction(std::function<void(int)> swapIntervalFunction) {
|
||||
|
@ -853,8 +1026,6 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
bool Run(GLRRenderThreadTask &task);
|
||||
|
||||
// Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot).
|
||||
void FlushSync();
|
||||
|
||||
|
@ -864,14 +1035,30 @@ private:
|
|||
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
|
||||
bool insideFrame_ = false;
|
||||
|
||||
GLRStep *curRenderStep_ = nullptr;
|
||||
std::vector<GLRStep *> steps_;
|
||||
FastVec<GLRInitStep> initSteps_;
|
||||
std::vector<GLRInitStep> initSteps_;
|
||||
|
||||
// Execution time state
|
||||
bool run_ = true;
|
||||
|
@ -883,7 +1070,7 @@ private:
|
|||
std::mutex pushMutex_;
|
||||
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.
|
||||
std::mutex syncMutex_;
|
||||
|
@ -900,6 +1087,7 @@ private:
|
|||
|
||||
std::function<void()> swapFunction_;
|
||||
std::function<void(int)> swapIntervalFunction_;
|
||||
bool retainControl_ = false;
|
||||
GLBufferStrategy bufferStrategy_ = GLBufferStrategy::SUBDATA;
|
||||
|
||||
int inflightFrames_ = MAX_INFLIGHT_FRAMES;
|
||||
|
@ -916,9 +1104,5 @@ private:
|
|||
#endif
|
||||
Draw::DeviceCaps caps_{};
|
||||
|
||||
std::string profilePassesString_;
|
||||
InvalidationCallback invalidationCallback_;
|
||||
|
||||
uint64_t frameIdGen_ = FRAME_TIME_HISTORY_LENGTH;
|
||||
HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory_;
|
||||
};
|
||||
|
|
|
@ -102,7 +102,7 @@ bool glsl_recompile(GLSLProgram *program, std::string *error_message) {
|
|||
|
||||
if (!program->vshader_source && !vsh_src) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
ERROR_LOG(G3D, "File missing: %s", program->fshader_filename);
|
||||
|
|
|
@ -180,9 +180,8 @@ public:
|
|||
|
||||
void Apply(GLRenderManager *render, uint8_t stencilRef, uint8_t stencilWriteMask, uint8_t stencilCompareMask) {
|
||||
render->SetDepth(depthTestEnabled, depthWriteEnabled, depthComp);
|
||||
render->SetStencil(
|
||||
stencilEnabled, stencilCompareOp, stencilRef, stencilCompareMask,
|
||||
stencilWriteMask, stencilFail, stencilZFail, stencilPass);
|
||||
render->SetStencilFunc(stencilEnabled, stencilCompareOp, stencilRef, stencilCompareMask);
|
||||
render->SetStencilOp(stencilWriteMask, stencilFail, stencilZFail, stencilPass);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -322,7 +321,7 @@ class OpenGLTexture;
|
|||
|
||||
class OpenGLContext : public DrawContext {
|
||||
public:
|
||||
OpenGLContext(bool canChangeSwapInterval);
|
||||
OpenGLContext();
|
||||
~OpenGLContext();
|
||||
|
||||
void SetTargetSize(int w, int h) override {
|
||||
|
@ -359,20 +358,14 @@ public:
|
|||
Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override;
|
||||
Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override;
|
||||
|
||||
void BeginFrame(DebugFlags debugFlags) override;
|
||||
void BeginFrame() 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 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;
|
||||
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.
|
||||
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
|
||||
|
@ -392,9 +385,9 @@ public:
|
|||
renderManager_.SetScissor({ left, top, width, height });
|
||||
}
|
||||
|
||||
void SetViewport(const Viewport &viewport) override {
|
||||
void SetViewports(int count, Viewport *viewports) override {
|
||||
// Same structure, different name.
|
||||
renderManager_.SetViewport((GLRViewport &)viewport);
|
||||
renderManager_.SetViewport((GLRViewport &)*viewports);
|
||||
}
|
||||
|
||||
void SetBlendFactor(float color[4]) override {
|
||||
|
@ -406,11 +399,12 @@ public:
|
|||
stencilWriteMask_ = writeMask;
|
||||
stencilCompareMask_ = compareMask;
|
||||
// Do we need to update on the fly here?
|
||||
renderManager_.SetStencil(
|
||||
renderManager_.SetStencilFunc(
|
||||
curPipeline_->depthStencil->stencilEnabled,
|
||||
curPipeline_->depthStencil->stencilCompareOp,
|
||||
refValue,
|
||||
compareMask,
|
||||
compareMask);
|
||||
renderManager_.SetStencilOp(
|
||||
writeMask,
|
||||
curPipeline_->depthStencil->stencilFail,
|
||||
curPipeline_->depthStencil->stencilZFail,
|
||||
|
@ -421,9 +415,12 @@ public:
|
|||
void BindNativeTexture(int sampler, void *nativeTexture) override;
|
||||
|
||||
void BindPipeline(Pipeline *pipeline) override;
|
||||
void BindVertexBuffer(Buffer *buffer, int offset) override {
|
||||
curVBuffer_ = (OpenGLBuffer *)buffer;
|
||||
curVBufferOffset_ = offset;
|
||||
void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) override {
|
||||
_assert_(start + count <= ARRAY_SIZE(curVBuffers_));
|
||||
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 {
|
||||
curIBuffer_ = (OpenGLBuffer *)indexBuffer;
|
||||
|
@ -460,7 +457,6 @@ public:
|
|||
case GPUVendor::VENDOR_BROADCOM: return "VENDOR_BROADCOM";
|
||||
case GPUVendor::VENDOR_VIVANTE: return "VENDOR_VIVANTE";
|
||||
case GPUVendor::VENDOR_APPLE: return "VENDOR_APPLE";
|
||||
case GPUVendor::VENDOR_MESA: return "VENDOR_MESA";
|
||||
case GPUVendor::VENDOR_UNKNOWN:
|
||||
default:
|
||||
return "VENDOR_UNKNOWN";
|
||||
|
@ -483,15 +479,10 @@ public:
|
|||
renderManager_.SetInvalidationCallback(callback);
|
||||
}
|
||||
|
||||
std::string GetGpuProfileString() const override {
|
||||
return renderManager_.GetGpuProfileString();
|
||||
}
|
||||
|
||||
private:
|
||||
void ApplySamplers();
|
||||
|
||||
GLRenderManager renderManager_;
|
||||
int frameCount_ = 0;
|
||||
|
||||
DeviceCaps caps_{};
|
||||
|
||||
|
@ -502,9 +493,9 @@ private:
|
|||
const GLRTexture *boundTextures_[MAX_TEXTURE_SLOTS]{};
|
||||
|
||||
AutoRef<OpenGLPipeline> curPipeline_;
|
||||
AutoRef<OpenGLBuffer> curVBuffer_;
|
||||
AutoRef<OpenGLBuffer> curVBuffers_[4]{};
|
||||
int curVBufferOffsets_[4]{};
|
||||
AutoRef<OpenGLBuffer> curIBuffer_;
|
||||
int curVBufferOffset_ = 0;
|
||||
int curIBufferOffset_ = 0;
|
||||
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.OES_packed_depth_stencil || gl_extensions.OES_depth24) {
|
||||
caps_.preferredDepthBufferFormat = DataFormat::D24_S8;
|
||||
|
@ -564,7 +555,6 @@ OpenGLContext::OpenGLContext(bool canChangeSwapInterval) : renderManager_(frameT
|
|||
caps_.textureDepthSupported = true;
|
||||
}
|
||||
|
||||
caps_.setMaxFrameLatencySupported = true;
|
||||
caps_.dualSourceBlend = gl_extensions.ARB_blend_func_extended || gl_extensions.EXT_blend_func_extended;
|
||||
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;
|
||||
|
@ -613,7 +603,6 @@ OpenGLContext::OpenGLContext(bool canChangeSwapInterval) : renderManager_(frameT
|
|||
case GPU_VENDOR_IMGTEC: caps_.vendor = GPUVendor::VENDOR_IMGTEC; break;
|
||||
case GPU_VENDOR_VIVANTE: caps_.vendor = GPUVendor::VENDOR_VIVANTE; 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:
|
||||
default:
|
||||
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;
|
||||
|
||||
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)) {
|
||||
|
@ -751,9 +740,7 @@ OpenGLContext::OpenGLContext(bool canChangeSwapInterval) : renderManager_(frameT
|
|||
// This too...
|
||||
shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_1xx;
|
||||
if (gl_extensions.EXT_gpu_shader4) {
|
||||
// Older macOS devices seem to have problems defining uint uniforms.
|
||||
// Let's just assume OpenGL 3.0+ is required.
|
||||
shaderLanguageDesc_.bitwiseOps = gl_extensions.VersionGEThan(3, 0, 0);
|
||||
shaderLanguageDesc_.bitwiseOps = true;
|
||||
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_);
|
||||
}
|
||||
|
||||
|
@ -793,8 +770,8 @@ OpenGLContext::~OpenGLContext() {
|
|||
}
|
||||
}
|
||||
|
||||
void OpenGLContext::BeginFrame(DebugFlags debugFlags) {
|
||||
renderManager_.BeginFrame(debugFlags & DebugFlags::PROFILE_TIMESTAMPS);
|
||||
void OpenGLContext::BeginFrame() {
|
||||
renderManager_.BeginFrame();
|
||||
FrameData &frameData = frameData_[renderManager_.GetCurFrame()];
|
||||
renderManager_.BeginPushBuffer(frameData.push);
|
||||
}
|
||||
|
@ -803,12 +780,8 @@ void OpenGLContext::EndFrame() {
|
|||
FrameData &frameData = frameData_[renderManager_.GetCurFrame()];
|
||||
renderManager_.EndPushBuffer(frameData.push); // upload the data!
|
||||
renderManager_.Finish();
|
||||
Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
|
||||
}
|
||||
|
||||
void OpenGLContext::Present(PresentMode presentMode, int vblanks) {
|
||||
renderManager_.Present();
|
||||
frameCount_++;
|
||||
Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
|
||||
}
|
||||
|
||||
void OpenGLContext::Invalidate(InvalidationFlags flags) {
|
||||
|
@ -830,7 +803,7 @@ InputLayout *OpenGLContext::CreateInputLayout(const InputLayoutDesc &desc) {
|
|||
return fmt;
|
||||
}
|
||||
|
||||
static GLuint TypeToTarget(TextureType type) {
|
||||
GLuint TypeToTarget(TextureType type) {
|
||||
switch (type) {
|
||||
#ifndef USING_GLES2
|
||||
case TextureType::LINEAR1D: return GL_TEXTURE_1D;
|
||||
|
@ -868,33 +841,25 @@ public:
|
|||
return tex_;
|
||||
}
|
||||
|
||||
void UpdateTextureLevels(GLRenderManager *render, const uint8_t *const *data, int numLevels, TextureCallback initDataCallback);
|
||||
|
||||
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_;
|
||||
GLRTexture *tex_;
|
||||
|
||||
DataFormat format_;
|
||||
TextureType type_;
|
||||
int mipLevels_;
|
||||
bool generateMips_; // Generate mips requested
|
||||
bool generatedMips_; // Has generated mips
|
||||
bool generatedMips_;
|
||||
};
|
||||
|
||||
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;
|
||||
generateMips_ = desc.generateMips;
|
||||
width_ = desc.width;
|
||||
height_ = desc.height;
|
||||
depth_ = desc.depth;
|
||||
format_ = desc.format;
|
||||
type_ = desc.type;
|
||||
|
||||
GLenum target = TypeToTarget(desc.type);
|
||||
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())
|
||||
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 width = width_;
|
||||
int height = height_;
|
||||
int depth = depth_;
|
||||
for (int i = 0; i < numLevels; i++) {
|
||||
SetImageData(0, 0, 0, width, height, depth, level, 0, data[i], initDataCallback);
|
||||
for (auto data : desc.initData) {
|
||||
SetImageData(0, 0, 0, width, height, depth, level, 0, data, desc.initDataCallback);
|
||||
width = (width + 1) / 2;
|
||||
height = (height + 1) / 2;
|
||||
depth = (depth + 1) / 2;
|
||||
level++;
|
||||
}
|
||||
mipLevels_ = generateMips_ ? mipLevels_ : level;
|
||||
mipLevels_ = desc.generateMips ? desc.mipLevels : level;
|
||||
|
||||
bool genMips = false;
|
||||
if (numLevels < mipLevels_ && generateMips_) {
|
||||
if ((int)desc.initData.size() < desc.mipLevels && desc.generateMips) {
|
||||
// Assumes the texture is bound for editing
|
||||
genMips = true;
|
||||
generatedMips_ = true;
|
||||
|
@ -931,7 +892,7 @@ void OpenGLTexture::UpdateTextureLevels(GLRenderManager *render, const uint8_t *
|
|||
OpenGLTexture::~OpenGLTexture() {
|
||||
if (tex_) {
|
||||
render_->DeleteTexture(tex_);
|
||||
tex_ = nullptr;
|
||||
tex_ = 0;
|
||||
generatedMips_ = false;
|
||||
}
|
||||
}
|
||||
|
@ -950,7 +911,7 @@ public:
|
|||
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) {
|
||||
// When switching to texStorage we need to handle this correctly.
|
||||
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)];
|
||||
|
||||
bool texDataPopulated = false;
|
||||
if (initDataCallback) {
|
||||
texDataPopulated = initDataCallback(texData, data, width, height, depth, width * (int)alignment, height * width * (int)alignment);
|
||||
if (callback) {
|
||||
texDataPopulated = callback(texData, data, width, height, depth, width * (int)alignment, height * width * (int)alignment);
|
||||
}
|
||||
if (texDataPopulated) {
|
||||
if (format_ == DataFormat::A1R5G5B5_UNORM_PACK16) {
|
||||
|
@ -1027,7 +988,7 @@ static void LogReadPixelsError(GLenum error) {
|
|||
}
|
||||
#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) {
|
||||
// Can't readback depth or stencil on GLES.
|
||||
return false;
|
||||
|
@ -1040,7 +1001,8 @@ bool OpenGLContext::CopyFramebufferToMemory(Framebuffer *src, int channelBits, i
|
|||
aspect |= GL_DEPTH_BUFFER_BIT;
|
||||
if (channelBits & FB_STENCIL_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);
|
||||
}
|
||||
|
||||
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) {
|
||||
OpenGLDepthStencilState *ds = new OpenGLDepthStencilState();
|
||||
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++) {
|
||||
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 {
|
||||
queries.push_back({ &locs_->samplerLocs_[0], "sampler0" });
|
||||
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) {
|
||||
_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();
|
||||
_assert_(curPipeline_->inputLayout);
|
||||
renderManager_.Draw(curPipeline_->inputLayout->inputLayout_, curVBuffer_->buffer_, curVBufferOffset_, curPipeline_->prim, offset, vertexCount);
|
||||
if (curPipeline_->inputLayout) {
|
||||
renderManager_.BindVertexBuffer(curPipeline_->inputLayout->inputLayout_, curVBuffers_[0]->buffer_, curVBufferOffsets_[0]);
|
||||
}
|
||||
renderManager_.Draw(curPipeline_->prim, offset, vertexCount);
|
||||
}
|
||||
|
||||
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");
|
||||
ApplySamplers();
|
||||
_assert_(curPipeline_->inputLayout);
|
||||
renderManager_.DrawIndexed(
|
||||
curPipeline_->inputLayout->inputLayout_,
|
||||
curVBuffer_->buffer_, curVBufferOffset_,
|
||||
curIBuffer_->buffer_, curIBufferOffset_ + offset * sizeof(uint32_t),
|
||||
curPipeline_->prim, vertexCount, GL_UNSIGNED_SHORT);
|
||||
if (curPipeline_->inputLayout) {
|
||||
renderManager_.BindVertexBuffer(curPipeline_->inputLayout->inputLayout_, curVBuffers_[0]->buffer_, curVBufferOffsets_[0]);
|
||||
}
|
||||
renderManager_.BindIndexBuffer(curIBuffer_->buffer_);
|
||||
renderManager_.DrawIndexed(curPipeline_->prim, vertexCount, GL_UNSIGNED_SHORT, (void *)((intptr_t)curIBufferOffset_ + offset * sizeof(uint32_t)));
|
||||
}
|
||||
|
||||
void OpenGLContext::DrawUP(const void *vdata, int vertexCount) {
|
||||
_assert_(curPipeline_->inputLayout != nullptr);
|
||||
int stride = curPipeline_->inputLayout->stride;
|
||||
uint32_t dataSize = stride * vertexCount;
|
||||
size_t dataSize = stride * vertexCount;
|
||||
|
||||
FrameData &frameData = frameData_[renderManager_.GetCurFrame()];
|
||||
|
||||
GLRBuffer *buf;
|
||||
uint32_t offset;
|
||||
uint8_t *dest = frameData.push->Allocate(dataSize, 4, &buf, &offset);
|
||||
memcpy(dest, vdata, dataSize);
|
||||
size_t offset = frameData.push->Push(vdata, dataSize, &buf);
|
||||
|
||||
ApplySamplers();
|
||||
_assert_(curPipeline_->inputLayout);
|
||||
renderManager_.Draw(curPipeline_->inputLayout->inputLayout_, buf, offset, curPipeline_->prim, 0, vertexCount);
|
||||
if (curPipeline_->inputLayout) {
|
||||
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) {
|
||||
|
@ -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_);
|
||||
}
|
||||
|
||||
DrawContext *T3DCreateGLContext(bool canChangeSwapInterval) {
|
||||
return new OpenGLContext(canChangeSwapInterval);
|
||||
DrawContext *T3DCreateGLContext() {
|
||||
return new OpenGLContext();
|
||||
}
|
||||
|
||||
OpenGLInputLayout::~OpenGLInputLayout() {
|
||||
|
@ -1429,12 +1387,13 @@ OpenGLInputLayout::~OpenGLInputLayout() {
|
|||
void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) {
|
||||
// TODO: This is only accurate if there's only one stream. But whatever, for now we
|
||||
// never use multiple streams anyway.
|
||||
stride = desc.stride;
|
||||
stride = desc.bindings.empty() ? 0 : (GLsizei)desc.bindings[0].stride;
|
||||
|
||||
std::vector<GLRInputLayout::Entry> entries;
|
||||
for (auto &attr : desc.attributes) {
|
||||
GLRInputLayout::Entry entry;
|
||||
entry.location = attr.location;
|
||||
entry.stride = (GLsizei)desc.bindings[attr.binding].stride;
|
||||
entry.offset = attr.offset;
|
||||
switch (attr.format) {
|
||||
case DataFormat::R32G32_FLOAT:
|
||||
|
@ -1466,7 +1425,7 @@ void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) {
|
|||
entries.push_back(entry);
|
||||
}
|
||||
if (!entries.empty()) {
|
||||
inputLayout_ = render_->CreateInputLayout(entries, stride);
|
||||
inputLayout_ = render_->CreateInputLayout(entries);
|
||||
} else {
|
||||
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).
|
||||
_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);
|
||||
return fbo;
|
||||
}
|
||||
|
@ -1598,23 +1557,7 @@ uint32_t OpenGLContext::GetDataFormatSupport(DataFormat fmt) const {
|
|||
case DataFormat::BC1_RGBA_UNORM_BLOCK:
|
||||
case DataFormat::BC2_UNORM_BLOCK:
|
||||
case DataFormat::BC3_UNORM_BLOCK:
|
||||
return gl_extensions.supportsBC123 ? FMT_TEXTURE : 0;
|
||||
|
||||
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;
|
||||
|
||||
return FMT_TEXTURE;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -63,10 +63,15 @@ static EShLanguage GetShLanguageFromStage(const ShaderStage stage) {
|
|||
}
|
||||
|
||||
void ShaderTranslationInit() {
|
||||
// TODO: We have TLS issues on UWP
|
||||
#if !PPSSPP_PLATFORM(UWP)
|
||||
glslang::InitializeProcess();
|
||||
#endif
|
||||
}
|
||||
void ShaderTranslationShutdown() {
|
||||
#if !PPSSPP_PLATFORM(UWP)
|
||||
glslang::FinalizeProcess();
|
||||
#endif
|
||||
}
|
||||
|
||||
struct Builtin {
|
||||
|
@ -224,6 +229,11 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, const ShaderLan
|
|||
return result;
|
||||
}
|
||||
|
||||
#if PPSSPP_PLATFORM(UWP)
|
||||
*errorMessage = "No shader translation available (UWP)";
|
||||
return false;
|
||||
#endif
|
||||
|
||||
errorMessage->clear();
|
||||
|
||||
glslang::TProgram program;
|
||||
|
|
|
@ -57,13 +57,12 @@ std::string VulkanVendorString(uint32_t vendorId) {
|
|||
case VULKAN_VENDOR_QUALCOMM: return "Qualcomm";
|
||||
case VULKAN_VENDOR_IMGTEC: return "Imagination";
|
||||
case VULKAN_VENDOR_APPLE: return "Apple";
|
||||
case VULKAN_VENDOR_MESA: return "Mesa";
|
||||
default:
|
||||
return StringFromFormat("%08x", vendorId);
|
||||
}
|
||||
}
|
||||
|
||||
const char *VulkanPresentModeToString(VkPresentModeKHR presentMode) {
|
||||
const char *PresentModeString(VkPresentModeKHR presentMode) {
|
||||
switch (presentMode) {
|
||||
case VK_PRESENT_MODE_IMMEDIATE_KHR: return "IMMEDIATE";
|
||||
case VK_PRESENT_MODE_MAILBOX_KHR: return "MAILBOX";
|
||||
|
@ -536,7 +535,7 @@ int VulkanContext::GetBestPhysicalDevice() {
|
|||
|
||||
void VulkanContext::ChooseDevice(int 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();
|
||||
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());
|
||||
_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[] = {
|
||||
VK_FORMAT_D24_UNORM_S8_UINT,
|
||||
VK_FORMAT_D32_SFLOAT_S8_UINT,
|
||||
VK_FORMAT_D16_UNORM_S8_UINT,
|
||||
};
|
||||
|
||||
deviceInfo_.preferredDepthStencilFormat = VK_FORMAT_UNDEFINED;
|
||||
for (size_t i = 0; i < ARRAY_SIZE(depthStencilFormats); i++) {
|
||||
VkFormatProperties props;
|
||||
|
@ -575,8 +573,7 @@ void VulkanContext::ChooseDevice(int physical_device) {
|
|||
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
|
||||
// support for VMA.
|
||||
// This is as good a place as any to do this.
|
||||
vkGetPhysicalDeviceMemoryProperties(physical_devices_[physical_device_], &memory_properties_);
|
||||
INFO_LOG(G3D, "Memory Types (%d):", memory_properties_.memoryTypeCount);
|
||||
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};
|
||||
// Add to chain even if not supported, GetPhysicalDeviceFeatures is supposed to ignore unknown structs.
|
||||
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;
|
||||
multiViewFeatures.pNext = &presentWaitFeatures;
|
||||
presentWaitFeatures.pNext = &presentIdFeatures;
|
||||
presentIdFeatures.pNext = nullptr;
|
||||
|
||||
vkGetPhysicalDeviceFeatures2KHR(physical_devices_[physical_device_], &features2);
|
||||
deviceFeatures_.available.standard = features2.features;
|
||||
deviceFeatures_.available.multiview = multiViewFeatures;
|
||||
deviceFeatures_.available.presentWait = presentWaitFeatures;
|
||||
deviceFeatures_.available.presentId = presentIdFeatures;
|
||||
} else {
|
||||
vkGetPhysicalDeviceFeatures(physical_devices_[physical_device_], &deviceFeatures_.available.standard);
|
||||
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_);
|
||||
|
||||
device_extensions_enabled_.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
|
@ -668,6 +672,11 @@ VkResult VulkanContext::CreateDevice() {
|
|||
extensionsLookup_.KHR_get_memory_requirements2 = true;
|
||||
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)) {
|
||||
extensionsLookup_.KHR_create_renderpass2 = true;
|
||||
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_.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 };
|
||||
|
||||
VkDeviceCreateInfo device_info{ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
|
||||
|
@ -726,9 +700,6 @@ VkResult VulkanContext::CreateDevice() {
|
|||
device_info.pNext = &features2;
|
||||
features2.features = deviceFeatures_.enabled.standard;
|
||||
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 {
|
||||
device_info.pEnabledFeatures = &deviceFeatures_.enabled.standard;
|
||||
}
|
||||
|
@ -740,9 +711,7 @@ VkResult VulkanContext::CreateDevice() {
|
|||
} else {
|
||||
VulkanLoadDeviceFunctions(device_, extensionsLookup_);
|
||||
}
|
||||
INFO_LOG(G3D, "Vulkan Device created: %s", physicalDeviceProperties_[physical_device_].properties.deviceName);
|
||||
|
||||
// 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.
|
||||
INFO_LOG(G3D, "Vulkan Device created");
|
||||
VulkanSetAvailable(true);
|
||||
|
||||
VmaAllocatorCreateInfo allocatorInfo = {};
|
||||
|
@ -917,187 +886,6 @@ VkResult VulkanContext::ReinitSurface() {
|
|||
case WINDOWSYSTEM_DISPLAY:
|
||||
{
|
||||
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;
|
||||
retval = vkCreateDisplayPlaneSurfaceKHR(instance_, &display, nullptr, &surface_);
|
||||
break;
|
||||
|
@ -1233,12 +1021,6 @@ static std::string surface_transforms_to_string(VkSurfaceTransformFlagsKHR trans
|
|||
}
|
||||
|
||||
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_);
|
||||
if (res == VK_ERROR_SURFACE_LOST_KHR) {
|
||||
// Not much to do.
|
||||
|
@ -1273,26 +1055,23 @@ bool VulkanContext::InitSwapchain() {
|
|||
surfCapabilities_.maxImageExtent.width, surfCapabilities_.maxImageExtent.height,
|
||||
swapChainExtent_.width, swapChainExtent_.height);
|
||||
|
||||
availablePresentModes_.clear();
|
||||
// TODO: Find a better way to specify the prioritized present mode while being able
|
||||
// to fall back in a sensible way.
|
||||
VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_MAX_ENUM_KHR;
|
||||
std::string modes = "";
|
||||
for (size_t i = 0; i < presentModeCount; i++) {
|
||||
modes += VulkanPresentModeToString(presentModes[i]);
|
||||
modes += PresentModeString(presentModes[i]);
|
||||
if (i != presentModeCount - 1) {
|
||||
modes += ", ";
|
||||
}
|
||||
availablePresentModes_.push_back(presentModes[i]);
|
||||
}
|
||||
|
||||
INFO_LOG(G3D, "Supported present modes: %s", modes.c_str());
|
||||
for (size_t i = 0; i < presentModeCount; i++) {
|
||||
bool match = false;
|
||||
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) && 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.
|
||||
if (match || swapchainPresentMode == VK_PRESENT_MODE_MAX_ENUM_KHR) {
|
||||
|
@ -1302,6 +1081,10 @@ bool VulkanContext::InitSwapchain() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
#ifdef __ANDROID__
|
||||
// HACK
|
||||
swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
|
||||
#endif
|
||||
delete[] presentModes;
|
||||
// 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
|
||||
|
@ -1315,7 +1098,7 @@ bool VulkanContext::InitSwapchain() {
|
|||
}
|
||||
|
||||
INFO_LOG(G3D, "Chosen present mode: %d (%s). numSwapChainImages: %d/%d",
|
||||
swapchainPresentMode, VulkanPresentModeToString(swapchainPresentMode),
|
||||
swapchainPresentMode, PresentModeString(swapchainPresentMode),
|
||||
desiredNumberOfSwapChainImages, surfCapabilities_.maxImageCount);
|
||||
|
||||
// We mostly follow the practices from
|
||||
|
@ -1324,8 +1107,8 @@ bool VulkanContext::InitSwapchain() {
|
|||
VkSurfaceTransformFlagBitsKHR preTransform;
|
||||
std::string supportedTransforms = surface_transforms_to_string(surfCapabilities_.supportedTransforms);
|
||||
std::string currentTransform = surface_transforms_to_string(surfCapabilities_.currentTransform);
|
||||
g_display.rotation = DisplayRotation::ROTATE_0;
|
||||
g_display.rot_matrix.setIdentity();
|
||||
g_display_rotation = DisplayRotation::ROTATE_0;
|
||||
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;
|
||||
// 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) {
|
||||
// Normal, sensible rotations. Let's handle it.
|
||||
preTransform = surfCapabilities_.currentTransform;
|
||||
g_display.rot_matrix.setIdentity();
|
||||
g_display_rot_matrix.setIdentity();
|
||||
switch (surfCapabilities_.currentTransform) {
|
||||
case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
|
||||
g_display.rotation = DisplayRotation::ROTATE_90;
|
||||
g_display.rot_matrix.setRotationZ90();
|
||||
g_display_rotation = DisplayRotation::ROTATE_90;
|
||||
g_display_rot_matrix.setRotationZ90();
|
||||
std::swap(swapChainExtent_.width, swapChainExtent_.height);
|
||||
break;
|
||||
case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
|
||||
g_display.rotation = DisplayRotation::ROTATE_180;
|
||||
g_display.rot_matrix.setRotationZ180();
|
||||
g_display_rotation = DisplayRotation::ROTATE_180;
|
||||
g_display_rot_matrix.setRotationZ180();
|
||||
break;
|
||||
case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
|
||||
g_display.rotation = DisplayRotation::ROTATE_270;
|
||||
g_display.rot_matrix.setRotationZ270();
|
||||
g_display_rotation = DisplayRotation::ROTATE_270;
|
||||
g_display_rot_matrix.setRotationZ270();
|
||||
std::swap(swapChainExtent_.width, swapChainExtent_.height);
|
||||
break;
|
||||
default:
|
||||
|
@ -1395,8 +1178,6 @@ bool VulkanContext::InitSwapchain() {
|
|||
swap_chain_info.clipped = true;
|
||||
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.
|
||||
// if (surfCapabilities_.supportedUsageFlags & 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) {
|
||||
int deleteCount = 0;
|
||||
|
||||
for (auto &callback : callbacks_) {
|
||||
callback.func(vulkan, callback.userdata);
|
||||
deleteCount++;
|
||||
}
|
||||
callbacks_.clear();
|
||||
|
||||
VkDevice device = vulkan->GetDevice();
|
||||
for (auto &cmdPool : cmdPools_) {
|
||||
vkDestroyCommandPool(device, cmdPool, nullptr);
|
||||
deleteCount++;
|
||||
}
|
||||
cmdPools_.clear();
|
||||
for (auto &descPool : descPools_) {
|
||||
vkDestroyDescriptorPool(device, descPool, nullptr);
|
||||
deleteCount++;
|
||||
}
|
||||
descPools_.clear();
|
||||
for (auto &module : modules_) {
|
||||
vkDestroyShaderModule(device, module, nullptr);
|
||||
deleteCount++;
|
||||
}
|
||||
modules_.clear();
|
||||
for (auto &buf : buffers_) {
|
||||
vkDestroyBuffer(device, buf, nullptr);
|
||||
deleteCount++;
|
||||
}
|
||||
buffers_.clear();
|
||||
for (auto &buf : buffersWithAllocs_) {
|
||||
vmaDestroyBuffer(allocator, buf.buffer, buf.alloc);
|
||||
deleteCount++;
|
||||
}
|
||||
buffersWithAllocs_.clear();
|
||||
for (auto &bufView : bufferViews_) {
|
||||
vkDestroyBufferView(device, bufView, nullptr);
|
||||
deleteCount++;
|
||||
}
|
||||
bufferViews_.clear();
|
||||
for (auto &imageWithAlloc : imagesWithAllocs_) {
|
||||
vmaDestroyImage(allocator, imageWithAlloc.image, imageWithAlloc.alloc);
|
||||
deleteCount++;
|
||||
}
|
||||
imagesWithAllocs_.clear();
|
||||
for (auto &imageView : imageViews_) {
|
||||
vkDestroyImageView(device, imageView, nullptr);
|
||||
deleteCount++;
|
||||
}
|
||||
imageViews_.clear();
|
||||
for (auto &mem : deviceMemory_) {
|
||||
vkFreeMemory(device, mem, nullptr);
|
||||
deleteCount++;
|
||||
}
|
||||
deviceMemory_.clear();
|
||||
for (auto &sampler : samplers_) {
|
||||
vkDestroySampler(device, sampler, nullptr);
|
||||
deleteCount++;
|
||||
}
|
||||
samplers_.clear();
|
||||
for (auto &pipeline : pipelines_) {
|
||||
vkDestroyPipeline(device, pipeline, nullptr);
|
||||
deleteCount++;
|
||||
}
|
||||
pipelines_.clear();
|
||||
for (auto &pcache : pipelineCaches_) {
|
||||
vkDestroyPipelineCache(device, pcache, nullptr);
|
||||
deleteCount++;
|
||||
}
|
||||
pipelineCaches_.clear();
|
||||
for (auto &renderPass : renderPasses_) {
|
||||
vkDestroyRenderPass(device, renderPass, nullptr);
|
||||
deleteCount++;
|
||||
}
|
||||
renderPasses_.clear();
|
||||
for (auto &framebuffer : framebuffers_) {
|
||||
vkDestroyFramebuffer(device, framebuffer, nullptr);
|
||||
deleteCount++;
|
||||
}
|
||||
framebuffers_.clear();
|
||||
for (auto &pipeLayout : pipelineLayouts_) {
|
||||
vkDestroyPipelineLayout(device, pipeLayout, nullptr);
|
||||
deleteCount++;
|
||||
}
|
||||
pipelineLayouts_.clear();
|
||||
for (auto &descSetLayout : descSetLayouts_) {
|
||||
vkDestroyDescriptorSetLayout(device, descSetLayout, nullptr);
|
||||
deleteCount++;
|
||||
}
|
||||
descSetLayouts_.clear();
|
||||
for (auto &queryPool : queryPools_) {
|
||||
vkDestroyQueryPool(device, queryPool, nullptr);
|
||||
deleteCount++;
|
||||
}
|
||||
queryPools_.clear();
|
||||
deleteCount_ = deleteCount;
|
||||
}
|
||||
|
||||
void VulkanContext::GetImageMemoryRequirements(VkImage image, VkMemoryRequirements *mem_reqs, bool *dedicatedAllocation) {
|
||||
|
|
|
@ -36,7 +36,6 @@ enum {
|
|||
VULKAN_VENDOR_QUALCOMM = 0x00005143,
|
||||
VULKAN_VENDOR_IMGTEC = 0x00001010, // PowerVR
|
||||
VULKAN_VENDOR_APPLE = 0x0000106b, // Apple through MoltenVK
|
||||
VULKAN_VENDOR_MESA = 0x00010005, // lavapipe
|
||||
};
|
||||
|
||||
VK_DEFINE_HANDLE(VmaAllocator);
|
||||
|
@ -138,10 +137,6 @@ public:
|
|||
void Take(VulkanDeleteList &del);
|
||||
void PerformDeletes(VulkanContext *vulkan, VmaAllocator allocator);
|
||||
|
||||
int GetLastDeleteCount() const {
|
||||
return deleteCount_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<VkCommandPool> cmdPools_;
|
||||
std::vector<VkDescriptorPool> descPools_;
|
||||
|
@ -161,7 +156,6 @@ private:
|
|||
std::vector<VkDescriptorSetLayout> descSetLayouts_;
|
||||
std::vector<VkQueryPool> queryPools_;
|
||||
std::vector<Callback> callbacks_;
|
||||
int deleteCount_ = 0;
|
||||
};
|
||||
|
||||
// VulkanContext manages the device and swapchain, and deferred deletion of objects.
|
||||
|
@ -228,8 +222,7 @@ public:
|
|||
// Simple workaround for the casting warning.
|
||||
template <class T>
|
||||
void SetDebugName(T handle, VkObjectType type, const char *name) {
|
||||
if (extensionsLookup_.EXT_debug_utils && handle != VK_NULL_HANDLE) {
|
||||
_dbg_assert_(handle != VK_NULL_HANDLE);
|
||||
if (extensionsLookup_.EXT_debug_utils) {
|
||||
SetDebugNameImpl((uint64_t)handle, type, name);
|
||||
}
|
||||
}
|
||||
|
@ -270,8 +263,6 @@ public:
|
|||
struct AllPhysicalDeviceFeatures {
|
||||
VkPhysicalDeviceFeatures standard;
|
||||
VkPhysicalDeviceMultiviewFeatures multiview;
|
||||
VkPhysicalDevicePresentWaitFeaturesKHR presentWait;
|
||||
VkPhysicalDevicePresentIdFeaturesKHR presentId;
|
||||
};
|
||||
|
||||
const PhysicalDeviceProps &GetPhysicalDeviceProperties(int i = -1) const {
|
||||
|
@ -297,13 +288,6 @@ public:
|
|||
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 {
|
||||
return memory_properties_;
|
||||
}
|
||||
|
@ -327,6 +311,7 @@ public:
|
|||
for (const auto &iter : instance_layer_properties_) {
|
||||
for (const auto &ext : iter.extensions) {
|
||||
if (!strcmp(extensionName, ext.extensionName)) {
|
||||
INFO_LOG(G3D, "%s found in layer extensions: %s", extensionName, iter.properties.layerName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -344,7 +329,6 @@ public:
|
|||
}
|
||||
|
||||
int GetInflightFrames() const {
|
||||
// out of MAX_INFLIGHT_FRAMES.
|
||||
return inflightFrames_;
|
||||
}
|
||||
// Don't call while a frame is in progress.
|
||||
|
@ -389,18 +373,6 @@ public:
|
|||
return surfFormats_;
|
||||
}
|
||||
|
||||
VkPresentModeKHR GetPresentMode() const {
|
||||
return presentMode_;
|
||||
}
|
||||
|
||||
std::vector<VkPresentModeKHR> GetAvailablePresentModes() const {
|
||||
return availablePresentModes_;
|
||||
}
|
||||
|
||||
int GetLastDeleteCount() const {
|
||||
return frame_[curFrame_].deleteList.GetLastDeleteCount();
|
||||
}
|
||||
|
||||
private:
|
||||
bool ChooseQueue();
|
||||
|
||||
|
@ -488,9 +460,6 @@ private:
|
|||
VkSurfaceCapabilitiesKHR surfCapabilities_{};
|
||||
std::vector<VkSurfaceFormatKHR> surfFormats_{};
|
||||
|
||||
VkPresentModeKHR presentMode_;
|
||||
std::vector<VkPresentModeKHR> availablePresentModes_;
|
||||
|
||||
std::vector<VkCommandBuffer> cmdQueue_;
|
||||
|
||||
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 *VulkanFormatToString(VkFormat format);
|
||||
const char *VulkanPresentModeToString(VkPresentModeKHR presentMode);
|
||||
|
||||
std::string FormatDriverVersion(const VkPhysicalDeviceProperties &props);
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <mutex>
|
||||
|
||||
#include "Common/Log.h"
|
||||
#include "Common/System/System.h"
|
||||
#include "Common/GPU/Vulkan/VulkanContext.h"
|
||||
#include "Common/GPU/Vulkan/VulkanDebug.h"
|
||||
|
||||
|
@ -48,62 +47,22 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback(
|
|||
const char *pMessage = pCallbackData->pMessage;
|
||||
|
||||
int messageCode = pCallbackData->messageIdNumber;
|
||||
switch (messageCode) {
|
||||
case 101294395:
|
||||
if (messageCode == 101294395) {
|
||||
// UNASSIGNED-CoreValidation-Shader-OutputNotConsumed - benign perf warning
|
||||
return false;
|
||||
case 1303270965:
|
||||
}
|
||||
if (messageCode == 1303270965) {
|
||||
// Benign perf warning, image blit using GENERAL layout.
|
||||
// TODO: Oops, turns out we filtered out a bit too much here!
|
||||
// We really need that performance flag check to sort out the stuff that matters.
|
||||
// Will enable it soon, but it'll take some fixing.
|
||||
//
|
||||
if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)
|
||||
// UNASSIGNED
|
||||
return false;
|
||||
break;
|
||||
|
||||
case 606910136:
|
||||
case -392708513:
|
||||
case -384083808:
|
||||
}
|
||||
if (messageCode == 606910136 || messageCode == -392708513 || messageCode == -384083808) {
|
||||
// VUID-vkCmdDraw-None-02686
|
||||
// Kinda false positive, or at least very unnecessary, now that I solved the real issue.
|
||||
// See https://github.com/hrydgard/ppsspp/pull/16354
|
||||
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;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_errorCountMutex);
|
||||
|
@ -140,7 +99,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback(
|
|||
#ifdef _WIN32
|
||||
OutputDebugStringA(msg.c_str());
|
||||
if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
|
||||
if (options->breakOnError && System_GetPropertyBool(SYSPROP_DEBUGGER_PRESENT)) {
|
||||
if (options->breakOnError && IsDebuggerPresent()) {
|
||||
DebugBreak();
|
||||
}
|
||||
if (options->msgBoxOnError) {
|
||||
|
@ -148,7 +107,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback(
|
|||
}
|
||||
} 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.
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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_;
|
||||
};
|
|
@ -4,19 +4,6 @@
|
|||
#include "Common/Log.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) {
|
||||
this->index = index;
|
||||
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());
|
||||
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 };
|
||||
query_ci.queryCount = MAX_TIMESTAMP_QUERIES;
|
||||
query_ci.queryType = VK_QUERY_TYPE_TIMESTAMP;
|
||||
|
@ -60,13 +52,8 @@ void FrameData::Destroy(VulkanContext *vulkan) {
|
|||
vkDestroyCommandPool(device, cmdPoolInit, nullptr);
|
||||
vkDestroyCommandPool(device, cmdPoolMain, nullptr);
|
||||
vkDestroyFence(device, fence, nullptr);
|
||||
vkDestroyFence(device, readbackFence, 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) {
|
||||
|
@ -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));
|
||||
skipSwap = true;
|
||||
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:
|
||||
// Weird, shouldn't get any other values. Maybe lost device?
|
||||
_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.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);
|
||||
}
|
||||
|
||||
|
@ -150,7 +114,7 @@ VkCommandBuffer FrameData::GetInitCmd(VulkanContext *vulkan) {
|
|||
}
|
||||
|
||||
// Good spot to reset the query pool.
|
||||
if (profile.enabled) {
|
||||
if (profilingEnabled_) {
|
||||
vkCmdResetQueryPool(initCmd, profile.queryPool, 0, MAX_TIMESTAMP_QUERIES);
|
||||
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;
|
||||
|
||||
if (hasInitCommands) {
|
||||
if (profile.enabled) {
|
||||
if (profilingEnabled_) {
|
||||
// Pre-allocated query ID 1 - end of init cmdbuf.
|
||||
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) {
|
||||
fenceToTrigger = sharedData.readbackFence;
|
||||
fenceToTrigger = readbackFence;
|
||||
}
|
||||
|
||||
if (hasMainCommands) {
|
||||
|
@ -225,16 +189,12 @@ void FrameData::SubmitPending(VulkanContext *vulkan, FrameSubmitType type, Frame
|
|||
|
||||
VkResult res;
|
||||
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.
|
||||
res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger);
|
||||
if (sharedData.useMultiThreading) {
|
||||
std::lock_guard<std::mutex> lock(fenceMutex);
|
||||
readyForFence = true;
|
||||
fenceCondVar.notify_one();
|
||||
}
|
||||
} else {
|
||||
VLOG("Doing queue submit, fencing something (%p)", 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) {
|
||||
// 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);
|
||||
vkResetFences(vulkan->GetDevice(), 1, &sharedData.readbackFence);
|
||||
vkWaitForFences(vulkan->GetDevice(), 1, &readbackFence, true, UINT64_MAX);
|
||||
vkResetFences(vulkan->GetDevice(), 1, &readbackFence);
|
||||
syncDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
void FrameDataShared::Init(VulkanContext *vulkan, bool useMultiThreading, bool measurePresentTime) {
|
||||
void FrameDataShared::Init(VulkanContext *vulkan) {
|
||||
VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
|
||||
semaphoreCreateInfo.flags = 0;
|
||||
VkResult res = vkCreateSemaphore(vulkan->GetDevice(), &semaphoreCreateInfo, nullptr, &acquireSemaphore);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
res = vkCreateSemaphore(vulkan->GetDevice(), &semaphoreCreateInfo, nullptr, &renderingCompleteSemaphore);
|
||||
_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) {
|
||||
VkDevice device = vulkan->GetDevice();
|
||||
vkDestroySemaphore(device, acquireSemaphore, nullptr);
|
||||
vkDestroySemaphore(device, renderingCompleteSemaphore, nullptr);
|
||||
vkDestroyFence(device, readbackFence, nullptr);
|
||||
}
|
||||
|
|
|
@ -6,46 +6,23 @@
|
|||
#include <condition_variable>
|
||||
|
||||
#include "Common/GPU/Vulkan/VulkanContext.h"
|
||||
#include "Common/Data/Collections/Hashmaps.h"
|
||||
|
||||
enum {
|
||||
MAX_TIMESTAMP_QUERIES = 128,
|
||||
};
|
||||
|
||||
enum class VKRRunType {
|
||||
SUBMIT,
|
||||
PRESENT,
|
||||
SYNC,
|
||||
EXIT,
|
||||
};
|
||||
|
||||
struct QueueProfileContext {
|
||||
bool enabled = false;
|
||||
bool timestampsEnabled = false;
|
||||
VkQueryPool queryPool;
|
||||
std::vector<std::string> timestampDescriptions;
|
||||
std::string profileSummary;
|
||||
double cpuStartTime;
|
||||
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 {
|
||||
|
@ -53,12 +30,7 @@ struct FrameDataShared {
|
|||
VkSemaphore acquireSemaphore = VK_NULL_HANDLE;
|
||||
VkSemaphore renderingCompleteSemaphore = VK_NULL_HANDLE;
|
||||
|
||||
// For synchronous readbacks.
|
||||
VkFence readbackFence = VK_NULL_HANDLE;
|
||||
bool useMultiThreading;
|
||||
bool measurePresentTime;
|
||||
|
||||
void Init(VulkanContext *vulkan, bool useMultiThreading, bool measurePresentTime);
|
||||
void Init(VulkanContext *vulkan);
|
||||
void Destroy(VulkanContext *vulkan);
|
||||
};
|
||||
|
||||
|
@ -77,6 +49,7 @@ struct FrameData {
|
|||
bool readyForFence = true;
|
||||
|
||||
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.
|
||||
VkCommandPool cmdPoolInit = VK_NULL_HANDLE; // Written to from main thread
|
||||
|
@ -98,17 +71,9 @@ struct FrameData {
|
|||
// Swapchain.
|
||||
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.
|
||||
QueueProfileContext profile{};
|
||||
|
||||
// Async readback cache.
|
||||
DenseHashMap<ReadbackKey, CachedReadback *> readbacks_;
|
||||
|
||||
FrameData() : readbacks_(8) {}
|
||||
QueueProfileContext profile;
|
||||
bool profilingEnabled_ = false;
|
||||
|
||||
void Init(VulkanContext *vulkan, int index);
|
||||
void Destroy(VulkanContext *vulkan);
|
||||
|
@ -124,5 +89,5 @@ struct FrameData {
|
|||
|
||||
private:
|
||||
// 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
Loading…
Add table
Add a link
Reference in a new issue