mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-02-17 04:39:34 +00:00
Merge remote-tracking branch 'upstream/master' into Font-ltn12-hack
This commit is contained in:
commit
0e52856faf
@ -6,7 +6,7 @@
|
||||
|
||||
# Core definitions
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: normal
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
|
||||
.core-defs:
|
||||
variables:
|
||||
|
@ -1,11 +1,11 @@
|
||||
# vim:noexpandtab:
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(PPSSPP)
|
||||
enable_testing()
|
||||
|
||||
#This is supposed to work but doesn't!
|
||||
if(NOT ANDROID)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
|
||||
@ -120,8 +120,16 @@ if(OPENXR)
|
||||
add_definitions(-DOPENXR)
|
||||
add_library(openxr SHARED IMPORTED)
|
||||
include_directories(ext/openxr)
|
||||
set_property(TARGET openxr PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/ext/openxr/libs/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)
|
||||
@ -221,7 +229,9 @@ include_directories(ext/glslang)
|
||||
# Anyway, glew will be going away anyway.
|
||||
include_directories(ext/glew)
|
||||
|
||||
if(NOT OPENGL_LIBRARIES AND USING_GLES2)
|
||||
if(OPENXR)
|
||||
set(OPENGL_LIBRARIES GLESv3 EGL)
|
||||
elseif(NOT OPENGL_LIBRARIES AND USING_GLES2)
|
||||
set(OPENGL_LIBRARIES GLESv2 EGL)
|
||||
endif()
|
||||
|
||||
@ -313,7 +323,7 @@ if(NOT MSVC)
|
||||
add_definitions(-DPNG_ARM_NEON_OPT=0)
|
||||
|
||||
if(ANDROID)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17")
|
||||
endif()
|
||||
if(CLANG)
|
||||
add_definitions(-Wno-nullability-completeness)
|
||||
@ -368,8 +378,9 @@ if(NOT MSVC)
|
||||
endif()
|
||||
|
||||
if(IOS)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0")
|
||||
elseif(APPLE AND NOT CMAKE_CROSSCOMPILING)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8")
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -U__STRICT_ANSI__")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
||||
elseif(NOT ANDROID)
|
||||
@ -426,6 +437,15 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
|
||||
set(CommonJIT
|
||||
Core/MIPS/JitCommon/JitCommon.cpp
|
||||
Core/MIPS/JitCommon/JitCommon.h
|
||||
Core/MIPS/JitCommon/JitBlockCache.cpp
|
||||
Core/MIPS/JitCommon/JitBlockCache.h
|
||||
Core/MIPS/JitCommon/JitState.cpp
|
||||
Core/MIPS/JitCommon/JitState.h
|
||||
)
|
||||
|
||||
set(CommonX86
|
||||
Common/ABI.cpp
|
||||
Common/ABI.h
|
||||
@ -464,7 +484,10 @@ set(CommonMIPS
|
||||
source_group(MIPS FILES ${CommonMIPS})
|
||||
|
||||
set(CommonRISCV64
|
||||
${CommonJIT}
|
||||
Common/RiscVCPUDetect.cpp
|
||||
Common/RiscVEmitter.cpp
|
||||
Common/RiscVEmitter.h
|
||||
Core/MIPS/fake/FakeJit.cpp
|
||||
Core/MIPS/fake/FakeJit.h
|
||||
)
|
||||
@ -609,15 +632,14 @@ add_library(Common STATIC
|
||||
Common/GPU/Vulkan/VulkanRenderManager.h
|
||||
Common/GPU/Vulkan/VulkanQueueRunner.cpp
|
||||
Common/GPU/Vulkan/VulkanQueueRunner.h
|
||||
Common/GPU/Vulkan/VulkanFrameData.cpp
|
||||
Common/GPU/Vulkan/VulkanFrameData.h
|
||||
Common/Input/GestureDetector.cpp
|
||||
Common/Input/GestureDetector.h
|
||||
Common/Input/KeyCodes.h
|
||||
Common/Input/InputState.cpp
|
||||
Common/Input/InputState.h
|
||||
Common/Math/fast/fast_math.c
|
||||
Common/Math/fast/fast_matrix.c
|
||||
Common/Math/fast/fast_matrix_neon.S
|
||||
Common/Math/fast/fast_matrix_sse.c
|
||||
Common/Math/curves.cpp
|
||||
Common/Math/curves.h
|
||||
Common/Math/expression_parser.cpp
|
||||
@ -628,6 +650,8 @@ add_library(Common STATIC
|
||||
Common/Math/lin/vec3.h
|
||||
Common/Math/math_util.cpp
|
||||
Common/Math/math_util.h
|
||||
Common/Math/Statistics.h
|
||||
Common/Math/Statistics.cpp
|
||||
Common/Net/HTTPClient.cpp
|
||||
Common/Net/HTTPClient.h
|
||||
Common/Net/HTTPHeaders.cpp
|
||||
@ -1070,14 +1094,20 @@ if(ANDROID)
|
||||
|
||||
if (OPENXR)
|
||||
set(nativeExtra ${nativeExtra}
|
||||
Common/VR/PPSSPPVR.cpp
|
||||
Common/VR/PPSSPPVR.h
|
||||
Common/VR/VRBase.cpp
|
||||
Common/VR/VRBase.h
|
||||
Common/VR/VRFramebuffer.cpp
|
||||
Common/VR/VRFramebuffer.h
|
||||
Common/VR/VRInput.cpp
|
||||
Common/VR/VRInput.h
|
||||
Common/VR/VRMath.cpp
|
||||
Common/VR/VRMath.h
|
||||
Common/VR/VRRenderer.cpp
|
||||
Common/VR/VRRenderer.h
|
||||
Common/VR/VRTweaks.cpp
|
||||
Common/VR/VRTweaks.h
|
||||
)
|
||||
set(nativeExtraLibs ${nativeExtraLibs} openxr)
|
||||
endif()
|
||||
@ -1526,14 +1556,16 @@ set(GPU_SOURCES
|
||||
${GPU_NEON}
|
||||
GPU/Common/Draw2D.cpp
|
||||
GPU/Common/Draw2D.h
|
||||
GPU/Common/DepalettizeCommon.cpp
|
||||
GPU/Common/DepalettizeCommon.h
|
||||
GPU/Common/TextureShaderCommon.cpp
|
||||
GPU/Common/TextureShaderCommon.h
|
||||
GPU/Common/DepalettizeShaderCommon.cpp
|
||||
GPU/Common/DepalettizeShaderCommon.h
|
||||
GPU/Common/FragmentShaderGenerator.cpp
|
||||
GPU/Common/FragmentShaderGenerator.h
|
||||
GPU/Common/VertexShaderGenerator.cpp
|
||||
GPU/Common/VertexShaderGenerator.h
|
||||
GPU/Common/GeometryShaderGenerator.cpp
|
||||
GPU/Common/GeometryShaderGenerator.h
|
||||
GPU/Common/FramebufferManagerCommon.cpp
|
||||
GPU/Common/FramebufferManagerCommon.h
|
||||
GPU/Common/GPUDebugInterface.cpp
|
||||
@ -1577,6 +1609,8 @@ set(GPU_SOURCES
|
||||
GPU/Debugger/Breakpoints.h
|
||||
GPU/Debugger/Debugger.cpp
|
||||
GPU/Debugger/Debugger.h
|
||||
GPU/Debugger/GECommandTable.cpp
|
||||
GPU/Debugger/GECommandTable.h
|
||||
GPU/Debugger/Playback.cpp
|
||||
GPU/Debugger/Playback.h
|
||||
GPU/Debugger/Record.cpp
|
||||
@ -1626,6 +1660,7 @@ set(GPU_SOURCES
|
||||
# SHARED on ANDROID, STATIC everywhere else
|
||||
add_library(${CoreLibName} ${CoreLinkType}
|
||||
${CoreExtra}
|
||||
${CommonJIT}
|
||||
Core/Config.cpp
|
||||
Core/Config.h
|
||||
Core/ConfigValues.h
|
||||
@ -1933,12 +1968,6 @@ add_library(${CoreLibName} ${CoreLinkType}
|
||||
Core/FileLoaders/RamCachingFileLoader.h
|
||||
Core/FileLoaders/RetryingFileLoader.cpp
|
||||
Core/FileLoaders/RetryingFileLoader.h
|
||||
Core/MIPS/JitCommon/JitCommon.cpp
|
||||
Core/MIPS/JitCommon/JitCommon.h
|
||||
Core/MIPS/JitCommon/JitBlockCache.cpp
|
||||
Core/MIPS/JitCommon/JitBlockCache.h
|
||||
Core/MIPS/JitCommon/JitState.cpp
|
||||
Core/MIPS/JitCommon/JitState.h
|
||||
Core/MIPS/MIPS.cpp
|
||||
Core/MIPS/MIPS.h
|
||||
Core/MIPS/MIPSAnalyst.cpp
|
||||
@ -2042,6 +2071,8 @@ endif()
|
||||
target_link_libraries(${CoreLibName} Common native kirk cityhash sfmt19937 xbrz xxhash ${GlslangLibs}
|
||||
${CoreExtraLibs} ${OPENGL_LIBRARIES} ${X11_LIBRARIES} ${CMAKE_DL_LIBS})
|
||||
|
||||
target_compile_features(${CoreLibName} PUBLIC cxx_std_17)
|
||||
|
||||
if(FFmpeg_FOUND)
|
||||
target_compile_definitions(${CoreLibName} PRIVATE USE_FFMPEG=1)
|
||||
set_target_properties(${CoreLibName} PROPERTIES NO_SYSTEM_FROM_IMPORTED true)
|
||||
@ -2337,6 +2368,7 @@ if(UNITTEST)
|
||||
unittest/TestIRPassSimplify.cpp
|
||||
unittest/TestX64Emitter.cpp
|
||||
unittest/TestVertexJit.cpp
|
||||
unittest/TestRiscVEmitter.cpp
|
||||
unittest/TestSoftwareGPUJit.cpp
|
||||
unittest/TestThreadManager.cpp
|
||||
unittest/JitHarness.cpp
|
||||
@ -2429,7 +2461,7 @@ if(NOT ANDROID)
|
||||
endif()
|
||||
# packaging and code signing
|
||||
if(IOS)
|
||||
set(DEPLOYMENT_TARGET 8.0)
|
||||
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)
|
||||
list(REMOVE_ITEM IOSAssets ${CMAKE_CURRENT_SOURCE_DIR}/ios/assets/Default-568h@3x.png)
|
||||
@ -2483,6 +2515,8 @@ if(UNIX AND NOT ANDROID AND NOT APPLE)
|
||||
DIRECTORY "${CMAKE_BINARY_DIR}/assets"
|
||||
DESTINATION "${CMAKE_INSTALL_DATADIR}/ppsspp"
|
||||
PATTERN ".git*" EXCLUDE
|
||||
PATTERN "mime" EXCLUDE
|
||||
PATTERN "lang/README.md" EXCLUDE
|
||||
)
|
||||
install(
|
||||
FILES "${CMAKE_BINARY_DIR}/ppsspp.desktop"
|
||||
@ -2495,7 +2529,11 @@ if(UNIX AND NOT ANDROID AND NOT APPLE)
|
||||
)
|
||||
install(
|
||||
FILES "${CMAKE_SOURCE_DIR}/icons/icon-512.svg"
|
||||
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pixmaps"
|
||||
RENAME ppsspp.svg
|
||||
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps"
|
||||
RENAME "ppsspp.svg"
|
||||
)
|
||||
install(
|
||||
FILES "${CMAKE_SOURCE_DIR}/assets/mime/ppsspp.xml"
|
||||
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/mime/packages"
|
||||
)
|
||||
endif()
|
||||
|
@ -153,6 +153,7 @@
|
||||
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
@ -179,6 +180,7 @@
|
||||
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
@ -205,6 +207,7 @@
|
||||
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
@ -232,6 +235,7 @@
|
||||
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
@ -264,6 +268,7 @@
|
||||
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
@ -298,6 +303,7 @@
|
||||
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
@ -332,6 +338,7 @@
|
||||
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
@ -366,6 +373,7 @@
|
||||
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
@ -441,6 +449,7 @@
|
||||
<ClInclude Include="GPU\Vulkan\VulkanBarrier.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanContext.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanDebug.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanFrameData.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanImage.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanLoader.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanMemory.h" />
|
||||
@ -452,12 +461,12 @@
|
||||
<ClInclude Include="Input\KeyCodes.h" />
|
||||
<ClInclude Include="Math\curves.h" />
|
||||
<ClInclude Include="Math\expression_parser.h" />
|
||||
<ClInclude Include="Math\fast\fast_math.h" />
|
||||
<ClInclude Include="Math\fast\fast_matrix.h" />
|
||||
<ClInclude Include="Math\geom2d.h" />
|
||||
<ClInclude Include="Math\lin\matrix4x4.h" />
|
||||
<ClInclude Include="Math\lin\vec3.h" />
|
||||
<ClInclude Include="Math\math_util.h" />
|
||||
<ClInclude Include="Math\Statistics.h" />
|
||||
<ClInclude Include="Net\NetBuffer.h" />
|
||||
<ClInclude Include="Net\HTTPClient.h" />
|
||||
<ClInclude Include="Net\HTTPHeaders.h" />
|
||||
@ -475,6 +484,7 @@
|
||||
<ClInclude Include="Render\Text\draw_text_uwp.h" />
|
||||
<ClInclude Include="Render\Text\draw_text_win.h" />
|
||||
<ClInclude Include="LogReporting.h" />
|
||||
<ClInclude Include="RiscVEmitter.h" />
|
||||
<ClInclude Include="Serialize\SerializeDeque.h" />
|
||||
<ClInclude Include="Serialize\SerializeFuncs.h" />
|
||||
<ClInclude Include="Serialize\SerializeList.h" />
|
||||
@ -861,6 +871,7 @@
|
||||
<ClCompile Include="GPU\Vulkan\VulkanBarrier.cpp" />
|
||||
<ClCompile Include="GPU\Vulkan\VulkanContext.cpp" />
|
||||
<ClCompile Include="GPU\Vulkan\VulkanDebug.cpp" />
|
||||
<ClCompile Include="GPU\Vulkan\VulkanFrameData.cpp" />
|
||||
<ClCompile Include="GPU\Vulkan\VulkanImage.cpp" />
|
||||
<ClCompile Include="GPU\Vulkan\VulkanLoader.cpp" />
|
||||
<ClCompile Include="GPU\Vulkan\VulkanMemory.cpp" />
|
||||
@ -872,12 +883,11 @@
|
||||
<ClCompile Include="Log.cpp" />
|
||||
<ClCompile Include="Math\curves.cpp" />
|
||||
<ClCompile Include="Math\expression_parser.cpp" />
|
||||
<ClCompile Include="Math\fast\fast_math.c" />
|
||||
<ClCompile Include="Math\fast\fast_matrix.c" />
|
||||
<ClCompile Include="Math\fast\fast_matrix_sse.c" />
|
||||
<ClCompile Include="Math\lin\matrix4x4.cpp" />
|
||||
<ClCompile Include="Math\lin\vec3.cpp" />
|
||||
<ClCompile Include="Math\math_util.cpp" />
|
||||
<ClCompile Include="Math\Statistics.cpp" />
|
||||
<ClCompile Include="Net\NetBuffer.cpp" />
|
||||
<ClCompile Include="Net\HTTPClient.cpp" />
|
||||
<ClCompile Include="Net\HTTPHeaders.cpp" />
|
||||
@ -896,6 +906,7 @@
|
||||
<ClCompile Include="Render\Text\draw_text_win.cpp" />
|
||||
<ClCompile Include="LogReporting.cpp" />
|
||||
<ClCompile Include="RiscVCPUDetect.cpp" />
|
||||
<ClCompile Include="RiscVEmitter.cpp" />
|
||||
<ClCompile Include="Serialize\Serializer.cpp" />
|
||||
<ClCompile Include="Data\Convert\ColorConv.cpp" />
|
||||
<ClCompile Include="ConsoleListener.cpp" />
|
||||
@ -989,10 +1000,6 @@
|
||||
<Project>{f761046e-6c38-4428-a5f1-38391a37bb34}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Math\fast\fast_matrix_neon.S" />
|
||||
<None Include="Math\lin\matrix_neon.s" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
|
@ -161,9 +161,6 @@
|
||||
<ClInclude Include="Math\lin\vec3.h">
|
||||
<Filter>Math\lin</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Math\fast\fast_math.h">
|
||||
<Filter>Math\fast</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Math\fast\fast_matrix.h">
|
||||
<Filter>Math\fast</Filter>
|
||||
</ClInclude>
|
||||
@ -421,6 +418,13 @@
|
||||
<ClInclude Include="GPU\Vulkan\VulkanBarrier.h">
|
||||
<Filter>GPU\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RiscVEmitter.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanFrameData.h">
|
||||
<Filter>GPU\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Math\Statistics.h">
|
||||
<Filter>Math</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ABI.cpp" />
|
||||
@ -567,15 +571,9 @@
|
||||
<ClCompile Include="Math\lin\vec3.cpp">
|
||||
<Filter>Math\lin</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Math\fast\fast_math.c">
|
||||
<Filter>Math\fast</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Math\fast\fast_matrix.c">
|
||||
<Filter>Math\fast</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Math\fast\fast_matrix_sse.c">
|
||||
<Filter>Math\fast</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Data\Format\RIFF.cpp">
|
||||
<Filter>Data\Format</Filter>
|
||||
</ClCompile>
|
||||
@ -798,6 +796,13 @@
|
||||
<ClCompile Include="GPU\Vulkan\VulkanBarrier.cpp">
|
||||
<Filter>GPU\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RiscVEmitter.cpp" />
|
||||
<ClCompile Include="GPU\Vulkan\VulkanFrameData.cpp">
|
||||
<Filter>GPU\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Math\Statistics.cpp">
|
||||
<Filter>Math</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Crypto">
|
||||
@ -908,12 +913,4 @@
|
||||
<Filter>ext\libpng17</Filter>
|
||||
</Text>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Math\lin\matrix_neon.s">
|
||||
<Filter>Math\lin</Filter>
|
||||
</None>
|
||||
<None Include="Math\fast\fast_matrix_neon.S">
|
||||
<Filter>Math\fast</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -127,6 +127,16 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void IterateMut(T func) {
|
||||
for (size_t i = 0; i < map.size(); i++) {
|
||||
if (state[i] == BucketState::TAKEN) {
|
||||
func(map[i].key, map[i].value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note! Does NOT delete any pointed-to data (in case you stored pointers in the map).
|
||||
void Clear() {
|
||||
memset(state.data(), (int)BucketState::FREE, state.size());
|
||||
count_ = 0;
|
||||
|
@ -3,25 +3,62 @@
|
||||
#include <vector>
|
||||
|
||||
// Insert-only small-set implementation. Performs no allocation unless MaxFastSize is exceeded.
|
||||
// Can also be used as a small vector, then use push_back (or push_in_place) instead of insert.
|
||||
// Duplicates are thus allowed if you use that, but not if you exclusively use insert.
|
||||
template <class T, int MaxFastSize>
|
||||
struct TinySet {
|
||||
~TinySet() { delete slowLookup_; }
|
||||
inline void insert(T t) {
|
||||
inline void insert(const T &t) {
|
||||
// Fast linear scan.
|
||||
for (int i = 0; i < fastCount; i++) {
|
||||
for (int i = 0; i < fastCount_; i++) {
|
||||
if (fastLookup_[i] == t)
|
||||
return; // We already have it.
|
||||
}
|
||||
// Fast insertion
|
||||
if (fastCount < MaxFastSize) {
|
||||
fastLookup_[fastCount++] = t;
|
||||
if (fastCount_ < MaxFastSize) {
|
||||
fastLookup_[fastCount_++] = t;
|
||||
return;
|
||||
}
|
||||
// Fall back to slow path.
|
||||
insertSlow(t);
|
||||
}
|
||||
inline void push_back(const T &t) {
|
||||
if (fastCount_ < MaxFastSize) {
|
||||
fastLookup_[fastCount_++] = t;
|
||||
return;
|
||||
}
|
||||
if (!slowLookup_) {
|
||||
slowLookup_ = new std::vector<T>();
|
||||
}
|
||||
slowLookup_->push_back(t);
|
||||
}
|
||||
inline T *add_back() {
|
||||
if (fastCount_ < MaxFastSize) {
|
||||
return &fastLookup_[fastCount_++];
|
||||
}
|
||||
if (!slowLookup_) {
|
||||
slowLookup_ = new std::vector<T>();
|
||||
}
|
||||
T t;
|
||||
slowLookup_->push_back(t);
|
||||
return slowLookup_->back();
|
||||
}
|
||||
void append(const TinySet<T, MaxFastSize> &other) {
|
||||
size_t otherSize = other.size();
|
||||
if (size() + otherSize <= MaxFastSize) {
|
||||
// Fast case
|
||||
for (size_t i = 0; i < otherSize; i++) {
|
||||
fastLookup_[fastCount_ + i] = other.fastLookup_[i];
|
||||
}
|
||||
fastCount_ += other.fastCount_;
|
||||
} else {
|
||||
for (size_t i = 0; i < otherSize; i++) {
|
||||
push_back(other[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool contains(T t) const {
|
||||
for (int i = 0; i < fastCount; i++) {
|
||||
for (int i = 0; i < fastCount_; i++) {
|
||||
if (fastLookup_[i] == t)
|
||||
return true;
|
||||
}
|
||||
@ -35,7 +72,7 @@ struct TinySet {
|
||||
}
|
||||
bool contains(const TinySet<T, MaxFastSize> &otherSet) {
|
||||
// Awkward, kind of ruins the fun.
|
||||
for (int i = 0; i < fastCount; i++) {
|
||||
for (int i = 0; i < fastCount_; i++) {
|
||||
if (otherSet.contains(fastLookup_[i]))
|
||||
return true;
|
||||
}
|
||||
@ -48,9 +85,37 @@ struct TinySet {
|
||||
return false;
|
||||
}
|
||||
void clear() {
|
||||
// TODO: Keep slowLookup_ around? That would be more similar to real vector behavior.
|
||||
delete slowLookup_;
|
||||
slowLookup_ = nullptr;
|
||||
fastCount = 0;
|
||||
fastCount_ = 0;
|
||||
}
|
||||
bool empty() const {
|
||||
return fastCount_ == 0;
|
||||
}
|
||||
size_t size() const {
|
||||
if (!slowLookup_) {
|
||||
return fastCount_;
|
||||
} else {
|
||||
return slowLookup_->size() + MaxFastSize;
|
||||
}
|
||||
}
|
||||
T &operator[] (size_t index) {
|
||||
if (index < MaxFastSize) {
|
||||
return fastLookup_[index];
|
||||
} else {
|
||||
return (*slowLookup_)[index - MaxFastSize];
|
||||
}
|
||||
}
|
||||
const T &operator[] (size_t index) const {
|
||||
if (index < MaxFastSize) {
|
||||
return fastLookup_[index];
|
||||
} else {
|
||||
return (*slowLookup_)[index - MaxFastSize];
|
||||
}
|
||||
}
|
||||
const T &back() const {
|
||||
return (*this)[size() - 1];
|
||||
}
|
||||
|
||||
private:
|
||||
@ -65,7 +130,7 @@ private:
|
||||
}
|
||||
slowLookup_->push_back(t);
|
||||
}
|
||||
int fastCount_ = 0; // first in the struct just so it's more visible in the VS debugger.
|
||||
T fastLookup_[MaxFastSize];
|
||||
int fastCount = 0;
|
||||
std::vector<T> *slowLookup_ = nullptr;
|
||||
};
|
||||
|
@ -82,6 +82,20 @@ inline void Uint8x3ToFloat4(float f[4], uint32_t u) {
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void Uint8x3ToFloat3(float f[4], uint32_t u) {
|
||||
#if defined(_M_SSE) || PPSSPP_ARCH(ARM_NEON)
|
||||
float temp[4];
|
||||
Uint8x4ToFloat4(temp, u & 0xFFFFFF);
|
||||
f[0] = temp[0];
|
||||
f[1] = temp[1];
|
||||
f[2] = temp[2];
|
||||
#else
|
||||
f[0] = ((u >> 0) & 0xFF) * (1.0f / 255.0f);
|
||||
f[1] = ((u >> 8) & 0xFF) * (1.0f / 255.0f);
|
||||
f[2] = ((u >> 16) & 0xFF) * (1.0f / 255.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void Uint8x3ToInt4(int i[4], uint32_t u) {
|
||||
i[0] = ((u >> 0) & 0xFF);
|
||||
i[1] = ((u >> 8) & 0xFF);
|
||||
|
@ -95,7 +95,6 @@ bool decompress_string(const std::string& str, std::string *dest) {
|
||||
inflateEnd(&zs);
|
||||
|
||||
if (ret != Z_STREAM_END) { // an error occurred that was not EOF
|
||||
std::ostringstream oss;
|
||||
ERROR_LOG(IO, "Exception during zlib decompression: (%i) %s", ret, zs.msg);
|
||||
return false;
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ void Section::Set(const char* key, const char* newValue)
|
||||
else
|
||||
{
|
||||
// The key did not already exist in this section - let's add it.
|
||||
lines.push_back(std::string(key) + " = " + EscapeComments(newValue));
|
||||
lines.emplace_back(std::string(key) + " = " + EscapeComments(newValue));
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,7 +272,7 @@ void Section::Set(const char* key, const std::vector<std::string>& newValues)
|
||||
}
|
||||
|
||||
void Section::AddComment(const std::string &comment) {
|
||||
lines.push_back("# " + comment);
|
||||
lines.emplace_back("# " + comment);
|
||||
}
|
||||
|
||||
bool Section::Get(const char* key, std::vector<std::string>& values)
|
||||
|
@ -39,7 +39,7 @@ const char *I18NCategory::T(const char *key, const char *def) {
|
||||
if (def)
|
||||
missedKeyLog_[key] = def;
|
||||
else
|
||||
missedKeyLog_[key] = modifiedKey.c_str();
|
||||
missedKeyLog_[key] = modifiedKey;
|
||||
// INFO_LOG(SYSTEM, "Missed translation key in %s: %s", name_.c_str(), key);
|
||||
return def ? def : key;
|
||||
}
|
||||
|
@ -206,11 +206,13 @@ bool Android_FileExists(const std::string &fileUri) {
|
||||
return exists;
|
||||
}
|
||||
|
||||
std::vector<File::FileInfo> Android_ListContentUri(const std::string &path) {
|
||||
std::vector<File::FileInfo> Android_ListContentUri(const std::string &path, bool *exists) {
|
||||
if (!g_nativeActivity) {
|
||||
*exists = false;
|
||||
return std::vector<File::FileInfo>();
|
||||
}
|
||||
auto env = getEnv();
|
||||
*exists = true;
|
||||
|
||||
double start = time_now_d();
|
||||
|
||||
@ -224,8 +226,12 @@ std::vector<File::FileInfo> Android_ListContentUri(const std::string &path) {
|
||||
jstring str = (jstring)env->GetObjectArrayElement(fileList, i);
|
||||
const char *charArray = env->GetStringUTFChars(str, 0);
|
||||
if (charArray) { // paranoia
|
||||
std::string line = charArray;
|
||||
File::FileInfo info;
|
||||
if (ParseFileInfo(std::string(charArray), &info)) {
|
||||
if (line == "X") {
|
||||
// Indicates an exception thrown, path doesn't exist.
|
||||
*exists = false;
|
||||
} else if (ParseFileInfo(line, &info)) {
|
||||
// We can just reconstruct the URI.
|
||||
info.fullName = Path(path) / info.name;
|
||||
items.push_back(info);
|
||||
@ -238,7 +244,7 @@ std::vector<File::FileInfo> Android_ListContentUri(const std::string &path) {
|
||||
|
||||
double elapsed = time_now_d() - start;
|
||||
if (elapsed > 0.1) {
|
||||
INFO_LOG(FILESYS, "Listing directory on content URI took %0.3f s (%d files)", elapsed, (int)size);
|
||||
INFO_LOG(FILESYS, "Listing directory on content URI took %0.3f s (%d files)", elapsed, (int)items.size());
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ int64_t Android_GetFreeSpaceByFilePath(const std::string &filePath);
|
||||
bool Android_IsExternalStoragePreservedLegacy();
|
||||
const char *Android_ErrorToString(StorageError error);
|
||||
|
||||
std::vector<File::FileInfo> Android_ListContentUri(const std::string &uri);
|
||||
std::vector<File::FileInfo> Android_ListContentUri(const std::string &uri, bool *exists);
|
||||
|
||||
void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj);
|
||||
|
||||
@ -78,7 +78,8 @@ inline int64_t Android_GetFreeSpaceByContentUri(const std::string &uri) { return
|
||||
inline int64_t Android_GetFreeSpaceByFilePath(const std::string &filePath) { return -1; }
|
||||
inline bool Android_IsExternalStoragePreservedLegacy() { return false; }
|
||||
inline const char *Android_ErrorToString(StorageError error) { return ""; }
|
||||
inline std::vector<File::FileInfo> Android_ListContentUri(const std::string &uri) {
|
||||
inline std::vector<File::FileInfo> Android_ListContentUri(const std::string &uri, bool *exists) {
|
||||
*exists = false;
|
||||
return std::vector<File::FileInfo>();
|
||||
}
|
||||
|
||||
|
@ -148,7 +148,7 @@ std::vector<File::FileInfo> ApplyFilter(std::vector<File::FileInfo> files, const
|
||||
std::string tmp;
|
||||
while (*filter) {
|
||||
if (*filter == ':') {
|
||||
filters.insert("." + tmp);
|
||||
filters.emplace("." + tmp);
|
||||
tmp.clear();
|
||||
} else {
|
||||
tmp.push_back(*filter);
|
||||
@ -156,7 +156,7 @@ std::vector<File::FileInfo> ApplyFilter(std::vector<File::FileInfo> files, const
|
||||
filter++;
|
||||
}
|
||||
if (!tmp.empty())
|
||||
filters.insert("." + tmp);
|
||||
filters.emplace("." + tmp);
|
||||
}
|
||||
|
||||
auto pred = [&](const File::FileInfo &info) {
|
||||
@ -171,10 +171,11 @@ std::vector<File::FileInfo> ApplyFilter(std::vector<File::FileInfo> files, const
|
||||
|
||||
bool GetFilesInDir(const Path &directory, std::vector<FileInfo> *files, const char *filter, int flags) {
|
||||
if (directory.Type() == PathType::CONTENT_URI) {
|
||||
std::vector<File::FileInfo> fileList = Android_ListContentUri(directory.ToString());
|
||||
bool exists = false;
|
||||
std::vector<File::FileInfo> fileList = Android_ListContentUri(directory.ToString(), &exists);
|
||||
*files = ApplyFilter(fileList, filter);
|
||||
std::sort(files->begin(), files->end());
|
||||
return true;
|
||||
return exists;
|
||||
}
|
||||
|
||||
std::set<std::string> filters;
|
||||
@ -219,7 +220,7 @@ 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) {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
do {
|
||||
const std::string virtualName = ConvertWStringToUTF8(ffd.cFileName);
|
||||
@ -266,7 +267,7 @@ bool GetFilesInDir(const Path &directory, std::vector<FileInfo> *files, const ch
|
||||
struct dirent *result = NULL;
|
||||
DIR *dirp = opendir(directory.c_str());
|
||||
if (!dirp)
|
||||
return 0;
|
||||
return false;
|
||||
while ((result = readdir(dirp))) {
|
||||
const std::string virtualName(result->d_name);
|
||||
// check for "." and ".."
|
||||
|
@ -258,15 +258,20 @@ std::wstring Path::ToWString() const {
|
||||
std::string Path::ToVisualString() const {
|
||||
if (type_ == PathType::CONTENT_URI) {
|
||||
return AndroidContentURI(path_).ToVisualString();
|
||||
#if PPSSPP_PLATFORM(WINDOWS)
|
||||
} else if (type_ == PathType::NATIVE) {
|
||||
return ReplaceAll(path_, "/", "\\");
|
||||
#endif
|
||||
} else {
|
||||
return path_;
|
||||
}
|
||||
return path_;
|
||||
}
|
||||
|
||||
bool Path::CanNavigateUp() const {
|
||||
if (type_ == PathType::CONTENT_URI) {
|
||||
return AndroidContentURI(path_).CanNavigateUp();
|
||||
}
|
||||
if (path_ == "/" || path_ == "") {
|
||||
if (path_ == "/" || path_.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (type_ == PathType::HTTP) {
|
||||
@ -352,7 +357,6 @@ bool Path::ComputePathTo(const Path &other, std::string &path) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string diff;
|
||||
if (type_ == PathType::CONTENT_URI) {
|
||||
AndroidContentURI a(path_);
|
||||
AndroidContentURI b(other.path_);
|
||||
|
@ -82,9 +82,9 @@ public:
|
||||
SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override;
|
||||
RasterState *CreateRasterState(const RasterStateDesc &desc) override;
|
||||
Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override;
|
||||
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) override;
|
||||
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) override;
|
||||
Texture *CreateTexture(const TextureDesc &desc) override;
|
||||
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) override;
|
||||
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) override;
|
||||
Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override;
|
||||
|
||||
void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override;
|
||||
@ -100,13 +100,12 @@ public:
|
||||
}
|
||||
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override;
|
||||
|
||||
uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) override;
|
||||
|
||||
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
|
||||
|
||||
void InvalidateCachedState() override;
|
||||
|
||||
void BindTextures(int start, int count, Texture **textures) override;
|
||||
void BindNativeTexture(int index, void *nativeTexture) override;
|
||||
void BindSamplerStates(int start, int count, SamplerState **states) override;
|
||||
void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) override;
|
||||
void BindIndexBuffer(Buffer *indexBuffer, int offset) override;
|
||||
@ -265,21 +264,24 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de
|
||||
caps_.framebufferStencilBlitSupported = false;
|
||||
caps_.framebufferDepthCopySupported = true;
|
||||
caps_.framebufferSeparateDepthCopySupported = false; // Though could be emulated with a draw.
|
||||
caps_.textureDepthSupported = true;
|
||||
caps_.texture3DSupported = true;
|
||||
caps_.fragmentShaderInt32Supported = true;
|
||||
caps_.anisoSupported = true;
|
||||
caps_.textureNPOTFullySupported = true;
|
||||
caps_.fragmentShaderDepthWriteSupported = true;
|
||||
caps_.blendMinMaxSupported = true;
|
||||
|
||||
D3D11_FEATURE_DATA_D3D11_OPTIONS options{};
|
||||
HRESULT result = device_->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options, sizeof(options));
|
||||
if (SUCCEEDED(result)) {
|
||||
if (options.OutputMergerLogicOp) {
|
||||
// Actually, need to check that the format supports logic ops as well.
|
||||
// Which normal UNORM formats don't seem to do. So meh.
|
||||
// Which normal UNORM formats don't seem to do in D3D11. So meh. We can't enable logicOp support.
|
||||
// caps_.logicOpSupported = true;
|
||||
}
|
||||
}
|
||||
|
||||
IDXGIDevice* dxgiDevice = nullptr;
|
||||
IDXGIAdapter* adapter = nullptr;
|
||||
HRESULT hr = device_->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgiDevice));
|
||||
@ -471,6 +473,7 @@ static DXGI_FORMAT dataFormatToD3D11(DataFormat format) {
|
||||
case DataFormat::R8G8B8A8_UNORM_SRGB: return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
|
||||
case DataFormat::B8G8R8A8_UNORM: return DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
case DataFormat::B8G8R8A8_UNORM_SRGB: return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
|
||||
case DataFormat::R16_UNORM: return DXGI_FORMAT_R16_UNORM;
|
||||
case DataFormat::R16_FLOAT: return DXGI_FORMAT_R16_FLOAT;
|
||||
case DataFormat::R16G16_FLOAT: return DXGI_FORMAT_R16G16_FLOAT;
|
||||
case DataFormat::R16G16B16A16_FLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT;
|
||||
@ -683,6 +686,7 @@ const char *semanticToD3D11(int semantic, UINT *index) {
|
||||
switch (semantic) {
|
||||
case SEM_POSITION: return "POSITION";
|
||||
case SEM_COLOR0: *index = 0; return "COLOR";
|
||||
case SEM_COLOR1: *index = 1; return "COLOR";
|
||||
case SEM_TEXCOORD0: *index = 0; return "TEXCOORD";
|
||||
case SEM_TEXCOORD1: *index = 1; return "TEXCOORD";
|
||||
case SEM_NORMAL: return "NORMAL";
|
||||
@ -920,7 +924,7 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) {
|
||||
return tex;
|
||||
}
|
||||
|
||||
ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) {
|
||||
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");
|
||||
return nullptr;
|
||||
@ -965,7 +969,7 @@ ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLang
|
||||
}
|
||||
if (errorMsgs) {
|
||||
errors = std::string((const char *)errorMsgs->GetBufferPointer(), errorMsgs->GetBufferSize());
|
||||
ERROR_LOG(G3D, "Failed compiling:\n%s\n%s", data, errors.c_str());
|
||||
ERROR_LOG(G3D, "Failed compiling %s:\n%s\n%s", tag, data, errors.c_str());
|
||||
errorMsgs->Release();
|
||||
}
|
||||
|
||||
@ -1003,7 +1007,7 @@ ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLang
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) {
|
||||
D3D11Pipeline *dPipeline = new D3D11Pipeline();
|
||||
dPipeline->blend = (D3D11BlendState *)desc.blend;
|
||||
dPipeline->depthStencil = (D3D11DepthStencilState *)desc.depthStencil;
|
||||
@ -1388,6 +1392,12 @@ void D3D11DrawContext::BindTextures(int start, int count, Texture **textures) {
|
||||
context_->PSSetShaderResources(start, count, views);
|
||||
}
|
||||
|
||||
void D3D11DrawContext::BindNativeTexture(int index, void *nativeTexture) {
|
||||
// Collect the resource views from the textures.
|
||||
ID3D11ShaderResourceView *view = (ID3D11ShaderResourceView *)nativeTexture;
|
||||
context_->PSSetShaderResources(index, 1, &view);
|
||||
}
|
||||
|
||||
void D3D11DrawContext::BindSamplerStates(int start, int count, SamplerState **states) {
|
||||
ID3D11SamplerState *samplers[MAX_BOUND_TEXTURES];
|
||||
_assert_(start + count <= ARRAY_SIZE(samplers));
|
||||
@ -1499,7 +1509,7 @@ bool D3D11DrawContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int bx, int by, int bw, int bh, Draw::DataFormat format, void *pixels, int pixelStride, 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) {
|
||||
@ -1559,15 +1569,18 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel
|
||||
}
|
||||
|
||||
D3D11_BOX srcBox{ (UINT)bx, (UINT)by, 0, (UINT)(bx + bw), (UINT)(by + bh), 1 };
|
||||
DataFormat srcFormat = DataFormat::UNDEFINED;
|
||||
switch (channelBits) {
|
||||
case FB_COLOR_BIT:
|
||||
context_->CopySubresourceRegion(packTex, 0, bx, by, 0, fb ? fb->colorTex : bbRenderTargetTex_, 0, &srcBox);
|
||||
srcFormat = DataFormat::R8G8B8A8_UNORM;
|
||||
break;
|
||||
case FB_DEPTH_BIT:
|
||||
case FB_STENCIL_BIT:
|
||||
// For depth/stencil buffers, we can't reliably copy subrectangles, so just copy the whole resource.
|
||||
_assert_(fb); // Can't copy depth/stencil from backbuffer. Shouldn't happen thanks to checks above.
|
||||
context_->CopyResource(packTex, fb->depthStencilTex);
|
||||
srcFormat = Draw::DataFormat::D24_S8;
|
||||
break;
|
||||
default:
|
||||
_assert_(false);
|
||||
@ -1584,28 +1597,51 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel
|
||||
return false;
|
||||
}
|
||||
|
||||
const int srcByteOffset = by * map.RowPitch + bx * 4;
|
||||
const size_t srcByteOffset = by * map.RowPitch + bx * DataFormatSizeInBytes(srcFormat);
|
||||
const uint8_t *srcWithOffset = (const uint8_t *)map.pData + srcByteOffset;
|
||||
switch (channelBits) {
|
||||
case FB_COLOR_BIT:
|
||||
// Pixel size always 4 here because we always request BGRA8888.
|
||||
ConvertFromRGBA8888((uint8_t *)pixels, (uint8_t *)map.pData + srcByteOffset, pixelStride, map.RowPitch / sizeof(uint32_t), bw, bh, format);
|
||||
ConvertFromRGBA8888((uint8_t *)pixels, srcWithOffset, pixelStride, map.RowPitch / sizeof(uint32_t), bw, bh, destFormat);
|
||||
break;
|
||||
case FB_DEPTH_BIT:
|
||||
for (int y = by; y < by + bh; y++) {
|
||||
float *dest = (float *)((uint8_t *)pixels + y * pixelStride * sizeof(float));
|
||||
const uint32_t *src = (const uint32_t *)((const uint8_t *)map.pData + map.RowPitch * y);
|
||||
for (int x = bx; x < bx + bw; x++) {
|
||||
dest[x] = (src[x] & 0xFFFFFF) / (256.f * 256.f * 256.f);
|
||||
if (srcFormat == destFormat) {
|
||||
// Can just memcpy when it matches no matter the format!
|
||||
uint8_t *dst = (uint8_t *)pixels;
|
||||
const uint8_t *src = (const uint8_t *)srcWithOffset;
|
||||
for (int y = 0; y < bh; ++y) {
|
||||
memcpy(dst, src, bw * DataFormatSizeInBytes(srcFormat));
|
||||
dst += pixelStride * DataFormatSizeInBytes(srcFormat);
|
||||
src += map.RowPitch;
|
||||
}
|
||||
} else if (destFormat == DataFormat::D32F) {
|
||||
ConvertToD32F((uint8_t *)pixels, srcWithOffset, pixelStride, map.RowPitch / sizeof(uint32_t), bw, bh, srcFormat);
|
||||
} else if (destFormat == DataFormat::D16) {
|
||||
ConvertToD16((uint8_t *)pixels, srcWithOffset, pixelStride, map.RowPitch / sizeof(uint32_t), bw, bh, srcFormat);
|
||||
} else {
|
||||
_assert_(false);
|
||||
}
|
||||
break;
|
||||
case FB_STENCIL_BIT:
|
||||
for (int y = by; y < by + bh; y++) {
|
||||
uint8_t *destStencil = (uint8_t *)pixels + y * pixelStride;
|
||||
const uint32_t *src = (const uint32_t *)((const uint8_t *)map.pData + map.RowPitch * y);
|
||||
for (int x = bx; x < bx + bw; x++) {
|
||||
destStencil[x] = src[x] >> 24;
|
||||
if (srcFormat == destFormat) {
|
||||
// Can just memcpy when it matches no matter the format!
|
||||
uint8_t *dst = (uint8_t *)pixels;
|
||||
const uint8_t *src = (const uint8_t *)srcWithOffset;
|
||||
for (int y = 0; y < bh; ++y) {
|
||||
memcpy(dst, src, bw * DataFormatSizeInBytes(srcFormat));
|
||||
dst += pixelStride * DataFormatSizeInBytes(srcFormat);
|
||||
src += map.RowPitch;
|
||||
}
|
||||
} else if (destFormat == DataFormat::S8) {
|
||||
for (int y = 0; y < bh; y++) {
|
||||
uint8_t *destStencil = (uint8_t *)pixels + y * pixelStride;
|
||||
const uint32_t *src = (const uint32_t *)(srcWithOffset + map.RowPitch * y);
|
||||
for (int x = 0; x < bw; x++) {
|
||||
destStencil[x] = src[x] >> 24;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_assert_(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1709,21 +1745,6 @@ uint64_t D3D11DrawContext::GetNativeObject(NativeObject obj, void *srcObject) {
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t D3D11DrawContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) {
|
||||
D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo;
|
||||
switch (channelBit) {
|
||||
case FB_COLOR_BIT: return (uintptr_t)fb->colorTex;
|
||||
case FB_DEPTH_BIT: return (uintptr_t)fb->depthStencilTex;
|
||||
case FB_COLOR_BIT | FB_VIEW_BIT: return (uintptr_t)fb->colorRTView;
|
||||
case FB_DEPTH_BIT | FB_VIEW_BIT: return (uintptr_t)fb->depthStencilRTView;
|
||||
case FB_COLOR_BIT | FB_FORMAT_BIT: return (uintptr_t)fb->colorFormat;
|
||||
case FB_DEPTH_BIT | FB_FORMAT_BIT: return (uintptr_t)fb->depthStencilFormat;
|
||||
case FB_STENCIL_BIT | FB_FORMAT_BIT: return (uintptr_t)fb->depthStencilFormat;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void D3D11DrawContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) {
|
||||
D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo;
|
||||
if (fb) {
|
||||
|
@ -47,14 +47,14 @@ LPD3DBLOB CompileShaderToByteCodeD3D9(const char *code, const char *target, std:
|
||||
pShaderCode = nullptr;
|
||||
}
|
||||
} else {
|
||||
*errorMessage = "";
|
||||
errorMessage->clear();
|
||||
}
|
||||
|
||||
return pShaderCode;
|
||||
}
|
||||
|
||||
bool CompilePixelShaderD3D9(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DPIXELSHADER9 *pShader, std::string *errorMessage) {
|
||||
LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "ps_2_0", errorMessage);
|
||||
LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "ps_3_0", errorMessage);
|
||||
if (pShaderCode) {
|
||||
// Create pixel shader.
|
||||
device->CreatePixelShader((DWORD*)pShaderCode->GetBufferPointer(), pShader);
|
||||
@ -66,7 +66,7 @@ bool CompilePixelShaderD3D9(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT
|
||||
}
|
||||
|
||||
bool CompileVertexShaderD3D9(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DVERTEXSHADER9 *pShader, std::string *errorMessage) {
|
||||
LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "vs_2_0", errorMessage);
|
||||
LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "vs_3_0", errorMessage);
|
||||
if (pShaderCode) {
|
||||
// Create vertex shader.
|
||||
device->CreateVertexShader((DWORD*)pShaderCode->GetBufferPointer(), pShader);
|
||||
|
@ -2,12 +2,10 @@
|
||||
|
||||
#include "Common/GPU/D3D9/D3D9StateCache.h"
|
||||
|
||||
namespace DX9 {
|
||||
|
||||
DirectXState dxstate;
|
||||
|
||||
LPDIRECT3DDEVICE9 pD3Ddevice = nullptr;
|
||||
LPDIRECT3DDEVICE9EX pD3DdeviceEx = nullptr;
|
||||
LPDIRECT3DDEVICE9 pD3Ddevice9 = nullptr;
|
||||
LPDIRECT3DDEVICE9EX pD3DdeviceEx9 = nullptr;
|
||||
|
||||
int DirectXState::state_count = 0;
|
||||
|
||||
@ -62,6 +60,4 @@ void DirectXState::Restore() {
|
||||
texAddressW.restore(); count++;
|
||||
}
|
||||
|
||||
} // namespace DX9
|
||||
|
||||
#endif // _MSC_VER
|
||||
|
@ -4,11 +4,9 @@
|
||||
|
||||
#include "Common/GPU/D3D9/D3D9ShaderCompiler.h"
|
||||
|
||||
namespace DX9 {
|
||||
|
||||
// TODO: Get rid of these somehow.
|
||||
extern LPDIRECT3DDEVICE9 pD3Ddevice;
|
||||
extern LPDIRECT3DDEVICE9EX pD3DdeviceEx;
|
||||
extern LPDIRECT3DDEVICE9 pD3Ddevice9;
|
||||
extern LPDIRECT3DDEVICE9EX pD3DdeviceEx9;
|
||||
|
||||
class DirectXState {
|
||||
private:
|
||||
@ -44,7 +42,7 @@ private:
|
||||
return _value;
|
||||
}
|
||||
void restore() {
|
||||
pD3Ddevice->SetRenderState(cap, _value);
|
||||
pD3Ddevice9->SetRenderState(cap, _value);
|
||||
}
|
||||
};
|
||||
|
||||
@ -69,7 +67,7 @@ private:
|
||||
p1 = old;
|
||||
}
|
||||
void restore() {
|
||||
pD3Ddevice->SetRenderState(_state1, p1);
|
||||
pD3Ddevice9->SetRenderState(_state1, p1);
|
||||
}
|
||||
};
|
||||
|
||||
@ -94,7 +92,7 @@ private:
|
||||
p1 = old;
|
||||
}
|
||||
void restore() {
|
||||
pD3Ddevice->SetSamplerState(0, _state1, p1);
|
||||
pD3Ddevice9->SetSamplerState(0, _state1, p1);
|
||||
}
|
||||
};
|
||||
|
||||
@ -123,7 +121,7 @@ private:
|
||||
p1 = old;
|
||||
}
|
||||
void restore() {
|
||||
pD3Ddevice->SetSamplerState(0, _state1, p1d);
|
||||
pD3Ddevice9->SetSamplerState(0, _state1, p1d);
|
||||
}
|
||||
};
|
||||
|
||||
@ -141,11 +139,11 @@ private:
|
||||
inline void set(DWORD newp1, DWORD newp2) {
|
||||
if (p1 != newp1) {
|
||||
p1 = newp1;
|
||||
pD3Ddevice->SetRenderState(_state1, p1);
|
||||
pD3Ddevice9->SetRenderState(_state1, p1);
|
||||
}
|
||||
if (p2 != newp2) {
|
||||
p2 = newp2;
|
||||
pD3Ddevice->SetRenderState(_state2, p2);
|
||||
pD3Ddevice9->SetRenderState(_state2, p2);
|
||||
}
|
||||
}
|
||||
void force(DWORD newp1, DWORD newp2) {
|
||||
@ -156,8 +154,8 @@ private:
|
||||
p2 = old2;
|
||||
}
|
||||
void restore() {
|
||||
pD3Ddevice->SetRenderState(_state1, p1);
|
||||
pD3Ddevice->SetRenderState(_state2, p2);
|
||||
pD3Ddevice9->SetRenderState(_state1, p1);
|
||||
pD3Ddevice9->SetRenderState(_state2, p2);
|
||||
}
|
||||
};
|
||||
|
||||
@ -177,15 +175,15 @@ private:
|
||||
inline void set(DWORD newp1, DWORD newp2, DWORD newp3) {
|
||||
if (p1 != newp1) {
|
||||
p1 = newp1;
|
||||
pD3Ddevice->SetRenderState(_state1, p1);
|
||||
pD3Ddevice9->SetRenderState(_state1, p1);
|
||||
}
|
||||
if (p2 != newp2) {
|
||||
p2 = newp2;
|
||||
pD3Ddevice->SetRenderState(_state2, p2);
|
||||
pD3Ddevice9->SetRenderState(_state2, p2);
|
||||
}
|
||||
if (p3 != newp3) {
|
||||
p3 = newp3;
|
||||
pD3Ddevice->SetRenderState(_state3, p3);
|
||||
pD3Ddevice9->SetRenderState(_state3, p3);
|
||||
}
|
||||
}
|
||||
void force(DWORD newp1, DWORD newp2, DWORD newp3) {
|
||||
@ -198,9 +196,9 @@ private:
|
||||
p3 = old3;
|
||||
}
|
||||
void restore() {
|
||||
pD3Ddevice->SetRenderState(_state1, p1);
|
||||
pD3Ddevice->SetRenderState(_state2, p2);
|
||||
pD3Ddevice->SetRenderState(_state3, p3);
|
||||
pD3Ddevice9->SetRenderState(_state1, p1);
|
||||
pD3Ddevice9->SetRenderState(_state2, p2);
|
||||
pD3Ddevice9->SetRenderState(_state3, p3);
|
||||
}
|
||||
};
|
||||
|
||||
@ -222,19 +220,19 @@ private:
|
||||
inline void set(DWORD newp1, DWORD newp2, DWORD newp3, DWORD newp4) {
|
||||
if (p1 != newp1) {
|
||||
p1 = newp1;
|
||||
pD3Ddevice->SetRenderState(_state1, p1);
|
||||
pD3Ddevice9->SetRenderState(_state1, p1);
|
||||
}
|
||||
if (p2 != newp2) {
|
||||
p2 = newp2;
|
||||
pD3Ddevice->SetRenderState(_state2, p2);
|
||||
pD3Ddevice9->SetRenderState(_state2, p2);
|
||||
}
|
||||
if (p3 != newp3) {
|
||||
p3 = newp3;
|
||||
pD3Ddevice->SetRenderState(_state3, p3);
|
||||
pD3Ddevice9->SetRenderState(_state3, p3);
|
||||
}
|
||||
if (p4 != newp4) {
|
||||
p4 = newp4;
|
||||
pD3Ddevice->SetRenderState(_state4, p4);
|
||||
pD3Ddevice9->SetRenderState(_state4, p4);
|
||||
}
|
||||
}
|
||||
void force(DWORD newp1, DWORD newp2, DWORD newp3, DWORD newp4) {
|
||||
@ -249,10 +247,10 @@ private:
|
||||
p4 = old4;
|
||||
}
|
||||
void restore() {
|
||||
pD3Ddevice->SetRenderState(_state1, p1);
|
||||
pD3Ddevice->SetRenderState(_state2, p2);
|
||||
pD3Ddevice->SetRenderState(_state3, p3);
|
||||
pD3Ddevice->SetRenderState(_state3, p4);
|
||||
pD3Ddevice9->SetRenderState(_state1, p1);
|
||||
pD3Ddevice9->SetRenderState(_state2, p2);
|
||||
pD3Ddevice9->SetRenderState(_state3, p3);
|
||||
pD3Ddevice9->SetRenderState(_state3, p4);
|
||||
}
|
||||
};
|
||||
|
||||
@ -284,7 +282,7 @@ private:
|
||||
c = old;
|
||||
}
|
||||
inline void restore() {
|
||||
pD3Ddevice->SetRenderState(D3DRS_BLENDFACTOR, c);
|
||||
pD3Ddevice9->SetRenderState(D3DRS_BLENDFACTOR, c);
|
||||
}
|
||||
};
|
||||
|
||||
@ -318,7 +316,7 @@ private:
|
||||
}
|
||||
|
||||
inline void restore() {
|
||||
pD3Ddevice->SetViewport(&viewport);
|
||||
pD3Ddevice9->SetViewport(&viewport);
|
||||
}
|
||||
};
|
||||
|
||||
@ -340,7 +338,7 @@ private:
|
||||
}
|
||||
|
||||
inline void restore() {
|
||||
pD3Ddevice->SetScissorRect(&rect);
|
||||
pD3Ddevice9->SetScissorRect(&rect);
|
||||
}
|
||||
};
|
||||
|
||||
@ -400,17 +398,3 @@ public:
|
||||
#undef STATE2
|
||||
|
||||
extern DirectXState dxstate;
|
||||
|
||||
struct GLExtensions {
|
||||
bool OES_depth24;
|
||||
bool OES_packed_depth_stencil;
|
||||
bool OES_depth_texture;
|
||||
bool EXT_discard_framebuffer;
|
||||
bool FBO_ARB;
|
||||
};
|
||||
|
||||
extern GLExtensions gl_extensions;
|
||||
|
||||
void CheckGLExtensions();
|
||||
|
||||
};
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "Common/Math/lin/matrix4x4.h"
|
||||
#include "Common/GPU/thin3d.h"
|
||||
#include "Common/GPU/D3D9/D3D9StateCache.h"
|
||||
#include "Common/OSVersion.h"
|
||||
#include "Common/StringUtils.h"
|
||||
|
||||
#include "Common/Log.h"
|
||||
@ -114,6 +115,7 @@ static const D3DSTENCILOP stencilOpToD3D9[] = {
|
||||
|
||||
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;
|
||||
case DataFormat::B8G8R8A8_UNORM: return D3DFMT_A8R8G8B8;
|
||||
case DataFormat::R4G4B4A4_UNORM_PACK16: return D3DFMT_A4R4G4B4; // emulated
|
||||
@ -152,7 +154,6 @@ public:
|
||||
D3DCMPFUNC stencilCompareOp;
|
||||
|
||||
void Apply(LPDIRECT3DDEVICE9 device, uint8_t stencilRef, uint8_t stencilWriteMask, uint8_t stencilCompareMask) {
|
||||
using namespace DX9;
|
||||
dxstate.depthTest.set(depthTestEnabled);
|
||||
if (depthTestEnabled) {
|
||||
dxstate.depthWrite.set(depthWriteEnabled);
|
||||
@ -174,7 +175,6 @@ public:
|
||||
DWORD cullMode; // D3DCULL_*
|
||||
|
||||
void Apply(LPDIRECT3DDEVICE9 device) {
|
||||
using namespace DX9;
|
||||
dxstate.cullMode.set(cullMode);
|
||||
dxstate.scissorTest.enable();
|
||||
}
|
||||
@ -188,7 +188,6 @@ public:
|
||||
uint32_t colorMask;
|
||||
|
||||
void Apply(LPDIRECT3DDEVICE9 device) {
|
||||
using namespace DX9;
|
||||
dxstate.blend.set(enabled);
|
||||
dxstate.blendFunc.set(srcCol, dstCol, srcAlpha, dstAlpha);
|
||||
dxstate.blendEquation.set(eqCol, eqAlpha);
|
||||
@ -202,12 +201,19 @@ public:
|
||||
D3DTEXTUREFILTERTYPE magFilt, minFilt, mipFilt;
|
||||
|
||||
void Apply(LPDIRECT3DDEVICE9 device, int index) {
|
||||
using namespace DX9;
|
||||
dxstate.texAddressU.set(wrapS);
|
||||
dxstate.texAddressV.set(wrapT);
|
||||
dxstate.texMagFilter.set(magFilt);
|
||||
dxstate.texMinFilter.set(minFilt);
|
||||
dxstate.texMipFilter.set(mipFilt);
|
||||
if (index == 0) {
|
||||
dxstate.texAddressU.set(wrapS);
|
||||
dxstate.texAddressV.set(wrapT);
|
||||
dxstate.texMagFilter.set(magFilt);
|
||||
dxstate.texMinFilter.set(minFilt);
|
||||
dxstate.texMipFilter.set(mipFilt);
|
||||
} else {
|
||||
pD3Ddevice9->SetSamplerState(index, D3DSAMP_ADDRESSU, wrapS);
|
||||
pD3Ddevice9->SetSamplerState(index, D3DSAMP_ADDRESSV, wrapT);
|
||||
pD3Ddevice9->SetSamplerState(index, D3DSAMP_MAGFILTER, magFilt);
|
||||
pD3Ddevice9->SetSamplerState(index, D3DSAMP_MINFILTER, minFilt);
|
||||
pD3Ddevice9->SetSamplerState(index, D3DSAMP_MIPFILTER, mipFilt);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -259,17 +265,23 @@ class D3D9Pipeline : public Pipeline {
|
||||
public:
|
||||
D3D9Pipeline() {}
|
||||
~D3D9Pipeline() {
|
||||
if (vshader) {
|
||||
vshader->Release();
|
||||
}
|
||||
if (pshader) {
|
||||
pshader->Release();
|
||||
}
|
||||
}
|
||||
|
||||
D3D9ShaderModule *vshader;
|
||||
D3D9ShaderModule *pshader;
|
||||
D3D9ShaderModule *vshader = nullptr;
|
||||
D3D9ShaderModule *pshader = nullptr;
|
||||
|
||||
D3DPRIMITIVETYPE prim;
|
||||
D3DPRIMITIVETYPE prim{};
|
||||
AutoRef<D3D9InputLayout> inputLayout;
|
||||
AutoRef<D3D9DepthStencilState> depthStencil;
|
||||
AutoRef<D3D9BlendState> blend;
|
||||
AutoRef<D3D9RasterState> raster;
|
||||
UniformBufferDesc dynamicUniforms;
|
||||
UniformBufferDesc dynamicUniforms{};
|
||||
|
||||
void Apply(LPDIRECT3DDEVICE9 device, uint8_t stencilRef, uint8_t stencilWriteMask, uint8_t stencilCompareMask);
|
||||
};
|
||||
@ -440,6 +452,17 @@ void D3D9Texture::SetImageData(int x, int y, int z, int width, int height, int d
|
||||
if (data != rect.pBits)
|
||||
memcpy(dest, source, sizeof(uint32_t) * width);
|
||||
break;
|
||||
|
||||
case DataFormat::R8_UNORM:
|
||||
if (data != rect.pBits)
|
||||
memcpy(dest, source, width);
|
||||
break;
|
||||
|
||||
case DataFormat::R16_UNORM:
|
||||
if (data != rect.pBits)
|
||||
memcpy(dest, source, sizeof(uint16_t) * width);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unhandled data format copy.
|
||||
DebugBreak();
|
||||
@ -487,13 +510,13 @@ public:
|
||||
}
|
||||
uint32_t GetDataFormatSupport(DataFormat fmt) const override;
|
||||
|
||||
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) override;
|
||||
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) override;
|
||||
DepthStencilState *CreateDepthStencilState(const DepthStencilStateDesc &desc) override;
|
||||
BlendState *CreateBlendState(const BlendStateDesc &desc) override;
|
||||
SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override;
|
||||
RasterState *CreateRasterState(const RasterStateDesc &desc) override;
|
||||
Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override;
|
||||
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) override;
|
||||
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) override;
|
||||
InputLayout *CreateInputLayout(const InputLayoutDesc &desc) override;
|
||||
Texture *CreateTexture(const TextureDesc &desc) override;
|
||||
|
||||
@ -505,6 +528,7 @@ public:
|
||||
// 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 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;
|
||||
@ -518,6 +542,8 @@ public:
|
||||
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
|
||||
|
||||
void BindTextures(int start, int count, Texture **textures) override;
|
||||
void BindNativeTexture(int index, void *nativeTexture) override;
|
||||
|
||||
void BindSamplerStates(int start, int count, SamplerState **states) override {
|
||||
_assert_(start + count <= MAX_BOUND_TEXTURES);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
@ -630,6 +656,63 @@ void D3D9Context::InvalidateCachedState() {
|
||||
curPipeline_ = nullptr;
|
||||
}
|
||||
|
||||
// TODO: Move this detection elsewhere when it's needed elsewhere, not before. It's ugly.
|
||||
// Source: https://envytools.readthedocs.io/en/latest/hw/pciid.html#gf100
|
||||
enum NVIDIAGeneration {
|
||||
NV_PRE_KEPLER,
|
||||
NV_KEPLER,
|
||||
NV_MAXWELL,
|
||||
NV_PASCAL,
|
||||
NV_VOLTA,
|
||||
NV_TURING, // or later
|
||||
};
|
||||
|
||||
static NVIDIAGeneration NVIDIAGetDeviceGeneration(int deviceID) {
|
||||
if (deviceID >= 0x1180 && deviceID <= 0x11bf)
|
||||
return NV_KEPLER; // GK104
|
||||
if (deviceID >= 0x11c0 && deviceID <= 0x11fa)
|
||||
return NV_KEPLER; // GK106
|
||||
if (deviceID >= 0x0fc0 && deviceID <= 0x0fff)
|
||||
return NV_KEPLER; // GK107
|
||||
if (deviceID >= 0x1003 && deviceID <= 0x1028)
|
||||
return NV_KEPLER; // GK110(B)
|
||||
if (deviceID >= 0x1280 && deviceID <= 0x12ba)
|
||||
return NV_KEPLER; // GK208
|
||||
if (deviceID >= 0x1381 && deviceID <= 0x13b0)
|
||||
return NV_MAXWELL; // GM107
|
||||
if (deviceID >= 0x1340 && deviceID <= 0x134d)
|
||||
return NV_MAXWELL; // GM108
|
||||
if (deviceID >= 0x13c0 && deviceID <= 0x13d9)
|
||||
return NV_MAXWELL; // GM204
|
||||
if (deviceID >= 0x1401 && deviceID <= 0x1427)
|
||||
return NV_MAXWELL; // GM206
|
||||
if (deviceID >= 0x15f7 && deviceID <= 0x15f9)
|
||||
return NV_PASCAL; // GP100
|
||||
if (deviceID >= 0x15f7 && deviceID <= 0x15f9)
|
||||
return NV_PASCAL; // GP100
|
||||
if (deviceID >= 0x1b00 && deviceID <= 0x1b38)
|
||||
return NV_PASCAL; // GP102
|
||||
if (deviceID >= 0x1b80 && deviceID <= 0x1be1)
|
||||
return NV_PASCAL; // GP104
|
||||
if (deviceID >= 0x1c02 && deviceID <= 0x1c62)
|
||||
return NV_PASCAL; // GP106
|
||||
if (deviceID >= 0x1c81 && deviceID <= 0x1c92)
|
||||
return NV_PASCAL; // GP107
|
||||
if (deviceID >= 0x1d01 && deviceID <= 0x1d12)
|
||||
return NV_PASCAL; // GP108
|
||||
if (deviceID >= 0x1d81 && deviceID <= 0x1dba)
|
||||
return NV_VOLTA; // GV100
|
||||
if (deviceID >= 0x1e02 && deviceID <= 0x1e3c)
|
||||
return NV_TURING; // TU102
|
||||
if (deviceID >= 0x1e82 && deviceID <= 0x1ed0)
|
||||
return NV_TURING; // TU104
|
||||
if (deviceID >= 0x1f02 && deviceID <= 0x1f51)
|
||||
return NV_TURING; // TU104
|
||||
if (deviceID >= 0x1e02)
|
||||
return NV_TURING; // More TU models or later, probably.
|
||||
return NV_PRE_KEPLER;
|
||||
}
|
||||
|
||||
#define FB_DIV 1
|
||||
#define FOURCC_INTZ ((D3DFORMAT)(MAKEFOURCC('I', 'N', 'T', 'Z')))
|
||||
|
||||
@ -649,27 +732,59 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID
|
||||
caps_.vendor = GPUVendor::VENDOR_UNKNOWN;
|
||||
}
|
||||
|
||||
if (!FAILED(device->GetDeviceCaps(&d3dCaps_))) {
|
||||
D3DCAPS9 caps;
|
||||
ZeroMemory(&caps, sizeof(caps));
|
||||
HRESULT result = 0;
|
||||
if (deviceEx_) {
|
||||
result = deviceEx_->GetDeviceCaps(&caps);
|
||||
} else {
|
||||
result = device_->GetDeviceCaps(&caps);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(result)) {
|
||||
sprintf(shadeLangVersion_, "PS: %04x VS: %04x", d3dCaps_.PixelShaderVersion & 0xFFFF, d3dCaps_.VertexShaderVersion & 0xFFFF);
|
||||
} else {
|
||||
WARN_LOG(G3D, "Direct3D9: Failed to get the device caps!");
|
||||
strcpy(shadeLangVersion_, "N/A");
|
||||
}
|
||||
|
||||
caps_.deviceID = identifier_.DeviceId;
|
||||
caps_.multiViewport = false;
|
||||
caps_.anisoSupported = true;
|
||||
caps_.depthRangeMinusOneToOne = false;
|
||||
caps_.preferredDepthBufferFormat = DataFormat::D24_S8;
|
||||
caps_.dualSourceBlend = false;
|
||||
caps_.tesselationShaderSupported = false;
|
||||
caps_.framebufferBlitSupported = true;
|
||||
caps_.framebufferCopySupported = false;
|
||||
caps_.framebufferDepthBlitSupported = true;
|
||||
caps_.framebufferDepthBlitSupported = false;
|
||||
caps_.framebufferStencilBlitSupported = false;
|
||||
caps_.framebufferDepthCopySupported = false;
|
||||
caps_.framebufferSeparateDepthCopySupported = false;
|
||||
caps_.texture3DSupported = true;
|
||||
caps_.textureNPOTFullySupported = true;
|
||||
caps_.fragmentShaderDepthWriteSupported = true;
|
||||
caps_.blendMinMaxSupported = true;
|
||||
|
||||
if ((caps.RasterCaps & D3DPRASTERCAPS_ANISOTROPY) != 0 && caps.MaxAnisotropy > 1) {
|
||||
caps_.anisoSupported = true;
|
||||
}
|
||||
if ((caps.TextureCaps & (D3DPTEXTURECAPS_NONPOW2CONDITIONAL | D3DPTEXTURECAPS_POW2)) == 0) {
|
||||
caps_.textureNPOTFullySupported = true;
|
||||
}
|
||||
|
||||
// VS range culling (killing triangles in the vertex shader using NaN) causes problems on Intel.
|
||||
// Also causes problems on old NVIDIA.
|
||||
switch (caps_.vendor) {
|
||||
case Draw::GPUVendor::VENDOR_INTEL:
|
||||
bugs_.Infest(Bugs::BROKEN_NAN_IN_CONDITIONAL);
|
||||
break;
|
||||
case Draw::GPUVendor::VENDOR_NVIDIA:
|
||||
// Older NVIDIAs don't seem to like NaNs in their DX9 vertex shaders.
|
||||
// No idea if KEPLER is the right cutoff, but let's go with it.
|
||||
if (NVIDIAGetDeviceGeneration(caps_.deviceID) < NV_KEPLER) {
|
||||
bugs_.Infest(Bugs::BROKEN_NAN_IN_CONDITIONAL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (d3d) {
|
||||
D3DDISPLAYMODE displayMode;
|
||||
@ -677,19 +792,21 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID
|
||||
|
||||
// To be safe, make sure both the display format and the FBO format support INTZ.
|
||||
HRESULT displayINTZ = d3d->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, displayMode.Format, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, FOURCC_INTZ);
|
||||
HRESULT fboINTZ = d3d->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_A8R8G8B8, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, FOURCC_INTZ);
|
||||
supportsINTZ = SUCCEEDED(displayINTZ) && SUCCEEDED(fboINTZ);
|
||||
HRESULT displayINTY = d3d->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, displayMode.Format, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, ((D3DFORMAT)(MAKEFOURCC('I', 'N', 'T', 'Y'))));
|
||||
// Try to prevent INTZ on older Intel drivers that claim support.
|
||||
supportsINTZ = SUCCEEDED(displayINTZ) && !SUCCEEDED(displayINTY) && IsWin7OrHigher();
|
||||
}
|
||||
caps_.textureDepthSupported = supportsINTZ;
|
||||
|
||||
shaderLanguageDesc_.Init(HLSL_D3D9);
|
||||
|
||||
DX9::dxstate.Restore();
|
||||
dxstate.Restore();
|
||||
}
|
||||
|
||||
D3D9Context::~D3D9Context() {
|
||||
}
|
||||
|
||||
ShaderModule *D3D9Context::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t size, const std::string &tag) {
|
||||
ShaderModule *D3D9Context::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t size, const char *tag) {
|
||||
D3D9ShaderModule *shader = new D3D9ShaderModule(stage, tag);
|
||||
if (shader->Compile(device_, data, size)) {
|
||||
return shader;
|
||||
@ -699,23 +816,25 @@ ShaderModule *D3D9Context::CreateShaderModule(ShaderStage stage, ShaderLanguage
|
||||
}
|
||||
}
|
||||
|
||||
Pipeline *D3D9Context::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
Pipeline *D3D9Context::CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) {
|
||||
if (!desc.shaders.size()) {
|
||||
ERROR_LOG(G3D, "Pipeline requires at least one shader");
|
||||
ERROR_LOG(G3D, "Pipeline %s requires at least one shader", tag);
|
||||
return NULL;
|
||||
}
|
||||
D3D9Pipeline *pipeline = new D3D9Pipeline();
|
||||
for (auto iter : desc.shaders) {
|
||||
if (!iter) {
|
||||
ERROR_LOG(G3D, "NULL shader passed to CreateGraphicsPipeline");
|
||||
ERROR_LOG(G3D, "NULL shader passed to CreateGraphicsPipeline(%s)", tag);
|
||||
delete pipeline;
|
||||
return NULL;
|
||||
}
|
||||
if (iter->GetStage() == ShaderStage::Fragment) {
|
||||
pipeline->pshader = static_cast<D3D9ShaderModule *>(iter);
|
||||
pipeline->pshader->AddRef();
|
||||
}
|
||||
else if (iter->GetStage() == ShaderStage::Vertex) {
|
||||
pipeline->vshader = static_cast<D3D9ShaderModule *>(iter);
|
||||
pipeline->vshader->AddRef();
|
||||
}
|
||||
}
|
||||
pipeline->prim = primToD3D9[(int)desc.prim];
|
||||
@ -808,6 +927,11 @@ void D3D9Context::BindTextures(int start, int count, Texture **textures) {
|
||||
}
|
||||
}
|
||||
|
||||
void D3D9Context::BindNativeTexture(int index, void *nativeTexture) {
|
||||
LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)nativeTexture;
|
||||
device_->SetTexture(index, texture);
|
||||
}
|
||||
|
||||
void D3D9Context::EndFrame() {
|
||||
curPipeline_ = nullptr;
|
||||
}
|
||||
@ -830,6 +954,10 @@ static void SemanticToD3D9UsageAndIndex(int semantic, BYTE *usage, BYTE *index)
|
||||
case SEM_COLOR0:
|
||||
*usage = D3DDECLUSAGE_COLOR;
|
||||
break;
|
||||
case SEM_COLOR1:
|
||||
*usage = D3DDECLUSAGE_COLOR;
|
||||
*index = 1;
|
||||
break;
|
||||
case SEM_TEXCOORD0:
|
||||
*usage = D3DDECLUSAGE_TEXCOORD;
|
||||
break;
|
||||
@ -1029,15 +1157,11 @@ void D3D9Context::Clear(int mask, uint32_t colorval, float depthVal, int stencil
|
||||
}
|
||||
|
||||
void D3D9Context::SetScissorRect(int left, int top, int width, int height) {
|
||||
using namespace DX9;
|
||||
|
||||
dxstate.scissorRect.set(left, top, left + width, top + height);
|
||||
dxstate.scissorTest.set(true);
|
||||
}
|
||||
|
||||
void D3D9Context::SetViewports(int count, Viewport *viewports) {
|
||||
using namespace DX9;
|
||||
|
||||
int x = (int)viewports[0].TopLeftX;
|
||||
int y = (int)viewports[0].TopLeftY;
|
||||
int w = (int)viewports[0].Width;
|
||||
@ -1050,7 +1174,6 @@ void D3D9Context::SetBlendFactor(float color[4]) {
|
||||
uint32_t g = (uint32_t)(color[1] * 255.0f);
|
||||
uint32_t b = (uint32_t)(color[2] * 255.0f);
|
||||
uint32_t a = (uint32_t)(color[3] * 255.0f);
|
||||
using namespace DX9;
|
||||
dxstate.blendColor.set(color);
|
||||
}
|
||||
|
||||
@ -1069,11 +1192,7 @@ bool D3D9ShaderModule::Compile(LPDIRECT3DDEVICE9 device, const uint8_t *data, si
|
||||
auto compile = [&](const char *profile) -> HRESULT {
|
||||
return dyn_D3DCompile(source, (UINT)strlen(source), nullptr, defines, includes, "main", profile, 0, 0, &codeBuffer, &errorBuffer);
|
||||
};
|
||||
HRESULT hr = compile(stage_ == ShaderStage::Fragment ? "ps_2_0" : "vs_2_0");
|
||||
if (FAILED(hr) && hr == D3DXERR_INVALIDDATA) {
|
||||
// Might be a post shader. Let's try using shader model 3.
|
||||
hr = compile(stage_ == ShaderStage::Fragment ? "ps_3_0" : "vs_3_0");
|
||||
}
|
||||
HRESULT hr = compile(stage_ == ShaderStage::Fragment ? "ps_3_0" : "vs_3_0");
|
||||
if (FAILED(hr)) {
|
||||
const char *error = errorBuffer ? (const char *)errorBuffer->GetBufferPointer() : "(no errorbuffer returned)";
|
||||
if (hr == ERROR_MOD_NOT_FOUND) {
|
||||
@ -1171,7 +1290,6 @@ D3D9Framebuffer::~D3D9Framebuffer() {
|
||||
}
|
||||
|
||||
void D3D9Context::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) {
|
||||
using namespace DX9;
|
||||
if (fbo) {
|
||||
D3D9Framebuffer *fb = (D3D9Framebuffer *)fbo;
|
||||
device_->SetRenderTarget(0, fb->surf);
|
||||
@ -1200,6 +1318,7 @@ void D3D9Context::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPa
|
||||
}
|
||||
|
||||
dxstate.scissorRect.restore();
|
||||
dxstate.scissorTest.restore();
|
||||
dxstate.viewport.restore();
|
||||
stepId_++;
|
||||
}
|
||||
@ -1283,6 +1402,108 @@ 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::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) {
|
||||
if (bx + bw > fb->Width()) {
|
||||
bw -= (bx + bw) - fb->Width();
|
||||
}
|
||||
if (by + bh > fb->Height()) {
|
||||
bh -= (by + bh) - fb->Height();
|
||||
}
|
||||
}
|
||||
|
||||
if (bh <= 0 || bw <= 0)
|
||||
return true;
|
||||
|
||||
DataFormat srcFormat = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
if (channelBits != FB_COLOR_BIT) {
|
||||
srcFormat = Draw::DataFormat::D24_S8;
|
||||
if (!supportsINTZ)
|
||||
return false;
|
||||
}
|
||||
|
||||
D3DSURFACE_DESC desc;
|
||||
D3DLOCKED_RECT locked;
|
||||
RECT rect = { (LONG)bx, (LONG)by, (LONG)bw, (LONG)bh };
|
||||
|
||||
LPDIRECT3DSURFACE9 offscreen = nullptr;
|
||||
HRESULT hr = E_UNEXPECTED;
|
||||
if (channelBits == FB_COLOR_BIT) {
|
||||
fb->tex->GetLevelDesc(0, &desc);
|
||||
|
||||
hr = device_->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &offscreen, nullptr);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = device_->GetRenderTargetData(fb->surf, offscreen);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = offscreen->LockRect(&locked, &rect, D3DLOCK_READONLY);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fb->depthstenciltex->GetLevelDesc(0, &desc);
|
||||
hr = fb->depthstenciltex->LockRect(0, &locked, &rect, D3DLOCK_READONLY);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
switch (channelBits) {
|
||||
case FB_COLOR_BIT:
|
||||
// Pixel size always 4 here because we always request BGRA8888.
|
||||
ConvertFromBGRA8888((uint8_t *)pixels, (const uint8_t *)locked.pBits, pixelStride, locked.Pitch / sizeof(uint32_t), bw, bh, destFormat);
|
||||
break;
|
||||
case FB_DEPTH_BIT:
|
||||
if (srcFormat == destFormat) {
|
||||
// Can just memcpy when it matches no matter the format!
|
||||
uint8_t *dst = (uint8_t *)pixels;
|
||||
const uint8_t *src = (const uint8_t *)locked.pBits;
|
||||
for (int y = 0; y < bh; ++y) {
|
||||
memcpy(dst, src, bw * DataFormatSizeInBytes(srcFormat));
|
||||
dst += pixelStride * DataFormatSizeInBytes(srcFormat);
|
||||
src += locked.Pitch;
|
||||
}
|
||||
} else if (destFormat == DataFormat::D32F) {
|
||||
ConvertToD32F((uint8_t *)pixels, (const uint8_t *)locked.pBits, pixelStride, locked.Pitch / sizeof(uint32_t), bw, bh, srcFormat);
|
||||
} else if (destFormat == DataFormat::D16) {
|
||||
ConvertToD16((uint8_t *)pixels, (const uint8_t *)locked.pBits, pixelStride, locked.Pitch / sizeof(uint32_t), bw, bh, srcFormat);
|
||||
} else {
|
||||
_assert_(false);
|
||||
}
|
||||
break;
|
||||
case FB_STENCIL_BIT:
|
||||
if (srcFormat == destFormat) {
|
||||
uint8_t *dst = (uint8_t *)pixels;
|
||||
const uint8_t *src = (const uint8_t *)locked.pBits;
|
||||
for (int y = 0; y < bh; ++y) {
|
||||
memcpy(dst, src, bw * DataFormatSizeInBytes(srcFormat));
|
||||
dst += pixelStride * DataFormatSizeInBytes(srcFormat);
|
||||
src += locked.Pitch;
|
||||
}
|
||||
} else if (destFormat == DataFormat::S8) {
|
||||
for (int y = 0; y < bh; y++) {
|
||||
uint8_t *destStencil = (uint8_t *)pixels + y * pixelStride;
|
||||
const uint32_t *src = (const uint32_t *)((const uint8_t *)locked.pBits + locked.Pitch * y);
|
||||
for (int x = 0; x < bw; x++) {
|
||||
destStencil[x] = src[x] >> 24;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_assert_(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (channelBits != FB_COLOR_BIT) {
|
||||
fb->depthstenciltex->UnlockRect(0);
|
||||
}
|
||||
if (offscreen) {
|
||||
offscreen->UnlockRect();
|
||||
offscreen->Release();
|
||||
}
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
void D3D9Context::HandleEvent(Event ev, int width, int height, void *param1, void *param2) {
|
||||
switch (ev) {
|
||||
case Event::LOST_BACKBUFFER:
|
||||
|
@ -32,6 +32,8 @@ enum class DataFormat : uint8_t {
|
||||
A1R5G5B5_UNORM_PACK16, // A1 in the UPPER bit.
|
||||
A1B5G5R5_UNORM_PACK16, // A1 in the UPPER bit. OpenGL-only.
|
||||
|
||||
R16_UNORM,
|
||||
|
||||
R16_FLOAT,
|
||||
R16G16_FLOAT,
|
||||
R16G16B16A16_FLOAT,
|
||||
@ -77,5 +79,6 @@ inline bool DataFormatIsColor(DataFormat fmt) {
|
||||
void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format);
|
||||
void ConvertFromBGRA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format);
|
||||
void ConvertToD32F(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format);
|
||||
void ConvertToD16(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format);
|
||||
|
||||
} // namespace
|
||||
|
@ -1,12 +1,35 @@
|
||||
#include "Common/GPU/OpenGL/DataFormatGL.h"
|
||||
#include "Common/GPU/OpenGL/GLFeatures.h"
|
||||
#include "Common/Log.h"
|
||||
|
||||
namespace Draw {
|
||||
|
||||
// TODO: Also output storage format (GL_RGBA8 etc) for modern GL usage.
|
||||
bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment) {
|
||||
bool Thin3DFormatToGLFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment) {
|
||||
alignment = 4;
|
||||
switch (fmt) {
|
||||
case DataFormat::R16_UNORM:
|
||||
internalFormat = GL_RGBA;
|
||||
format = GL_RED;
|
||||
type = GL_UNSIGNED_SHORT;
|
||||
alignment = 2;
|
||||
break;
|
||||
|
||||
case DataFormat::R8_UNORM:
|
||||
if (gl_extensions.IsGLES) {
|
||||
internalFormat = GL_LUMINANCE;
|
||||
format = GL_LUMINANCE;
|
||||
} else if (gl_extensions.VersionGEThan(3, 0)) {
|
||||
internalFormat = GL_RED;
|
||||
format = GL_RED;
|
||||
} else {
|
||||
internalFormat = GL_RGBA;
|
||||
format = GL_RED;
|
||||
}
|
||||
type = GL_UNSIGNED_BYTE;
|
||||
alignment = 1;
|
||||
break;
|
||||
|
||||
case DataFormat::R8G8B8A8_UNORM:
|
||||
internalFormat = GL_RGBA;
|
||||
format = GL_RGBA;
|
||||
|
@ -5,6 +5,6 @@
|
||||
|
||||
namespace Draw {
|
||||
|
||||
bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment);
|
||||
bool Thin3DFormatToGLFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment);
|
||||
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ static void ParseExtensionsString(const std::string& str, std::set<std::string>
|
||||
size_t next = 0;
|
||||
for (size_t pos = 0, len = str.length(); pos < len; ++pos) {
|
||||
if (str[pos] == ' ') {
|
||||
output.insert(str.substr(next, pos - next));
|
||||
output.emplace(str.substr(next, pos - next));
|
||||
// Skip the delimiter itself.
|
||||
next = pos + 1;
|
||||
}
|
||||
@ -60,7 +60,7 @@ static void ParseExtensionsString(const std::string& str, std::set<std::string>
|
||||
if (next == 0 && str.length() != 0) {
|
||||
output.insert(str);
|
||||
} else if (next < str.length()) {
|
||||
output.insert(str.substr(next));
|
||||
output.emplace(str.substr(next));
|
||||
}
|
||||
}
|
||||
|
||||
@ -386,6 +386,7 @@ void CheckGLExtensions() {
|
||||
gl_extensions.OES_texture_3D = g_set_gl_extensions.count("GL_OES_texture_3D") != 0;
|
||||
gl_extensions.EXT_buffer_storage = g_set_gl_extensions.count("GL_EXT_buffer_storage") != 0;
|
||||
gl_extensions.EXT_clip_cull_distance = g_set_gl_extensions.count("GL_EXT_clip_cull_distance") != 0;
|
||||
gl_extensions.EXT_depth_clamp = g_set_gl_extensions.count("GL_EXT_depth_clamp") != 0;
|
||||
gl_extensions.APPLE_clip_distance = g_set_gl_extensions.count("GL_APPLE_clip_distance") != 0;
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
@ -543,8 +544,8 @@ void CheckGLExtensions() {
|
||||
}
|
||||
if (gl_extensions.VersionGEThan(4, 3)) {
|
||||
gl_extensions.ARB_copy_image = true;
|
||||
gl_extensions.ARB_stencil_texturing = true;
|
||||
// ARB_explicit_uniform_location = true;
|
||||
// ARB_stencil_texturing = true;
|
||||
// ARB_texture_view = true;
|
||||
// ARB_vertex_attrib_binding = true;
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ struct GLExtensions {
|
||||
bool ARB_depth_clamp;
|
||||
bool ARB_uniform_buffer_object;
|
||||
bool ARB_texture_non_power_of_two;
|
||||
bool ARB_stencil_texturing;
|
||||
|
||||
// EXT
|
||||
bool EXT_swap_control_tear;
|
||||
@ -87,6 +88,7 @@ struct GLExtensions {
|
||||
bool EXT_draw_instanced;
|
||||
bool EXT_buffer_storage;
|
||||
bool EXT_clip_cull_distance;
|
||||
bool EXT_depth_clamp;
|
||||
|
||||
// NV
|
||||
bool NV_copy_image;
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "Common/GPU/OpenGL/GLFeatures.h"
|
||||
#include "Common/GPU/OpenGL/DataFormatGL.h"
|
||||
#include "Common/Math/math_util.h"
|
||||
#include "Common/VR/PPSSPPVR.h"
|
||||
|
||||
#include "Common/Log.h"
|
||||
#include "Common/MemoryUtil.h"
|
||||
@ -23,6 +24,12 @@
|
||||
#elif !defined(GL_CLIP_DISTANCE0)
|
||||
#define GL_CLIP_DISTANCE0 0x3000
|
||||
#endif
|
||||
#ifndef GL_DEPTH_STENCIL_TEXTURE_MODE
|
||||
#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA
|
||||
#endif
|
||||
#ifndef GL_STENCIL_INDEX
|
||||
#define GL_STENCIL_INDEX 0x1901
|
||||
#endif
|
||||
|
||||
static constexpr int TEXCACHE_NAME_CACHE_SIZE = 16;
|
||||
|
||||
@ -30,11 +37,6 @@ static constexpr int TEXCACHE_NAME_CACHE_SIZE = 16;
|
||||
extern void bindDefaultFBO();
|
||||
#endif
|
||||
|
||||
#ifdef OPENXR
|
||||
#include "VR/VRBase.h"
|
||||
#include "VR/VRRenderer.h"
|
||||
#endif
|
||||
|
||||
// Workaround for Retroarch. Simply declare
|
||||
// extern GLuint g_defaultFBO;
|
||||
// and set is as appropriate. Can adjust the variables in ext/native/base/display.h as
|
||||
@ -110,6 +112,19 @@ static std::string GetInfoLog(GLuint name, Getiv getiv, GetLog getLog) {
|
||||
return infoLog;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 std::vector<GLRInitStep> &steps, bool skipGLCalls) {
|
||||
if (skipGLCalls) {
|
||||
// Some bookkeeping still needs to be done.
|
||||
@ -243,8 +258,8 @@ void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool ski
|
||||
ERROR_LOG(G3D, "Could not link program:\n %s", infoLog.c_str());
|
||||
ERROR_LOG(G3D, "VS desc:\n%s", vsDesc.c_str());
|
||||
ERROR_LOG(G3D, "FS desc:\n%s", fsDesc.c_str());
|
||||
ERROR_LOG(G3D, "VS:\n%s\n", vsCode);
|
||||
ERROR_LOG(G3D, "FS:\n%s\n", fsCode);
|
||||
ERROR_LOG(G3D, "VS:\n%s\n", LineNumberString(vsCode).c_str());
|
||||
ERROR_LOG(G3D, "FS:\n%s\n", LineNumberString(fsCode).c_str());
|
||||
|
||||
#ifdef _WIN32
|
||||
OutputDebugStringUTF8(infoLog.c_str());
|
||||
@ -263,7 +278,27 @@ void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool ski
|
||||
for (size_t j = 0; j < program->queries_.size(); j++) {
|
||||
auto &query = program->queries_[j];
|
||||
_dbg_assert_(query.name);
|
||||
int location = glGetUniformLocation(program->program, query.name);
|
||||
|
||||
int location = -1;
|
||||
if (IsVRBuild() && IsMultiviewSupported()) {
|
||||
int index = GetStereoBufferIndex(query.name);
|
||||
if (index >= 0) {
|
||||
std::string layout = GetStereoBufferLayout(query.name);
|
||||
glUniformBlockBinding(program->program, glGetUniformBlockIndex(program->program, layout.c_str()), index);
|
||||
|
||||
GLuint buffer = 0;
|
||||
glGenBuffers(1, &buffer);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, buffer);
|
||||
glBufferData(GL_UNIFORM_BUFFER,2 * 16 * sizeof(float),NULL, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
location = buffer;
|
||||
} else {
|
||||
location = glGetUniformLocation(program->program, query.name);
|
||||
}
|
||||
} else {
|
||||
location = glGetUniformLocation(program->program, query.name);
|
||||
}
|
||||
|
||||
if (location < 0 && query.required) {
|
||||
WARN_LOG(G3D, "Required uniform query for '%s' failed", query.name);
|
||||
}
|
||||
@ -356,7 +391,7 @@ void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool ski
|
||||
|
||||
GLenum internalFormat, format, type;
|
||||
int alignment;
|
||||
Thin3DFormatToFormatAndType(step.texture_image.format, internalFormat, format, type, alignment);
|
||||
Thin3DFormatToGLFormatAndType(step.texture_image.format, internalFormat, format, type, alignment);
|
||||
if (step.texture_image.depth == 1) {
|
||||
glTexImage2D(tex->target,
|
||||
step.texture_image.level, internalFormat,
|
||||
@ -613,8 +648,11 @@ retry_depth:
|
||||
currentReadHandle_ = fbo->handle;
|
||||
}
|
||||
|
||||
void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCalls) {
|
||||
void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCalls, bool keepSteps) {
|
||||
if (skipGLCalls) {
|
||||
if (keepSteps) {
|
||||
return;
|
||||
}
|
||||
// Dry run
|
||||
for (size_t i = 0; i < steps.size(); i++) {
|
||||
const GLRStep &step = *steps[i];
|
||||
@ -669,7 +707,13 @@ void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCal
|
||||
switch (step.stepType) {
|
||||
case GLRStepType::RENDER:
|
||||
renderCount++;
|
||||
PerformRenderPass(step, renderCount == 1, renderCount == totalRenderCount);
|
||||
if (IsVRBuild()) {
|
||||
GLRStep vrStep = step;
|
||||
PreprocessStepVR(&vrStep);
|
||||
PerformRenderPass(vrStep, renderCount == 1, renderCount == totalRenderCount);
|
||||
} else {
|
||||
PerformRenderPass(step, renderCount == 1, renderCount == totalRenderCount);
|
||||
}
|
||||
break;
|
||||
case GLRStepType::COPY:
|
||||
PerformCopy(step);
|
||||
@ -695,7 +739,9 @@ void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCal
|
||||
glPopDebugGroup();
|
||||
#endif
|
||||
|
||||
delete steps[i];
|
||||
if (!keepSteps) {
|
||||
delete steps[i];
|
||||
}
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
@ -758,9 +804,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
||||
}
|
||||
|
||||
GLRProgram *curProgram = nullptr;
|
||||
int activeSlot = 0;
|
||||
if (first)
|
||||
glActiveTexture(GL_TEXTURE0 + activeSlot);
|
||||
int activeSlot = -1;
|
||||
|
||||
// State filtering tracking.
|
||||
int attrMask = 0;
|
||||
@ -779,7 +823,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
||||
int logicOp = -1;
|
||||
bool logicEnabled = false;
|
||||
#endif
|
||||
bool clipDistance0Enabled = false;
|
||||
bool clipDistanceEnabled[8]{};
|
||||
GLuint blendEqColor = (GLuint)-1;
|
||||
GLuint blendEqAlpha = (GLuint)-1;
|
||||
|
||||
@ -997,6 +1041,36 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
break;
|
||||
}
|
||||
case GLRRenderCommand::UNIFORMSTEREOMATRIX:
|
||||
{
|
||||
_dbg_assert_(curProgram);
|
||||
if (IsMultiviewSupported()) {
|
||||
int layout = GetStereoBufferIndex(c.uniformMatrix4.name);
|
||||
if (layout >= 0) {
|
||||
int size = 2 * 16 * sizeof(float);
|
||||
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.uniformMatrix4.m, size);
|
||||
glUnmapBuffer(GL_UNIFORM_BUFFER);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
} else {
|
||||
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.uniformMatrix4.m);
|
||||
} else {
|
||||
glUniformMatrix4fv(loc, 1, false, &c.uniformMatrix4.m[16]);
|
||||
}
|
||||
}
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
break;
|
||||
}
|
||||
case GLRRenderCommand::UNIFORMMATRIX:
|
||||
{
|
||||
_dbg_assert_(curProgram);
|
||||
@ -1037,15 +1111,25 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
||||
activeSlot = slot;
|
||||
}
|
||||
if (c.bind_fb_texture.aspect == GL_COLOR_BUFFER_BIT) {
|
||||
if (curTex[slot] != &c.bind_fb_texture.framebuffer->color_texture)
|
||||
if (curTex[slot] != &c.bind_fb_texture.framebuffer->color_texture) {
|
||||
glBindTexture(GL_TEXTURE_2D, c.bind_fb_texture.framebuffer->color_texture.texture);
|
||||
curTex[slot] = &c.bind_fb_texture.framebuffer->color_texture;
|
||||
curTex[slot] = &c.bind_fb_texture.framebuffer->color_texture;
|
||||
}
|
||||
} else if (c.bind_fb_texture.aspect == GL_DEPTH_BUFFER_BIT) {
|
||||
if (curTex[slot] != &c.bind_fb_texture.framebuffer->z_stencil_texture)
|
||||
if (curTex[slot] != &c.bind_fb_texture.framebuffer->z_stencil_texture) {
|
||||
glBindTexture(GL_TEXTURE_2D, c.bind_fb_texture.framebuffer->z_stencil_texture.texture);
|
||||
curTex[slot] = &c.bind_fb_texture.framebuffer->z_stencil_texture;
|
||||
curTex[slot] = &c.bind_fb_texture.framebuffer->z_stencil_texture;
|
||||
}
|
||||
// This should be uncommon, so always set the mode.
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);
|
||||
} else if (c.bind_fb_texture.aspect == GL_STENCIL_BUFFER_BIT) {
|
||||
if (curTex[slot] != &c.bind_fb_texture.framebuffer->z_stencil_texture) {
|
||||
glBindTexture(GL_TEXTURE_2D, c.bind_fb_texture.framebuffer->z_stencil_texture.texture);
|
||||
curTex[slot] = &c.bind_fb_texture.framebuffer->z_stencil_texture;
|
||||
}
|
||||
// This should be uncommon, so always set the mode.
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);
|
||||
} else {
|
||||
// TODO: Stencil texturing?
|
||||
curTex[slot] = nullptr;
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
@ -1055,14 +1139,18 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
||||
{
|
||||
if (curProgram != c.program.program) {
|
||||
glUseProgram(c.program.program->program);
|
||||
if (c.program.program->use_clip_distance0 != clipDistance0Enabled) {
|
||||
if (c.program.program->use_clip_distance0)
|
||||
glEnable(GL_CLIP_DISTANCE0);
|
||||
else
|
||||
glDisable(GL_CLIP_DISTANCE0);
|
||||
clipDistance0Enabled = c.program.program->use_clip_distance0;
|
||||
}
|
||||
curProgram = c.program.program;
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(clipDistanceEnabled); ++i) {
|
||||
if (c.program.program->use_clip_distance[i] == clipDistanceEnabled[i])
|
||||
continue;
|
||||
|
||||
if (c.program.program->use_clip_distance[i])
|
||||
glEnable(GL_CLIP_DISTANCE0 + (GLenum)i);
|
||||
else
|
||||
glDisable(GL_CLIP_DISTANCE0 + (GLenum)i);
|
||||
clipDistanceEnabled[i] = c.program.program->use_clip_distance[i];
|
||||
}
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
break;
|
||||
@ -1202,15 +1290,20 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
||||
}
|
||||
case GLRRenderCommand::TEXTURE_SUBIMAGE:
|
||||
{
|
||||
GLRTexture *tex = c.texture_subimage.texture;
|
||||
GLint slot = c.texture_subimage.slot;
|
||||
if (slot != activeSlot) {
|
||||
glActiveTexture(GL_TEXTURE0 + slot);
|
||||
activeSlot = slot;
|
||||
}
|
||||
// TODO: Need bind?
|
||||
GLRTexture *tex = c.texture_subimage.texture;
|
||||
if (!c.texture_subimage.data)
|
||||
Crash();
|
||||
_assert_(tex->target == GL_TEXTURE_2D);
|
||||
// For things to show in RenderDoc, need to split into glTexImage2D(..., nullptr) and glTexSubImage.
|
||||
GLuint internalFormat, format, type;
|
||||
int alignment;
|
||||
Thin3DFormatToFormatAndType(c.texture_subimage.format, internalFormat, format, type, alignment);
|
||||
Thin3DFormatToGLFormatAndType(c.texture_subimage.format, internalFormat, format, type, alignment);
|
||||
glTexSubImage2D(tex->target, c.texture_subimage.level, c.texture_subimage.x, c.texture_subimage.y, c.texture_subimage.width, c.texture_subimage.height, format, type, c.texture_subimage.data);
|
||||
if (c.texture_subimage.allocType == GLRAllocType::ALIGNED) {
|
||||
FreeAlignedMemory(c.texture_subimage.data);
|
||||
@ -1297,8 +1390,10 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
||||
glDisable(GL_COLOR_LOGIC_OP);
|
||||
}
|
||||
#endif
|
||||
if (clipDistance0Enabled)
|
||||
glDisable(GL_CLIP_DISTANCE0);
|
||||
for (size_t i = 0; i < ARRAY_SIZE(clipDistanceEnabled); ++i) {
|
||||
if (clipDistanceEnabled[i])
|
||||
glDisable(GL_CLIP_DISTANCE0 + (GLenum)i);
|
||||
}
|
||||
if ((colorMask & 15) != 15)
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
@ -1660,9 +1755,9 @@ void GLQueueRunner::fbo_unbind() {
|
||||
bindDefaultFBO();
|
||||
#endif
|
||||
|
||||
#ifdef OPENXR
|
||||
VR_BindFramebuffer(VR_GetEngine(), 0);
|
||||
#endif
|
||||
if (IsVRBuild()) {
|
||||
BindVRFramebuffer();
|
||||
}
|
||||
|
||||
currentDrawHandle_ = 0;
|
||||
currentReadHandle_ = 0;
|
||||
|
@ -47,6 +47,7 @@ enum class GLRRenderCommand : uint8_t {
|
||||
UNIFORM4UI,
|
||||
UNIFORM4F,
|
||||
UNIFORMMATRIX,
|
||||
UNIFORMSTEREOMATRIX,
|
||||
TEXTURESAMPLER,
|
||||
TEXTURELOD,
|
||||
VIEWPORT,
|
||||
@ -128,7 +129,7 @@ struct GLRRenderData {
|
||||
struct {
|
||||
const char *name; // if null, use loc
|
||||
const GLint *loc;
|
||||
float m[16];
|
||||
float m[32];
|
||||
} uniformMatrix4;
|
||||
struct {
|
||||
uint32_t clearColor;
|
||||
@ -148,11 +149,12 @@ struct GLRRenderData {
|
||||
struct {
|
||||
GLRTexture *texture;
|
||||
Draw::DataFormat format;
|
||||
int level;
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
uint8_t slot;
|
||||
uint8_t level;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
GLRAllocType allocType;
|
||||
uint8_t *data; // owned, delete[]-d
|
||||
} texture_subimage;
|
||||
@ -356,9 +358,12 @@ public:
|
||||
caps_ = caps;
|
||||
}
|
||||
|
||||
int GetStereoBufferIndex(const char *uniformName);
|
||||
std::string GetStereoBufferLayout(const char *uniformName);
|
||||
|
||||
void RunInitSteps(const std::vector<GLRInitStep> &steps, bool skipGLCalls);
|
||||
|
||||
void RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCalls);
|
||||
void RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCalls, bool keepSteps = false);
|
||||
void LogSteps(const std::vector<GLRStep *> &steps);
|
||||
|
||||
void CreateDeviceObjects();
|
||||
|
@ -3,16 +3,14 @@
|
||||
#include "Common/GPU/OpenGL/GLFeatures.h"
|
||||
#include "Common/GPU/thin3d.h"
|
||||
#include "Common/Thread/ThreadUtil.h"
|
||||
#include "Common/VR/PPSSPPVR.h"
|
||||
|
||||
#include "Core/Config.h"
|
||||
|
||||
#include "Common/Log.h"
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Common/Math/math_util.h"
|
||||
|
||||
#ifdef OPENXR
|
||||
#include "VR/VRBase.h"
|
||||
#include "VR/VRRenderer.h"
|
||||
#endif
|
||||
|
||||
#if 0 // def _DEBUG
|
||||
#define VLOG(...) INFO_LOG(G3D, __VA_ARGS__)
|
||||
#else
|
||||
@ -202,9 +200,6 @@ bool GLRenderManager::ThreadFrame() {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
if (!run_)
|
||||
return false;
|
||||
#ifdef OPENXR
|
||||
VR_BeginFrame(VR_GetEngine());
|
||||
#endif
|
||||
|
||||
// In case of syncs or other partial completion, we keep going until we complete a frame.
|
||||
do {
|
||||
@ -240,12 +235,13 @@ bool GLRenderManager::ThreadFrame() {
|
||||
INFO_LOG(G3D, "Running first frame (%d)", threadFrame_);
|
||||
firstFrame = false;
|
||||
}
|
||||
|
||||
// Render the scene.
|
||||
Run(threadFrame_);
|
||||
|
||||
VLOG("PULL: Finished frame %d", threadFrame_);
|
||||
} while (!nextFrame);
|
||||
#ifdef OPENXR
|
||||
VR_EndFrame(VR_GetEngine());
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -583,7 +579,19 @@ void GLRenderManager::Run(int frame) {
|
||||
}
|
||||
}
|
||||
|
||||
queueRunner_.RunSteps(stepsOnThread, skipGLCalls_);
|
||||
if (IsVRBuild()) {
|
||||
int passes = 1;
|
||||
if (!IsMultiviewSupported() && g_Config.bEnableStereo) {
|
||||
passes = 2;
|
||||
}
|
||||
for (int i = 0; i < passes; i++) {
|
||||
PreVRFrameRender(i);
|
||||
queueRunner_.RunSteps(stepsOnThread, skipGLCalls_, i < passes - 1);
|
||||
PostVRFrameRender();
|
||||
}
|
||||
} else {
|
||||
queueRunner_.RunSteps(stepsOnThread, skipGLCalls_);
|
||||
}
|
||||
stepsOnThread.clear();
|
||||
|
||||
if (!skipGLCalls_) {
|
||||
|
@ -91,6 +91,13 @@ public:
|
||||
std::string error;
|
||||
};
|
||||
|
||||
struct GLRProgramFlags {
|
||||
bool supportDualSource : 1;
|
||||
bool useClipDistance0 : 1;
|
||||
bool useClipDistance1 : 1;
|
||||
bool useClipDistance2 : 1;
|
||||
};
|
||||
|
||||
class GLRProgram {
|
||||
public:
|
||||
~GLRProgram() {
|
||||
@ -119,7 +126,7 @@ public:
|
||||
std::vector<Semantic> semantics_;
|
||||
std::vector<UniformLocQuery> queries_;
|
||||
std::vector<Initializer> initialize_;
|
||||
bool use_clip_distance0 = false;
|
||||
bool use_clip_distance[8]{};
|
||||
|
||||
struct UniformInfo {
|
||||
int loc_;
|
||||
@ -427,15 +434,17 @@ public:
|
||||
// not be an active render pass.
|
||||
GLRProgram *CreateProgram(
|
||||
std::vector<GLRShader *> shaders, std::vector<GLRProgram::Semantic> semantics, std::vector<GLRProgram::UniformLocQuery> queries,
|
||||
std::vector<GLRProgram::Initializer> initializers, bool supportDualSource, bool useClipDistance0) {
|
||||
std::vector<GLRProgram::Initializer> initializers, const GLRProgramFlags &flags) {
|
||||
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;
|
||||
step.create_program.program->queries_ = queries;
|
||||
step.create_program.program->initialize_ = initializers;
|
||||
step.create_program.program->use_clip_distance0 = useClipDistance0;
|
||||
step.create_program.support_dual_source = supportDualSource;
|
||||
step.create_program.program->use_clip_distance[0] = flags.useClipDistance0;
|
||||
step.create_program.program->use_clip_distance[1] = flags.useClipDistance1;
|
||||
step.create_program.program->use_clip_distance[2] = flags.useClipDistance2;
|
||||
step.create_program.support_dual_source = flags.supportDualSource;
|
||||
_assert_msg_(shaders.size() > 0, "Can't create a program with zero shaders");
|
||||
for (size_t i = 0; i < shaders.size(); i++) {
|
||||
step.create_program.shaders[i] = shaders[i];
|
||||
@ -555,7 +564,7 @@ public:
|
||||
initSteps_.push_back(step);
|
||||
}
|
||||
|
||||
void TextureSubImage(GLRTexture *texture, int level, int x, int y, int width, int height, Draw::DataFormat format, uint8_t *data, GLRAllocType allocType = GLRAllocType::NEW) {
|
||||
void TextureSubImage(int slot, GLRTexture *texture, int level, int x, int y, int width, int height, Draw::DataFormat format, uint8_t *data, GLRAllocType allocType = GLRAllocType::NEW) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
GLRRenderData _data{ GLRRenderCommand::TEXTURE_SUBIMAGE };
|
||||
_data.texture_subimage.texture = texture;
|
||||
@ -567,6 +576,7 @@ public:
|
||||
_data.texture_subimage.width = width;
|
||||
_data.texture_subimage.height = height;
|
||||
_data.texture_subimage.allocType = allocType;
|
||||
_data.texture_subimage.slot = slot;
|
||||
curRenderStep_->commands.push_back(_data);
|
||||
}
|
||||
|
||||
@ -742,6 +752,19 @@ public:
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void SetUniformM4x4Stereo(const char *name, const GLint *loc, const float *left, const float *right) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
#ifdef _DEBUG
|
||||
_dbg_assert_(curProgram_);
|
||||
#endif
|
||||
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) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
|
||||
#ifdef _DEBUG
|
||||
@ -989,6 +1012,7 @@ private:
|
||||
bool readyForFence = true;
|
||||
bool readyForRun = false;
|
||||
bool readyForSubmit = false;
|
||||
|
||||
bool skipSwap = false;
|
||||
GLRRunType type = GLRRunType::END;
|
||||
|
||||
|
@ -336,9 +336,9 @@ public:
|
||||
BlendState *CreateBlendState(const BlendStateDesc &desc) override;
|
||||
SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override;
|
||||
RasterState *CreateRasterState(const RasterStateDesc &desc) override;
|
||||
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) override;
|
||||
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) override;
|
||||
InputLayout *CreateInputLayout(const InputLayoutDesc &desc) override;
|
||||
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) override;
|
||||
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) override;
|
||||
|
||||
Texture *CreateTexture(const TextureDesc &desc) override;
|
||||
Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override;
|
||||
@ -401,6 +401,8 @@ public:
|
||||
}
|
||||
|
||||
void BindTextures(int start, int count, Texture **textures) override;
|
||||
void BindNativeTexture(int sampler, void *nativeTexture) override;
|
||||
|
||||
void BindPipeline(Pipeline *pipeline) override;
|
||||
void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) override {
|
||||
_assert_(start + count <= ARRAY_SIZE(curVBuffers_));
|
||||
@ -533,12 +535,14 @@ OpenGLContext::OpenGLContext() {
|
||||
}
|
||||
}
|
||||
caps_.texture3DSupported = gl_extensions.OES_texture_3D;
|
||||
caps_.textureDepthSupported = gl_extensions.GLES3 || gl_extensions.OES_depth_texture;
|
||||
} else {
|
||||
if (gl_extensions.VersionGEThan(3, 3, 0)) {
|
||||
caps_.fragmentShaderInt32Supported = true;
|
||||
}
|
||||
caps_.preferredDepthBufferFormat = DataFormat::D24_S8;
|
||||
caps_.texture3DSupported = true;
|
||||
caps_.textureDepthSupported = true;
|
||||
}
|
||||
|
||||
caps_.dualSourceBlend = gl_extensions.ARB_blend_func_extended || gl_extensions.EXT_blend_func_extended;
|
||||
@ -547,7 +551,9 @@ OpenGLContext::OpenGLContext() {
|
||||
caps_.framebufferBlitSupported = gl_extensions.NV_framebuffer_blit || gl_extensions.ARB_framebuffer_object || gl_extensions.GLES3;
|
||||
caps_.framebufferDepthBlitSupported = caps_.framebufferBlitSupported;
|
||||
caps_.framebufferStencilBlitSupported = caps_.framebufferBlitSupported;
|
||||
caps_.depthClampSupported = gl_extensions.ARB_depth_clamp;
|
||||
caps_.depthClampSupported = gl_extensions.ARB_depth_clamp || gl_extensions.EXT_depth_clamp;
|
||||
caps_.blendMinMaxSupported = gl_extensions.EXT_blend_minmax;
|
||||
|
||||
if (gl_extensions.IsGLES) {
|
||||
caps_.clipDistanceSupported = gl_extensions.EXT_clip_cull_distance || gl_extensions.APPLE_clip_distance;
|
||||
caps_.cullDistanceSupported = gl_extensions.EXT_clip_cull_distance;
|
||||
@ -567,6 +573,9 @@ OpenGLContext::OpenGLContext() {
|
||||
caps_.fragmentShaderDepthWriteSupported = true;
|
||||
}
|
||||
|
||||
// GLES has no support for logic framebuffer operations. There doesn't even seem to exist any such extensions.
|
||||
caps_.logicOpSupported = !gl_extensions.IsGLES;
|
||||
|
||||
// Interesting potential hack for emulating GL_DEPTH_CLAMP (use a separate varying, force depth in fragment shader):
|
||||
// This will induce a performance penalty on many architectures though so a blanket enable of this
|
||||
// is probably not a good idea.
|
||||
@ -704,8 +713,10 @@ OpenGLContext::OpenGLContext() {
|
||||
}
|
||||
}
|
||||
|
||||
if (gl_extensions.IsGLES) {
|
||||
// NOTE: We only support framebuffer fetch on ES3 due to past issues..
|
||||
if (gl_extensions.IsGLES && gl_extensions.GLES3) {
|
||||
caps_.framebufferFetchSupported = (gl_extensions.EXT_shader_framebuffer_fetch || gl_extensions.ARM_shader_framebuffer_fetch);
|
||||
|
||||
if (gl_extensions.EXT_shader_framebuffer_fetch) {
|
||||
shaderLanguageDesc_.framebufferFetchExtension = "#extension GL_EXT_shader_framebuffer_fetch : require";
|
||||
shaderLanguageDesc_.lastFragData = gl_extensions.GLES3 ? "fragColor0" : "gl_LastFragData[0]";
|
||||
@ -821,11 +832,14 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) :
|
||||
return;
|
||||
|
||||
int level = 0;
|
||||
int width = width_;
|
||||
int height = height_;
|
||||
int depth = depth_;
|
||||
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;
|
||||
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_ = desc.generateMips ? desc.mipLevels : level;
|
||||
@ -1071,7 +1085,7 @@ void OpenGLContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t off
|
||||
renderManager_.BufferSubdata(buf->buffer_, offset, size, dataCopy);
|
||||
}
|
||||
|
||||
Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) {
|
||||
if (!desc.shaders.size()) {
|
||||
ERROR_LOG(G3D, "Pipeline requires at least one shader");
|
||||
return nullptr;
|
||||
@ -1091,7 +1105,7 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
iter->AddRef();
|
||||
pipeline->shaders.push_back(static_cast<OpenGLShaderModule *>(iter));
|
||||
} else {
|
||||
ERROR_LOG(G3D, "ERROR: Tried to create graphics pipeline with a null shader module");
|
||||
ERROR_LOG(G3D, "ERROR: Tried to create graphics pipeline %s with a null shader module", tag);
|
||||
delete pipeline;
|
||||
return nullptr;
|
||||
}
|
||||
@ -1110,7 +1124,7 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
pipeline->inputLayout = (OpenGLInputLayout *)desc.inputLayout;
|
||||
return pipeline;
|
||||
} else {
|
||||
ERROR_LOG(G3D, "Failed to create pipeline - shaders failed to link");
|
||||
ERROR_LOG(G3D, "Failed to create pipeline %s - shaders failed to link", tag);
|
||||
delete pipeline;
|
||||
return nullptr;
|
||||
}
|
||||
@ -1130,6 +1144,12 @@ void OpenGLContext::BindTextures(int start, int count, Texture **textures) {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLContext::BindNativeTexture(int index, void *nativeTexture) {
|
||||
GLRTexture *tex = (GLRTexture *)nativeTexture;
|
||||
boundTextures_[index] = tex;
|
||||
renderManager_.BindTexture(index, tex);
|
||||
}
|
||||
|
||||
void OpenGLContext::ApplySamplers() {
|
||||
for (int i = 0; i < MAX_TEXTURE_SLOTS; i++) {
|
||||
const OpenGLSamplerState *samp = boundSamplers_[i];
|
||||
@ -1155,7 +1175,7 @@ void OpenGLContext::ApplySamplers() {
|
||||
}
|
||||
}
|
||||
|
||||
ShaderModule *OpenGLContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) {
|
||||
ShaderModule *OpenGLContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) {
|
||||
OpenGLShaderModule *shader = new OpenGLShaderModule(&renderManager_, stage, tag);
|
||||
if (shader->Compile(&renderManager_, language, data, dataSize)) {
|
||||
return shader;
|
||||
@ -1183,9 +1203,11 @@ bool OpenGLPipeline::LinkShaders() {
|
||||
}
|
||||
|
||||
std::vector<GLRProgram::Semantic> semantics;
|
||||
semantics.reserve(8);
|
||||
// Bind all the common vertex data points. Mismatching ones will be ignored.
|
||||
semantics.push_back({ SEM_POSITION, "Position" });
|
||||
semantics.push_back({ SEM_COLOR0, "Color0" });
|
||||
semantics.push_back({ SEM_COLOR1, "Color1" });
|
||||
semantics.push_back({ SEM_TEXCOORD0, "TexCoord0" });
|
||||
semantics.push_back({ SEM_NORMAL, "Normal" });
|
||||
semantics.push_back({ SEM_TANGENT, "Tangent" });
|
||||
@ -1211,14 +1233,15 @@ bool OpenGLPipeline::LinkShaders() {
|
||||
}
|
||||
std::vector<GLRProgram::Initializer> initialize;
|
||||
for (int i = 0; i < MAX_TEXTURE_SLOTS; ++i) {
|
||||
if (i < queries.size()) {
|
||||
if (i < (int)samplers_.size()) {
|
||||
initialize.push_back({ &samplerLocs_[i], 0, i });
|
||||
} else {
|
||||
samplerLocs_[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
program_ = render_->CreateProgram(linkShaders, semantics, queries, initialize, false, false);
|
||||
GLRProgramFlags flags{};
|
||||
program_ = render_->CreateProgram(linkShaders, semantics, queries, initialize, flags);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1475,7 +1498,8 @@ uint32_t OpenGLContext::GetDataFormatSupport(DataFormat fmt) const {
|
||||
return FMT_INPUTLAYOUT;
|
||||
|
||||
case DataFormat::R8_UNORM:
|
||||
return 0;
|
||||
return FMT_TEXTURE;
|
||||
|
||||
case DataFormat::BC1_RGBA_UNORM_BLOCK:
|
||||
case DataFormat::BC2_UNORM_BLOCK:
|
||||
case DataFormat::BC3_UNORM_BLOCK:
|
||||
|
@ -202,6 +202,7 @@ void init_resources(TBuiltInResource &Resources) {
|
||||
Resources.maxCullDistances = 8;
|
||||
Resources.maxCombinedClipAndCullDistances = 8;
|
||||
Resources.maxSamples = 4;
|
||||
Resources.maxDualSourceDrawBuffersEXT = 1;
|
||||
Resources.limits.nonInductiveForLoops = 1;
|
||||
Resources.limits.whileLoops = 1;
|
||||
Resources.limits.doWhileLoops = 1;
|
||||
|
@ -85,6 +85,12 @@ struct UniformBufferDesc {
|
||||
std::vector<UniformDesc> uniforms;
|
||||
};
|
||||
|
||||
struct UniformDef {
|
||||
const char *type;
|
||||
const char *name;
|
||||
int index;
|
||||
};
|
||||
|
||||
struct SamplerDef {
|
||||
const char *name;
|
||||
// TODO: Might need unsigned samplers, 3d samplers, or other types in the future.
|
||||
|
@ -152,16 +152,18 @@ std::string Postprocess(std::string code, ShaderLanguage lang, ShaderStage stage
|
||||
return output;
|
||||
}
|
||||
|
||||
static_assert(Draw::SEM_TEXCOORD0 == 3, "Semantic shader hardcoded in glsl below.");
|
||||
|
||||
bool ConvertToVulkanGLSL(std::string *dest, TranslatedShaderMetadata *destMetadata, std::string src, ShaderStage stage, std::string *errorMessage) {
|
||||
std::stringstream out;
|
||||
|
||||
static struct {
|
||||
static const struct {
|
||||
ShaderStage stage;
|
||||
const char *needle;
|
||||
const char *replacement;
|
||||
} replacements[] = {
|
||||
{ ShaderStage::Vertex, "attribute vec4 a_position;", "layout(location = 0) in vec4 a_position;" },
|
||||
{ ShaderStage::Vertex, "attribute vec2 a_texcoord0;", "layout(location = 2) in vec2 a_texcoord0;"},
|
||||
{ ShaderStage::Vertex, "attribute vec2 a_texcoord0;", "layout(location = 3) in vec2 a_texcoord0;"},
|
||||
{ ShaderStage::Vertex, "varying vec2 v_position;", "layout(location = 0) out vec2 v_position;" },
|
||||
{ ShaderStage::Fragment, "varying vec2 v_position;", "layout(location = 0) in vec2 v_position;" },
|
||||
{ ShaderStage::Fragment, "texture2D(", "texture(" },
|
||||
@ -233,7 +235,7 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, const ShaderLan
|
||||
return false;
|
||||
#endif
|
||||
|
||||
*errorMessage = "";
|
||||
errorMessage->clear();
|
||||
|
||||
glslang::TProgram program;
|
||||
const char *shaderStrings[1]{};
|
||||
|
@ -24,6 +24,7 @@ const char * const hlsl_preamble_fs =
|
||||
"#define vec4 float4\n"
|
||||
"#define uvec3 uint3\n"
|
||||
"#define uvec4 uint4\n"
|
||||
"#define ivec2 int2\n"
|
||||
"#define ivec3 int3\n"
|
||||
"#define ivec4 int4\n"
|
||||
"#define mat4 float4x4\n"
|
||||
@ -52,6 +53,30 @@ static const char * const vulkan_glsl_preamble_vs =
|
||||
"precision highp float;\n"
|
||||
"\n";
|
||||
|
||||
static const char * const hlsl_preamble_gs =
|
||||
"#define vec2 float2\n"
|
||||
"#define vec3 float3\n"
|
||||
"#define vec4 float4\n"
|
||||
"#define ivec2 int2\n"
|
||||
"#define ivec4 int4\n"
|
||||
"#define mat2 float2x2\n"
|
||||
"#define mat4 float4x4\n"
|
||||
"#define mat3x4 float4x3\n" // note how the conventions are backwards
|
||||
"#define splat3(x) vec3(x, x, x)\n"
|
||||
"#define lowp\n"
|
||||
"#define mediump\n"
|
||||
"#define highp\n"
|
||||
"\n";
|
||||
|
||||
static const char * const vulkan_glsl_preamble_gs =
|
||||
"#version 450\n"
|
||||
"#extension GL_ARB_separate_shader_objects : enable\n"
|
||||
"#extension GL_ARB_shading_language_420pack : enable\n"
|
||||
"#define mul(x, y) ((x) * (y))\n"
|
||||
"#define splat3(x) vec3(x)\n"
|
||||
"precision highp float;\n"
|
||||
"\n";
|
||||
|
||||
static const char * const hlsl_preamble_vs =
|
||||
"#define vec2 float2\n"
|
||||
"#define vec3 float3\n"
|
||||
@ -67,15 +92,17 @@ static const char * const hlsl_preamble_vs =
|
||||
"#define highp\n"
|
||||
"\n";
|
||||
|
||||
static const char * const semanticNames[8] = {
|
||||
static const char * const semanticNames[] = {
|
||||
"POSITION",
|
||||
"COLOR0",
|
||||
"COLOR1",
|
||||
"TEXCOORD0",
|
||||
"TEXCOORD1",
|
||||
"NORMAL",
|
||||
"TANGENT",
|
||||
"BINORMAL",
|
||||
};
|
||||
static_assert(ARRAY_SIZE(semanticNames) == Draw::SEM_MAX, "Missing semantic in semanticNames");
|
||||
|
||||
// Unsafe. But doesn't matter, we'll use big buffers for shader gen.
|
||||
ShaderWriter & ShaderWriter::F(const char *format, ...) {
|
||||
@ -96,6 +123,9 @@ void ShaderWriter::Preamble(const char **gl_extensions, size_t num_gl_extensions
|
||||
case ShaderStage::Fragment:
|
||||
W(vulkan_glsl_preamble_fs);
|
||||
break;
|
||||
case ShaderStage::Geometry:
|
||||
W(vulkan_glsl_preamble_gs);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -114,6 +144,9 @@ void ShaderWriter::Preamble(const char **gl_extensions, size_t num_gl_extensions
|
||||
W(hlsl_d3d11_preamble_fs);
|
||||
}
|
||||
break;
|
||||
case ShaderStage::Geometry:
|
||||
W(hlsl_preamble_gs);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -144,6 +177,11 @@ void ShaderWriter::Preamble(const char **gl_extensions, size_t num_gl_extensions
|
||||
}
|
||||
C("#define gl_VertexIndex gl_VertexID\n");
|
||||
break;
|
||||
case ShaderStage::Geometry:
|
||||
if (lang_.gles) {
|
||||
C("precision highp float;\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -171,7 +209,7 @@ void ShaderWriter::BeginVSMain(Slice<InputDef> inputs, Slice<UniformDef> uniform
|
||||
F(" vec4 pos : %s;\n", lang_.shaderLanguage == HLSL_D3D11 ? "SV_Position" : "POSITION");
|
||||
C("};\n");
|
||||
|
||||
C("VS_OUTPUT main( "); // 2 spaces for the D3D9 rewind
|
||||
C("VS_OUTPUT main( "); // 2 spaces for the rewind
|
||||
if (lang_.shaderLanguage == HLSL_D3D11) {
|
||||
C("uint gl_VertexIndex : SV_VertexID, ");
|
||||
}
|
||||
@ -307,12 +345,52 @@ void ShaderWriter::BeginFSMain(Slice<UniformDef> uniforms, Slice<VaryingDef> var
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderWriter::BeginGSMain(Slice<VaryingDef> varyings, Slice<VaryingDef> outVaryings) {
|
||||
_assert_(this->stage_ == ShaderStage::Geometry);
|
||||
switch (lang_.shaderLanguage) {
|
||||
case HLSL_D3D11:
|
||||
// Untested, but should work.
|
||||
C("\nstruct GS_OUTPUT {\n");
|
||||
for (auto &varying : outVaryings) {
|
||||
F(" %s %s : %s;\n", varying.type, varying.name, semanticNames[varying.semantic]);
|
||||
}
|
||||
F(" vec4 pos : %s;\n", lang_.shaderLanguage == HLSL_D3D11 ? "SV_Position" : "POSITION");
|
||||
C("};\n");
|
||||
C("#define EmitVertex() emit.Append(gsout)\n");
|
||||
|
||||
C("void main(");
|
||||
for (auto &varying : varyings) {
|
||||
F(" in %s %s : %s, ", varying.type, varying.name, semanticNames[varying.semantic]);
|
||||
}
|
||||
C("inout TriangleStream<GS_OUTPUT> emit) {\n");
|
||||
C(" GS_OUTPUT gsout;\n");
|
||||
break;
|
||||
case GLSL_VULKAN:
|
||||
for (auto &varying : varyings) {
|
||||
F("layout(location = %d) %s in %s %s[]; // %s\n", varying.index, varying.precision ? varying.precision : "", varying.type, varying.name, semanticNames[varying.semantic]);
|
||||
}
|
||||
for (auto &varying : outVaryings) {
|
||||
F("layout(location = %d) %s out %s %s; // %s\n", varying.index, varying.precision ? varying.precision : "", varying.type, varying.name, semanticNames[varying.semantic]);
|
||||
}
|
||||
C("\nvoid main() {\n");
|
||||
break;
|
||||
case GLSL_3xx:
|
||||
C("\nvoid main() {\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderWriter::EndVSMain(Slice<VaryingDef> varyings) {
|
||||
_assert_(this->stage_ == ShaderStage::Vertex);
|
||||
switch (lang_.shaderLanguage) {
|
||||
case HLSL_D3D11:
|
||||
case HLSL_D3D9:
|
||||
C(" VS_OUTPUT vs_out;\n");
|
||||
if (strlen(lang_.viewportYSign)) {
|
||||
F(" gl_Position.y *= %s1.0;\n", lang_.viewportYSign);
|
||||
}
|
||||
C(" vs_out.pos = gl_Position;\n");
|
||||
for (auto &varying : varyings) {
|
||||
F(" vs_out.%s = %s;\n", varying.name, varying.name);
|
||||
@ -345,6 +423,11 @@ void ShaderWriter::EndFSMain(const char *vec4_color_variable, FSFlags flags) {
|
||||
C("}\n");
|
||||
}
|
||||
|
||||
void ShaderWriter::EndGSMain() {
|
||||
_assert_(this->stage_ == ShaderStage::Geometry);
|
||||
C("}\n");
|
||||
}
|
||||
|
||||
void ShaderWriter::HighPrecisionFloat() {
|
||||
if ((ShaderLanguageIsOpenGL(lang_.shaderLanguage) && lang_.gles) || lang_.shaderLanguage == GLSL_VULKAN) {
|
||||
C("precision highp float;\n");
|
||||
@ -420,3 +503,53 @@ ShaderWriter &ShaderWriter::SampleTexture2D(const char *sampName, const char *uv
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ShaderWriter &ShaderWriter::SampleTexture2DOffset(const char *sampName, const char *uv, int offX, int offY) {
|
||||
switch (lang_.shaderLanguage) {
|
||||
case HLSL_D3D11:
|
||||
F("%s.Sample(%sSamp, %s, int2(%d, %d))", sampName, sampName, uv, offX, offY);
|
||||
break;
|
||||
case HLSL_D3D9:
|
||||
// Not supported, we do a normal sample here to not crash or something
|
||||
F("tex2D(%s, %s)", sampName, uv);
|
||||
break;
|
||||
default:
|
||||
// Note: we ignore the sampler. make sure you bound samplers to the textures correctly.
|
||||
F("%sOffset(%s, %s, ivec2(%d, %d))", lang_.texture, sampName, uv, offX, offY);
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ShaderWriter &ShaderWriter::LoadTexture2D(const char *sampName, const char *uv, int level) {
|
||||
switch (lang_.shaderLanguage) {
|
||||
case HLSL_D3D11:
|
||||
F("%s.Load(ivec3(%s, %d))", sampName, uv, level);
|
||||
break;
|
||||
case HLSL_D3D9:
|
||||
// Not supported, we return a bad value
|
||||
C("float4(1.0, 0.0, 1.0, 1.0)");
|
||||
break;
|
||||
default:
|
||||
// Note: we ignore the sampler. make sure you bound samplers to the textures correctly.
|
||||
F("texelFetch(%s, %s, %d)", sampName, uv, level);
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ShaderWriter &ShaderWriter::GetTextureSize(const char *szVariable, const char *texName) {
|
||||
switch (lang_.shaderLanguage) {
|
||||
case HLSL_D3D11:
|
||||
F(" float2 %s; %s.GetDimensions(%s.x, %s.y);", szVariable, texName, szVariable, szVariable);
|
||||
break;
|
||||
case HLSL_D3D9:
|
||||
F(" float2 %s; %s.GetDimensions(%s.x, %s.y);", szVariable, texName, szVariable, szVariable);
|
||||
break;
|
||||
default:
|
||||
// Note: we ignore the sampler. make sure you bound samplers to the textures correctly.
|
||||
F("vec2 %s = textureSize(%s, 0);", szVariable, texName);
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
@ -22,12 +22,6 @@ struct InputDef {
|
||||
int semantic;
|
||||
};
|
||||
|
||||
struct UniformDef {
|
||||
const char *type;
|
||||
const char *name;
|
||||
int index;
|
||||
};
|
||||
|
||||
struct VaryingDef {
|
||||
const char *type;
|
||||
const char *name;
|
||||
@ -43,7 +37,7 @@ enum FSFlags {
|
||||
|
||||
class ShaderWriter {
|
||||
public:
|
||||
ShaderWriter(char *buffer, const ShaderLanguageDesc &lang, ShaderStage stage, const char **gl_extensions, size_t num_gl_extensions) : p_(buffer), lang_(lang), stage_(stage) {
|
||||
ShaderWriter(char *buffer, const ShaderLanguageDesc &lang, ShaderStage stage, const char **gl_extensions = nullptr, size_t num_gl_extensions = 0) : p_(buffer), lang_(lang), stage_(stage) {
|
||||
Preamble(gl_extensions, num_gl_extensions);
|
||||
}
|
||||
ShaderWriter(const ShaderWriter &) = delete;
|
||||
@ -83,16 +77,24 @@ public:
|
||||
|
||||
void ConstFloat(const char *name, float value);
|
||||
|
||||
ShaderWriter &SampleTexture2D(const char *sampName, const char *uv);
|
||||
ShaderWriter &SampleTexture2D(const char *texName, const char *uv);
|
||||
ShaderWriter &SampleTexture2DOffset(const char *texName, const char *uv, int offX, int offY);
|
||||
ShaderWriter &LoadTexture2D(const char *texName, const char *integer_uv, int level);
|
||||
ShaderWriter &GetTextureSize(const char *szVariable, const char *texName);
|
||||
|
||||
// Simple shaders with no special tricks.
|
||||
void BeginVSMain(Slice<InputDef> inputs, Slice<UniformDef> uniforms, Slice<VaryingDef> varyings);
|
||||
void BeginFSMain(Slice<UniformDef> uniforms, Slice<VaryingDef> varyings, FSFlags flags);
|
||||
void BeginGSMain(Slice<VaryingDef> varyings, Slice<VaryingDef> outVaryings);
|
||||
|
||||
// For simple shaders that output a single color, we can deal with this generically.
|
||||
void EndVSMain(Slice<VaryingDef> varyings);
|
||||
void EndFSMain(const char *vec4_color_variable, FSFlags flags);
|
||||
void EndGSMain();
|
||||
|
||||
const ShaderLanguageDesc &Lang() const {
|
||||
return lang_;
|
||||
}
|
||||
|
||||
void Rewind(size_t offset) {
|
||||
p_ -= offset;
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
void VulkanBarrier::Flush(VkCommandBuffer cmd) {
|
||||
if (!imageBarriers_.empty()) {
|
||||
vkCmdPipelineBarrier(cmd, srcStageMask_, dstStageMask_, 0, 0, nullptr, 0, nullptr, (uint32_t)imageBarriers_.size(), imageBarriers_.data());
|
||||
vkCmdPipelineBarrier(cmd, srcStageMask_, dstStageMask_, dependencyFlags_, 0, nullptr, 0, nullptr, (uint32_t)imageBarriers_.size(), imageBarriers_.data());
|
||||
}
|
||||
imageBarriers_.clear();
|
||||
srcStageMask_ = 0;
|
||||
|
@ -19,8 +19,11 @@ public:
|
||||
VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask,
|
||||
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask
|
||||
) {
|
||||
_dbg_assert_(image != VK_NULL_HANDLE);
|
||||
|
||||
srcStageMask_ |= srcStageMask;
|
||||
dstStageMask_ |= dstStageMask;
|
||||
dependencyFlags_ |= VK_DEPENDENCY_BY_REGION_BIT;
|
||||
|
||||
VkImageMemoryBarrier imageBarrier;
|
||||
imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
@ -45,6 +48,8 @@ public:
|
||||
void TransitionImageAuto(
|
||||
VkImage image, int baseMip, int numMipLevels, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout
|
||||
) {
|
||||
_dbg_assert_(image != VK_NULL_HANDLE);
|
||||
|
||||
VkAccessFlags srcAccessMask = 0;
|
||||
VkAccessFlags dstAccessMask = 0;
|
||||
switch (oldImageLayout) {
|
||||
@ -112,4 +117,5 @@ private:
|
||||
VkPipelineStageFlags srcStageMask_ = 0;
|
||||
VkPipelineStageFlags dstStageMask_ = 0;
|
||||
std::vector<VkImageMemoryBarrier> imageBarriers_;
|
||||
VkDependencyFlags dependencyFlags_ = 0;
|
||||
};
|
||||
|
@ -600,8 +600,7 @@ void VulkanContext::ChooseDevice(int physical_device) {
|
||||
deviceFeatures_.enabled.samplerAnisotropy = deviceFeatures_.available.samplerAnisotropy;
|
||||
deviceFeatures_.enabled.shaderClipDistance = deviceFeatures_.available.shaderClipDistance;
|
||||
deviceFeatures_.enabled.shaderCullDistance = deviceFeatures_.available.shaderCullDistance;
|
||||
// For easy wireframe mode, someday.
|
||||
deviceFeatures_.enabled.fillModeNonSolid = deviceFeatures_.available.fillModeNonSolid;
|
||||
deviceFeatures_.enabled.geometryShader = deviceFeatures_.available.geometryShader;
|
||||
|
||||
GetDeviceLayerExtensionList(nullptr, device_extension_properties_);
|
||||
|
||||
@ -648,6 +647,7 @@ VkResult VulkanContext::CreateDevice() {
|
||||
}
|
||||
_dbg_assert_(found);
|
||||
|
||||
// TODO: A lot of these are on by default in later Vulkan versions, should check for that, technically.
|
||||
extensionsLookup_.KHR_maintenance1 = EnableDeviceExtension(VK_KHR_MAINTENANCE1_EXTENSION_NAME);
|
||||
extensionsLookup_.KHR_maintenance2 = EnableDeviceExtension(VK_KHR_MAINTENANCE2_EXTENSION_NAME);
|
||||
extensionsLookup_.KHR_maintenance3 = EnableDeviceExtension(VK_KHR_MAINTENANCE3_EXTENSION_NAME);
|
||||
@ -666,7 +666,10 @@ VkResult VulkanContext::CreateDevice() {
|
||||
extensionsLookup_.KHR_create_renderpass2 = true;
|
||||
extensionsLookup_.KHR_depth_stencil_resolve = EnableDeviceExtension(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
extensionsLookup_.EXT_shader_stencil_export = EnableDeviceExtension(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME);
|
||||
extensionsLookup_.EXT_fragment_shader_interlock = EnableDeviceExtension(VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME);
|
||||
extensionsLookup_.ARM_rasterization_order_attachment_access = EnableDeviceExtension(VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME);
|
||||
|
||||
VkDeviceCreateInfo device_info{ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
|
||||
device_info.queueCreateInfoCount = 1;
|
||||
@ -684,7 +687,7 @@ VkResult VulkanContext::CreateDevice() {
|
||||
} else {
|
||||
VulkanLoadDeviceFunctions(device_, extensionsLookup_);
|
||||
}
|
||||
INFO_LOG(G3D, "Device created.\n");
|
||||
INFO_LOG(G3D, "Vulkan Device created");
|
||||
VulkanSetAvailable(true);
|
||||
|
||||
VmaAllocatorCreateInfo allocatorInfo = {};
|
||||
@ -1008,12 +1011,21 @@ bool VulkanContext::InitSwapchain() {
|
||||
res = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_devices_[physical_device_], surface_, &presentModeCount, presentModes);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
|
||||
VkExtent2D currentExtent { surfCapabilities_.currentExtent };
|
||||
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSurfaceCapabilitiesKHR.html
|
||||
// currentExtent is the current width and height of the surface, or the special value (0xFFFFFFFF, 0xFFFFFFFF) indicating that the surface size will be determined by the extent of a swapchain targeting the surface.
|
||||
if (currentExtent.width == 0xFFFFFFFFu || currentExtent.height == 0xFFFFFFFFu) {
|
||||
_dbg_assert_((bool)cbGetDrawSize_)
|
||||
if (cbGetDrawSize_) {
|
||||
currentExtent = cbGetDrawSize_();
|
||||
}
|
||||
}
|
||||
|
||||
swapChainExtent_.width = clamp(surfCapabilities_.currentExtent.width, surfCapabilities_.minImageExtent.width, surfCapabilities_.maxImageExtent.width);
|
||||
swapChainExtent_.height = clamp(surfCapabilities_.currentExtent.height, surfCapabilities_.minImageExtent.height, surfCapabilities_.maxImageExtent.height);
|
||||
swapChainExtent_.width = clamp(currentExtent.width, surfCapabilities_.minImageExtent.width, surfCapabilities_.maxImageExtent.width);
|
||||
swapChainExtent_.height = clamp(currentExtent.height, surfCapabilities_.minImageExtent.height, surfCapabilities_.maxImageExtent.height);
|
||||
|
||||
INFO_LOG(G3D, "surfCapabilities_.current: %dx%d min: %dx%d max: %dx%d computed: %dx%d",
|
||||
surfCapabilities_.currentExtent.width, surfCapabilities_.currentExtent.height,
|
||||
currentExtent.width, currentExtent.height,
|
||||
surfCapabilities_.minImageExtent.width, surfCapabilities_.minImageExtent.height,
|
||||
surfCapabilities_.maxImageExtent.width, surfCapabilities_.maxImageExtent.height,
|
||||
swapChainExtent_.width, swapChainExtent_.height);
|
||||
@ -1158,6 +1170,10 @@ bool VulkanContext::InitSwapchain() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanContext::SetCbGetDrawSize(std::function<VkExtent2D()> cb) {
|
||||
cbGetDrawSize_ = cb;
|
||||
}
|
||||
|
||||
VkFence VulkanContext::CreateFence(bool presignalled) {
|
||||
VkFence fence;
|
||||
VkFenceCreateInfo fenceInfo{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
|
||||
@ -1195,12 +1211,15 @@ void VulkanContext::DestroyDevice() {
|
||||
device_ = nullptr;
|
||||
}
|
||||
|
||||
bool VulkanContext::CreateShaderModule(const std::vector<uint32_t> &spirv, VkShaderModule *shaderModule) {
|
||||
bool VulkanContext::CreateShaderModule(const std::vector<uint32_t> &spirv, VkShaderModule *shaderModule, const char *tag) {
|
||||
VkShaderModuleCreateInfo sm{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
|
||||
sm.pCode = spirv.data();
|
||||
sm.codeSize = spirv.size() * sizeof(uint32_t);
|
||||
sm.flags = 0;
|
||||
VkResult result = vkCreateShaderModule(device_, &sm, nullptr, shaderModule);
|
||||
if (tag) {
|
||||
SetDebugName(*shaderModule, VK_OBJECT_TYPE_SHADER_MODULE, tag);
|
||||
}
|
||||
if (result != VK_SUCCESS) {
|
||||
return false;
|
||||
} else {
|
||||
@ -1259,10 +1278,10 @@ bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *sourceCode,
|
||||
|
||||
glslang::TProgram program;
|
||||
const char *shaderStrings[1];
|
||||
TBuiltInResource Resources;
|
||||
TBuiltInResource Resources{};
|
||||
init_resources(Resources);
|
||||
|
||||
int defaultVersion;
|
||||
int defaultVersion = 0;
|
||||
EShMessages messages;
|
||||
EProfile profile;
|
||||
|
||||
@ -1557,6 +1576,7 @@ const char *VulkanFormatToString(VkFormat format) {
|
||||
switch (format) {
|
||||
case VK_FORMAT_A1R5G5B5_UNORM_PACK16: return "A1R5G5B5_UNORM_PACK16";
|
||||
case VK_FORMAT_A2B10G10R10_UNORM_PACK32: return "A2B10G10R10_UNORM_PACK32";
|
||||
case VK_FORMAT_A2R10G10B10_UNORM_PACK32: return "A2R10G10B10_UNORM_PACK32";
|
||||
case VK_FORMAT_A8B8G8R8_SNORM_PACK32: return "A8B8G8R8_SNORM_PACK32";
|
||||
case VK_FORMAT_A8B8G8R8_SRGB_PACK32: return "A8B8G8R8_SRGB_PACK32";
|
||||
case VK_FORMAT_A8B8G8R8_UNORM_PACK32: return "A8B8G8R8_UNORM_PACK32";
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
|
||||
#include "Common/Log.h"
|
||||
#include "Common/GPU/Vulkan/VulkanLoader.h"
|
||||
@ -190,6 +191,7 @@ public:
|
||||
VkResult ReinitSurface();
|
||||
|
||||
bool InitSwapchain();
|
||||
void SetCbGetDrawSize(std::function<VkExtent2D()>);
|
||||
|
||||
void DestroySwapchain();
|
||||
void DestroySurface();
|
||||
@ -201,7 +203,7 @@ public:
|
||||
|
||||
// Utility functions for shorter code
|
||||
VkFence CreateFence(bool presignalled);
|
||||
bool CreateShaderModule(const std::vector<uint32_t> &spirv, VkShaderModule *shaderModule);
|
||||
bool CreateShaderModule(const std::vector<uint32_t> &spirv, VkShaderModule *shaderModule, const char *tag);
|
||||
|
||||
int GetBackbufferWidth() { return (int)swapChainExtent_.width; }
|
||||
int GetBackbufferHeight() { return (int)swapChainExtent_.height; }
|
||||
@ -363,6 +365,7 @@ private:
|
||||
// that we really don't want in everything that uses VulkanContext.
|
||||
void *winsysData1_;
|
||||
void *winsysData2_;
|
||||
std::function<VkExtent2D()> cbGetDrawSize_;
|
||||
|
||||
VkInstance instance_ = VK_NULL_HANDLE;
|
||||
VkDevice device_ = VK_NULL_HANDLE;
|
||||
|
@ -86,7 +86,6 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback(
|
||||
} else {
|
||||
WARN_LOG(G3D, "VKDEBUG: %s", msg.c_str());
|
||||
}
|
||||
|
||||
// false indicates that layer should not bail-out of an
|
||||
// API call that had validation failures. This may mean that the
|
||||
// app dies inside the driver due to invalid parameter(s).
|
||||
|
224
Common/GPU/Vulkan/VulkanFrameData.cpp
Normal file
224
Common/GPU/Vulkan/VulkanFrameData.cpp
Normal file
@ -0,0 +1,224 @@
|
||||
#include <mutex>
|
||||
|
||||
#include "VulkanFrameData.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/StringUtils.h"
|
||||
|
||||
void FrameData::Init(VulkanContext *vulkan, int index) {
|
||||
this->index = index;
|
||||
VkDevice device = vulkan->GetDevice();
|
||||
|
||||
VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
|
||||
cmd_pool_info.queueFamilyIndex = vulkan->GetGraphicsQueueFamilyIndex();
|
||||
cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
||||
VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &cmdPoolInit);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &cmdPoolMain);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
|
||||
VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
|
||||
cmd_alloc.commandPool = cmdPoolInit;
|
||||
cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
cmd_alloc.commandBufferCount = 1;
|
||||
res = vkAllocateCommandBuffers(device, &cmd_alloc, &initCmd);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
cmd_alloc.commandPool = cmdPoolMain;
|
||||
res = vkAllocateCommandBuffers(device, &cmd_alloc, &mainCmd);
|
||||
res = vkAllocateCommandBuffers(device, &cmd_alloc, &presentCmd);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
|
||||
vulkan->SetDebugName(initCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("initCmd%d", index).c_str());
|
||||
vulkan->SetDebugName(mainCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("mainCmd%d", index).c_str());
|
||||
vulkan->SetDebugName(presentCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("presentCmd%d", index).c_str());
|
||||
|
||||
// Creating the frame fence with true so they can be instantly waited on the first frame
|
||||
fence = vulkan->CreateFence(true);
|
||||
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;
|
||||
res = vkCreateQueryPool(device, &query_ci, nullptr, &profile.queryPool);
|
||||
}
|
||||
|
||||
void FrameData::Destroy(VulkanContext *vulkan) {
|
||||
VkDevice device = vulkan->GetDevice();
|
||||
vkDestroyCommandPool(device, cmdPoolInit, nullptr);
|
||||
vkDestroyCommandPool(device, cmdPoolMain, nullptr);
|
||||
vkDestroyFence(device, fence, nullptr);
|
||||
vkDestroyFence(device, readbackFence, nullptr);
|
||||
vkDestroyQueryPool(device, profile.queryPool, nullptr);
|
||||
}
|
||||
|
||||
void FrameData::AcquireNextImage(VulkanContext *vulkan, FrameDataShared &shared) {
|
||||
_dbg_assert_(!hasAcquired);
|
||||
|
||||
// Get the index of the next available swapchain image, and a semaphore to block command buffer execution on.
|
||||
VkResult res = vkAcquireNextImageKHR(vulkan->GetDevice(), vulkan->GetSwapchain(), UINT64_MAX, shared.acquireSemaphore, (VkFence)VK_NULL_HANDLE, &curSwapchainImage);
|
||||
switch (res) {
|
||||
case VK_SUCCESS:
|
||||
hasAcquired = true;
|
||||
break;
|
||||
case VK_SUBOPTIMAL_KHR:
|
||||
hasAcquired = true;
|
||||
// Hopefully the resize will happen shortly. Ignore - one frame might look bad or something.
|
||||
WARN_LOG(G3D, "VK_SUBOPTIMAL_KHR returned - ignoring");
|
||||
break;
|
||||
case VK_ERROR_OUT_OF_DATE_KHR:
|
||||
// We do not set hasAcquired here!
|
||||
WARN_LOG(G3D, "VK_ERROR_OUT_OF_DATE_KHR returned from AcquireNextImage - processing the frame, but not presenting");
|
||||
skipSwap = true;
|
||||
break;
|
||||
default:
|
||||
// Weird, shouldn't get any other values. Maybe lost device?
|
||||
_assert_msg_(false, "vkAcquireNextImageKHR failed! result=%s", VulkanResultToString(res));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VkResult FrameData::QueuePresent(VulkanContext *vulkan, FrameDataShared &shared) {
|
||||
_dbg_assert_(hasAcquired);
|
||||
hasAcquired = false;
|
||||
_dbg_assert_(!skipSwap);
|
||||
|
||||
VkSwapchainKHR swapchain = vulkan->GetSwapchain();
|
||||
VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
|
||||
present.swapchainCount = 1;
|
||||
present.pSwapchains = &swapchain;
|
||||
present.pImageIndices = &curSwapchainImage;
|
||||
present.pWaitSemaphores = &shared.renderingCompleteSemaphore;
|
||||
present.waitSemaphoreCount = 1;
|
||||
|
||||
return vkQueuePresentKHR(vulkan->GetGraphicsQueue(), &present);
|
||||
}
|
||||
|
||||
VkCommandBuffer FrameData::GetInitCmd(VulkanContext *vulkan) {
|
||||
if (!hasInitCommands) {
|
||||
VkCommandBufferBeginInfo begin = {
|
||||
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
nullptr,
|
||||
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
|
||||
};
|
||||
vkResetCommandPool(vulkan->GetDevice(), cmdPoolInit, 0);
|
||||
VkResult res = vkBeginCommandBuffer(initCmd, &begin);
|
||||
if (res != VK_SUCCESS) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// Good spot to reset the query pool.
|
||||
vkCmdResetQueryPool(initCmd, profile.queryPool, 0, MAX_TIMESTAMP_QUERIES);
|
||||
vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, profile.queryPool, 0);
|
||||
|
||||
hasInitCommands = true;
|
||||
}
|
||||
return initCmd;
|
||||
}
|
||||
|
||||
void FrameData::SubmitPending(VulkanContext *vulkan, FrameSubmitType type, FrameDataShared &sharedData) {
|
||||
VkCommandBuffer cmdBufs[2];
|
||||
int numCmdBufs = 0;
|
||||
|
||||
VkFence fenceToTrigger = VK_NULL_HANDLE;
|
||||
|
||||
if (hasInitCommands) {
|
||||
if (profilingEnabled_) {
|
||||
// Pre-allocated query ID 1 - end of init cmdbuf.
|
||||
vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, profile.queryPool, 1);
|
||||
}
|
||||
|
||||
VkResult res = vkEndCommandBuffer(initCmd);
|
||||
cmdBufs[numCmdBufs++] = initCmd;
|
||||
|
||||
_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (init)! result=%s", VulkanResultToString(res));
|
||||
hasInitCommands = false;
|
||||
}
|
||||
|
||||
if ((hasMainCommands || hasPresentCommands) && type == FrameSubmitType::Sync) {
|
||||
fenceToTrigger = readbackFence;
|
||||
}
|
||||
|
||||
if (hasMainCommands) {
|
||||
VkResult res = vkEndCommandBuffer(mainCmd);
|
||||
_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (main)! result=%s", VulkanResultToString(res));
|
||||
|
||||
cmdBufs[numCmdBufs++] = mainCmd;
|
||||
hasMainCommands = false;
|
||||
}
|
||||
|
||||
if (hasPresentCommands && type != FrameSubmitType::Pending) {
|
||||
VkResult res = vkEndCommandBuffer(presentCmd);
|
||||
_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (present)! result=%s", VulkanResultToString(res));
|
||||
|
||||
cmdBufs[numCmdBufs++] = presentCmd;
|
||||
hasPresentCommands = false;
|
||||
|
||||
if (type == FrameSubmitType::Present) {
|
||||
fenceToTrigger = fence;
|
||||
}
|
||||
}
|
||||
|
||||
if (!numCmdBufs && fenceToTrigger == VK_NULL_HANDLE) {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO };
|
||||
VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
|
||||
if (type == FrameSubmitType::Present && !skipSwap) {
|
||||
_dbg_assert_(hasAcquired);
|
||||
submit_info.waitSemaphoreCount = 1;
|
||||
submit_info.pWaitSemaphores = &sharedData.acquireSemaphore;
|
||||
submit_info.pWaitDstStageMask = waitStage;
|
||||
}
|
||||
submit_info.commandBufferCount = (uint32_t)numCmdBufs;
|
||||
submit_info.pCommandBuffers = cmdBufs;
|
||||
if (type == FrameSubmitType::Present && !skipSwap) {
|
||||
submit_info.signalSemaphoreCount = 1;
|
||||
submit_info.pSignalSemaphores = &sharedData.renderingCompleteSemaphore;
|
||||
}
|
||||
|
||||
VkResult res;
|
||||
if (fenceToTrigger == fence) {
|
||||
// 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);
|
||||
std::lock_guard<std::mutex> lock(fenceMutex);
|
||||
readyForFence = true;
|
||||
fenceCondVar.notify_one();
|
||||
} else {
|
||||
res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger);
|
||||
}
|
||||
|
||||
if (res == VK_ERROR_DEVICE_LOST) {
|
||||
_assert_msg_(false, "Lost the Vulkan device in vkQueueSubmit! If this happens again, switch Graphics Backend away from Vulkan");
|
||||
} else {
|
||||
_assert_msg_(res == VK_SUCCESS, "vkQueueSubmit failed (main)! result=%s", VulkanResultToString(res));
|
||||
}
|
||||
|
||||
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, &readbackFence, true, UINT64_MAX);
|
||||
vkResetFences(vulkan->GetDevice(), 1, &readbackFence);
|
||||
syncDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void FrameDataShared::Destroy(VulkanContext *vulkan) {
|
||||
VkDevice device = vulkan->GetDevice();
|
||||
vkDestroySemaphore(device, acquireSemaphore, nullptr);
|
||||
vkDestroySemaphore(device, renderingCompleteSemaphore, nullptr);
|
||||
}
|
93
Common/GPU/Vulkan/VulkanFrameData.h
Normal file
93
Common/GPU/Vulkan/VulkanFrameData.h
Normal file
@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "Common/GPU/Vulkan/VulkanContext.h"
|
||||
|
||||
enum {
|
||||
MAX_TIMESTAMP_QUERIES = 128,
|
||||
};
|
||||
|
||||
enum class VKRRunType {
|
||||
PRESENT,
|
||||
SYNC,
|
||||
EXIT,
|
||||
};
|
||||
|
||||
struct QueueProfileContext {
|
||||
VkQueryPool queryPool;
|
||||
std::vector<std::string> timestampDescriptions;
|
||||
std::string profileSummary;
|
||||
double cpuStartTime;
|
||||
double cpuEndTime;
|
||||
};
|
||||
|
||||
struct FrameDataShared {
|
||||
// Permanent objects
|
||||
VkSemaphore acquireSemaphore = VK_NULL_HANDLE;
|
||||
VkSemaphore renderingCompleteSemaphore = VK_NULL_HANDLE;
|
||||
|
||||
void Init(VulkanContext *vulkan);
|
||||
void Destroy(VulkanContext *vulkan);
|
||||
};
|
||||
|
||||
enum class FrameSubmitType {
|
||||
Pending,
|
||||
Sync,
|
||||
Present,
|
||||
};
|
||||
|
||||
// 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;
|
||||
|
||||
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
|
||||
VkCommandPool cmdPoolMain = VK_NULL_HANDLE; // Written to from render thread, which also submits
|
||||
|
||||
VkCommandBuffer initCmd = VK_NULL_HANDLE;
|
||||
VkCommandBuffer mainCmd = VK_NULL_HANDLE;
|
||||
VkCommandBuffer presentCmd = VK_NULL_HANDLE;
|
||||
|
||||
bool hasInitCommands = false;
|
||||
bool hasMainCommands = false;
|
||||
bool hasPresentCommands = false;
|
||||
|
||||
bool hasFencePending = false;
|
||||
bool hasAcquired = false;
|
||||
|
||||
bool syncDone = false;
|
||||
|
||||
// Swapchain.
|
||||
uint32_t curSwapchainImage = -1;
|
||||
|
||||
// Profiling.
|
||||
QueueProfileContext profile;
|
||||
bool profilingEnabled_ = false;
|
||||
|
||||
void Init(VulkanContext *vulkan, int index);
|
||||
void Destroy(VulkanContext *vulkan);
|
||||
|
||||
void AcquireNextImage(VulkanContext *vulkan, FrameDataShared &shared);
|
||||
VkResult QueuePresent(VulkanContext *vulkan, FrameDataShared &shared);
|
||||
|
||||
// Generally called from the main thread, unlike most of the rest.
|
||||
VkCommandBuffer GetInitCmd(VulkanContext *vulkan);
|
||||
|
||||
// This will only submit if we are actually recording init commands.
|
||||
void SubmitPending(VulkanContext *vulkan, FrameSubmitType type, FrameDataShared &shared);
|
||||
|
||||
private:
|
||||
// Metadata for logging etc
|
||||
int index;
|
||||
};
|
@ -36,6 +36,10 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int depth, i
|
||||
ERROR_LOG(G3D, "Can't create a zero-size VulkanTexture");
|
||||
return false;
|
||||
}
|
||||
if (w > 4096 || h > 4096) {
|
||||
ERROR_LOG(G3D, "Can't create a texture this large");
|
||||
return false;
|
||||
}
|
||||
|
||||
Wipe();
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "Common/GPU/Vulkan/VulkanLoader.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/System/System.h"
|
||||
#include "Common/VR/PPSSPPVR.h"
|
||||
|
||||
#if !PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(SWITCH)
|
||||
#include <dlfcn.h>
|
||||
@ -65,8 +66,10 @@ PFN_vkBindImageMemory vkBindImageMemory;
|
||||
PFN_vkBindImageMemory2 vkBindImageMemory2;
|
||||
PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
|
||||
PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
|
||||
PFN_vkGetDeviceBufferMemoryRequirements vkGetDeviceBufferMemoryRequirements;
|
||||
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
|
||||
PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
|
||||
PFN_vkGetDeviceImageMemoryRequirements vkGetDeviceImageMemoryRequirements;
|
||||
PFN_vkCreateFence vkCreateFence;
|
||||
PFN_vkDestroyFence vkDestroyFence;
|
||||
PFN_vkGetFenceStatus vkGetFenceStatus;
|
||||
@ -306,10 +309,10 @@ void VulkanSetAvailable(bool available) {
|
||||
|
||||
bool VulkanMayBeAvailable() {
|
||||
|
||||
#ifdef OPENXR
|
||||
//unsupported at the moment
|
||||
return false;
|
||||
#endif
|
||||
//unsupported in VR at the moment
|
||||
if (IsVRBuild()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_vulkanAvailabilityChecked) {
|
||||
return g_vulkanMayBeAvailable;
|
||||
@ -603,8 +606,10 @@ void VulkanLoadDeviceFunctions(VkDevice device, const VulkanExtensions &enabledE
|
||||
LOAD_DEVICE_FUNC(device, vkBindImageMemory2);
|
||||
LOAD_DEVICE_FUNC(device, vkGetBufferMemoryRequirements);
|
||||
LOAD_DEVICE_FUNC(device, vkGetBufferMemoryRequirements2);
|
||||
LOAD_DEVICE_FUNC(device, vkGetDeviceBufferMemoryRequirements);
|
||||
LOAD_DEVICE_FUNC(device, vkGetImageMemoryRequirements);
|
||||
LOAD_DEVICE_FUNC(device, vkGetImageMemoryRequirements2);
|
||||
LOAD_DEVICE_FUNC(device, vkGetDeviceImageMemoryRequirements);
|
||||
LOAD_DEVICE_FUNC(device, vkCreateFence);
|
||||
LOAD_DEVICE_FUNC(device, vkDestroyFence);
|
||||
LOAD_DEVICE_FUNC(device, vkResetFences);
|
||||
|
@ -71,8 +71,10 @@ extern PFN_vkBindImageMemory vkBindImageMemory;
|
||||
extern PFN_vkBindImageMemory2 vkBindImageMemory2;
|
||||
extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
|
||||
extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
|
||||
extern PFN_vkGetDeviceBufferMemoryRequirements vkGetDeviceBufferMemoryRequirements;
|
||||
extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
|
||||
extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
|
||||
extern PFN_vkGetDeviceImageMemoryRequirements vkGetDeviceImageMemoryRequirements;
|
||||
extern PFN_vkQueueBindSparse vkQueueBindSparse;
|
||||
extern PFN_vkCreateFence vkCreateFence;
|
||||
extern PFN_vkDestroyFence vkDestroyFence;
|
||||
@ -239,6 +241,8 @@ struct VulkanExtensions {
|
||||
bool KHR_depth_stencil_resolve;
|
||||
bool EXT_shader_stencil_export;
|
||||
bool EXT_swapchain_colorspace;
|
||||
bool ARM_rasterization_order_attachment_access;
|
||||
bool EXT_fragment_shader_interlock;
|
||||
// bool EXT_depth_range_unrestricted; // Allows depth outside [0.0, 1.0] in 32-bit float depth buffers.
|
||||
};
|
||||
|
||||
|
@ -59,6 +59,8 @@ bool VulkanPushBuffer::AddBuffer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
vulkan_->SetDebugName(info.buffer, VK_OBJECT_TYPE_BUFFER, name_);
|
||||
|
||||
buffers_.push_back(info);
|
||||
buf_ = buffers_.size() - 1;
|
||||
return true;
|
||||
@ -222,5 +224,9 @@ VkResult VulkanDescSetPool::Recreate(bool grow) {
|
||||
info_.pPoolSizes = &sizes_[0];
|
||||
info_.poolSizeCount = (uint32_t)sizes_.size();
|
||||
|
||||
return vkCreateDescriptorPool(vulkan_->GetDevice(), &info_, nullptr, &descPool_);
|
||||
VkResult result = vkCreateDescriptorPool(vulkan_->GetDevice(), &info_, nullptr, &descPool_);
|
||||
if (result == VK_SUCCESS) {
|
||||
vulkan_->SetDebugName(descPool_, VK_OBJECT_TYPE_DESCRIPTOR_POOL, tag_);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -143,8 +143,7 @@ private:
|
||||
// Only appropriate for use in a per-frame pool.
|
||||
class VulkanDescSetPool {
|
||||
public:
|
||||
VulkanDescSetPool(const char *tag, bool grow) : tag_(tag), grow_(grow) {
|
||||
}
|
||||
VulkanDescSetPool(const char *tag, bool grow) : tag_(tag), grow_(grow) {}
|
||||
~VulkanDescSetPool();
|
||||
|
||||
// Must call this before use: defines how to clear cache of ANY returned values from Allocate().
|
||||
|
@ -34,13 +34,13 @@ void VulkanProfiler::BeginFrame(VulkanContext *vulkan, VkCommandBuffer firstComm
|
||||
static const char * const indent[4] = { "", " ", " ", " " };
|
||||
|
||||
if (!scopes_.empty()) {
|
||||
NOTICE_LOG(G3D, "Profiling events this frame:");
|
||||
INFO_LOG(G3D, "Profiling events this frame:");
|
||||
}
|
||||
|
||||
// Log it all out.
|
||||
for (auto &scope : scopes_) {
|
||||
if (scope.endQueryId == -1) {
|
||||
NOTICE_LOG(G3D, "Unclosed scope: %s", scope.name.c_str());
|
||||
WARN_LOG(G3D, "Unclosed scope: %s", scope.name.c_str());
|
||||
continue;
|
||||
}
|
||||
uint64_t startTime = results[scope.startQueryId];
|
||||
@ -50,7 +50,7 @@ void VulkanProfiler::BeginFrame(VulkanContext *vulkan, VkCommandBuffer firstComm
|
||||
|
||||
double milliseconds = (double)delta * timestampConversionFactor;
|
||||
|
||||
NOTICE_LOG(G3D, "%s%s (%0.3f ms)", indent[scope.level & 3], scope.name.c_str(), milliseconds);
|
||||
INFO_LOG(G3D, "%s%s (%0.3f ms)", indent[scope.level & 3], scope.name.c_str(), milliseconds);
|
||||
}
|
||||
|
||||
scopes_.clear();
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,10 +4,11 @@
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
|
||||
#include "Common/Thread/Promise.h"
|
||||
#include "Common/Data/Collections/Hashmaps.h"
|
||||
#include "Common/GPU/Vulkan/VulkanContext.h"
|
||||
#include "Common/GPU/Vulkan/VulkanBarrier.h"
|
||||
#include "Common/GPU/Vulkan/VulkanFrameData.h"
|
||||
#include "Common/Data/Convert/SmallDataConvert.h"
|
||||
#include "Common/Data/Collections/TinySet.h"
|
||||
#include "Common/GPU/DataFormat.h"
|
||||
@ -16,17 +17,16 @@ class VKRFramebuffer;
|
||||
struct VKRGraphicsPipeline;
|
||||
struct VKRComputePipeline;
|
||||
struct VKRImage;
|
||||
struct FrameData;
|
||||
|
||||
enum {
|
||||
QUEUE_HACK_MGS2_ACID = 1,
|
||||
QUEUE_HACK_SONIC = 2,
|
||||
// Killzone PR = 4.
|
||||
QUEUE_HACK_RENDERPASS_MERGE = 8,
|
||||
};
|
||||
|
||||
enum class VKRRenderCommand : uint8_t {
|
||||
REMOVED,
|
||||
BIND_PIPELINE, // raw pipeline
|
||||
BIND_GRAPHICS_PIPELINE, // async
|
||||
BIND_COMPUTE_PIPELINE, // async
|
||||
STENCIL,
|
||||
@ -37,30 +37,60 @@ enum class VKRRenderCommand : uint8_t {
|
||||
DRAW,
|
||||
DRAW_INDEXED,
|
||||
PUSH_CONSTANTS,
|
||||
SELF_DEPENDENCY_BARRIER,
|
||||
DEBUG_ANNOTATION,
|
||||
NUM_RENDER_COMMANDS,
|
||||
};
|
||||
|
||||
enum PipelineFlags {
|
||||
PIPELINE_FLAG_NONE = 0,
|
||||
PIPELINE_FLAG_USES_LINES = (1 << 2),
|
||||
PIPELINE_FLAG_USES_BLEND_CONSTANT = (1 << 3),
|
||||
PIPELINE_FLAG_USES_DEPTH_STENCIL = (1 << 4), // Reads or writes the depth buffer.
|
||||
enum class PipelineFlags {
|
||||
NONE = 0,
|
||||
USES_BLEND_CONSTANT = (1 << 1),
|
||||
USES_DEPTH_STENCIL = (1 << 2), // Reads or writes the depth or stencil buffers.
|
||||
USES_INPUT_ATTACHMENT = (1 << 3),
|
||||
USES_GEOMETRY_SHADER = (1 << 4),
|
||||
};
|
||||
ENUM_CLASS_BITOPS(PipelineFlags);
|
||||
|
||||
// Pipelines need to be created for the right type of render pass.
|
||||
enum RenderPassType {
|
||||
// These four are organized so that bit 0 is DEPTH and bit 1 is INPUT, so
|
||||
// they can be OR-ed together in MergeRPTypes.
|
||||
RP_TYPE_COLOR,
|
||||
RP_TYPE_COLOR_DEPTH,
|
||||
RP_TYPE_COLOR_INPUT,
|
||||
RP_TYPE_COLOR_DEPTH_INPUT,
|
||||
|
||||
// This is the odd one out, and gets special handling in MergeRPTypes.
|
||||
RP_TYPE_BACKBUFFER, // For the backbuffer we can always use CLEAR/DONT_CARE, so bandwidth cost for a depth channel is negligible.
|
||||
|
||||
// Later will add pure-color render passes.
|
||||
RP_TYPE_COUNT,
|
||||
};
|
||||
|
||||
inline bool RenderPassTypeHasDepth(RenderPassType type) {
|
||||
return type == RP_TYPE_BACKBUFFER || type == RP_TYPE_COLOR_DEPTH || type == RP_TYPE_COLOR_DEPTH_INPUT;
|
||||
}
|
||||
|
||||
inline bool RenderPassTypeHasInput(RenderPassType type) {
|
||||
return type == RP_TYPE_COLOR_INPUT || type == RP_TYPE_COLOR_DEPTH_INPUT;
|
||||
}
|
||||
|
||||
struct VkRenderData {
|
||||
VKRRenderCommand cmd;
|
||||
union {
|
||||
struct {
|
||||
VkPipeline pipeline;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
} pipeline;
|
||||
struct {
|
||||
VKRGraphicsPipeline *pipeline;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
} graphics_pipeline;
|
||||
struct {
|
||||
VKRComputePipeline *pipeline;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
} compute_pipeline;
|
||||
struct {
|
||||
VkPipelineLayout pipelineLayout;
|
||||
VkDescriptorSet ds;
|
||||
int numUboOffsets;
|
||||
uint32_t uboOffsets[3];
|
||||
@ -70,17 +100,16 @@ struct VkRenderData {
|
||||
uint32_t offset;
|
||||
} draw;
|
||||
struct {
|
||||
VkPipelineLayout pipelineLayout;
|
||||
VkDescriptorSet ds;
|
||||
int numUboOffsets;
|
||||
uint32_t uboOffsets[3];
|
||||
VkBuffer vbuffer; // might need to increase at some point
|
||||
VkDeviceSize voffset;
|
||||
VkBuffer ibuffer;
|
||||
VkDeviceSize ioffset;
|
||||
uint32_t voffset;
|
||||
uint32_t ioffset;
|
||||
uint32_t count;
|
||||
int16_t instances;
|
||||
VkIndexType indexType;
|
||||
int16_t indexType;
|
||||
} drawIndexed;
|
||||
struct {
|
||||
uint32_t clearColor;
|
||||
@ -103,12 +132,14 @@ struct VkRenderData {
|
||||
uint32_t color;
|
||||
} blendColor;
|
||||
struct {
|
||||
VkPipelineLayout pipelineLayout;
|
||||
VkShaderStageFlags stages;
|
||||
uint8_t offset;
|
||||
uint8_t size;
|
||||
uint8_t data[40]; // Should be enough for now.
|
||||
} push;
|
||||
struct {
|
||||
const char *annotation;
|
||||
} debugAnnotation;
|
||||
};
|
||||
};
|
||||
|
||||
@ -121,25 +152,29 @@ enum class VKRStepType : uint8_t {
|
||||
READBACK_IMAGE,
|
||||
};
|
||||
|
||||
// Must be the same order as Draw::RPAction
|
||||
enum class VKRRenderPassLoadAction : uint8_t {
|
||||
DONT_CARE,
|
||||
KEEP, // default. avoid when possible.
|
||||
CLEAR,
|
||||
KEEP,
|
||||
DONT_CARE,
|
||||
};
|
||||
|
||||
enum class VKRRenderPassStoreAction : uint8_t {
|
||||
STORE, // default. avoid when possible.
|
||||
DONT_CARE,
|
||||
};
|
||||
|
||||
struct TransitionRequest {
|
||||
VkImageAspectFlags aspect; // COLOR or DEPTH
|
||||
VKRFramebuffer *fb;
|
||||
VkImageAspectFlags aspect; // COLOR or DEPTH
|
||||
VkImageLayout targetLayout;
|
||||
|
||||
bool operator == (const TransitionRequest &other) const {
|
||||
return fb == other.fb && aspect == other.aspect && targetLayout == other.targetLayout;
|
||||
}
|
||||
};
|
||||
|
||||
struct QueueProfileContext {
|
||||
VkQueryPool queryPool;
|
||||
std::vector<std::string> timestampDescriptions;
|
||||
std::string profileSummary;
|
||||
double cpuStartTime;
|
||||
double cpuEndTime;
|
||||
};
|
||||
class VKRRenderPass;
|
||||
|
||||
struct VKRStep {
|
||||
VKRStep(VKRStepType _type) : stepType(_type) {}
|
||||
@ -147,7 +182,7 @@ struct VKRStep {
|
||||
|
||||
VKRStepType stepType;
|
||||
std::vector<VkRenderData> commands;
|
||||
std::vector<TransitionRequest> preTransitions;
|
||||
TinySet<TransitionRequest, 4> preTransitions;
|
||||
TinySet<VKRFramebuffer *, 8> dependencies;
|
||||
const char *tag;
|
||||
union {
|
||||
@ -156,6 +191,9 @@ struct VKRStep {
|
||||
VKRRenderPassLoadAction colorLoad;
|
||||
VKRRenderPassLoadAction depthLoad;
|
||||
VKRRenderPassLoadAction stencilLoad;
|
||||
VKRRenderPassStoreAction colorStore;
|
||||
VKRRenderPassStoreAction depthStore;
|
||||
VKRRenderPassStoreAction stencilStore;
|
||||
u8 clearStencil;
|
||||
uint32_t clearColor;
|
||||
float clearDepth;
|
||||
@ -164,8 +202,11 @@ struct VKRStep {
|
||||
int numReads;
|
||||
VkImageLayout finalColorLayout;
|
||||
VkImageLayout finalDepthStencilLayout;
|
||||
u32 pipelineFlags;
|
||||
PipelineFlags pipelineFlags; // contains the self dependency flag, in the form of USES_INPUT_ATTACHMENT
|
||||
VkRect2D renderArea;
|
||||
// Render pass type. Deduced after finishing recording the pass, from the used pipelines.
|
||||
// NOTE: Storing the render pass here doesn't do much good, we change the compatible parameters (load/store ops) during step optimization.
|
||||
RenderPassType renderPassType;
|
||||
} render;
|
||||
struct {
|
||||
VKRFramebuffer *src;
|
||||
@ -195,16 +236,53 @@ struct VKRStep {
|
||||
};
|
||||
};
|
||||
|
||||
struct RPKey {
|
||||
// Only render-pass-compatibility-volatile things can be here.
|
||||
VKRRenderPassLoadAction colorLoadAction;
|
||||
VKRRenderPassLoadAction depthLoadAction;
|
||||
VKRRenderPassLoadAction stencilLoadAction;
|
||||
VKRRenderPassStoreAction colorStoreAction;
|
||||
VKRRenderPassStoreAction depthStoreAction;
|
||||
VKRRenderPassStoreAction stencilStoreAction;
|
||||
};
|
||||
|
||||
class VKRRenderPass {
|
||||
public:
|
||||
VKRRenderPass(const RPKey &key) : key_(key) {}
|
||||
|
||||
VkRenderPass Get(VulkanContext *vulkan, RenderPassType rpType);
|
||||
void Destroy(VulkanContext *vulkan) {
|
||||
for (int i = 0; i < RP_TYPE_COUNT; i++) {
|
||||
if (pass[i]) {
|
||||
vulkan->Delete().QueueDeleteRenderPass(pass[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
VkRenderPass pass[RP_TYPE_COUNT]{};
|
||||
RPKey key_;
|
||||
};
|
||||
|
||||
// These are enqueued from the main thread,
|
||||
// and the render thread pops them off
|
||||
struct VKRRenderThreadTask {
|
||||
std::vector<VKRStep *> steps;
|
||||
int frame;
|
||||
VKRRunType runType;
|
||||
};
|
||||
|
||||
class VulkanQueueRunner {
|
||||
public:
|
||||
VulkanQueueRunner(VulkanContext *vulkan) : vulkan_(vulkan), renderPasses_(16) {}
|
||||
|
||||
void SetBackbuffer(VkFramebuffer fb, VkImage img) {
|
||||
backbuffer_ = fb;
|
||||
backbufferImage_ = img;
|
||||
}
|
||||
|
||||
void PreprocessSteps(std::vector<VKRStep *> &steps);
|
||||
void RunSteps(VkCommandBuffer cmd, std::vector<VKRStep *> &steps, QueueProfileContext *profile);
|
||||
void RunSteps(std::vector<VKRStep *> &steps, FrameData &frameData, FrameDataShared &frameDataShared);
|
||||
void LogSteps(const std::vector<VKRStep *> &steps, bool verbose);
|
||||
|
||||
std::string StepToString(const VKRStep &step) const;
|
||||
@ -212,14 +290,18 @@ public:
|
||||
void CreateDeviceObjects();
|
||||
void DestroyDeviceObjects();
|
||||
|
||||
VkRenderPass GetBackbufferRenderPass() const {
|
||||
return backbufferRenderPass_;
|
||||
// Swapchain
|
||||
void DestroyBackBuffers();
|
||||
bool CreateSwapchain(VkCommandBuffer cmdInit);
|
||||
|
||||
bool HasBackbuffers() const {
|
||||
return !framebuffers_.empty();
|
||||
}
|
||||
|
||||
// Get a render pass that's compatible with all our framebuffers.
|
||||
// Note that it's precached, cannot look up in the map as this might be on another thread.
|
||||
VkRenderPass GetFramebufferRenderPass() const {
|
||||
return framebufferRenderPass_;
|
||||
VKRRenderPass *GetCompatibleRenderPass() const {
|
||||
return compatibleRenderPass_;
|
||||
}
|
||||
|
||||
inline int RPIndex(VKRRenderPassLoadAction color, VKRRenderPassLoadAction depth) {
|
||||
@ -228,24 +310,11 @@ public:
|
||||
|
||||
void CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels);
|
||||
|
||||
struct RPKey {
|
||||
VKRRenderPassLoadAction colorLoadAction;
|
||||
VKRRenderPassLoadAction depthLoadAction;
|
||||
VKRRenderPassLoadAction stencilLoadAction;
|
||||
};
|
||||
VKRRenderPass *GetRenderPass(const RPKey &key);
|
||||
|
||||
// Only call this from the render thread! Also ok during initialization (LoadCache).
|
||||
VkRenderPass GetRenderPass(
|
||||
VKRRenderPassLoadAction colorLoadAction, VKRRenderPassLoadAction depthLoadAction, VKRRenderPassLoadAction stencilLoadAction) {
|
||||
RPKey key{ colorLoadAction, depthLoadAction, stencilLoadAction };
|
||||
return GetRenderPass(key);
|
||||
}
|
||||
|
||||
VkRenderPass GetRenderPass(const RPKey &key);
|
||||
|
||||
bool GetRenderPassKey(VkRenderPass passToFind, RPKey *outKey) const {
|
||||
bool GetRenderPassKey(VKRRenderPass *passToFind, RPKey *outKey) const {
|
||||
bool found = false;
|
||||
renderPasses_.Iterate([passToFind, &found, outKey](const RPKey &rpkey, VkRenderPass pass) {
|
||||
renderPasses_.Iterate([passToFind, &found, outKey](const RPKey &rpkey, const VKRRenderPass *pass) {
|
||||
if (pass == passToFind) {
|
||||
found = true;
|
||||
*outKey = rpkey;
|
||||
@ -268,9 +337,10 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void InitBackbufferRenderPass();
|
||||
bool InitBackbufferFramebuffers(int width, int height);
|
||||
bool InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering.
|
||||
|
||||
void PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
VKRRenderPass *PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
void PerformCopy(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
void PerformBlit(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
@ -291,20 +361,21 @@ private:
|
||||
|
||||
static void SetupTransitionToTransferSrc(VKRImage &img, VkImageAspectFlags aspect, VulkanBarrier *recordBarrier);
|
||||
static void SetupTransitionToTransferDst(VKRImage &img, VkImageAspectFlags aspect, VulkanBarrier *recordBarrier);
|
||||
static void SetupTransferDstWriteAfterWrite(VKRImage &img, VkImageAspectFlags aspect, VulkanBarrier *recordBarrier);
|
||||
|
||||
static void SelfDependencyBarrier(VKRImage &img, VkImageAspectFlags aspect, VulkanBarrier *recordBarrier);
|
||||
|
||||
VulkanContext *vulkan_;
|
||||
|
||||
VkFramebuffer backbuffer_ = VK_NULL_HANDLE;
|
||||
VkImage backbufferImage_ = VK_NULL_HANDLE;
|
||||
|
||||
VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE;
|
||||
|
||||
// The "Compatible" render pass. Used when creating pipelines that render to "normal" framebuffers.
|
||||
VkRenderPass framebufferRenderPass_ = VK_NULL_HANDLE;
|
||||
// The "Compatible" render pass. Should be able to get rid of this soon.
|
||||
VKRRenderPass *compatibleRenderPass_ = nullptr;
|
||||
|
||||
// Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents.
|
||||
// TODO: Create these on demand.
|
||||
DenseHashMap<RPKey, VkRenderPass, (VkRenderPass)VK_NULL_HANDLE> renderPasses_;
|
||||
// Each VKRRenderPass contains all compatibility classes (which attachments they have, etc).
|
||||
DenseHashMap<RPKey, VKRRenderPass *, nullptr> renderPasses_;
|
||||
|
||||
// Readback buffer. Currently we only support synchronous readback, so we only really need one.
|
||||
// We size it generously.
|
||||
@ -324,4 +395,20 @@ private:
|
||||
// Stored here to help reuse the allocation.
|
||||
|
||||
VulkanBarrier recordBarrier_;
|
||||
|
||||
// Swap chain management
|
||||
struct SwapchainImageData {
|
||||
VkImage image;
|
||||
VkImageView view;
|
||||
};
|
||||
std::vector<VkFramebuffer> framebuffers_;
|
||||
std::vector<SwapchainImageData> swapchainImages_;
|
||||
uint32_t swapchainImageCount_ = 0;
|
||||
struct DepthBufferInfo {
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VmaAllocation alloc = VK_NULL_HANDLE;
|
||||
VkImageView view = VK_NULL_HANDLE;
|
||||
};
|
||||
DepthBufferInfo depth_;
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,8 @@
|
||||
#include <thread>
|
||||
#include <queue>
|
||||
|
||||
#include "Common/Math/Statistics.h"
|
||||
#include "Common/Thread/Promise.h"
|
||||
#include "Common/System/Display.h"
|
||||
#include "Common/GPU/Vulkan/VulkanContext.h"
|
||||
#include "Common/Data/Convert/SmallDataConvert.h"
|
||||
@ -42,26 +44,28 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int
|
||||
|
||||
class VKRFramebuffer {
|
||||
public:
|
||||
VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VkRenderPass renderPass, int _width, int _height, const char *tag);
|
||||
VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, bool createDepthStencilBuffer, const char *tag);
|
||||
~VKRFramebuffer();
|
||||
|
||||
VkFramebuffer framebuf = VK_NULL_HANDLE;
|
||||
VKRImage color{};
|
||||
VKRImage depth{};
|
||||
VkFramebuffer Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType);
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
VKRImage color{}; // color.image is always there.
|
||||
VKRImage depth{}; // depth.image is allowed to be VK_NULL_HANDLE.
|
||||
|
||||
const char *Tag() const {
|
||||
return tag_.c_str();
|
||||
}
|
||||
|
||||
void UpdateTag(const char *newTag);
|
||||
|
||||
// TODO: Hide.
|
||||
VulkanContext *vulkan_;
|
||||
std::string tag;
|
||||
};
|
||||
private:
|
||||
VkFramebuffer framebuf[RP_TYPE_COUNT]{};
|
||||
|
||||
enum class VKRRunType {
|
||||
END,
|
||||
SYNC,
|
||||
};
|
||||
|
||||
enum {
|
||||
MAX_TIMESTAMP_QUERIES = 128,
|
||||
std::string tag_;
|
||||
};
|
||||
|
||||
struct BoundingRect {
|
||||
@ -121,13 +125,23 @@ struct VKRGraphicsPipelineDesc {
|
||||
VkPipelineDynamicStateCreateInfo ds{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
|
||||
VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
|
||||
VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
|
||||
VkPipelineShaderStageCreateInfo shaderStageInfo[2]{};
|
||||
|
||||
// Replaced the ShaderStageInfo with promises here so we can wait for compiles to finish.
|
||||
Promise<VkShaderModule> *vertexShader = nullptr;
|
||||
Promise<VkShaderModule> *fragmentShader = nullptr;
|
||||
Promise<VkShaderModule> *geometryShader = nullptr;
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo inputAssembly{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
|
||||
VkVertexInputAttributeDescription attrs[8]{};
|
||||
VkVertexInputBindingDescription ibd{};
|
||||
VkPipelineVertexInputStateCreateInfo vis{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
|
||||
VkPipelineViewportStateCreateInfo views{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
|
||||
VkGraphicsPipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
|
||||
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
|
||||
// Does not include the render pass type, it's passed in separately since the
|
||||
// desc is persistent.
|
||||
RPKey rpKey{};
|
||||
};
|
||||
|
||||
// All the data needed to create a compute pipeline.
|
||||
@ -136,26 +150,33 @@ struct VKRComputePipelineDesc {
|
||||
VkComputePipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };
|
||||
};
|
||||
|
||||
// Wrapped pipeline, which will later allow for background compilation while emulating the rest of the frame.
|
||||
// Wrapped pipeline. Doesn't own desc.
|
||||
struct VKRGraphicsPipeline {
|
||||
VKRGraphicsPipeline() {
|
||||
pipeline = VK_NULL_HANDLE;
|
||||
~VKRGraphicsPipeline() {
|
||||
for (int i = 0; i < RP_TYPE_COUNT; i++) {
|
||||
delete pipeline[i];
|
||||
}
|
||||
}
|
||||
VKRGraphicsPipelineDesc *desc = nullptr; // While non-zero, is pending and pipeline isn't valid.
|
||||
std::atomic<VkPipeline> pipeline;
|
||||
|
||||
bool Create(VulkanContext *vulkan);
|
||||
bool Pending() const {
|
||||
return pipeline == VK_NULL_HANDLE && desc != nullptr;
|
||||
}
|
||||
bool Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType);
|
||||
|
||||
// This deletes the whole VKRGraphicsPipeline, you must remove your last pointer to it when doing this.
|
||||
void QueueForDeletion(VulkanContext *vulkan);
|
||||
|
||||
u32 GetVariantsBitmask() const;
|
||||
|
||||
VKRGraphicsPipelineDesc *desc = nullptr; // not owned!
|
||||
Promise<VkPipeline> *pipeline[RP_TYPE_COUNT]{};
|
||||
std::string tag;
|
||||
};
|
||||
|
||||
struct VKRComputePipeline {
|
||||
VKRComputePipeline() {
|
||||
pipeline = VK_NULL_HANDLE;
|
||||
~VKRComputePipeline() {
|
||||
delete pipeline;
|
||||
}
|
||||
|
||||
VKRComputePipelineDesc *desc = nullptr;
|
||||
std::atomic<VkPipeline> pipeline;
|
||||
Promise<VkPipeline> *pipeline = nullptr;
|
||||
|
||||
bool Create(VulkanContext *vulkan);
|
||||
bool Pending() const {
|
||||
@ -164,13 +185,16 @@ struct VKRComputePipeline {
|
||||
};
|
||||
|
||||
struct CompileQueueEntry {
|
||||
CompileQueueEntry(VKRGraphicsPipeline *p) : type(Type::GRAPHICS), graphics(p) {}
|
||||
CompileQueueEntry(VKRComputePipeline *p) : type(Type::COMPUTE), compute(p) {}
|
||||
CompileQueueEntry(VKRGraphicsPipeline *p, VkRenderPass _compatibleRenderPass, RenderPassType _renderPassType)
|
||||
: type(Type::GRAPHICS), graphics(p), compatibleRenderPass(_compatibleRenderPass), renderPassType(_renderPassType) {}
|
||||
CompileQueueEntry(VKRComputePipeline *p) : type(Type::COMPUTE), compute(p), renderPassType(RP_TYPE_COLOR_DEPTH) {}
|
||||
enum class Type {
|
||||
GRAPHICS,
|
||||
COMPUTE,
|
||||
};
|
||||
Type type;
|
||||
VkRenderPass compatibleRenderPass;
|
||||
RenderPassType renderPassType;
|
||||
VKRGraphicsPipeline *graphics = nullptr;
|
||||
VKRComputePipeline *compute = nullptr;
|
||||
};
|
||||
@ -180,16 +204,10 @@ public:
|
||||
VulkanRenderManager(VulkanContext *vulkan);
|
||||
~VulkanRenderManager();
|
||||
|
||||
void ThreadFunc();
|
||||
void CompileThreadFunc();
|
||||
void DrainCompileQueue();
|
||||
|
||||
// Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again.
|
||||
void BeginFrame(bool enableProfiling, bool enableLogProfiler);
|
||||
// Can run on a different thread!
|
||||
void Finish();
|
||||
void Run(int frame);
|
||||
|
||||
// Zaps queued up commands. Use if you know there's a risk you've queued up stuff that has already been deleted. Can happen during in-game shutdown.
|
||||
void Wipe();
|
||||
|
||||
@ -214,6 +232,8 @@ public:
|
||||
// as the other backends, even though there's no actual binding happening here.
|
||||
VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBits, int attachment);
|
||||
|
||||
void BindCurrentFramebufferAsInputAttachment0(VkImageAspectFlags aspectBits);
|
||||
|
||||
bool CopyFramebufferToMemorySync(VKRFramebuffer *src, VkImageAspectFlags aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag);
|
||||
void CopyImageToMemorySync(VkImage image, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag);
|
||||
|
||||
@ -222,49 +242,35 @@ public:
|
||||
|
||||
// Deferred creation, like in GL. Unlike GL though, the purpose is to allow background creation and avoiding
|
||||
// stalling the emulation thread as much as possible.
|
||||
VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc) {
|
||||
VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline();
|
||||
pipeline->desc = desc;
|
||||
// We delay creating pipelines until the end of the current render pass, so we can create the right type immediately.
|
||||
// Unless a variantBitmask is passed in, in which case we can just go ahead.
|
||||
// WARNING: desc must stick around during the lifetime of the pipeline! It's not enough to build it on the stack and drop it.
|
||||
VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, PipelineFlags pipelineFlags, uint32_t variantBitmask, const char *tag);
|
||||
VKRComputePipeline *CreateComputePipeline(VKRComputePipelineDesc *desc);
|
||||
|
||||
void NudgeCompilerThread() {
|
||||
compileMutex_.lock();
|
||||
compileQueue_.push_back(CompileQueueEntry(pipeline));
|
||||
compileCond_.notify_one();
|
||||
compileMutex_.unlock();
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
VKRComputePipeline *CreateComputePipeline(VKRComputePipelineDesc *desc) {
|
||||
VKRComputePipeline *pipeline = new VKRComputePipeline();
|
||||
pipeline->desc = desc;
|
||||
compileMutex_.lock();
|
||||
compileQueue_.push_back(CompileQueueEntry(pipeline));
|
||||
compileCond_.notify_one();
|
||||
compileMutex_.unlock();
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
void BindPipeline(VkPipeline pipeline, PipelineFlags flags) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
|
||||
_dbg_assert_(pipeline != VK_NULL_HANDLE);
|
||||
VkRenderData data{ VKRRenderCommand::BIND_PIPELINE };
|
||||
data.pipeline.pipeline = pipeline;
|
||||
curPipelineFlags_ |= flags;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags) {
|
||||
void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
|
||||
_dbg_assert_(pipeline != nullptr);
|
||||
VkRenderData data{ VKRRenderCommand::BIND_GRAPHICS_PIPELINE };
|
||||
pipelinesToCheck_.push_back(pipeline);
|
||||
data.graphics_pipeline.pipeline = pipeline;
|
||||
data.graphics_pipeline.pipelineLayout = pipelineLayout;
|
||||
curPipelineFlags_ |= flags;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void BindPipeline(VKRComputePipeline *pipeline, PipelineFlags flags) {
|
||||
void BindPipeline(VKRComputePipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
|
||||
_dbg_assert_(pipeline != nullptr);
|
||||
VkRenderData data{ VKRRenderCommand::BIND_COMPUTE_PIPELINE };
|
||||
data.compute_pipeline.pipeline = pipeline;
|
||||
data.compute_pipeline.pipelineLayout = pipelineLayout;
|
||||
curPipelineFlags_ |= flags;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
@ -351,7 +357,6 @@ public:
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
|
||||
_dbg_assert_(size + offset < 40);
|
||||
VkRenderData data{ VKRRenderCommand::PUSH_CONSTANTS };
|
||||
data.push.pipelineLayout = pipelineLayout;
|
||||
data.push.stages = stages;
|
||||
data.push.offset = offset;
|
||||
data.push.size = size;
|
||||
@ -361,12 +366,36 @@ public:
|
||||
|
||||
void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask);
|
||||
|
||||
void Draw(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) {
|
||||
// Cheaply set that we don't care about the contents of a surface at the start of the current render pass.
|
||||
// This set the corresponding load-op of the current render pass to DONT_CARE.
|
||||
// Useful when we don't know at bind-time whether we will overwrite the surface or not.
|
||||
void SetLoadDontCare(VkImageAspectFlags aspects) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
|
||||
if (aspects & VK_IMAGE_ASPECT_COLOR_BIT)
|
||||
curRenderStep_->render.colorLoad = VKRRenderPassLoadAction::DONT_CARE;
|
||||
if (aspects & VK_IMAGE_ASPECT_DEPTH_BIT)
|
||||
curRenderStep_->render.depthLoad = VKRRenderPassLoadAction::DONT_CARE;
|
||||
if (aspects & VK_IMAGE_ASPECT_STENCIL_BIT)
|
||||
curRenderStep_->render.stencilLoad = VKRRenderPassLoadAction::DONT_CARE;
|
||||
}
|
||||
|
||||
// Cheaply set that we don't care about the contents of a surface at the end of the current render pass.
|
||||
// This set the corresponding store-op of the current render pass to DONT_CARE.
|
||||
void SetStoreDontCare(VkImageAspectFlags aspects) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
|
||||
if (aspects & VK_IMAGE_ASPECT_COLOR_BIT)
|
||||
curRenderStep_->render.colorStore = VKRRenderPassStoreAction::DONT_CARE;
|
||||
if (aspects & VK_IMAGE_ASPECT_DEPTH_BIT)
|
||||
curRenderStep_->render.depthStore = VKRRenderPassStoreAction::DONT_CARE;
|
||||
if (aspects & VK_IMAGE_ASPECT_STENCIL_BIT)
|
||||
curRenderStep_->render.stencilStore = VKRRenderPassStoreAction::DONT_CARE;
|
||||
}
|
||||
|
||||
void Draw(VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_);
|
||||
VkRenderData data{ VKRRenderCommand::DRAW };
|
||||
data.draw.count = count;
|
||||
data.draw.offset = offset;
|
||||
data.draw.pipelineLayout = layout;
|
||||
data.draw.ds = descSet;
|
||||
data.draw.vbuffer = vbuffer;
|
||||
data.draw.voffset = voffset;
|
||||
@ -378,12 +407,11 @@ public:
|
||||
curRenderStep_->render.numDraws++;
|
||||
}
|
||||
|
||||
void DrawIndexed(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) {
|
||||
void DrawIndexed(VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_);
|
||||
VkRenderData data{ VKRRenderCommand::DRAW_INDEXED };
|
||||
data.drawIndexed.count = count;
|
||||
data.drawIndexed.instances = numInstances;
|
||||
data.drawIndexed.pipelineLayout = layout;
|
||||
data.drawIndexed.ds = descSet;
|
||||
data.drawIndexed.vbuffer = vbuffer;
|
||||
data.drawIndexed.voffset = voffset;
|
||||
@ -398,21 +426,14 @@ public:
|
||||
curRenderStep_->render.numDraws++;
|
||||
}
|
||||
|
||||
VkCommandBuffer GetInitCmd();
|
||||
// These can be useful both when inspecting in RenderDoc, and when manually inspecting recorded commands
|
||||
// in the debugger.
|
||||
void DebugAnnotate(const char *annotation) {
|
||||
VkRenderData data{ VKRRenderCommand::DEBUG_ANNOTATION };
|
||||
data.debugAnnotation.annotation = annotation;
|
||||
}
|
||||
|
||||
VkRenderPass GetBackbufferRenderPass() {
|
||||
return queueRunner_.GetBackbufferRenderPass();
|
||||
}
|
||||
VkRenderPass GetFramebufferRenderPass() {
|
||||
return queueRunner_.GetFramebufferRenderPass();
|
||||
}
|
||||
VkRenderPass GetCompatibleRenderPass() {
|
||||
if (curRenderStep_ && curRenderStep_->render.framebuffer != nullptr) {
|
||||
return queueRunner_.GetFramebufferRenderPass();
|
||||
} else {
|
||||
return queueRunner_.GetBackbufferRenderPass();
|
||||
}
|
||||
}
|
||||
VkCommandBuffer GetInitCmd();
|
||||
|
||||
// Gets a frame-unique ID of the current step being recorded. Can be used to figure out
|
||||
// when the current step has changed, which means the caller will need to re-record its state.
|
||||
@ -424,11 +445,7 @@ public:
|
||||
void DestroyBackbuffers();
|
||||
|
||||
bool HasBackbuffers() {
|
||||
return !framebuffers_.empty();
|
||||
}
|
||||
|
||||
void SetSplitSubmit(bool split) {
|
||||
splitSubmit_ = split;
|
||||
return queueRunner_.HasBackbuffers();
|
||||
}
|
||||
|
||||
void SetInflightFrames(int f) {
|
||||
@ -453,58 +470,23 @@ public:
|
||||
return outOfDateFrames_ > VulkanContext::MAX_INFLIGHT_FRAMES;
|
||||
}
|
||||
|
||||
void ResetStats();
|
||||
|
||||
private:
|
||||
bool InitBackbufferFramebuffers(int width, int height);
|
||||
bool InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering.
|
||||
void EndCurRenderStep();
|
||||
|
||||
void ThreadFunc();
|
||||
void CompileThreadFunc();
|
||||
void DrainCompileQueue();
|
||||
|
||||
void Run(VKRRenderThreadTask &task);
|
||||
void BeginSubmitFrame(int frame);
|
||||
void EndSubmitFrame(int frame);
|
||||
void Submit(int frame, bool triggerFence);
|
||||
|
||||
// Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot).
|
||||
void FlushSync();
|
||||
void EndSyncFrame(int frame);
|
||||
|
||||
void StopThread();
|
||||
|
||||
// Permanent objects
|
||||
VkSemaphore acquireSemaphore_;
|
||||
VkSemaphore renderingCompleteSemaphore_;
|
||||
|
||||
// Per-frame data, round-robin so we can overlap submission with execution of the previous frame.
|
||||
struct FrameData {
|
||||
std::mutex push_mutex;
|
||||
std::condition_variable push_condVar;
|
||||
|
||||
std::mutex pull_mutex;
|
||||
std::condition_variable pull_condVar;
|
||||
|
||||
bool readyForFence = true;
|
||||
bool readyForRun = false;
|
||||
bool skipSwap = false;
|
||||
VKRRunType type = VKRRunType::END;
|
||||
|
||||
VkFence fence;
|
||||
VkFence readbackFence; // Strictly speaking we might only need one of these.
|
||||
bool readbackFenceUsed = false;
|
||||
|
||||
// These are on different threads so need separate pools.
|
||||
VkCommandPool cmdPoolInit;
|
||||
VkCommandPool cmdPoolMain;
|
||||
VkCommandBuffer initCmd;
|
||||
VkCommandBuffer mainCmd;
|
||||
bool hasInitCommands = false;
|
||||
std::vector<VKRStep *> steps;
|
||||
|
||||
// Swapchain.
|
||||
bool hasBegun = false;
|
||||
uint32_t curSwapchainImage = -1;
|
||||
|
||||
// Profiling.
|
||||
QueueProfileContext profile;
|
||||
bool profilingEnabled_;
|
||||
};
|
||||
FrameDataShared frameDataShared_;
|
||||
|
||||
FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES];
|
||||
int newInflightFrames_ = -1;
|
||||
@ -523,25 +505,33 @@ private:
|
||||
int curHeight_ = -1;
|
||||
|
||||
bool insideFrame_ = false;
|
||||
bool run_ = false;
|
||||
|
||||
// This is the offset within this frame, in case of a mid-frame sync.
|
||||
int renderStepOffset_ = 0;
|
||||
VKRStep *curRenderStep_ = nullptr;
|
||||
bool curStepHasViewport_ = false;
|
||||
bool curStepHasScissor_ = false;
|
||||
u32 curPipelineFlags_ = 0;
|
||||
PipelineFlags curPipelineFlags_{};
|
||||
BoundingRect curRenderArea_;
|
||||
|
||||
std::vector<VKRStep *> steps_;
|
||||
bool splitSubmit_ = false;
|
||||
|
||||
// Execution time state
|
||||
bool run_ = true;
|
||||
VulkanContext *vulkan_;
|
||||
std::thread thread_;
|
||||
std::mutex mutex_;
|
||||
int threadInitFrame_ = 0;
|
||||
VulkanQueueRunner queueRunner_;
|
||||
|
||||
// For pushing data on the queue.
|
||||
std::mutex pushMutex_;
|
||||
std::condition_variable pushCondVar_;
|
||||
|
||||
std::queue<VKRRenderThreadTask> renderThreadQueue_;
|
||||
|
||||
// For readbacks and other reasons we need to sync with the render thread.
|
||||
std::mutex syncMutex_;
|
||||
std::condition_variable syncCondVar_;
|
||||
|
||||
// Shader compilation thread to compile while emulating the rest of the frame.
|
||||
// Only one right now but we could use more.
|
||||
std::thread compileThread_;
|
||||
@ -550,22 +540,11 @@ private:
|
||||
std::mutex compileMutex_;
|
||||
std::vector<CompileQueueEntry> compileQueue_;
|
||||
|
||||
// Swap chain management
|
||||
struct SwapchainImageData {
|
||||
VkImage image;
|
||||
VkImageView view;
|
||||
};
|
||||
std::vector<VkFramebuffer> framebuffers_;
|
||||
std::vector<SwapchainImageData> swapchainImages_;
|
||||
uint32_t swapchainImageCount_ = 0;
|
||||
struct DepthBufferInfo {
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VmaAllocation alloc = VK_NULL_HANDLE;
|
||||
VkImageView view = VK_NULL_HANDLE;
|
||||
};
|
||||
DepthBufferInfo depth_;
|
||||
// pipelines to check and possibly create at the end of the current render pass.
|
||||
std::vector<VKRGraphicsPipeline *> pipelinesToCheck_;
|
||||
|
||||
// This works great - except see issue #10097. WTF?
|
||||
bool useThread_ = true;
|
||||
// For nicer output in the little internal GPU profiler.
|
||||
SimpleStat initTimeMs_;
|
||||
SimpleStat totalGPUTimeMs_;
|
||||
SimpleStat renderCPUTimeMs_;
|
||||
};
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "Common/GPU/Vulkan/VulkanContext.h"
|
||||
#include "Common/GPU/Vulkan/VulkanImage.h"
|
||||
#include "Common/GPU/Vulkan/VulkanMemory.h"
|
||||
#include "Common/Thread/Promise.h"
|
||||
|
||||
#include "Core/Config.h"
|
||||
|
||||
@ -176,9 +177,9 @@ VkShaderStageFlagBits StageToVulkan(ShaderStage stage) {
|
||||
case ShaderStage::Vertex: return VK_SHADER_STAGE_VERTEX_BIT;
|
||||
case ShaderStage::Geometry: return VK_SHADER_STAGE_GEOMETRY_BIT;
|
||||
case ShaderStage::Compute: return VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
default:
|
||||
case ShaderStage::Fragment: return VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
}
|
||||
return VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
}
|
||||
|
||||
// Not registering this as a resource holder, instead the pipeline is registered. It will
|
||||
@ -192,17 +193,20 @@ public:
|
||||
const std::string &GetSource() const { return source_; }
|
||||
~VKShaderModule() {
|
||||
if (module_) {
|
||||
vulkan_->Delete().QueueDeleteShaderModule(module_);
|
||||
DEBUG_LOG(G3D, "Queueing %s (shmodule %p) for release", tag_.c_str(), module_);
|
||||
VkShaderModule shaderModule = module_->BlockUntilReady();
|
||||
vulkan_->Delete().QueueDeleteShaderModule(shaderModule);
|
||||
delete module_;
|
||||
}
|
||||
}
|
||||
VkShaderModule Get() const { return module_; }
|
||||
Promise<VkShaderModule> *Get() const { return module_; }
|
||||
ShaderStage GetStage() const override {
|
||||
return stage_;
|
||||
}
|
||||
|
||||
private:
|
||||
VulkanContext *vulkan_;
|
||||
VkShaderModule module_ = VK_NULL_HANDLE;
|
||||
Promise<VkShaderModule> *module_ = nullptr;
|
||||
VkShaderStageFlagBits vkstage_;
|
||||
bool ok_ = false;
|
||||
ShaderStage stage_;
|
||||
@ -211,13 +215,13 @@ private:
|
||||
};
|
||||
|
||||
bool VKShaderModule::Compile(VulkanContext *vulkan, ShaderLanguage language, const uint8_t *data, size_t size) {
|
||||
vulkan_ = vulkan;
|
||||
// We'll need this to free it later.
|
||||
vulkan_ = vulkan;
|
||||
source_ = (const char *)data;
|
||||
std::vector<uint32_t> spirv;
|
||||
std::string errorMessage;
|
||||
if (!GLSLtoSPV(vkstage_, source_.c_str(), GLSLVariant::VULKAN, spirv, &errorMessage)) {
|
||||
WARN_LOG(G3D, "Shader compile to module failed: %s", errorMessage.c_str());
|
||||
WARN_LOG(G3D, "Shader compile to module failed (%s): %s", tag_.c_str(), errorMessage.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -230,10 +234,12 @@ bool VKShaderModule::Compile(VulkanContext *vulkan, ShaderLanguage language, con
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vulkan->CreateShaderModule(spirv, &module_)) {
|
||||
VkShaderModule shaderModule = VK_NULL_HANDLE;
|
||||
if (vulkan->CreateShaderModule(spirv, &shaderModule, vkstage_ == VK_SHADER_STAGE_VERTEX_BIT ? "thin3d_vs" : "thin3d_fs")) {
|
||||
module_ = Promise<VkShaderModule>::AlreadyDone(shaderModule);
|
||||
ok_ = true;
|
||||
} else {
|
||||
WARN_LOG(G3D, "vkCreateShaderModule failed");
|
||||
WARN_LOG(G3D, "vkCreateShaderModule failed (%s)", tag_.c_str());
|
||||
ok_ = false;
|
||||
}
|
||||
return ok_;
|
||||
@ -248,13 +254,18 @@ public:
|
||||
|
||||
class VKPipeline : public Pipeline {
|
||||
public:
|
||||
VKPipeline(VulkanContext *vulkan, size_t size, PipelineFlags _flags) : flags(_flags), vulkan_(vulkan) {
|
||||
VKPipeline(VulkanContext *vulkan, size_t size, PipelineFlags _flags, const char *tag) : vulkan_(vulkan), flags(_flags), tag_(tag) {
|
||||
uboSize_ = (int)size;
|
||||
ubo_ = new uint8_t[uboSize_];
|
||||
}
|
||||
~VKPipeline() {
|
||||
vulkan_->Delete().QueueDeletePipeline(backbufferPipeline);
|
||||
vulkan_->Delete().QueueDeletePipeline(framebufferPipeline);
|
||||
DEBUG_LOG(G3D, "Queueing %s (pipeline) for release", tag_.c_str());
|
||||
if (pipeline) {
|
||||
pipeline->QueueForDeletion(vulkan_);
|
||||
}
|
||||
for (auto dep : deps) {
|
||||
dep->Release();
|
||||
}
|
||||
delete[] ubo_;
|
||||
}
|
||||
|
||||
@ -267,15 +278,16 @@ public:
|
||||
return buf->PushAligned(ubo_, uboSize_, vulkan->GetPhysicalDeviceProperties().properties.limits.minUniformBufferOffsetAlignment, vkbuf);
|
||||
}
|
||||
|
||||
int GetUniformLoc(const char *name);
|
||||
int GetUBOSize() const {
|
||||
return uboSize_;
|
||||
}
|
||||
|
||||
VkPipeline backbufferPipeline = VK_NULL_HANDLE;
|
||||
VkPipeline framebufferPipeline = VK_NULL_HANDLE;
|
||||
|
||||
VKRGraphicsPipeline *pipeline = nullptr;
|
||||
VKRGraphicsPipelineDesc vkrDesc;
|
||||
PipelineFlags flags;
|
||||
|
||||
std::vector<VKShaderModule *> deps;
|
||||
|
||||
int stride[4]{};
|
||||
int dynamicUniformSize = 0;
|
||||
|
||||
@ -285,6 +297,7 @@ private:
|
||||
VulkanContext *vulkan_;
|
||||
uint8_t *ubo_;
|
||||
int uboSize_;
|
||||
std::string tag_;
|
||||
};
|
||||
|
||||
class VKTexture;
|
||||
@ -351,9 +364,11 @@ class VKFramebuffer;
|
||||
|
||||
class VKContext : public DrawContext {
|
||||
public:
|
||||
VKContext(VulkanContext *vulkan, bool splitSubmit);
|
||||
VKContext(VulkanContext *vulkan);
|
||||
virtual ~VKContext();
|
||||
|
||||
void DebugAnnotate(const char *annotation) override;
|
||||
|
||||
const DeviceCaps &GetDeviceCaps() const override {
|
||||
return caps_;
|
||||
}
|
||||
@ -374,8 +389,8 @@ public:
|
||||
InputLayout *CreateInputLayout(const InputLayoutDesc &desc) override;
|
||||
SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override;
|
||||
RasterState *CreateRasterState(const RasterStateDesc &desc) override;
|
||||
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) override;
|
||||
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) override;
|
||||
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) override;
|
||||
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) override;
|
||||
|
||||
Texture *CreateTexture(const TextureDesc &desc) override;
|
||||
Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override;
|
||||
@ -391,11 +406,10 @@ public:
|
||||
// These functions should be self explanatory.
|
||||
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
|
||||
Framebuffer *GetCurrentRenderTarget() override {
|
||||
return curFramebuffer_;
|
||||
return (Framebuffer *)curFramebuffer_.ptr;
|
||||
}
|
||||
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override;
|
||||
|
||||
uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) override;
|
||||
void BindCurrentFramebufferForColorInput() override;
|
||||
|
||||
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
|
||||
|
||||
@ -406,6 +420,7 @@ public:
|
||||
|
||||
void BindSamplerStates(int start, int count, SamplerState **state) override;
|
||||
void BindTextures(int start, int count, Texture **textures) override;
|
||||
void BindNativeTexture(int sampler, void *nativeTexture) override;
|
||||
|
||||
void BindPipeline(Pipeline *pipeline) override {
|
||||
curPipeline_ = (VKPipeline *)pipeline;
|
||||
@ -431,7 +446,7 @@ public:
|
||||
void DrawIndexed(int vertexCount, int offset) override;
|
||||
void DrawUP(const void *vdata, int vertexCount) override;
|
||||
|
||||
void BindCompatiblePipeline();
|
||||
void BindCurrentPipeline();
|
||||
void ApplyDynamicState();
|
||||
|
||||
void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override;
|
||||
@ -442,6 +457,10 @@ public:
|
||||
|
||||
void FlushState() override {}
|
||||
|
||||
void ResetStats() override {
|
||||
renderManager_.ResetStats();
|
||||
}
|
||||
|
||||
std::string GetInfoString(InfoField info) const override {
|
||||
// TODO: Make these actually query the right information
|
||||
switch (info) {
|
||||
@ -464,34 +483,7 @@ public:
|
||||
std::vector<std::string> GetFeatureList() const override;
|
||||
std::vector<std::string> GetExtensionList() const override;
|
||||
|
||||
uint64_t GetNativeObject(NativeObject obj, void *srcObject) override {
|
||||
switch (obj) {
|
||||
case NativeObject::CONTEXT:
|
||||
return (uint64_t)vulkan_;
|
||||
case NativeObject::FRAMEBUFFER_RENDERPASS:
|
||||
// Return a representative renderpass.
|
||||
return (uint64_t)renderManager_.GetFramebufferRenderPass();
|
||||
case NativeObject::BACKBUFFER_RENDERPASS:
|
||||
return (uint64_t)renderManager_.GetBackbufferRenderPass();
|
||||
case NativeObject::COMPATIBLE_RENDERPASS:
|
||||
return (uint64_t)renderManager_.GetCompatibleRenderPass();
|
||||
case NativeObject::INIT_COMMANDBUFFER:
|
||||
return (uint64_t)renderManager_.GetInitCmd();
|
||||
case NativeObject::BOUND_TEXTURE0_IMAGEVIEW:
|
||||
return (uint64_t)boundImageView_[0];
|
||||
case NativeObject::BOUND_TEXTURE1_IMAGEVIEW:
|
||||
return (uint64_t)boundImageView_[1];
|
||||
case NativeObject::RENDER_MANAGER:
|
||||
return (uint64_t)(uintptr_t)&renderManager_;
|
||||
case NativeObject::NULL_IMAGEVIEW:
|
||||
return (uint64_t)GetNullTexture()->GetImageView();
|
||||
case NativeObject::TEXTURE_VIEW:
|
||||
return (uint64_t)(((VKTexture *)srcObject)->GetImageView());
|
||||
default:
|
||||
Crash();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
uint64_t GetNativeObject(NativeObject obj, void *srcObject) override;
|
||||
|
||||
void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override;
|
||||
|
||||
@ -501,6 +493,8 @@ public:
|
||||
|
||||
void InvalidateCachedState() override;
|
||||
|
||||
void InvalidateFramebuffer(FBInvalidationStage stage, uint32_t channels) override;
|
||||
|
||||
private:
|
||||
VulkanTexture *GetNullTexture();
|
||||
VulkanContext *vulkan_ = nullptr;
|
||||
@ -518,7 +512,7 @@ private:
|
||||
VkDescriptorSetLayout descriptorSetLayout_ = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE;
|
||||
VkPipelineCache pipelineCache_ = VK_NULL_HANDLE;
|
||||
AutoRef<Framebuffer> curFramebuffer_;
|
||||
AutoRef<VKFramebuffer> curFramebuffer_;
|
||||
|
||||
VkDevice device_;
|
||||
VkQueue queue_;
|
||||
@ -560,6 +554,11 @@ static int GetBpp(VkFormat format) {
|
||||
case VK_FORMAT_R8G8B8A8_UNORM:
|
||||
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||
return 32;
|
||||
case VK_FORMAT_R8_UNORM:
|
||||
return 8;
|
||||
case VK_FORMAT_R8G8_UNORM:
|
||||
case VK_FORMAT_R16_UNORM:
|
||||
return 16;
|
||||
case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
|
||||
case VK_FORMAT_B4G4R4A4_UNORM_PACK16:
|
||||
case VK_FORMAT_R5G5B5A1_UNORM_PACK16:
|
||||
@ -583,6 +582,9 @@ static VkFormat DataFormatToVulkan(DataFormat format) {
|
||||
case DataFormat::D32F: return VK_FORMAT_D32_SFLOAT;
|
||||
case DataFormat::D32F_S8: return VK_FORMAT_D32_SFLOAT_S8_UINT;
|
||||
case DataFormat::S8: return VK_FORMAT_S8_UINT;
|
||||
|
||||
case DataFormat::R16_UNORM: return VK_FORMAT_R16_UNORM;
|
||||
|
||||
case DataFormat::R16_FLOAT: return VK_FORMAT_R16_SFLOAT;
|
||||
case DataFormat::R16G16_FLOAT: return VK_FORMAT_R16G16_SFLOAT;
|
||||
case DataFormat::R16G16B16A16_FLOAT: return VK_FORMAT_R16G16B16A16_SFLOAT;
|
||||
@ -669,7 +671,7 @@ public:
|
||||
s.magFilter = desc.magFilter == TextureFilter::LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
|
||||
s.minFilter = desc.minFilter == TextureFilter::LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
|
||||
s.mipmapMode = desc.mipFilter == TextureFilter::LINEAR ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
s.maxLod = desc.maxLod;
|
||||
s.maxLod = VK_LOD_CLAMP_NONE;
|
||||
VkResult res = vkCreateSampler(vulkan_->GetDevice(), &s, nullptr, &sampler_);
|
||||
_assert_(VK_SUCCESS == res);
|
||||
}
|
||||
@ -769,7 +771,7 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushBuffer *push, const Textur
|
||||
return true;
|
||||
}
|
||||
|
||||
VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
|
||||
VKContext::VKContext(VulkanContext *vulkan)
|
||||
: vulkan_(vulkan), renderManager_(vulkan) {
|
||||
shaderLanguageDesc_.Init(GLSL_VULKAN);
|
||||
|
||||
@ -791,11 +793,15 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
|
||||
caps_.framebufferSeparateDepthCopySupported = true; // Will pretty much always be the case.
|
||||
caps_.preferredDepthBufferFormat = DataFormat::D24_S8; // TODO: Ask vulkan.
|
||||
caps_.texture3DSupported = true;
|
||||
caps_.textureDepthSupported = true;
|
||||
caps_.fragmentShaderInt32Supported = true;
|
||||
caps_.textureNPOTFullySupported = true;
|
||||
caps_.fragmentShaderDepthWriteSupported = true;
|
||||
caps_.blendMinMaxSupported = true;
|
||||
caps_.logicOpSupported = vulkan->GetDeviceFeatures().enabled.logicOp != 0;
|
||||
|
||||
auto deviceProps = vulkan->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDeviceIndex()).properties;
|
||||
|
||||
switch (deviceProps.vendorID) {
|
||||
case VULKAN_VENDOR_AMD: caps_.vendor = GPUVendor::VENDOR_AMD; break;
|
||||
case VULKAN_VENDOR_ARM: caps_.vendor = GPUVendor::VENDOR_ARM; break;
|
||||
@ -817,6 +823,11 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
|
||||
// Color write mask not masking write in certain scenarios with a depth test, see #10421.
|
||||
// Known still present on driver 0x80180000 and Adreno 5xx (possibly more.)
|
||||
bugs_.Infest(Bugs::COLORWRITEMASK_BROKEN_WITH_DEPTHTEST);
|
||||
|
||||
// Trying to follow all the rules in https://registry.khronos.org/vulkan/specs/1.3/html/vkspec.html#synchronization-pipeline-barriers-subpass-self-dependencies
|
||||
// and https://registry.khronos.org/vulkan/specs/1.3/html/vkspec.html#renderpass-feedbackloop, but still it doesn't
|
||||
// quite work - artifacts on triangle boundaries on Adreno.
|
||||
bugs_.Infest(Bugs::SUBPASS_FEEDBACK_BROKEN);
|
||||
} else if (caps_.vendor == GPUVendor::VENDOR_AMD) {
|
||||
// See issue #10074, and also #10065 (AMD) and #10109 for the choice of the driver version to check for.
|
||||
if (deviceProps.driverVersion < 0x00407000) {
|
||||
@ -826,19 +837,33 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
|
||||
// Workaround for Intel driver bug. TODO: Re-enable after some driver version
|
||||
bugs_.Infest(Bugs::DUAL_SOURCE_BLENDING_BROKEN);
|
||||
} else if (caps_.vendor == GPUVendor::VENDOR_ARM) {
|
||||
int majorVersion = VK_API_VERSION_MAJOR(deviceProps.driverVersion);
|
||||
|
||||
// These GPUs (up to some certain hardware version?) have a bug where draws where gl_Position.w == .z
|
||||
// corrupt the depth buffer. This is easily worked around by simply scaling Z down a tiny bit when this case
|
||||
// is detected. See: https://github.com/hrydgard/ppsspp/issues/11937
|
||||
bugs_.Infest(Bugs::EQUAL_WZ_CORRUPTS_DEPTH);
|
||||
// At least one driver at the upper end of the range is known to be likely to suffer from the bug causing issue #13833 (Midnight Club map broken).
|
||||
bugs_.Infest(Bugs::MALI_STENCIL_DISCARD_BUG);
|
||||
|
||||
// This started in driver 31 or 32.
|
||||
if (VK_API_VERSION_MAJOR(deviceProps.driverVersion) >= 32) {
|
||||
// Nearly identical to the the Adreno bug, see #13833 (Midnight Club map broken) and other issues.
|
||||
// Reported fixed in major version 40 - let's add a check once confirmed.
|
||||
bugs_.Infest(Bugs::NO_DEPTH_CANNOT_DISCARD_STENCIL);
|
||||
|
||||
// This started in driver 31 or 32, fixed in 40 - let's add a check once confirmed.
|
||||
if (majorVersion >= 32) {
|
||||
bugs_.Infest(Bugs::MALI_CONSTANT_LOAD_BUG); // See issue #15661
|
||||
}
|
||||
|
||||
// Older ARM devices have very slow geometry shaders, not worth using. At least before 15.
|
||||
// Also seen to cause weird issues on 18, so let's lump it in.
|
||||
if (majorVersion <= 18) {
|
||||
bugs_.Infest(Bugs::GEOMETRY_SHADERS_SLOW_OR_BROKEN);
|
||||
}
|
||||
}
|
||||
|
||||
// Limited, through input attachments and self-dependencies.
|
||||
// We turn it off here already if buggy.
|
||||
caps_.framebufferFetchSupported = !bugs_.Has(Bugs::SUBPASS_FEEDBACK_BROKEN);
|
||||
|
||||
caps_.deviceID = deviceProps.deviceID;
|
||||
device_ = vulkan->GetDevice();
|
||||
|
||||
@ -891,6 +916,8 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
|
||||
VkResult res = vkCreateDescriptorSetLayout(device_, &dsl, nullptr, &descriptorSetLayout_);
|
||||
_assert_(VK_SUCCESS == res);
|
||||
|
||||
vulkan_->SetDebugName(descriptorSetLayout_, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, "thin3d_d_layout");
|
||||
|
||||
VkPipelineLayoutCreateInfo pl = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
|
||||
pl.pPushConstantRanges = nullptr;
|
||||
pl.pushConstantRangeCount = 0;
|
||||
@ -899,11 +926,11 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
|
||||
res = vkCreatePipelineLayout(device_, &pl, nullptr, &pipelineLayout_);
|
||||
_assert_(VK_SUCCESS == res);
|
||||
|
||||
vulkan_->SetDebugName(pipelineLayout_, VK_OBJECT_TYPE_PIPELINE_LAYOUT, "thin3d_p_layout");
|
||||
|
||||
VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
|
||||
res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_);
|
||||
_assert_(VK_SUCCESS == res);
|
||||
|
||||
renderManager_.SetSplitSubmit(splitSubmit);
|
||||
}
|
||||
|
||||
VKContext::~VKContext() {
|
||||
@ -964,7 +991,7 @@ void VKContext::WipeQueue() {
|
||||
}
|
||||
|
||||
VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) {
|
||||
DescriptorSetKey key;
|
||||
DescriptorSetKey key{};
|
||||
|
||||
FrameData *frame = &frame_[vulkan_->GetCurFrame()];
|
||||
|
||||
@ -985,6 +1012,8 @@ VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
vulkan_->SetDebugName(descSet, VK_OBJECT_TYPE_DESCRIPTOR_SET, "(thin3d desc set)");
|
||||
|
||||
VkDescriptorBufferInfo bufferDesc;
|
||||
bufferDesc.buffer = buf;
|
||||
bufferDesc.offset = 0;
|
||||
@ -1034,18 +1063,39 @@ VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) {
|
||||
return descSet;
|
||||
}
|
||||
|
||||
Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) {
|
||||
VKInputLayout *input = (VKInputLayout *)desc.inputLayout;
|
||||
VKBlendState *blend = (VKBlendState *)desc.blend;
|
||||
VKDepthStencilState *depth = (VKDepthStencilState *)desc.depthStencil;
|
||||
VKRasterState *raster = (VKRasterState *)desc.raster;
|
||||
|
||||
u32 pipelineFlags = 0;
|
||||
PipelineFlags pipelineFlags = (PipelineFlags)0;
|
||||
if (depth->info.depthTestEnable || depth->info.stencilTestEnable) {
|
||||
pipelineFlags |= PIPELINE_FLAG_USES_DEPTH_STENCIL;
|
||||
pipelineFlags |= PipelineFlags::USES_DEPTH_STENCIL;
|
||||
}
|
||||
// TODO: We need code to set USES_BLEND_CONSTANT here too, if we're ever gonna use those in thin3d code.
|
||||
|
||||
VKPipeline *pipeline = new VKPipeline(vulkan_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float), (PipelineFlags)pipelineFlags);
|
||||
VKPipeline *pipeline = new VKPipeline(vulkan_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float), pipelineFlags, tag);
|
||||
|
||||
VKRGraphicsPipelineDesc &gDesc = pipeline->vkrDesc;
|
||||
|
||||
std::vector<VkPipelineShaderStageCreateInfo> stages;
|
||||
stages.resize(desc.shaders.size());
|
||||
|
||||
for (auto &iter : desc.shaders) {
|
||||
VKShaderModule *vkshader = (VKShaderModule *)iter;
|
||||
vkshader->AddRef();
|
||||
pipeline->deps.push_back(vkshader);
|
||||
if (vkshader->GetStage() == ShaderStage::Vertex) {
|
||||
gDesc.vertexShader = vkshader->Get();
|
||||
} else if (vkshader->GetStage() == ShaderStage::Fragment) {
|
||||
gDesc.fragmentShader = vkshader->Get();
|
||||
} else {
|
||||
ERROR_LOG(G3D, "Bad stage");
|
||||
delete pipeline;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (input) {
|
||||
for (int i = 0; i < (int)input->bindings.size(); i++) {
|
||||
@ -1054,84 +1104,51 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
} else {
|
||||
pipeline->stride[0] = 0;
|
||||
}
|
||||
_dbg_assert_(input->bindings.size() == 1);
|
||||
_dbg_assert_((int)input->attributes.size() == (int)input->visc.vertexAttributeDescriptionCount);
|
||||
|
||||
std::vector<VkPipelineShaderStageCreateInfo> stages;
|
||||
stages.resize(desc.shaders.size());
|
||||
int i = 0;
|
||||
for (auto &iter : desc.shaders) {
|
||||
VKShaderModule *vkshader = (VKShaderModule *)iter;
|
||||
if (!vkshader) {
|
||||
ERROR_LOG(G3D, "CreateGraphicsPipeline got passed a null shader");
|
||||
return nullptr;
|
||||
}
|
||||
VkPipelineShaderStageCreateInfo &stage = stages[i++];
|
||||
stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
stage.pNext = nullptr;
|
||||
stage.pSpecializationInfo = nullptr;
|
||||
stage.stage = StageToVulkan(vkshader->GetStage());
|
||||
stage.module = vkshader->Get();
|
||||
stage.pName = "main";
|
||||
stage.flags = 0;
|
||||
gDesc.ibd = input->bindings[0];
|
||||
for (size_t i = 0; i < input->attributes.size(); i++) {
|
||||
gDesc.attrs[i] = input->attributes[i];
|
||||
}
|
||||
gDesc.vis.vertexAttributeDescriptionCount = input->visc.vertexAttributeDescriptionCount;
|
||||
gDesc.vis.vertexBindingDescriptionCount = input->visc.vertexBindingDescriptionCount;
|
||||
gDesc.vis.pVertexBindingDescriptions = &gDesc.ibd;
|
||||
gDesc.vis.pVertexAttributeDescriptions = gDesc.attrs;
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo inputAssembly = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
|
||||
inputAssembly.topology = primToVK[(int)desc.prim];
|
||||
inputAssembly.primitiveRestartEnable = false;
|
||||
gDesc.blend0 = blend->attachments[0];
|
||||
gDesc.cbs = blend->info;
|
||||
gDesc.cbs.pAttachments = &gDesc.blend0;
|
||||
|
||||
gDesc.dss = depth->info;
|
||||
|
||||
raster->ToVulkan(&gDesc.rs);
|
||||
|
||||
// Copy bindings from input layout.
|
||||
gDesc.inputAssembly.topology = primToVK[(int)desc.prim];
|
||||
|
||||
// We treat the three stencil states as a unit in other places, so let's do that here too.
|
||||
VkDynamicState dynamics[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK };
|
||||
VkPipelineDynamicStateCreateInfo dynamicInfo = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
|
||||
dynamicInfo.dynamicStateCount = depth->info.stencilTestEnable ? ARRAY_SIZE(dynamics) : 2;
|
||||
dynamicInfo.pDynamicStates = dynamics;
|
||||
const VkDynamicState dynamics[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK };
|
||||
gDesc.ds.dynamicStateCount = depth->info.stencilTestEnable ? ARRAY_SIZE(dynamics) : 2;
|
||||
for (size_t i = 0; i < gDesc.ds.dynamicStateCount; i++) {
|
||||
gDesc.dynamicStates[i] = dynamics[i];
|
||||
}
|
||||
gDesc.ds.pDynamicStates = gDesc.dynamicStates;
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
|
||||
ms.pSampleMask = nullptr;
|
||||
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
gDesc.ms.pSampleMask = nullptr;
|
||||
gDesc.ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
VkPipelineViewportStateCreateInfo vs{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
|
||||
vs.viewportCount = 1;
|
||||
vs.scissorCount = 1;
|
||||
vs.pViewports = nullptr; // dynamic
|
||||
vs.pScissors = nullptr; // dynamic
|
||||
gDesc.views.viewportCount = 1;
|
||||
gDesc.views.scissorCount = 1;
|
||||
gDesc.views.pViewports = nullptr; // dynamic
|
||||
gDesc.views.pScissors = nullptr; // dynamic
|
||||
|
||||
gDesc.pipelineLayout = pipelineLayout_;
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
|
||||
raster->ToVulkan(&rs);
|
||||
raster->ToVulkan(&gDesc.rs);
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo emptyVisc{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
|
||||
|
||||
VkGraphicsPipelineCreateInfo createInfo[2]{};
|
||||
for (auto &info : createInfo) {
|
||||
info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
info.flags = 0;
|
||||
info.stageCount = (uint32_t)stages.size();
|
||||
info.pStages = stages.data();
|
||||
info.pColorBlendState = &blend->info;
|
||||
info.pDepthStencilState = &depth->info;
|
||||
info.pDynamicState = &dynamicInfo;
|
||||
info.pInputAssemblyState = &inputAssembly;
|
||||
info.pTessellationState = nullptr;
|
||||
info.pMultisampleState = &ms;
|
||||
info.pVertexInputState = input ? &input->visc : &emptyVisc;
|
||||
info.pRasterizationState = &rs;
|
||||
info.pViewportState = &vs; // Must set viewport and scissor counts even if we set the actual state dynamically.
|
||||
info.layout = pipelineLayout_;
|
||||
info.subpass = 0;
|
||||
}
|
||||
|
||||
createInfo[0].renderPass = renderManager_.GetBackbufferRenderPass();
|
||||
createInfo[1].renderPass = renderManager_.GetFramebufferRenderPass();
|
||||
|
||||
// OK, need to create new pipelines.
|
||||
VkPipeline pipelines[2]{};
|
||||
VkResult result = vkCreateGraphicsPipelines(device_, pipelineCache_, 2, createInfo, nullptr, pipelines);
|
||||
if (result != VK_SUCCESS) {
|
||||
ERROR_LOG(G3D, "Failed to create graphics pipeline");
|
||||
delete pipeline;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pipeline->backbufferPipeline = pipelines[0];
|
||||
pipeline->framebufferPipeline = pipelines[1];
|
||||
pipeline->pipeline = renderManager_.CreateGraphicsPipeline(&gDesc, pipelineFlags, 1 << RP_TYPE_BACKBUFFER, tag ? tag : "thin3d");
|
||||
|
||||
if (desc.uniformDesc) {
|
||||
pipeline->dynamicUniformSize = (int)desc.uniformDesc->uniformBufferSize;
|
||||
@ -1289,28 +1306,22 @@ void VKContext::BindTextures(int start, int count, Texture **textures) {
|
||||
}
|
||||
}
|
||||
|
||||
ShaderModule *VKContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t size, const std::string &tag) {
|
||||
void VKContext::BindNativeTexture(int sampler, void *nativeTexture) {
|
||||
boundTextures_[sampler] = nullptr;
|
||||
boundImageView_[sampler] = (VkImageView)nativeTexture;
|
||||
}
|
||||
|
||||
ShaderModule *VKContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t size, const char *tag) {
|
||||
VKShaderModule *shader = new VKShaderModule(stage, tag);
|
||||
if (shader->Compile(vulkan_, language, data, size)) {
|
||||
return shader;
|
||||
} else {
|
||||
ERROR_LOG(G3D, "Failed to compile shader:\n%s", (const char *)LineNumberString((const char *)data).c_str());
|
||||
ERROR_LOG(G3D, "Failed to compile shader %s:\n%s", tag, (const char *)LineNumberString((const char *)data).c_str());
|
||||
shader->Release();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int VKPipeline::GetUniformLoc(const char *name) {
|
||||
int loc = -1;
|
||||
|
||||
// HACK! As we only use one uniform we hardcode it.
|
||||
if (!strcmp(name, "WorldViewProj")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return loc;
|
||||
}
|
||||
|
||||
void VKContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {
|
||||
curPipeline_->SetDynamicUniformData(ub, size);
|
||||
}
|
||||
@ -1336,9 +1347,9 @@ void VKContext::Draw(int vertexCount, int offset) {
|
||||
return;
|
||||
}
|
||||
|
||||
BindCompatiblePipeline();
|
||||
BindCurrentPipeline();
|
||||
ApplyDynamicState();
|
||||
renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount, offset);
|
||||
renderManager_.Draw(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount, offset);
|
||||
}
|
||||
|
||||
void VKContext::DrawIndexed(int vertexCount, int offset) {
|
||||
@ -1356,9 +1367,9 @@ void VKContext::DrawIndexed(int vertexCount, int offset) {
|
||||
return;
|
||||
}
|
||||
|
||||
BindCompatiblePipeline();
|
||||
BindCurrentPipeline();
|
||||
ApplyDynamicState();
|
||||
renderManager_.DrawIndexed(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1, VK_INDEX_TYPE_UINT16);
|
||||
renderManager_.DrawIndexed(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1, VK_INDEX_TYPE_UINT16);
|
||||
}
|
||||
|
||||
void VKContext::DrawUP(const void *vdata, int vertexCount) {
|
||||
@ -1372,18 +1383,13 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
BindCompatiblePipeline();
|
||||
BindCurrentPipeline();
|
||||
ApplyDynamicState();
|
||||
renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount);
|
||||
renderManager_.Draw(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount);
|
||||
}
|
||||
|
||||
void VKContext::BindCompatiblePipeline() {
|
||||
VkRenderPass renderPass = renderManager_.GetCompatibleRenderPass();
|
||||
if (renderPass == renderManager_.GetBackbufferRenderPass()) {
|
||||
renderManager_.BindPipeline(curPipeline_->backbufferPipeline, curPipeline_->flags);
|
||||
} else {
|
||||
renderManager_.BindPipeline(curPipeline_->framebufferPipeline, curPipeline_->flags);
|
||||
}
|
||||
void VKContext::BindCurrentPipeline() {
|
||||
renderManager_.BindPipeline(curPipeline_->pipeline, curPipeline_->flags, pipelineLayout_);
|
||||
}
|
||||
|
||||
void VKContext::Clear(int clearMask, uint32_t colorval, float depthVal, int stencilVal) {
|
||||
@ -1397,8 +1403,8 @@ void VKContext::Clear(int clearMask, uint32_t colorval, float depthVal, int sten
|
||||
renderManager_.Clear(colorval, depthVal, stencilVal, mask);
|
||||
}
|
||||
|
||||
DrawContext *T3DCreateVulkanContext(VulkanContext *vulkan, bool split) {
|
||||
return new VKContext(vulkan, split);
|
||||
DrawContext *T3DCreateVulkanContext(VulkanContext *vulkan) {
|
||||
return new VKContext(vulkan);
|
||||
}
|
||||
|
||||
void AddFeature(std::vector<std::string> &features, const char *name, VkBool32 available, VkBool32 enabled) {
|
||||
@ -1428,7 +1434,7 @@ std::vector<std::string> VKContext::GetFeatureList() const {
|
||||
AddFeature(features, "occlusionQueryPrecise", available.occlusionQueryPrecise, enabled.occlusionQueryPrecise);
|
||||
AddFeature(features, "multiDrawIndirect", available.multiDrawIndirect, enabled.multiDrawIndirect);
|
||||
|
||||
features.push_back(std::string("Preferred depth buffer format: ") + VulkanFormatToString(vulkan_->GetDeviceInfo().preferredDepthStencilFormat));
|
||||
features.emplace_back(std::string("Preferred depth buffer format: ") + VulkanFormatToString(vulkan_->GetDeviceInfo().preferredDepthStencilFormat));
|
||||
|
||||
return features;
|
||||
}
|
||||
@ -1487,15 +1493,16 @@ public:
|
||||
buf_ = nullptr;
|
||||
}
|
||||
VKRFramebuffer *GetFB() const { return buf_; }
|
||||
void UpdateTag(const char *newTag) override {
|
||||
buf_->UpdateTag(newTag);
|
||||
}
|
||||
private:
|
||||
VKRFramebuffer *buf_;
|
||||
};
|
||||
|
||||
Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) {
|
||||
VkCommandBuffer cmd = renderManager_.GetInitCmd();
|
||||
// TODO: We always create with depth here, even when it's not needed (such as color temp FBOs).
|
||||
// Should optimize those away.
|
||||
VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetFramebufferRenderPass(), desc.width, desc.height, desc.tag);
|
||||
VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetQueueRunner()->GetCompatibleRenderPass(), desc.width, desc.height, desc.z_stencil, desc.tag);
|
||||
return new VKFramebuffer(vkrfb);
|
||||
}
|
||||
|
||||
@ -1580,22 +1587,8 @@ void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChanne
|
||||
boundImageView_[binding] = renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, attachment);
|
||||
}
|
||||
|
||||
uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) {
|
||||
if (!fbo)
|
||||
return 0;
|
||||
|
||||
VKFramebuffer *fb = (VKFramebuffer *)fbo;
|
||||
VkImageView view = VK_NULL_HANDLE;
|
||||
switch (channelBit) {
|
||||
case FB_COLOR_BIT:
|
||||
view = fb->GetFB()->color.imageView;
|
||||
break;
|
||||
case FB_DEPTH_BIT:
|
||||
case FB_STENCIL_BIT:
|
||||
view = fb->GetFB()->depth.imageView;
|
||||
break;
|
||||
}
|
||||
return (uintptr_t)view;
|
||||
void VKContext::BindCurrentFramebufferForColorInput() {
|
||||
renderManager_.BindCurrentFramebufferAsInputAttachment0(VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
}
|
||||
|
||||
void VKContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) {
|
||||
@ -1623,4 +1616,47 @@ void VKContext::HandleEvent(Event ev, int width, int height, void *param1, void
|
||||
}
|
||||
}
|
||||
|
||||
void VKContext::InvalidateFramebuffer(FBInvalidationStage stage, uint32_t channels) {
|
||||
VkImageAspectFlags flags = 0;
|
||||
if (channels & FBChannel::FB_COLOR_BIT)
|
||||
flags |= VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
if (channels & FBChannel::FB_DEPTH_BIT)
|
||||
flags |= VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
if (channels & FBChannel::FB_STENCIL_BIT)
|
||||
flags |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
if (stage == FB_INVALIDATION_LOAD) {
|
||||
renderManager_.SetLoadDontCare(flags);
|
||||
} else if (stage == FB_INVALIDATION_STORE) {
|
||||
renderManager_.SetStoreDontCare(flags);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t VKContext::GetNativeObject(NativeObject obj, void *srcObject) {
|
||||
switch (obj) {
|
||||
case NativeObject::CONTEXT:
|
||||
return (uint64_t)vulkan_;
|
||||
case NativeObject::INIT_COMMANDBUFFER:
|
||||
return (uint64_t)renderManager_.GetInitCmd();
|
||||
case NativeObject::BOUND_TEXTURE0_IMAGEVIEW:
|
||||
return (uint64_t)boundImageView_[0];
|
||||
case NativeObject::BOUND_TEXTURE1_IMAGEVIEW:
|
||||
return (uint64_t)boundImageView_[1];
|
||||
case NativeObject::RENDER_MANAGER:
|
||||
return (uint64_t)(uintptr_t)&renderManager_;
|
||||
case NativeObject::NULL_IMAGEVIEW:
|
||||
return (uint64_t)GetNullTexture()->GetImageView();
|
||||
case NativeObject::TEXTURE_VIEW:
|
||||
return (uint64_t)(((VKTexture *)srcObject)->GetImageView());
|
||||
case NativeObject::BOUND_FRAMEBUFFER_COLOR_IMAGEVIEW:
|
||||
return (uint64_t)curFramebuffer_->GetFB()->color.imageView;
|
||||
default:
|
||||
Crash();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void VKContext::DebugAnnotate(const char *annotation) {
|
||||
renderManager_.DebugAnnotate(annotation);
|
||||
}
|
||||
|
||||
} // namespace Draw
|
||||
|
@ -33,6 +33,9 @@ size_t DataFormatSizeInBytes(DataFormat fmt) {
|
||||
case DataFormat::R8G8B8A8_SNORM: return 4;
|
||||
case DataFormat::R8G8B8A8_UINT: return 4;
|
||||
case DataFormat::R8G8B8A8_SINT: return 4;
|
||||
|
||||
case DataFormat::R16_UNORM: return 2;
|
||||
|
||||
case DataFormat::R16_FLOAT: return 2;
|
||||
case DataFormat::R16G16_FLOAT: return 4;
|
||||
case DataFormat::R16G16B16A16_FLOAT: return 8;
|
||||
@ -66,10 +69,15 @@ bool DataFormatIsDepthStencil(DataFormat fmt) {
|
||||
}
|
||||
}
|
||||
|
||||
RefCountedObject::~RefCountedObject() {
|
||||
_dbg_assert_(refcount_ == 0xDEDEDE);
|
||||
}
|
||||
|
||||
bool RefCountedObject::Release() {
|
||||
if (refcount_ > 0 && refcount_ < 10000) {
|
||||
if (--refcount_ == 0) {
|
||||
// Make it very obvious if we try to free this again.
|
||||
refcount_ = 0xDEDEDE;
|
||||
delete this;
|
||||
return true;
|
||||
}
|
||||
@ -427,7 +435,7 @@ vec3 hsv2rgb(vec3 c) {
|
||||
}
|
||||
layout (location = 0) in vec4 pos;
|
||||
layout (location = 1) in vec4 inColor;
|
||||
layout (location = 2) in vec2 inTexCoord;
|
||||
layout (location = 3) in vec2 inTexCoord;
|
||||
layout (location = 0) out vec4 outColor;
|
||||
layout (location = 1) out vec2 outTexCoord;
|
||||
out gl_PerVertex { vec4 gl_Position; };
|
||||
@ -442,6 +450,8 @@ void main() {
|
||||
)"
|
||||
} };
|
||||
|
||||
static_assert(SEM_TEXCOORD0 == 3, "Semantic shader hardcoded in glsl above.");
|
||||
|
||||
const UniformBufferDesc vsTexColBufDesc{ sizeof(VsTexColUB),{
|
||||
{ "WorldViewProj", 0, -1, UniformType::MATRIX4X4, 0 },
|
||||
{ "TintSaturation", 4, -1, UniformType::FLOAT2, 64 },
|
||||
@ -576,7 +586,36 @@ void ConvertFromBGRA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, u
|
||||
dst += dstStride * 3;
|
||||
}
|
||||
} else {
|
||||
WARN_LOG(G3D, "Unable to convert from format to BGRA: %d", (int)format);
|
||||
// But here it shouldn't matter if they do intersect
|
||||
uint16_t *dst16 = (uint16_t *)dst;
|
||||
switch (format) {
|
||||
case Draw::DataFormat::R5G6B5_UNORM_PACK16: // BGR 565
|
||||
for (uint32_t y = 0; y < height; ++y) {
|
||||
ConvertBGRA8888ToRGB565(dst16, src32, width);
|
||||
src32 += srcStride;
|
||||
dst16 += dstStride;
|
||||
}
|
||||
break;
|
||||
case Draw::DataFormat::A1R5G5B5_UNORM_PACK16: // ABGR 1555
|
||||
for (uint32_t y = 0; y < height; ++y) {
|
||||
ConvertBGRA8888ToRGBA5551(dst16, src32, width);
|
||||
src32 += srcStride;
|
||||
dst16 += dstStride;
|
||||
}
|
||||
break;
|
||||
case Draw::DataFormat::A4R4G4B4_UNORM_PACK16: // ABGR 4444
|
||||
for (uint32_t y = 0; y < height; ++y) {
|
||||
ConvertBGRA8888ToRGBA4444(dst16, src32, width);
|
||||
src32 += srcStride;
|
||||
dst16 += dstStride;
|
||||
}
|
||||
break;
|
||||
case Draw::DataFormat::R8G8B8A8_UNORM:
|
||||
case Draw::DataFormat::UNDEFINED:
|
||||
default:
|
||||
WARN_LOG(G3D, "Unable to convert from format to BGRA: %d", (int)format);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -618,6 +657,48 @@ void ConvertToD32F(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This is missing the conversion to the quarter-range we use if depth clamp is not available.
|
||||
// That conversion doesn't necessarily belong here in thin3d, though.
|
||||
void ConvertToD16(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format) {
|
||||
if (format == Draw::DataFormat::D32F) {
|
||||
const float *src32 = (const float *)src;
|
||||
uint16_t *dst16 = (uint16_t *)dst;
|
||||
if (src == dst) {
|
||||
return;
|
||||
} else {
|
||||
for (uint32_t y = 0; y < height; ++y) {
|
||||
for (uint32_t x = 0; x < width; ++x) {
|
||||
dst16[x] = (uint16_t)(src32[x] * 65535.0f);
|
||||
}
|
||||
src32 += srcStride;
|
||||
dst16 += dstStride;
|
||||
}
|
||||
}
|
||||
} else if (format == Draw::DataFormat::D16) {
|
||||
_assert_(src != dst);
|
||||
const uint16_t *src16 = (const uint16_t *)src;
|
||||
uint16_t *dst16 = (uint16_t *)dst;
|
||||
for (uint32_t y = 0; y < height; ++y) {
|
||||
memcpy(dst16, src16, width * 2);
|
||||
src16 += srcStride;
|
||||
dst16 += dstStride;
|
||||
}
|
||||
} else if (format == Draw::DataFormat::D24_S8) {
|
||||
_assert_(src != dst);
|
||||
const uint32_t *src32 = (const uint32_t *)src;
|
||||
uint16_t *dst16 = (uint16_t *)dst;
|
||||
for (uint32_t y = 0; y < height; ++y) {
|
||||
for (uint32_t x = 0; x < width; ++x) {
|
||||
dst16[x] = (src32[x] & 0x00FFFFFF) >> 8;
|
||||
}
|
||||
src32 += srcStride;
|
||||
dst16 += dstStride;
|
||||
}
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
const char *Bugs::GetBugName(uint32_t bug) {
|
||||
switch (bug) {
|
||||
case NO_DEPTH_CANNOT_DISCARD_STENCIL: return "NO_DEPTH_CANNOT_DISCARD_STENCIL";
|
||||
@ -628,9 +709,9 @@ const char *Bugs::GetBugName(uint32_t bug) {
|
||||
case COLORWRITEMASK_BROKEN_WITH_DEPTHTEST: return "COLORWRITEMASK_BROKEN_WITH_DEPTHTEST";
|
||||
case BROKEN_FLAT_IN_SHADER: return "BROKEN_FLAT_IN_SHADER";
|
||||
case EQUAL_WZ_CORRUPTS_DEPTH: return "EQUAL_WZ_CORRUPTS_DEPTH";
|
||||
case MALI_STENCIL_DISCARD_BUG: return "MALI_STENCIL_DISCARD_BUG";
|
||||
case RASPBERRY_SHADER_COMP_HANG: return "RASPBERRY_SHADER_COMP_HANG";
|
||||
case MALI_CONSTANT_LOAD_BUG: return "MALI_CONSTANT_LOAD_BUG";
|
||||
case SUBPASS_FEEDBACK_BROKEN: return "SUBPASS_FEEDBACK_BROKEN";
|
||||
default: return "(N/A)";
|
||||
}
|
||||
}
|
||||
|
@ -114,6 +114,7 @@ enum BufferUsageFlag : int {
|
||||
enum Semantic : int {
|
||||
SEM_POSITION,
|
||||
SEM_COLOR0,
|
||||
SEM_COLOR1,
|
||||
SEM_TEXCOORD0,
|
||||
SEM_TEXCOORD1,
|
||||
SEM_NORMAL,
|
||||
@ -239,12 +240,10 @@ enum class NativeObject {
|
||||
BACKBUFFER_COLOR_TEX,
|
||||
BACKBUFFER_DEPTH_TEX,
|
||||
FEATURE_LEVEL,
|
||||
COMPATIBLE_RENDERPASS,
|
||||
BACKBUFFER_RENDERPASS,
|
||||
FRAMEBUFFER_RENDERPASS,
|
||||
INIT_COMMANDBUFFER,
|
||||
BOUND_TEXTURE0_IMAGEVIEW,
|
||||
BOUND_TEXTURE1_IMAGEVIEW,
|
||||
BOUND_FRAMEBUFFER_COLOR_IMAGEVIEW,
|
||||
RENDER_MANAGER,
|
||||
TEXTURE_VIEW,
|
||||
NULL_IMAGEVIEW,
|
||||
@ -261,6 +260,11 @@ enum FBChannel {
|
||||
FB_FORMAT_BIT = 128, // Actually retrieves the native format instead. D3D11 only.
|
||||
};
|
||||
|
||||
enum FBInvalidationStage {
|
||||
FB_INVALIDATION_LOAD = 1,
|
||||
FB_INVALIDATION_STORE = 2,
|
||||
};
|
||||
|
||||
enum FBBlitFilter {
|
||||
FB_BLIT_NEAREST = 0,
|
||||
FB_BLIT_LINEAR = 1,
|
||||
@ -326,14 +330,17 @@ public:
|
||||
COLORWRITEMASK_BROKEN_WITH_DEPTHTEST = 5,
|
||||
BROKEN_FLAT_IN_SHADER = 6,
|
||||
EQUAL_WZ_CORRUPTS_DEPTH = 7,
|
||||
MALI_STENCIL_DISCARD_BUG = 8,
|
||||
RASPBERRY_SHADER_COMP_HANG = 9,
|
||||
MALI_CONSTANT_LOAD_BUG = 10,
|
||||
RASPBERRY_SHADER_COMP_HANG = 8,
|
||||
MALI_CONSTANT_LOAD_BUG = 9,
|
||||
SUBPASS_FEEDBACK_BROKEN = 10,
|
||||
GEOMETRY_SHADERS_SLOW_OR_BROKEN = 11,
|
||||
MAX_BUG,
|
||||
};
|
||||
|
||||
protected:
|
||||
uint32_t flags_ = 0;
|
||||
|
||||
static_assert(sizeof(flags_) * 8 > MAX_BUG, "Ran out of space for bugs.");
|
||||
};
|
||||
|
||||
class RefCountedObject {
|
||||
@ -341,7 +348,9 @@ public:
|
||||
RefCountedObject() {
|
||||
refcount_ = 1;
|
||||
}
|
||||
virtual ~RefCountedObject() {}
|
||||
RefCountedObject(const RefCountedObject &other) = delete;
|
||||
RefCountedObject& operator=(RefCountedObject const&) = delete;
|
||||
virtual ~RefCountedObject();
|
||||
|
||||
void AddRef() { refcount_++; }
|
||||
bool Release();
|
||||
@ -406,6 +415,7 @@ class Framebuffer : public RefCountedObject {
|
||||
public:
|
||||
int Width() { return width_; }
|
||||
int Height() { return height_; }
|
||||
virtual void UpdateTag(const char *tag) {}
|
||||
protected:
|
||||
int width_ = -1, height_ = -1;
|
||||
};
|
||||
@ -492,7 +502,6 @@ struct SamplerStateDesc {
|
||||
TextureAddressMode wrapU;
|
||||
TextureAddressMode wrapV;
|
||||
TextureAddressMode wrapW;
|
||||
float maxLod;
|
||||
bool shadowCompareEnabled;
|
||||
Comparison shadowCompareFunc;
|
||||
BorderColor borderColor;
|
||||
@ -542,6 +551,8 @@ struct DeviceCaps {
|
||||
bool fragmentShaderInt32Supported;
|
||||
bool textureNPOTFullySupported;
|
||||
bool fragmentShaderDepthWriteSupported;
|
||||
bool textureDepthSupported;
|
||||
bool blendMinMaxSupported;
|
||||
|
||||
std::string deviceName; // The device name to use when creating the thin3d context, to get the same one.
|
||||
};
|
||||
@ -553,6 +564,7 @@ typedef std::function<bool(uint8_t *data, const uint8_t *initData, uint32_t w, u
|
||||
struct TextureDesc {
|
||||
TextureType type;
|
||||
DataFormat format;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
int depth;
|
||||
@ -566,9 +578,9 @@ struct TextureDesc {
|
||||
};
|
||||
|
||||
enum class RPAction {
|
||||
DONT_CARE,
|
||||
CLEAR,
|
||||
KEEP,
|
||||
KEEP = 0,
|
||||
CLEAR = 1,
|
||||
DONT_CARE = 2,
|
||||
};
|
||||
|
||||
struct RenderPassInfo {
|
||||
@ -604,13 +616,18 @@ public:
|
||||
|
||||
virtual void SetErrorCallback(ErrorCallbackFn callback, void *userdata) {}
|
||||
|
||||
virtual void DebugAnnotate(const char *annotation) {}
|
||||
|
||||
// Partial pipeline state, used to create pipelines. (in practice, in d3d11 they'll use the native state objects directly).
|
||||
// TODO: Possibly ditch these and just put the descs directly in PipelineDesc since only D3D11 benefits.
|
||||
virtual DepthStencilState *CreateDepthStencilState(const DepthStencilStateDesc &desc) = 0;
|
||||
virtual BlendState *CreateBlendState(const BlendStateDesc &desc) = 0;
|
||||
virtual SamplerState *CreateSamplerState(const SamplerStateDesc &desc) = 0;
|
||||
virtual RasterState *CreateRasterState(const RasterStateDesc &desc) = 0;
|
||||
// virtual ComputePipeline CreateComputePipeline(const ComputePipelineDesc &desc) = 0
|
||||
virtual InputLayout *CreateInputLayout(const InputLayoutDesc &desc) = 0;
|
||||
virtual ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag = "thin3d") = 0;
|
||||
virtual Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) = 0;
|
||||
|
||||
// Note that these DO NOT AddRef so you must not ->Release presets unless you manually AddRef them.
|
||||
ShaderModule *GetVshaderPreset(VertexShaderPreset preset) { return vsPresets_[preset]; }
|
||||
@ -623,9 +640,6 @@ public:
|
||||
// On some hardware, you might get a 24-bit depth buffer even though you only wanted a 16-bit one.
|
||||
virtual Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) = 0;
|
||||
|
||||
virtual ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag = "thin3d") = 0;
|
||||
virtual Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) = 0;
|
||||
|
||||
// Copies data from the CPU over into the buffer, at a specific offset. This does not change the size of the buffer and cannot write outside it.
|
||||
virtual void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) = 0;
|
||||
|
||||
@ -646,15 +660,21 @@ public:
|
||||
// binding must be < MAX_TEXTURE_SLOTS (0, 1 are okay if it's 2).
|
||||
virtual void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) = 0;
|
||||
|
||||
// deprecated
|
||||
// Framebuffer fetch / input attachment support, needs to be explicit in Vulkan.
|
||||
virtual void BindCurrentFramebufferForColorInput() {}
|
||||
|
||||
// deprecated, only used by D3D9
|
||||
virtual uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) = 0;
|
||||
|
||||
// Useful in OpenGL ES to give hints about framebuffers on tiler GPUs.
|
||||
virtual void InvalidateFramebuffer(Framebuffer *fbo) {}
|
||||
// Could be useful in OpenGL ES to give hints about framebuffers on tiler GPUs
|
||||
// using glInvalidateFramebuffer, although drivers are known to botch that so we currently don't use it.
|
||||
// In Vulkan, this sets the LOAD_OP or the STORE_OP (depending on stage) of the current render pass instance to DONT_CARE.
|
||||
// channels is a bitwise combination of FBChannel::COLOR, DEPTH and STENCIL.
|
||||
virtual void InvalidateFramebuffer(FBInvalidationStage stage, uint32_t channels) {}
|
||||
|
||||
// Dynamic state
|
||||
virtual void SetScissorRect(int left, int top, int width, int height) = 0;
|
||||
@ -667,6 +687,13 @@ public:
|
||||
virtual void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) = 0;
|
||||
virtual void BindIndexBuffer(Buffer *indexBuffer, int offset) = 0;
|
||||
|
||||
// Sometimes it's necessary to bind a texture not created by thin3d, and use with a thin3d pipeline.
|
||||
// Not pretty, and one way in the future could be to create all textures through thin3d.
|
||||
// Data types:
|
||||
// * Vulkan: VkImageView
|
||||
// * D3D11: ID3D11ShaderResourceView*
|
||||
virtual void BindNativeTexture(int sampler, void *nativeTexture) = 0;
|
||||
|
||||
// Only supports a single dynamic uniform buffer, for maximum compatibility with the old APIs and ease of emulation.
|
||||
// More modern methods will be added later.
|
||||
virtual void UpdateDynamicUniformBuffer(const void *ub, size_t size) = 0;
|
||||
@ -710,6 +737,9 @@ public:
|
||||
// Flush state like scissors etc so the caller can do its own custom drawing.
|
||||
virtual void FlushState() {}
|
||||
|
||||
// This is called when we launch a new game, so any collected internal stats in the backends don't carry over.
|
||||
virtual void ResetStats() {}
|
||||
|
||||
virtual int GetCurrentStepId() const = 0;
|
||||
|
||||
protected:
|
||||
|
@ -31,6 +31,6 @@ DrawContext *T3DCreateDX9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapt
|
||||
DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context, ID3D11Device1 *device1, ID3D11DeviceContext1 *context1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector<std::string> adapterNames);
|
||||
#endif
|
||||
|
||||
DrawContext *T3DCreateVulkanContext(VulkanContext *context, bool splitSubmit);
|
||||
DrawContext *T3DCreateVulkanContext(VulkanContext *context);
|
||||
|
||||
} // namespace Draw
|
||||
|
@ -35,7 +35,7 @@ bool ShouldLogNTimes(const char *identifier, int count) {
|
||||
std::lock_guard<std::mutex> lock(logNTimesLock);
|
||||
auto iter = logNTimes.find(identifier);
|
||||
if (iter == logNTimes.end()) {
|
||||
logNTimes.insert(std::pair<const char*, int>(identifier, 1));
|
||||
logNTimes.emplace(identifier, 1);
|
||||
return true;
|
||||
} else {
|
||||
if (iter->second >= count) {
|
||||
|
11
Common/Math/Statistics.cpp
Normal file
11
Common/Math/Statistics.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include <cstdio>
|
||||
|
||||
#include "Common/Math/Statistics.h"
|
||||
|
||||
void SimpleStat::Format(char *buffer, size_t sz) {
|
||||
if (min_ == INFINITY) {
|
||||
snprintf(buffer, sz, "%s: N/A\n", name_);
|
||||
} else {
|
||||
snprintf(buffer, sz, "%s: %0.2f (%0.2f..%0.2f, avg %0.2f)\n", name_, value_, min_, max_, smoothed_);
|
||||
}
|
||||
}
|
42
Common/Math/Statistics.h
Normal file
42
Common/Math/Statistics.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
// Very simple stat for convenience. Keeps track of min, max, smoothed.
|
||||
struct SimpleStat {
|
||||
SimpleStat(const char *name) : name_(name) { Reset(); }
|
||||
|
||||
void Update(double value) {
|
||||
value_ = value;
|
||||
if (min_ == INFINITY) {
|
||||
smoothed_ = value;
|
||||
} else {
|
||||
// TODO: Make factor adjustable?
|
||||
smoothed_ = 0.99 * smoothed_ + 0.01 * value;
|
||||
}
|
||||
if (value < min_) {
|
||||
min_ = value;
|
||||
}
|
||||
if (value > max_) {
|
||||
max_ = value;
|
||||
}
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
value_ = 0.0;
|
||||
smoothed_ = 0.0; // doens't really need init
|
||||
min_ = INFINITY;
|
||||
max_ = -INFINITY;
|
||||
}
|
||||
|
||||
void Format(char *buffer, size_t sz);
|
||||
|
||||
private:
|
||||
const char *name_;
|
||||
|
||||
// These are initialized in Reset().
|
||||
double value_;
|
||||
double min_;
|
||||
double max_;
|
||||
double smoothed_;
|
||||
};
|
@ -580,8 +580,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseExpression(char* exp, IExpressionFunctions* funcs, uint32_t& dest)
|
||||
{
|
||||
bool parseExpression(const char *exp, IExpressionFunctions *funcs, uint32_t &dest) {
|
||||
PostfixExpression postfix;
|
||||
if (initPostfixExpression(exp,funcs,postfix) == false) return false;
|
||||
return parsePostfixExpression(postfix,funcs,dest);
|
||||
|
@ -1,12 +0,0 @@
|
||||
#include "ppsspp_config.h"
|
||||
|
||||
#include "fast_math.h"
|
||||
#include "fast_matrix.h"
|
||||
|
||||
void InitFastMath() {
|
||||
#ifndef _MSC_VER
|
||||
#if PPSSPP_ARCH(ARM_NEON) && !PPSSPP_ARCH(ARM64)
|
||||
fast_matrix_mul_4x4 = &fast_matrix_mul_4x4_neon;
|
||||
#endif
|
||||
#endif
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Fast Math
|
||||
// A mini library of math kernels. These should be large enough to be worth calling
|
||||
// as functions, and generic enough to fit in the "native" library (not PSP specific stuff).
|
||||
|
||||
// NEON versions are dynamically selected at runtime, when you call InitFastMath.
|
||||
|
||||
// SSE versions are hard linked at compile time.
|
||||
|
||||
// See fast_matrix.h for the first set of functions.
|
||||
|
||||
void InitFastMath();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,6 +1,110 @@
|
||||
#include "fast_math.h"
|
||||
#include "ppsspp_config.h"
|
||||
|
||||
#include "fast_matrix.h"
|
||||
|
||||
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
|
||||
|
||||
#include <emmintrin.h>
|
||||
|
||||
#include "fast_matrix.h"
|
||||
|
||||
void fast_matrix_mul_4x4_sse(float *dest, const float *a, const float *b) {
|
||||
int i;
|
||||
__m128 a_col_1 = _mm_loadu_ps(a);
|
||||
__m128 a_col_2 = _mm_loadu_ps(&a[4]);
|
||||
__m128 a_col_3 = _mm_loadu_ps(&a[8]);
|
||||
__m128 a_col_4 = _mm_loadu_ps(&a[12]);
|
||||
|
||||
for (i = 0; i < 16; i += 4) {
|
||||
__m128 r_col = _mm_mul_ps(a_col_1, _mm_set1_ps(b[i]));
|
||||
r_col = _mm_add_ps(r_col, _mm_mul_ps(a_col_2, _mm_set1_ps(b[i + 1])));
|
||||
r_col = _mm_add_ps(r_col, _mm_mul_ps(a_col_3, _mm_set1_ps(b[i + 2])));
|
||||
r_col = _mm_add_ps(r_col, _mm_mul_ps(a_col_4, _mm_set1_ps(b[i + 3])));
|
||||
_mm_storeu_ps(&dest[i], r_col);
|
||||
}
|
||||
}
|
||||
|
||||
#elif PPSSPP_ARCH(ARM_NEON)
|
||||
|
||||
#if defined(_MSC_VER) && PPSSPP_ARCH(ARM64)
|
||||
#include <arm64_neon.h>
|
||||
#else
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
#if PPSSPP_ARCH(ARM)
|
||||
static inline float32x4_t vfmaq_laneq_f32(float32x4_t _s, float32x4_t _a, float32x4_t _b, int lane) {
|
||||
if (lane == 0) return vmlaq_lane_f32(_s, _a, vget_low_f32(_b), 0);
|
||||
else if (lane == 1) return vmlaq_lane_f32(_s, _a, vget_low_f32(_b), 1);
|
||||
else if (lane == 2) return vmlaq_lane_f32(_s, _a, vget_high_f32(_b), 0);
|
||||
else if (lane == 3) return vmlaq_lane_f32(_s, _a, vget_high_f32(_b), 1);
|
||||
else return vdupq_n_f32(0.f);
|
||||
}
|
||||
#endif
|
||||
|
||||
// From https://developer.arm.com/documentation/102467/0100/Matrix-multiplication-example
|
||||
void fast_matrix_mul_4x4_neon(float *C, const float *A, const float *B) {
|
||||
// these are the columns A
|
||||
float32x4_t A0;
|
||||
float32x4_t A1;
|
||||
float32x4_t A2;
|
||||
float32x4_t A3;
|
||||
|
||||
// these are the columns B
|
||||
float32x4_t B0;
|
||||
float32x4_t B1;
|
||||
float32x4_t B2;
|
||||
float32x4_t B3;
|
||||
|
||||
// these are the columns C
|
||||
float32x4_t C0;
|
||||
float32x4_t C1;
|
||||
float32x4_t C2;
|
||||
float32x4_t C3;
|
||||
|
||||
A0 = vld1q_f32(A);
|
||||
A1 = vld1q_f32(A + 4);
|
||||
A2 = vld1q_f32(A + 8);
|
||||
A3 = vld1q_f32(A + 12);
|
||||
|
||||
// Zero accumulators for C values
|
||||
C0 = vmovq_n_f32(0);
|
||||
C1 = vmovq_n_f32(0);
|
||||
C2 = vmovq_n_f32(0);
|
||||
C3 = vmovq_n_f32(0);
|
||||
|
||||
// Multiply accumulate in 4x1 blocks, i.e. each column in C
|
||||
B0 = vld1q_f32(B);
|
||||
C0 = vfmaq_laneq_f32(C0, A0, B0, 0);
|
||||
C0 = vfmaq_laneq_f32(C0, A1, B0, 1);
|
||||
C0 = vfmaq_laneq_f32(C0, A2, B0, 2);
|
||||
C0 = vfmaq_laneq_f32(C0, A3, B0, 3);
|
||||
vst1q_f32(C, C0);
|
||||
|
||||
B1 = vld1q_f32(B + 4);
|
||||
C1 = vfmaq_laneq_f32(C1, A0, B1, 0);
|
||||
C1 = vfmaq_laneq_f32(C1, A1, B1, 1);
|
||||
C1 = vfmaq_laneq_f32(C1, A2, B1, 2);
|
||||
C1 = vfmaq_laneq_f32(C1, A3, B1, 3);
|
||||
vst1q_f32(C + 4, C1);
|
||||
|
||||
B2 = vld1q_f32(B + 8);
|
||||
C2 = vfmaq_laneq_f32(C2, A0, B2, 0);
|
||||
C2 = vfmaq_laneq_f32(C2, A1, B2, 1);
|
||||
C2 = vfmaq_laneq_f32(C2, A2, B2, 2);
|
||||
C2 = vfmaq_laneq_f32(C2, A3, B2, 3);
|
||||
vst1q_f32(C + 8, C2);
|
||||
|
||||
B3 = vld1q_f32(B + 12);
|
||||
C3 = vfmaq_laneq_f32(C3, A0, B3, 0);
|
||||
C3 = vfmaq_laneq_f32(C3, A1, B3, 1);
|
||||
C3 = vfmaq_laneq_f32(C3, A2, B3, 2);
|
||||
C3 = vfmaq_laneq_f32(C3, A3, B3, 3);
|
||||
vst1q_f32(C + 12, C3);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define xx 0
|
||||
#define xy 1
|
||||
#define xz 2
|
||||
@ -40,6 +144,4 @@ void fast_matrix_mul_4x4_c(float *dest, const float *a, const float *b) {
|
||||
dest[ww] = b[wx] * a[xw] + b[wy] * a[yw] + b[wz] * a[zw] + b[ww] * a[ww];
|
||||
}
|
||||
|
||||
#ifndef fast_matrix_mul_4x4
|
||||
fptr_fast_matrix_mul_4x4 fast_matrix_mul_4x4 = &fast_matrix_mul_4x4_c;
|
||||
#endif
|
||||
#endif
|
||||
|
@ -6,11 +6,8 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// A mini library of matrix math kernels.
|
||||
// A mini library of 4x4 matrix muls.
|
||||
|
||||
// TODO: Really need to wrap this block in a macro or something, will get repetitive.
|
||||
|
||||
typedef void(*fptr_fast_matrix_mul_4x4)(float *dest, const float *a, const float *b);
|
||||
extern void fast_matrix_mul_4x4_c(float *dest, const float *a, const float *b);
|
||||
extern void fast_matrix_mul_4x4_neon(float *dest, const float *a, const float *b);
|
||||
extern void fast_matrix_mul_4x4_sse(float *dest, const float *a, const float *b);
|
||||
@ -18,13 +15,12 @@ extern void fast_matrix_mul_4x4_sse(float *dest, const float *a, const float *b)
|
||||
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
|
||||
// Hard link to SSE implementations on x86/amd64
|
||||
#define fast_matrix_mul_4x4 fast_matrix_mul_4x4_sse
|
||||
#elif PPSSPP_ARCH(ARM64)
|
||||
#define fast_matrix_mul_4x4 fast_matrix_mul_4x4_c
|
||||
#elif PPSSPP_ARCH(ARM_NEON)
|
||||
#define fast_matrix_mul_4x4 fast_matrix_mul_4x4_neon
|
||||
#else
|
||||
extern fptr_fast_matrix_mul_4x4 fast_matrix_mul_4x4;
|
||||
#define fast_matrix_mul_4x4 fast_matrix_mul_4x4_c
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
@ -1,54 +0,0 @@
|
||||
#include "ppsspp_config.h"
|
||||
#if PPSSPP_ARCH(ARM_NEON) && !PPSSPP_ARCH(ARM64)
|
||||
|
||||
.syntax unified // Allow both ARM and Thumb-2 instructions
|
||||
.text
|
||||
.align 2 // Align the function code to a 4-byte (2^n) word boundary.
|
||||
.arm // Use ARM instructions instead of Thumb.
|
||||
|
||||
@ From ARM samples
|
||||
@
|
||||
@ matrix_mul_float:
|
||||
@ Calculate 4x4 (matrix 0) * (matrix 1) and store to result 4x4 matrix.
|
||||
@ matrix 0, matrix 1 and result pointers can be the same,
|
||||
@ ie. my_matrix = my_matrix * my_matrix is possible.
|
||||
@
|
||||
@ r0 = pointer to 4x4 result matrix, single precision floats, column major order
|
||||
@ r1 = pointer to 4x4 matrix 0, single precision floats, column major order
|
||||
@ r2 = pointer to 4x4 matrix 1, single precision floats, column major order
|
||||
@
|
||||
|
||||
.globl _fast_matrix_mul_4x4_neon
|
||||
_fast_matrix_mul_4x4_neon:
|
||||
.globl fast_matrix_mul_4x4_neon
|
||||
fast_matrix_mul_4x4_neon:
|
||||
vld1.32 {d16-d19}, [r1]! @ load first eight elements of matrix 0
|
||||
vld1.32 {d20-d23}, [r1]! @ load second eight elements of matrix 0
|
||||
vld1.32 {d0-d3}, [r2]! @ load first eight elements of matrix 1
|
||||
vld1.32 {d4-d7}, [r2]! @ load second eight elements of matrix 1
|
||||
|
||||
vmul.f32 q12, q8, d0[0] @ rslt col0 = (mat0 col0) * (mat1 col0 elt0)
|
||||
vmul.f32 q13, q8, d2[0] @ rslt col1 = (mat0 col0) * (mat1 col1 elt0)
|
||||
vmul.f32 q14, q8, d4[0] @ rslt col2 = (mat0 col0) * (mat1 col2 elt0)
|
||||
vmul.f32 q15, q8, d6[0] @ rslt col3 = (mat0 col0) * (mat1 col3 elt0)
|
||||
|
||||
vmla.f32 q12, q9, d0[1] @ rslt col0 += (mat0 col1) * (mat1 col0 elt1)
|
||||
vmla.f32 q13, q9, d2[1] @ rslt col1 += (mat0 col1) * (mat1 col1 elt1)
|
||||
vmla.f32 q14, q9, d4[1] @ rslt col2 += (mat0 col1) * (mat1 col2 elt1)
|
||||
vmla.f32 q15, q9, d6[1] @ rslt col3 += (mat0 col1) * (mat1 col3 elt1)
|
||||
|
||||
vmla.f32 q12, q10, d1[0] @ rslt col0 += (mat0 col2) * (mat1 col0 elt2)
|
||||
vmla.f32 q13, q10, d3[0] @ rslt col1 += (mat0 col2) * (mat1 col1 elt2)
|
||||
vmla.f32 q14, q10, d5[0] @ rslt col2 += (mat0 col2) * (mat1 col2 elt2)
|
||||
vmla.f32 q15, q10, d7[0] @ rslt col3 += (mat0 col2) * (mat1 col2 elt2)
|
||||
|
||||
vmla.f32 q12, q11, d1[1] @ rslt col0 += (mat0 col3) * (mat1 col0 elt3)
|
||||
vmla.f32 q13, q11, d3[1] @ rslt col1 += (mat0 col3) * (mat1 col1 elt3)
|
||||
vmla.f32 q14, q11, d5[1] @ rslt col2 += (mat0 col3) * (mat1 col2 elt3)
|
||||
vmla.f32 q15, q11, d7[1] @ rslt col3 += (mat0 col3) * (mat1 col3 elt3)
|
||||
|
||||
vst1.32 {d24-d27}, [r0]! @ store first eight elements of result
|
||||
vst1.32 {d28-d31}, [r0]! @ store second eight elements of result
|
||||
bx lr
|
||||
|
||||
#endif
|
@ -1,25 +0,0 @@
|
||||
#include "ppsspp_config.h"
|
||||
|
||||
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
|
||||
|
||||
#include <emmintrin.h>
|
||||
|
||||
#include "fast_matrix.h"
|
||||
|
||||
void fast_matrix_mul_4x4_sse(float *dest, const float *a, const float *b) {
|
||||
int i;
|
||||
__m128 a_col_1 = _mm_loadu_ps(a);
|
||||
__m128 a_col_2 = _mm_loadu_ps(&a[4]);
|
||||
__m128 a_col_3 = _mm_loadu_ps(&a[8]);
|
||||
__m128 a_col_4 = _mm_loadu_ps(&a[12]);
|
||||
|
||||
for (i = 0; i < 16; i += 4) {
|
||||
__m128 r_col = _mm_mul_ps(a_col_1, _mm_set1_ps(b[i]));
|
||||
r_col = _mm_add_ps(r_col, _mm_mul_ps(a_col_2, _mm_set1_ps(b[i + 1])));
|
||||
r_col = _mm_add_ps(r_col, _mm_mul_ps(a_col_3, _mm_set1_ps(b[i + 2])));
|
||||
r_col = _mm_add_ps(r_col, _mm_mul_ps(a_col_4, _mm_set1_ps(b[i + 3])));
|
||||
_mm_storeu_ps(&dest[i], r_col);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,106 +0,0 @@
|
||||
@
|
||||
@ NEON matrix multiplication examples
|
||||
@
|
||||
|
||||
.syntax unified
|
||||
|
||||
@
|
||||
@ matrix_mul_float:
|
||||
@ Calculate 4x4 (matrix 0) * (matrix 1) and store to result 4x4 matrix.
|
||||
@ matrix 0, matrix 1 and result pointers can be the same,
|
||||
@ ie. my_matrix = my_matrix * my_matrix is possible.
|
||||
@
|
||||
@ r0 = pointer to 4x4 result matrix, single precision floats, column major order
|
||||
@ r1 = pointer to 4x4 matrix 0, single precision floats, column major order
|
||||
@ r2 = pointer to 4x4 matrix 1, single precision floats, column major order
|
||||
@
|
||||
|
||||
.global matrix_mul_float
|
||||
matrix_mul_float:
|
||||
vld1.32 {d16-d19}, [r1]! @ load first eight elements of matrix 0
|
||||
vld1.32 {d20-d23}, [r1]! @ load second eight elements of matrix 0
|
||||
vld1.32 {d0-d3}, [r2]! @ load first eight elements of matrix 1
|
||||
vld1.32 {d4-d7}, [r2]! @ load second eight elements of matrix 1
|
||||
|
||||
vmul.f32 q12, q8, d0[0] @ rslt col0 = (mat0 col0) * (mat1 col0 elt0)
|
||||
vmul.f32 q13, q8, d2[0] @ rslt col1 = (mat0 col0) * (mat1 col1 elt0)
|
||||
vmul.f32 q14, q8, d4[0] @ rslt col2 = (mat0 col0) * (mat1 col2 elt0)
|
||||
vmul.f32 q15, q8, d6[0] @ rslt col3 = (mat0 col0) * (mat1 col3 elt0)
|
||||
|
||||
vmla.f32 q12, q9, d0[1] @ rslt col0 += (mat0 col1) * (mat1 col0 elt1)
|
||||
vmla.f32 q13, q9, d2[1] @ rslt col1 += (mat0 col1) * (mat1 col1 elt1)
|
||||
vmla.f32 q14, q9, d4[1] @ rslt col2 += (mat0 col1) * (mat1 col2 elt1)
|
||||
vmla.f32 q15, q9, d6[1] @ rslt col3 += (mat0 col1) * (mat1 col3 elt1)
|
||||
|
||||
vmla.f32 q12, q10, d1[0] @ rslt col0 += (mat0 col2) * (mat1 col0 elt2)
|
||||
vmla.f32 q13, q10, d3[0] @ rslt col1 += (mat0 col2) * (mat1 col1 elt2)
|
||||
vmla.f32 q14, q10, d5[0] @ rslt col2 += (mat0 col2) * (mat1 col2 elt2)
|
||||
vmla.f32 q15, q10, d7[0] @ rslt col3 += (mat0 col2) * (mat1 col2 elt2)
|
||||
|
||||
vmla.f32 q12, q11, d1[1] @ rslt col0 += (mat0 col3) * (mat1 col0 elt3)
|
||||
vmla.f32 q13, q11, d3[1] @ rslt col1 += (mat0 col3) * (mat1 col1 elt3)
|
||||
vmla.f32 q14, q11, d5[1] @ rslt col2 += (mat0 col3) * (mat1 col2 elt3)
|
||||
vmla.f32 q15, q11, d7[1] @ rslt col3 += (mat0 col3) * (mat1 col3 elt3)
|
||||
|
||||
vst1.32 {d24-d27}, [r0]! @ store first eight elements of result
|
||||
vst1.32 {d28-d31}, [r0]! @ store second eight elements of result
|
||||
|
||||
mov pc, lr @ return to caller
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ Macro: mul_col_s16
|
||||
@
|
||||
@ Multiply a four s16 element column of a matrix by the columns of a second matrix
|
||||
@ to give a column of results. Elements are assumed to be in Q1.14 format.
|
||||
@ Inputs: col_d - d register containing a column of the matrix
|
||||
@ Outputs: res_d - d register containing the column of results
|
||||
@ Corrupts: register q12
|
||||
@ Assumes: the second matrix columns are in registers d16-d19 in column major order
|
||||
@
|
||||
|
||||
.macro mul_col_s16 res_d, col_d
|
||||
vmull.s16 q12, d16, \col_d[0] @ multiply col element 0 by matrix col 0
|
||||
vmlal.s16 q12, d17, \col_d[1] @ multiply-acc col element 1 by matrix col 1
|
||||
vmlal.s16 q12, d18, \col_d[2] @ multiply-acc col element 2 by matrix col 2
|
||||
vmlal.s16 q12, d19, \col_d[3] @ multiply-acc col element 3 by matrix col 3
|
||||
vqrshrn.s32 \res_d, q12, #14 @ shift right and narrow accumulator into
|
||||
@ Q1.14 fixed point format, with saturation
|
||||
.endm
|
||||
|
||||
@
|
||||
@ matrix_mul_fixed:
|
||||
@ Calculate 4x4 (matrix 0) * (matrix 1) and store to result 4x4 matrix.
|
||||
@ matrix 0, matrix 1 and result pointers can be the same,
|
||||
@ ie. my_matrix = my_matrix * my_matrix is possible
|
||||
@
|
||||
@ r0 = pointer to 4x4 result matrix, Q1.14 fixed point, column major order
|
||||
@ r1 = pointer to 4x4 matrix 0, Q1.14 fixed point, column major order
|
||||
@ r2 = pointer to 4x4 matrix 1, Q1.14 fixed point, column major order
|
||||
@
|
||||
|
||||
.global matrix_mul_fixed
|
||||
matrix_mul_fixed:
|
||||
vld1.16 {d16-d19}, [r1] @ load sixteen elements of matrix 0
|
||||
vld1.16 {d0-d3}, [r2] @ load sixteen elements of matrix 1
|
||||
|
||||
mul_col_s16 d4, d0 @ matrix 0 * matrix 1 col 0
|
||||
mul_col_s16 d5, d1 @ matrix 0 * matrix 1 col 1
|
||||
mul_col_s16 d6, d2 @ matrix 0 * matrix 1 col 2
|
||||
mul_col_s16 d7, d3 @ matrix 0 * matrix 1 col 3
|
||||
|
||||
vst1.16 {d4-d7}, [r0] @ store sixteen elements of result
|
||||
|
||||
mov pc, lr @ return to caller
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -28,6 +28,7 @@ inline bool isPowerOf2(int n) {
|
||||
return n == 1 || (n & (n - 1)) == 0;
|
||||
}
|
||||
|
||||
// Next power of 2.
|
||||
inline uint32_t RoundUpToPowerOf2(uint32_t v) {
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#define ATLAS_MAGIC ('A' + ('T' << 8) + ('L' << 16) | ('A' << 24))
|
||||
#define ATLAS_MAGIC ('A' | ('T' << 8) | ('L' << 16) | ('A' << 24))
|
||||
|
||||
// Metadata file structure v0:
|
||||
//
|
||||
|
2103
Common/RiscVEmitter.cpp
Normal file
2103
Common/RiscVEmitter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
472
Common/RiscVEmitter.h
Normal file
472
Common/RiscVEmitter.h
Normal file
@ -0,0 +1,472 @@
|
||||
// Copyright (c) 2022- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
#include "Common/CodeBlock.h"
|
||||
#include "Common/Common.h"
|
||||
|
||||
namespace RiscVGen {
|
||||
|
||||
enum RiscVReg {
|
||||
X0 = 0, X1, X2, X3, X4, X5, X6, X7,
|
||||
X8, X9, X10, X11, X12, X13, X14, X15,
|
||||
X16, X17, X18, X19, X20, X21, X22, X23,
|
||||
X24, X25, X26, X27, X28, X29, X30, X31,
|
||||
|
||||
R_ZERO = 0,
|
||||
R_RA = 1,
|
||||
R_SP = 2,
|
||||
R_GP = 3,
|
||||
R_TP = 4,
|
||||
R_FP = 8,
|
||||
|
||||
F0 = 0x20, F1, F2, F3, F4, F5, F6, F7,
|
||||
F8, F9, F10, F11, F12, F13, F14, F15,
|
||||
F16, F17, F18, F19, F20, F21, F22, F23,
|
||||
F24, F25, F26, F27, F28, F29, F30, F31,
|
||||
};
|
||||
|
||||
enum class FixupBranchType {
|
||||
B,
|
||||
J,
|
||||
CB,
|
||||
CJ,
|
||||
};
|
||||
|
||||
enum class Fence {
|
||||
I = 0b1000,
|
||||
O = 0b0100,
|
||||
R = 0b0010,
|
||||
W = 0b0001,
|
||||
RW = R | W,
|
||||
IO = I | O,
|
||||
};
|
||||
ENUM_CLASS_BITOPS(Fence);
|
||||
|
||||
enum class Atomic {
|
||||
NONE = 0b00,
|
||||
ACQUIRE = 0b10,
|
||||
RELEASE = 0b01,
|
||||
SEQUENTIAL = 0b11,
|
||||
};
|
||||
|
||||
enum class Round {
|
||||
NEAREST_EVEN = 0b000,
|
||||
TOZERO = 0b001,
|
||||
DOWN = 0b010,
|
||||
UP = 0b011,
|
||||
NEAREST_MAX = 0b100,
|
||||
DYNAMIC = 0b111,
|
||||
};
|
||||
|
||||
enum class FConv {
|
||||
W = 0x0000,
|
||||
WU = 0x0001,
|
||||
L = 0x0002,
|
||||
LU = 0x0003,
|
||||
|
||||
S = 0x1000,
|
||||
D = 0x1001,
|
||||
Q = 0x1003,
|
||||
};
|
||||
|
||||
enum class FMv {
|
||||
X,
|
||||
W,
|
||||
D,
|
||||
};
|
||||
|
||||
enum class Csr {
|
||||
FFlags = 0x001,
|
||||
FRm = 0x002,
|
||||
FCsr = 0x003,
|
||||
|
||||
Cycle = 0xC00,
|
||||
Time = 0xC01,
|
||||
InstRet = 0xC02,
|
||||
CycleH = 0xC80,
|
||||
TimeH = 0xC81,
|
||||
InstRetH = 0xC82,
|
||||
};
|
||||
|
||||
struct FixupBranch {
|
||||
FixupBranch() {}
|
||||
FixupBranch(const u8 *p, FixupBranchType t) : ptr(p), type(t) {}
|
||||
~FixupBranch();
|
||||
|
||||
const u8 *ptr = nullptr;
|
||||
FixupBranchType type;
|
||||
};
|
||||
|
||||
class RiscVEmitter {
|
||||
public:
|
||||
RiscVEmitter() {}
|
||||
RiscVEmitter(const u8 *codePtr, u8 *writablePtr);
|
||||
virtual ~RiscVEmitter() {}
|
||||
|
||||
void SetCodePointer(const u8 *ptr, u8 *writePtr);
|
||||
const u8 *GetCodePointer() const;
|
||||
u8 *GetWritableCodePtr();
|
||||
|
||||
void ReserveCodeSpace(u32 bytes);
|
||||
const u8 *AlignCode16();
|
||||
const u8 *AlignCodePage();
|
||||
void FlushIcache();
|
||||
void FlushIcacheSection(const u8 *start, const u8 *end);
|
||||
|
||||
void SetJumpTarget(FixupBranch &branch);
|
||||
bool BInRange(const void *func) const;
|
||||
bool JInRange(const void *func) const;
|
||||
|
||||
void LUI(RiscVReg rd, s32 simm32);
|
||||
void AUIPC(RiscVReg rd, s32 simm32);
|
||||
|
||||
void JAL(RiscVReg rd, const void *dst);
|
||||
void JALR(RiscVReg rd, RiscVReg rs1, s32 simm12);
|
||||
FixupBranch JAL(RiscVReg rd);
|
||||
|
||||
// Psuedo-instructions for convenience/clarity.
|
||||
void J(const void *dst) {
|
||||
JAL(R_ZERO, dst);
|
||||
}
|
||||
void JR(RiscVReg rs1, u32 simm12 = 0) {
|
||||
JALR(R_ZERO, rs1, simm12);
|
||||
}
|
||||
void RET() {
|
||||
JR(R_RA);
|
||||
}
|
||||
FixupBranch J() {
|
||||
return JAL(R_ZERO);
|
||||
}
|
||||
|
||||
void BEQ(RiscVReg rs1, RiscVReg rs2, const void *dst);
|
||||
void BNE(RiscVReg rs1, RiscVReg rs2, const void *dst);
|
||||
void BLT(RiscVReg rs1, RiscVReg rs2, const void *dst);
|
||||
void BGE(RiscVReg rs1, RiscVReg rs2, const void *dst);
|
||||
void BLTU(RiscVReg rs1, RiscVReg rs2, const void *dst);
|
||||
void BGEU(RiscVReg rs1, RiscVReg rs2, const void *dst);
|
||||
FixupBranch BEQ(RiscVReg rs1, RiscVReg rs2);
|
||||
FixupBranch BNE(RiscVReg rs1, RiscVReg rs2);
|
||||
FixupBranch BLT(RiscVReg rs1, RiscVReg rs2);
|
||||
FixupBranch BGE(RiscVReg rs1, RiscVReg rs2);
|
||||
FixupBranch BLTU(RiscVReg rs1, RiscVReg rs2);
|
||||
FixupBranch BGEU(RiscVReg rs1, RiscVReg rs2);
|
||||
|
||||
void LB(RiscVReg rd, RiscVReg addr, s32 simm12);
|
||||
void LH(RiscVReg rd, RiscVReg addr, s32 simm12);
|
||||
void LW(RiscVReg rd, RiscVReg addr, s32 simm12);
|
||||
void LBU(RiscVReg rd, RiscVReg addr, s32 simm12);
|
||||
void LHU(RiscVReg rd, RiscVReg addr, s32 simm12);
|
||||
|
||||
void SB(RiscVReg rs2, RiscVReg addr, s32 simm12);
|
||||
void SH(RiscVReg rs2, RiscVReg addr, s32 simm12);
|
||||
void SW(RiscVReg rs2, RiscVReg addr, s32 simm12);
|
||||
|
||||
void ADDI(RiscVReg rd, RiscVReg rs1, s32 simm12);
|
||||
void SLTI(RiscVReg rd, RiscVReg rs1, s32 simm12);
|
||||
void SLTIU(RiscVReg rd, RiscVReg rs1, s32 simm12);
|
||||
void XORI(RiscVReg rd, RiscVReg rs1, s32 simm12);
|
||||
void ORI(RiscVReg rd, RiscVReg rs1, s32 simm12);
|
||||
void ANDI(RiscVReg rd, RiscVReg rs1, s32 simm12);
|
||||
|
||||
void NOP() {
|
||||
ADDI(R_ZERO, R_ZERO, 0);
|
||||
}
|
||||
void MV(RiscVReg rd, RiscVReg rs1) {
|
||||
ADDI(rd, rs1, 0);
|
||||
}
|
||||
void NOT(RiscVReg rd, RiscVReg rs1) {
|
||||
XORI(rd, rs1, -1);
|
||||
}
|
||||
|
||||
// The temp reg is only possibly used for 64-bit values.
|
||||
template <typename T>
|
||||
void LI(RiscVReg rd, const T &v, RiscVReg temp = R_ZERO) {
|
||||
_assert_msg_(rd != R_ZERO, "LI to X0");
|
||||
_assert_msg_(rd < F0 && temp < F0, "LI to non-GPR");
|
||||
|
||||
uint64_t value = AsImmediate<T, std::is_signed<T>::value>(v);
|
||||
SetRegToImmediate(rd, value, temp);
|
||||
}
|
||||
|
||||
void SLLI(RiscVReg rd, RiscVReg rs1, u32 shamt);
|
||||
void SRLI(RiscVReg rd, RiscVReg rs1, u32 shamt);
|
||||
void SRAI(RiscVReg rd, RiscVReg rs1, u32 shamt);
|
||||
|
||||
void ADD(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SUB(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SLL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SLT(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SLTU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void XOR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SRL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SRA(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void OR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void AND(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
|
||||
void NEG(RiscVReg rd, RiscVReg rs) {
|
||||
SUB(rd, R_ZERO, rs);
|
||||
}
|
||||
|
||||
void FENCE(Fence predecessor, Fence successor);
|
||||
void FENCE_TSO();
|
||||
|
||||
void ECALL();
|
||||
void EBREAK();
|
||||
|
||||
// 64-bit instructions - oens ending in W sign extend result to 32 bits.
|
||||
void LWU(RiscVReg rd, RiscVReg addr, s32 simm12);
|
||||
void LD(RiscVReg rd, RiscVReg addr, s32 simm12);
|
||||
void SD(RiscVReg rs2, RiscVReg addr, s32 simm12);
|
||||
void ADDIW(RiscVReg rd, RiscVReg rs1, s32 simm12);
|
||||
void SLLIW(RiscVReg rd, RiscVReg rs1, u32 shamt);
|
||||
void SRLIW(RiscVReg rd, RiscVReg rs1, u32 shamt);
|
||||
void SRAIW(RiscVReg rd, RiscVReg rs1, u32 shamt);
|
||||
void ADDW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SUBW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SLLW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SRLW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SRAW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
|
||||
void NEGW(RiscVReg rd, RiscVReg rs) {
|
||||
SUBW(rd, R_ZERO, rs);
|
||||
}
|
||||
|
||||
// Integer multiplication and division.
|
||||
void MUL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void MULH(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void MULHSU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void MULHU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void DIV(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void DIVU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void REM(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void REMU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
// 64-bit only multiply and divide.
|
||||
void MULW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void DIVW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void DIVUW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void REMW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void REMUW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
|
||||
// Atomic memory operations.
|
||||
void LR(int bits, RiscVReg rd, RiscVReg addr, Atomic ordering);
|
||||
void SC(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOSWAP(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOADD(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOAND(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOOR(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOXOR(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOMIN(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOMAX(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOMINU(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOMAXU(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
|
||||
// Floating point (same funcs for single/double/quad, if supported.)
|
||||
void FL(int bits, RiscVReg rd, RiscVReg addr, s32 simm12);
|
||||
void FS(int bits, RiscVReg rs2, RiscVReg addr, s32 simm12);
|
||||
void FLW(RiscVReg rd, RiscVReg addr, s32 simm12) {
|
||||
FL(32, rd, addr, simm12);
|
||||
}
|
||||
void FSW(RiscVReg rs2, RiscVReg addr, s32 simm12) {
|
||||
FS(32, rs2, addr, simm12);
|
||||
}
|
||||
|
||||
void FMADD(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm = Round::DYNAMIC);
|
||||
void FMSUB(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm = Round::DYNAMIC);
|
||||
void FNMSUB(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm = Round::DYNAMIC);
|
||||
void FNMADD(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm = Round::DYNAMIC);
|
||||
|
||||
void FADD(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, Round rm = Round::DYNAMIC);
|
||||
void FSUB(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, Round rm = Round::DYNAMIC);
|
||||
void FMUL(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, Round rm = Round::DYNAMIC);
|
||||
void FDIV(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, Round rm = Round::DYNAMIC);
|
||||
void FSQRT(int bits, RiscVReg rd, RiscVReg rs1, Round rm = Round::DYNAMIC);
|
||||
|
||||
void FSGNJ(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void FSGNJN(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void FSGNJX(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
|
||||
void FMV(int bits, RiscVReg rd, RiscVReg rs) {
|
||||
FSGNJ(bits, rd, rs, rs);
|
||||
}
|
||||
void FNEG(int bits, RiscVReg rd, RiscVReg rs) {
|
||||
FSGNJN(bits, rd, rs, rs);
|
||||
}
|
||||
void FABS(int bits, RiscVReg rd, RiscVReg rs) {
|
||||
FSGNJX(bits, rd, rs, rs);
|
||||
}
|
||||
|
||||
void FMIN(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void FMAX(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
|
||||
void FCVT(FConv to, FConv from, RiscVReg rd, RiscVReg rs1, Round rm = Round::DYNAMIC);
|
||||
void FMV(FMv to, FMv from, RiscVReg rd, RiscVReg rs1);
|
||||
|
||||
void FEQ(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void FLT(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void FLE(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void FCLASS(int bits, RiscVReg rd, RiscVReg rs1);
|
||||
|
||||
// Control state register manipulation.
|
||||
void CSRRW(RiscVReg rd, Csr csr, RiscVReg rs1);
|
||||
void CSRRS(RiscVReg rd, Csr csr, RiscVReg rs1);
|
||||
void CSRRC(RiscVReg rd, Csr csr, RiscVReg rs1);
|
||||
void CSRRWI(RiscVReg rd, Csr csr, u8 uimm5);
|
||||
void CSRRSI(RiscVReg rd, Csr csr, u8 uimm5);
|
||||
void CSRRCI(RiscVReg rd, Csr csr, u8 uimm5);
|
||||
|
||||
void FRRM(RiscVReg rd) {
|
||||
CSRRS(rd, Csr::FRm, R_ZERO);
|
||||
}
|
||||
void FSRM(RiscVReg rs) {
|
||||
CSRRW(R_ZERO, Csr::FRm, rs);
|
||||
}
|
||||
void FSRMI(RiscVReg rd, Round rm) {
|
||||
_assert_msg_(rm != Round::DYNAMIC, "Cannot set FRm to DYNAMIC");
|
||||
CSRRWI(rd, Csr::FRm, (uint8_t)rm);
|
||||
}
|
||||
void FSRMI(Round rm) {
|
||||
FSRMI(R_ZERO, rm);
|
||||
}
|
||||
|
||||
// Compressed instructions.
|
||||
void C_ADDI4SPN(RiscVReg rd, u32 nzuimm10);
|
||||
void C_FLD(RiscVReg rd, RiscVReg addr, u8 uimm8);
|
||||
void C_LW(RiscVReg rd, RiscVReg addr, u8 uimm7);
|
||||
void C_FLW(RiscVReg rd, RiscVReg addr, u8 uimm7);
|
||||
void C_LD(RiscVReg rd, RiscVReg addr, u8 uimm8);
|
||||
void C_FSD(RiscVReg rs2, RiscVReg addr, u8 uimm8);
|
||||
void C_SW(RiscVReg rs2, RiscVReg addr, u8 uimm7);
|
||||
void C_FSW(RiscVReg rs2, RiscVReg addr, u8 uimm7);
|
||||
void C_SD(RiscVReg rs2, RiscVReg addr, u8 uimm8);
|
||||
|
||||
void C_NOP();
|
||||
void C_ADDI(RiscVReg rd, s8 nzsimm6);
|
||||
void C_JAL(const void *dst);
|
||||
FixupBranch C_JAL();
|
||||
void C_ADDIW(RiscVReg rd, s8 simm6);
|
||||
void C_LI(RiscVReg rd, s8 simm6);
|
||||
void C_ADDI16SP(s32 nzsimm10);
|
||||
void C_LUI(RiscVReg rd, s32 nzsimm18);
|
||||
void C_SRLI(RiscVReg rd, u8 nzuimm6);
|
||||
void C_SRAI(RiscVReg rd, u8 nzuimm6);
|
||||
void C_ANDI(RiscVReg rd, s8 simm6);
|
||||
void C_SUB(RiscVReg rd, RiscVReg rs2);
|
||||
void C_XOR(RiscVReg rd, RiscVReg rs2);
|
||||
void C_OR(RiscVReg rd, RiscVReg rs2);
|
||||
void C_AND(RiscVReg rd, RiscVReg rs2);
|
||||
void C_SUBW(RiscVReg rd, RiscVReg rs2);
|
||||
void C_ADDW(RiscVReg rd, RiscVReg rs2);
|
||||
void C_J(const void *dst);
|
||||
void C_BEQZ(RiscVReg rs1, const void *dst);
|
||||
void C_BNEZ(RiscVReg rs1, const void *dst);
|
||||
FixupBranch C_J();
|
||||
FixupBranch C_BEQZ(RiscVReg rs1);
|
||||
FixupBranch C_BNEZ(RiscVReg rs1);
|
||||
|
||||
void C_SLLI(RiscVReg rd, u8 nzuimm6);
|
||||
void C_FLDSP(RiscVReg rd, u32 uimm9);
|
||||
void C_LWSP(RiscVReg rd, u8 uimm8);
|
||||
void C_FLWSP(RiscVReg rd, u8 uimm8);
|
||||
void C_LDSP(RiscVReg rd, u32 uimm9);
|
||||
void C_JR(RiscVReg rs1);
|
||||
void C_MV(RiscVReg rd, RiscVReg rs2);
|
||||
void C_EBREAK();
|
||||
void C_JALR(RiscVReg rs1);
|
||||
void C_ADD(RiscVReg rd, RiscVReg rs2);
|
||||
void C_FSDSP(RiscVReg rs2, u32 uimm9);
|
||||
void C_SWSP(RiscVReg rs2, u8 uimm8);
|
||||
void C_FSWSP(RiscVReg rs2, u8 uimm8);
|
||||
void C_SDSP(RiscVReg rs2, u32 uimm9);
|
||||
|
||||
bool CBInRange(const void *func) const;
|
||||
bool CJInRange(const void *func) const;
|
||||
|
||||
bool SetAutoCompress(bool flag) {
|
||||
bool prev = autoCompress_;
|
||||
autoCompress_ = flag;
|
||||
return prev;
|
||||
}
|
||||
bool AutoCompress() const;
|
||||
|
||||
private:
|
||||
void SetJumpTarget(FixupBranch &branch, const void *dst);
|
||||
bool BInRange(const void *src, const void *dst) const;
|
||||
bool JInRange(const void *src, const void *dst) const;
|
||||
bool CBInRange(const void *src, const void *dst) const;
|
||||
bool CJInRange(const void *src, const void *dst) const;
|
||||
|
||||
void SetRegToImmediate(RiscVReg rd, uint64_t value, RiscVReg temp);
|
||||
|
||||
template <typename T, bool extend>
|
||||
uint64_t AsImmediate(const T &v) {
|
||||
static_assert(std::is_trivial<T>::value, "Immediate argument must be a simple type");
|
||||
static_assert(sizeof(T) <= 8, "Immediate argument size should be 8, 16, 32, or 64 bits");
|
||||
|
||||
// Copy the type to allow floats and avoid endian issues.
|
||||
if (sizeof(T) == 8) {
|
||||
uint64_t value;
|
||||
memcpy(&value, &v, sizeof(value));
|
||||
return value;
|
||||
} else if (sizeof(T) == 4) {
|
||||
uint32_t value;
|
||||
memcpy(&value, &v, sizeof(value));
|
||||
if (extend)
|
||||
return (int64_t)(int32_t)value;
|
||||
return value;
|
||||
} else if (sizeof(T) == 2) {
|
||||
uint16_t value;
|
||||
memcpy(&value, &v, sizeof(value));
|
||||
if (extend)
|
||||
return (int64_t)(int16_t)value;
|
||||
return value;
|
||||
} else if (sizeof(T) == 1) {
|
||||
uint8_t value;
|
||||
memcpy(&value, &v, sizeof(value));
|
||||
if (extend)
|
||||
return (int64_t)(int8_t)value;
|
||||
return value;
|
||||
}
|
||||
return (uint64_t)v;
|
||||
}
|
||||
|
||||
inline void Write32(u32 value) {
|
||||
Write16(value & 0x0000FFFF);
|
||||
Write16(value >> 16);
|
||||
}
|
||||
inline void Write16(u16 value) {
|
||||
*(u16 *)writable_ = value;
|
||||
code_ += 2;
|
||||
writable_ += 2;
|
||||
}
|
||||
|
||||
const u8 *code_ = nullptr;
|
||||
u8 *writable_ = nullptr;
|
||||
const u8 *lastCacheFlushEnd_ = nullptr;
|
||||
bool autoCompress_ = false;
|
||||
};
|
||||
|
||||
class MIPSCodeBlock : public CodeBlock<RiscVEmitter> {
|
||||
private:
|
||||
void PoisonMemory(int offset) override;
|
||||
};
|
||||
|
||||
};
|
@ -44,15 +44,16 @@
|
||||
#include "Common/Buffer.h"
|
||||
#include "Common/StringUtils.h"
|
||||
|
||||
void truncate_cpy(char *dest, size_t destSize, const char *src) {
|
||||
size_t truncate_cpy(char *dest, size_t destSize, const char *src) {
|
||||
size_t len = strlen(src);
|
||||
if (len >= destSize - 1) {
|
||||
memcpy(dest, src, destSize - 1);
|
||||
dest[destSize - 1] = '\0';
|
||||
len = destSize - 1;
|
||||
} else {
|
||||
memcpy(dest, src, len);
|
||||
dest[len] = '\0';
|
||||
}
|
||||
dest[len] = '\0';
|
||||
return len;
|
||||
}
|
||||
|
||||
const char* safe_string(const char* s) {
|
||||
@ -272,7 +273,7 @@ void SplitString(const std::string& str, const char delim, std::vector<std::stri
|
||||
size_t next = 0;
|
||||
for (size_t pos = 0, len = str.length(); pos < len; ++pos) {
|
||||
if (str[pos] == delim) {
|
||||
output.push_back(str.substr(next, pos - next));
|
||||
output.emplace_back(str.substr(next, pos - next));
|
||||
// Skip the delimiter itself.
|
||||
next = pos + 1;
|
||||
}
|
||||
@ -281,7 +282,7 @@ void SplitString(const std::string& str, const char delim, std::vector<std::stri
|
||||
if (next == 0) {
|
||||
output.push_back(str);
|
||||
} else if (next < str.length()) {
|
||||
output.push_back(str.substr(next));
|
||||
output.emplace_back(str.substr(next));
|
||||
}
|
||||
}
|
||||
|
||||
@ -293,7 +294,7 @@ void GetQuotedStrings(const std::string& str, std::vector<std::string>& output)
|
||||
if (str[pos] == '\"' || str[pos] == '\'') {
|
||||
if (even) {
|
||||
//quoted text
|
||||
output.push_back(str.substr(next, pos - next));
|
||||
output.emplace_back(str.substr(next, pos - next));
|
||||
even = 0;
|
||||
} else {
|
||||
//non quoted text
|
||||
|
@ -78,10 +78,10 @@ std::string ReplaceAll(std::string input, const std::string& src, const std::str
|
||||
|
||||
void SkipSpace(const char **ptr);
|
||||
|
||||
void truncate_cpy(char *dest, size_t destSize, const char *src);
|
||||
size_t truncate_cpy(char *dest, size_t destSize, const char *src);
|
||||
template<size_t Count>
|
||||
inline void truncate_cpy(char(&out)[Count], const char *src) {
|
||||
truncate_cpy(out, Count, src);
|
||||
inline size_t truncate_cpy(char(&out)[Count], const char *src) {
|
||||
return truncate_cpy(out, Count, src);
|
||||
}
|
||||
|
||||
const char* safe_string(const char* s);
|
||||
|
@ -19,7 +19,7 @@ struct Mailbox {
|
||||
|
||||
std::mutex mutex_;
|
||||
std::condition_variable condvar_;
|
||||
T data_ = nullptr;
|
||||
T data_{};
|
||||
|
||||
T Wait() {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
@ -43,7 +43,7 @@ struct Mailbox {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
if (!data_) {
|
||||
data_ = data;
|
||||
condvar_.notify_one();
|
||||
condvar_.notify_all();
|
||||
return true;
|
||||
} else {
|
||||
// Already has value.
|
||||
|
@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
#include "Common/Log.h"
|
||||
#include "Common/Thread/Channel.h"
|
||||
#include "Common/Thread/ThreadManager.h"
|
||||
|
||||
@ -33,6 +35,7 @@ public:
|
||||
// Has ownership over the data. Single use.
|
||||
// TODO: Split Mailbox (rx_ and tx_) up into separate proxy objects.
|
||||
// NOTE: Poll/BlockUntilReady should only be used from one thread.
|
||||
// TODO: Make movable?
|
||||
template<class T>
|
||||
class Promise {
|
||||
public:
|
||||
@ -47,15 +50,36 @@ public:
|
||||
return promise;
|
||||
}
|
||||
|
||||
static Promise<T> *AlreadyDone(T data) {
|
||||
Promise<T> *promise = new Promise<T>();
|
||||
promise->data_ = data;
|
||||
promise->ready_ = true;
|
||||
return promise;
|
||||
}
|
||||
|
||||
static Promise<T> *CreateEmpty() {
|
||||
Mailbox<T> *mailbox = new Mailbox<T>();
|
||||
Promise<T> *promise = new Promise<T>();
|
||||
promise->rx_ = mailbox;
|
||||
return promise;
|
||||
}
|
||||
|
||||
// Allow an empty promise to spawn, too, in case we want to delay it.
|
||||
void SpawnEmpty(ThreadManager *threadman, std::function<T()> fun, TaskType taskType) {
|
||||
PromiseTask<T> *task = new PromiseTask<T>(fun, rx_, taskType);
|
||||
threadman->EnqueueTask(task);
|
||||
}
|
||||
|
||||
~Promise() {
|
||||
std::lock_guard<std::mutex> guard(readyMutex_);
|
||||
// A promise should have been fulfilled before it's destroyed.
|
||||
_assert_(ready_);
|
||||
_assert_(!rx_);
|
||||
delete data_;
|
||||
}
|
||||
|
||||
// Returns T if the data is ready, nullptr if it's not.
|
||||
T Poll() {
|
||||
std::lock_guard<std::mutex> guard(readyMutex_);
|
||||
if (ready_) {
|
||||
return data_;
|
||||
} else {
|
||||
@ -71,6 +95,7 @@ public:
|
||||
}
|
||||
|
||||
T BlockUntilReady() {
|
||||
std::lock_guard<std::mutex> guard(readyMutex_);
|
||||
if (ready_) {
|
||||
return data_;
|
||||
} else {
|
||||
@ -82,10 +107,17 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// For outside injection of data, when not using Spawn
|
||||
void Post(T data) {
|
||||
rx_->Send(data);
|
||||
}
|
||||
|
||||
private:
|
||||
Promise() {}
|
||||
|
||||
T data_ = nullptr;
|
||||
// Promise can only be constructed in Spawn (or AlreadyDone).
|
||||
T data_{};
|
||||
bool ready_ = false;
|
||||
Mailbox<T> *rx_;
|
||||
std::mutex readyMutex_;
|
||||
Mailbox<T> *rx_ = nullptr;
|
||||
};
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "Common/Render/DrawBuffer.h"
|
||||
#include "Common/Render/Text/draw_text.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/LogReporting.h"
|
||||
#include "UI/TextureUtil.h"
|
||||
|
||||
UIContext::UIContext() {
|
||||
@ -167,6 +168,14 @@ void UIContext::ActivateTopScissor() {
|
||||
int y = floorf(scale_y * bounds.y);
|
||||
int w = std::max(0.0f, ceilf(scale_x * bounds.w));
|
||||
int h = std::max(0.0f, ceilf(scale_y * bounds.h));
|
||||
if (x < 0 || y < 0 || x + w > pixel_xres || y + h > pixel_yres) {
|
||||
// This won't actually report outside a game, but we can try.
|
||||
ERROR_LOG_REPORT(G3D, "UI scissor out of bounds in %sScreen: %d,%d-%d,%d / %d,%d", screenTag_ ? screenTag_ : "N/A", x, y, w, h, pixel_xres, pixel_yres);
|
||||
x = std::max(0, x);
|
||||
y = std::max(0, y);
|
||||
w = std::min(w, pixel_xres - x);
|
||||
h = std::min(h, pixel_yres - y);
|
||||
}
|
||||
draw_->SetScissorRect(x, y, w, h);
|
||||
} else {
|
||||
// Avoid rounding errors
|
||||
|
@ -74,7 +74,6 @@ public:
|
||||
const UI::Theme *theme;
|
||||
|
||||
// Utility methods
|
||||
|
||||
TextDrawer *Text() const { return textDrawer_; }
|
||||
|
||||
void SetFontStyle(const UI::FontStyle &style);
|
||||
@ -103,6 +102,10 @@ public:
|
||||
|
||||
void setUIAtlas(const std::string &name);
|
||||
|
||||
void SetScreenTag(const char *tag) {
|
||||
screenTag_ = tag;
|
||||
}
|
||||
|
||||
private:
|
||||
Draw::DrawContext *draw_ = nullptr;
|
||||
Bounds bounds_;
|
||||
@ -126,4 +129,6 @@ private:
|
||||
|
||||
std::string lastUIAtlas_;
|
||||
std::string UIAtlas_ = "ui_atlas.zim";
|
||||
|
||||
const char *screenTag_ = nullptr;
|
||||
};
|
||||
|
@ -71,7 +71,7 @@ public:
|
||||
// what screen it is.
|
||||
virtual void *dialogData() { return 0; }
|
||||
|
||||
virtual std::string tag() const { return std::string(""); }
|
||||
virtual const char *tag() const = 0;
|
||||
|
||||
virtual bool isTransparent() const { return false; }
|
||||
virtual bool isTopLevel() const { return false; }
|
||||
|
@ -12,15 +12,11 @@
|
||||
#include "Common/UI/Root.h"
|
||||
#include "Common/Data/Text/I18n.h"
|
||||
#include "Common/Render/DrawBuffer.h"
|
||||
#include "Common/VR/PPSSPPVR.h"
|
||||
|
||||
#include "Common/Log.h"
|
||||
#include "Common/StringUtils.h"
|
||||
|
||||
#ifdef OPENXR
|
||||
#include "VR/VRBase.h"
|
||||
#include "VR/VRRenderer.h"
|
||||
#endif
|
||||
|
||||
static const bool ClickDebug = false;
|
||||
|
||||
UIScreen::UIScreen()
|
||||
@ -93,9 +89,9 @@ void UIScreen::preRender() {
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 }, "UI");
|
||||
screenManager()->getUIContext()->BeginFrame();
|
||||
|
||||
#ifdef OPENXR
|
||||
VR_BindFramebuffer(VR_GetEngine(), 0);
|
||||
#endif
|
||||
if (IsVRBuild()) {
|
||||
BindVRFramebuffer();
|
||||
}
|
||||
|
||||
Draw::Viewport viewport;
|
||||
viewport.TopLeftX = 0;
|
||||
@ -121,6 +117,9 @@ void UIScreen::render() {
|
||||
|
||||
if (root_) {
|
||||
UIContext *uiContext = screenManager()->getUIContext();
|
||||
|
||||
uiContext->SetScreenTag(tag());
|
||||
|
||||
UI::LayoutViewHierarchy(*uiContext, root_, ignoreInsets_);
|
||||
|
||||
uiContext->PushTransform({translation_, scale_, alpha_});
|
||||
|
@ -136,7 +136,7 @@ public:
|
||||
void SetHiddenChoices(std::set<int> hidden) {
|
||||
hidden_ = hidden;
|
||||
}
|
||||
virtual std::string tag() const override { return std::string("listpopup"); }
|
||||
const char *tag() const override { return "listpopup"; }
|
||||
|
||||
UI::Event OnChoice;
|
||||
|
||||
@ -187,6 +187,8 @@ public:
|
||||
disabled_ = *value_ < 0;
|
||||
}
|
||||
|
||||
const char *tag() const override { return "SliderPopup"; }
|
||||
|
||||
Event OnChange;
|
||||
|
||||
private:
|
||||
@ -214,6 +216,8 @@ public:
|
||||
: PopupScreen(title, "OK", "Cancel"), units_(units), value_(value), originalValue_(*value), minValue_(minValue), maxValue_(maxValue), step_(step), changing_(false), liveUpdate_(liveUpdate) {}
|
||||
void CreatePopupContents(UI::ViewGroup *parent) override;
|
||||
|
||||
const char *tag() const override { return "SliderFloatPopup"; }
|
||||
|
||||
Event OnChange;
|
||||
|
||||
private:
|
||||
@ -241,6 +245,8 @@ public:
|
||||
: PopupScreen(title, "OK", "Cancel"), value_(value), placeholder_(placeholder), maxLen_(maxLen) {}
|
||||
virtual void CreatePopupContents(ViewGroup *parent) override;
|
||||
|
||||
const char *tag() const override { return "TextEditPopup"; }
|
||||
|
||||
Event OnChange;
|
||||
|
||||
private:
|
||||
|
364
Common/VR/PPSSPPVR.cpp
Normal file
364
Common/VR/PPSSPPVR.cpp
Normal file
@ -0,0 +1,364 @@
|
||||
#include "Common/GPU/OpenGL/GLRenderManager.h"
|
||||
|
||||
#include "Common/VR/PPSSPPVR.h"
|
||||
#include "Common/VR/VRBase.h"
|
||||
#include "Common/VR/VRInput.h"
|
||||
#include "Common/VR/VRMath.h"
|
||||
#include "Common/VR/VRRenderer.h"
|
||||
#include "Common/VR/VRTweaks.h"
|
||||
|
||||
#include "Core/HLE/sceDisplay.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/KeyMap.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
static long vrCompat[VR_COMPAT_MAX];
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
VR button mapping
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
|
||||
struct ButtonMapping {
|
||||
ovrButton ovr;
|
||||
int keycode;
|
||||
bool pressed;
|
||||
int repeat;
|
||||
|
||||
ButtonMapping(int keycode, ovrButton ovr) {
|
||||
this->keycode = keycode;
|
||||
this->ovr = ovr;
|
||||
pressed = false;
|
||||
repeat = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct MouseActivator {
|
||||
bool activate;
|
||||
ovrButton ovr;
|
||||
|
||||
MouseActivator(bool activate, ovrButton ovr) {
|
||||
this->activate = activate;
|
||||
this->ovr = ovr;
|
||||
}
|
||||
};
|
||||
|
||||
static std::vector<ButtonMapping> leftControllerMapping = {
|
||||
ButtonMapping(NKCODE_BUTTON_X, ovrButton_X),
|
||||
ButtonMapping(NKCODE_BUTTON_Y, ovrButton_Y),
|
||||
ButtonMapping(NKCODE_ALT_LEFT, ovrButton_GripTrigger),
|
||||
ButtonMapping(NKCODE_DPAD_UP, ovrButton_Up),
|
||||
ButtonMapping(NKCODE_DPAD_DOWN, ovrButton_Down),
|
||||
ButtonMapping(NKCODE_DPAD_LEFT, ovrButton_Left),
|
||||
ButtonMapping(NKCODE_DPAD_RIGHT, ovrButton_Right),
|
||||
ButtonMapping(NKCODE_BUTTON_THUMBL, ovrButton_LThumb),
|
||||
ButtonMapping(NKCODE_ENTER, ovrButton_Trigger),
|
||||
ButtonMapping(NKCODE_BACK, ovrButton_Enter),
|
||||
};
|
||||
|
||||
static std::vector<ButtonMapping> rightControllerMapping = {
|
||||
ButtonMapping(NKCODE_BUTTON_A, ovrButton_A),
|
||||
ButtonMapping(NKCODE_BUTTON_B, ovrButton_B),
|
||||
ButtonMapping(NKCODE_ALT_RIGHT, ovrButton_GripTrigger),
|
||||
ButtonMapping(NKCODE_DPAD_UP, ovrButton_Up),
|
||||
ButtonMapping(NKCODE_DPAD_DOWN, ovrButton_Down),
|
||||
ButtonMapping(NKCODE_DPAD_LEFT, ovrButton_Left),
|
||||
ButtonMapping(NKCODE_DPAD_RIGHT, ovrButton_Right),
|
||||
ButtonMapping(NKCODE_BUTTON_THUMBR, ovrButton_RThumb),
|
||||
ButtonMapping(NKCODE_ENTER, ovrButton_Trigger),
|
||||
};
|
||||
|
||||
static const int controllerIds[] = {DEVICE_ID_XR_CONTROLLER_LEFT, DEVICE_ID_XR_CONTROLLER_RIGHT};
|
||||
static std::vector<ButtonMapping> controllerMapping[2] = {
|
||||
leftControllerMapping,
|
||||
rightControllerMapping
|
||||
};
|
||||
static int mouseController = -1;
|
||||
static bool mousePressed[] = {false, false};
|
||||
|
||||
static std::vector<MouseActivator> mouseActivators = {
|
||||
MouseActivator(true, ovrButton_Trigger),
|
||||
MouseActivator(false, ovrButton_Up),
|
||||
MouseActivator(false, ovrButton_Down),
|
||||
MouseActivator(false, ovrButton_Left),
|
||||
MouseActivator(false, ovrButton_Right),
|
||||
};
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
VR app flow integration
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
|
||||
bool IsVRBuild() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void InitVROnAndroid(void* vm, void* activity, int version, const char* name) {
|
||||
ovrJava java;
|
||||
java.Vm = (JavaVM*)vm;
|
||||
java.ActivityObject = (jobject)activity;
|
||||
java.AppVersion = version;
|
||||
strcpy(java.AppName, name);
|
||||
VR_Init(java);
|
||||
|
||||
__DisplaySetFramerate(72);
|
||||
}
|
||||
|
||||
void EnterVR(bool firstStart) {
|
||||
if (firstStart) {
|
||||
VR_EnterVR(VR_GetEngine());
|
||||
IN_VRInit(VR_GetEngine());
|
||||
}
|
||||
VR_SetConfig(VR_CONFIG_VIEWPORT_VALID, false);
|
||||
}
|
||||
|
||||
void GetVRResolutionPerEye(int* width, int* height) {
|
||||
if (VR_GetEngine()->appState.Instance) {
|
||||
VR_GetResolution(VR_GetEngine(), width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool(*NativeTouch)(const TouchInput &touch), bool haptics, float dp_xscale, float dp_yscale) {
|
||||
//buttons
|
||||
KeyInput keyInput = {};
|
||||
for (int j = 0; j < 2; j++) {
|
||||
int status = IN_VRGetButtonState(j);
|
||||
for (ButtonMapping& m : controllerMapping[j]) {
|
||||
bool pressed = status & m.ovr;
|
||||
keyInput.flags = pressed ? KEY_DOWN : KEY_UP;
|
||||
keyInput.keyCode = m.keycode;
|
||||
keyInput.deviceId = controllerIds[j];
|
||||
|
||||
if (m.pressed != pressed) {
|
||||
if (pressed && haptics) {
|
||||
INVR_Vibrate(100, j, 1000);
|
||||
}
|
||||
NativeKey(keyInput);
|
||||
m.pressed = pressed;
|
||||
m.repeat = 0;
|
||||
} else if (pressed && (m.repeat > 30)) {
|
||||
keyInput.flags |= KEY_IS_REPEAT;
|
||||
NativeKey(keyInput);
|
||||
m.repeat = 0;
|
||||
} else {
|
||||
m.repeat++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//enable or disable mouse
|
||||
for (int j = 0; j < 2; j++) {
|
||||
int status = IN_VRGetButtonState(j);
|
||||
for (MouseActivator& m : mouseActivators) {
|
||||
if (status & m.ovr) {
|
||||
mouseController = m.activate ? j : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//mouse cursor
|
||||
if (mouseController >= 0) {
|
||||
//get position on screen
|
||||
XrPosef pose = IN_VRGetPose(mouseController);
|
||||
XrVector3f angles = XrQuaternionf_ToEulerAngles(pose.orientation);
|
||||
float width = (float)VR_GetConfig(VR_CONFIG_VIEWPORT_WIDTH);
|
||||
float height = (float)VR_GetConfig(VR_CONFIG_VIEWPORT_HEIGHT);
|
||||
float cx = width / 2;
|
||||
float cy = height / 2;
|
||||
float speed = (cx + cy) / 2;
|
||||
float x = cx - tan(ToRadians(angles.y - (float)VR_GetConfig(VR_CONFIG_MENU_YAW))) * speed;
|
||||
float y = cy - tan(ToRadians(angles.x)) * speed;
|
||||
|
||||
//set renderer
|
||||
VR_SetConfig(VR_CONFIG_MOUSE_X, (int)x);
|
||||
VR_SetConfig(VR_CONFIG_MOUSE_Y, (int)y);
|
||||
VR_SetConfig(VR_CONFIG_MOUSE_SIZE, 6 * (int)pow(VR_GetConfig(VR_CONFIG_CANVAS_DISTANCE), 0.25f));
|
||||
|
||||
//inform engine about the status
|
||||
TouchInput touch;
|
||||
touch.id = mouseController;
|
||||
touch.x = x * dp_xscale;
|
||||
touch.y = (height - y - 1) * dp_yscale;
|
||||
bool pressed = IN_VRGetButtonState(mouseController) & ovrButton_Trigger;
|
||||
if (mousePressed[mouseController] != pressed) {
|
||||
if (!pressed) {
|
||||
touch.flags = TOUCH_DOWN;
|
||||
NativeTouch(touch);
|
||||
touch.flags = TOUCH_UP;
|
||||
NativeTouch(touch);
|
||||
}
|
||||
mousePressed[mouseController] = pressed;
|
||||
}
|
||||
} else {
|
||||
VR_SetConfig(VR_CONFIG_MOUSE_SIZE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateVRScreenKey(const KeyInput &key) {
|
||||
std::vector<int> nativeKeys;
|
||||
if (KeyMap::KeyToPspButton(key.deviceId, key.keyCode, &nativeKeys)) {
|
||||
for (int& nativeKey : nativeKeys) {
|
||||
if (nativeKey == CTRL_SCREEN) {
|
||||
VR_SetConfig(VR_CONFIG_FORCE_2D, key.flags & KEY_DOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
// VR games compatibility
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
|
||||
void PreprocessSkyplane(GLRStep* step) {
|
||||
|
||||
// Do not do anything if the scene is not in VR.
|
||||
if (IsFlatVRScene()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it is the step we need to modify.
|
||||
for (auto& cmd : step->commands) {
|
||||
if (cmd.cmd == GLRRenderCommand::BIND_FB_TEXTURE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear sky with the fog color.
|
||||
if (!vrCompat[VR_COMPAT_FBO_CLEAR]) {
|
||||
GLRRenderData skyClear {};
|
||||
skyClear.cmd = GLRRenderCommand::CLEAR;
|
||||
skyClear.clear.colorMask = 0xF;
|
||||
skyClear.clear.clearMask = GL_COLOR_BUFFER_BIT;
|
||||
skyClear.clear.clearColor = vrCompat[VR_COMPAT_FOG_COLOR];
|
||||
step->commands.insert(step->commands.begin(), skyClear);
|
||||
vrCompat[VR_COMPAT_FBO_CLEAR] = true;
|
||||
}
|
||||
|
||||
// Remove original sky plane.
|
||||
bool depthEnabled = false;
|
||||
for (auto& command : step->commands) {
|
||||
if (command.cmd == GLRRenderCommand::DEPTH) {
|
||||
depthEnabled = command.depth.enabled;
|
||||
} else if ((command.cmd == GLRRenderCommand::DRAW_INDEXED) && !depthEnabled) {
|
||||
command.drawIndexed.count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PreprocessStepVR(void* step) {
|
||||
auto* glrStep = (GLRStep*)step;
|
||||
if (vrCompat[VR_COMPAT_SKYPLANE]) PreprocessSkyplane(glrStep);
|
||||
}
|
||||
|
||||
void SetVRCompat(VRCompatFlag flag, long value) {
|
||||
vrCompat[flag] = value;
|
||||
}
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
VR rendering integration
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
|
||||
void BindVRFramebuffer() {
|
||||
VR_BindFramebuffer(VR_GetEngine());
|
||||
}
|
||||
|
||||
bool StartVRRender() {
|
||||
if (!VR_GetConfig(VR_CONFIG_VIEWPORT_VALID)) {
|
||||
VR_InitRenderer(VR_GetEngine(), IsMultiviewSupported());
|
||||
VR_SetConfig(VR_CONFIG_VIEWPORT_VALID, true);
|
||||
}
|
||||
|
||||
if (VR_InitFrame(VR_GetEngine())) {
|
||||
|
||||
// Decide if the scene is 3D or not
|
||||
if (g_Config.bEnableVR && !VR_GetConfig(VR_CONFIG_FORCE_2D) && (VR_GetConfig(VR_CONFIG_3D_GEOMETRY_COUNT) > 15)) {
|
||||
bool stereo = VR_GetConfig(VR_CONFIG_6DOF_PRECISE) && g_Config.bEnableStereo;
|
||||
VR_SetConfig(VR_CONFIG_MODE, stereo ? VR_MODE_STEREO_6DOF : VR_MODE_MONO_6DOF);
|
||||
} else {
|
||||
VR_SetConfig(VR_CONFIG_MODE, VR_MODE_FLAT_SCREEN);
|
||||
}
|
||||
VR_SetConfig(VR_CONFIG_3D_GEOMETRY_COUNT, VR_GetConfig(VR_CONFIG_3D_GEOMETRY_COUNT) / 2);
|
||||
|
||||
// Set compatibility
|
||||
vrCompat[VR_COMPAT_SKYPLANE] = PSP_CoreParameter().compat.vrCompat().Skyplane;
|
||||
|
||||
// Set customizations
|
||||
VR_SetConfig(VR_CONFIG_6DOF_ENABLED, g_Config.bEnable6DoF);
|
||||
VR_SetConfig(VR_CONFIG_CANVAS_DISTANCE, g_Config.iCanvasDistance);
|
||||
VR_SetConfig(VR_CONFIG_FOV_SCALE, g_Config.iFieldOfViewPercentage);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FinishVRRender() {
|
||||
VR_FinishFrame(VR_GetEngine());
|
||||
}
|
||||
|
||||
void PreVRFrameRender(int fboIndex) {
|
||||
VR_BeginFrame(VR_GetEngine(), fboIndex);
|
||||
vrCompat[VR_COMPAT_FBO_CLEAR] = false;
|
||||
}
|
||||
|
||||
void PostVRFrameRender() {
|
||||
VR_EndFrame(VR_GetEngine());
|
||||
}
|
||||
|
||||
int GetVRFBOIndex() {
|
||||
return VR_GetConfig(VR_CONFIG_CURRENT_FBO);
|
||||
}
|
||||
|
||||
bool IsMultiviewSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsFlatVRScene() {
|
||||
return VR_GetConfig(VR_CONFIG_MODE) == VR_MODE_FLAT_SCREEN;
|
||||
}
|
||||
|
||||
bool Is2DVRObject(float* projMatrix, bool ortho) {
|
||||
bool is2D = VR_TweakIsMatrixBigScale(projMatrix) ||
|
||||
VR_TweakIsMatrixIdentity(projMatrix) ||
|
||||
VR_TweakIsMatrixOneOrtho(projMatrix) ||
|
||||
VR_TweakIsMatrixOneScale(projMatrix) ||
|
||||
VR_TweakIsMatrixOneTransform(projMatrix);
|
||||
if (!is2D && !ortho) {
|
||||
VR_SetConfig(VR_CONFIG_3D_GEOMETRY_COUNT, VR_GetConfig(VR_CONFIG_3D_GEOMETRY_COUNT) + 1);
|
||||
}
|
||||
return is2D;
|
||||
}
|
||||
|
||||
void UpdateVRProjection(float* projMatrix, float* leftEye, float* rightEye) {
|
||||
VR_TweakProjection(projMatrix, leftEye, VR_PROJECTION_MATRIX_LEFT_EYE);
|
||||
VR_TweakProjection(projMatrix, rightEye, VR_PROJECTION_MATRIX_RIGHT_EYE);
|
||||
VR_TweakMirroring(projMatrix);
|
||||
|
||||
// Set 6DoF scale
|
||||
float scale = pow(fabs(projMatrix[14]), 1.15f);
|
||||
if (PSP_CoreParameter().compat.vrCompat().UnitsPerMeter > 0) {
|
||||
scale = PSP_CoreParameter().compat.vrCompat().UnitsPerMeter;
|
||||
VR_SetConfig(VR_CONFIG_6DOF_PRECISE, true);
|
||||
} else {
|
||||
VR_SetConfig(VR_CONFIG_6DOF_PRECISE, false);
|
||||
}
|
||||
VR_SetConfig(VR_CONFIG_6DOF_SCALE, (int)(scale * 1000000));
|
||||
}
|
||||
|
||||
void UpdateVRView(float* leftEye, float* rightEye) {
|
||||
VR_TweakView(leftEye, VR_VIEW_MATRIX_LEFT_EYE);
|
||||
VR_TweakView(rightEye, VR_VIEW_MATRIX_RIGHT_EYE);
|
||||
}
|
74
Common/VR/PPSSPPVR.h
Normal file
74
Common/VR/PPSSPPVR.h
Normal file
@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common/Input/InputState.h"
|
||||
#include "Common/Input/KeyCodes.h"
|
||||
|
||||
enum VRCompatFlag {
|
||||
//compatibility tweaks
|
||||
VR_COMPAT_SKYPLANE,
|
||||
|
||||
//render state
|
||||
VR_COMPAT_FBO_CLEAR,
|
||||
|
||||
//uniforms
|
||||
VR_COMPAT_FOG_COLOR,
|
||||
|
||||
//end
|
||||
VR_COMPAT_MAX
|
||||
};
|
||||
|
||||
#ifdef OPENXR
|
||||
|
||||
// VR app flow integration
|
||||
bool IsVRBuild();
|
||||
void InitVROnAndroid(void* vm, void* activity, int version, const char* name);
|
||||
void EnterVR(bool firstStart);
|
||||
void GetVRResolutionPerEye(int* width, int* height);
|
||||
void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool(*NativeTouch)(const TouchInput &touch), bool haptics, float dp_xscale, float dp_yscale);
|
||||
void UpdateVRScreenKey(const KeyInput &key);
|
||||
|
||||
// VR games compatibility
|
||||
void PreprocessStepVR(void* step);
|
||||
void SetVRCompat(VRCompatFlag flag, long value);
|
||||
|
||||
// VR rendering integration
|
||||
void BindVRFramebuffer();
|
||||
bool StartVRRender();
|
||||
void FinishVRRender();
|
||||
void PreVRFrameRender(int fboIndex);
|
||||
void PostVRFrameRender();
|
||||
int GetVRFBOIndex();
|
||||
bool IsMultiviewSupported();
|
||||
bool IsFlatVRScene();
|
||||
bool Is2DVRObject(float* projMatrix, bool ortho);
|
||||
void UpdateVRProjection(float* projMatrix, float* leftEye, float* rightEye);
|
||||
void UpdateVRView(float* leftEye, float* rightEye);
|
||||
|
||||
#else //dummy integration
|
||||
|
||||
// VR app flow integration
|
||||
inline bool IsVRBuild() { return false; }
|
||||
inline void InitVROnAndroid(void* vm, void* activity, int version, const char* name) {}
|
||||
inline void EnterVR(bool firstTime) {}
|
||||
inline void GetVRResolutionPerEye(int* width, int* height) {}
|
||||
inline void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool(*NativeTouch)(const TouchInput &touch), bool haptics, float dp_xscale, float dp_yscale) {}
|
||||
inline void UpdateVRScreenKey(const KeyInput &key) {}
|
||||
|
||||
// VR games compatibility
|
||||
inline void PreprocessStepVR(void* step) {}
|
||||
inline void SetVRCompat(VRCompatFlag flag, long value) {}
|
||||
|
||||
// VR rendering integration
|
||||
inline void BindVRFramebuffer() {}
|
||||
inline bool StartVRRender() { return false; }
|
||||
inline void FinishVRRender() {}
|
||||
inline void PreVRFrameRender(int fboIndex) {}
|
||||
inline void PostVRFrameRender() {}
|
||||
inline int GetVRFBOIndex() { return 0; }
|
||||
inline bool IsMultiviewSupported() { return false; }
|
||||
inline bool IsFlatVRScene() { return true; }
|
||||
inline bool Is2DVRObject(float* projMatrix, bool ortho) { return false; }
|
||||
inline void UpdateVRProjection(float* projMatrix, float* leftEye, float* rightEye) {}
|
||||
inline void UpdateVRView(float* leftEye, float* rightEye) {}
|
||||
|
||||
#endif
|
@ -5,11 +5,53 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef OPENXR_PLATFORM_PICO
|
||||
enum ConfigsSetEXT {
|
||||
UNREAL_VERSION = 0,
|
||||
TRACKING_ORIGIN,
|
||||
OPENGL_NOERROR,
|
||||
ENABLE_SIX_DOF,
|
||||
PRESENTATION_FLAG,
|
||||
ENABLE_CPT,
|
||||
PLATFORM,
|
||||
FOVEATION_LEVEL,
|
||||
SET_DISPLAY_RATE = 8,
|
||||
MRC_TEXTURE_ID = 9,
|
||||
};
|
||||
|
||||
enum PxrTrackingDof {
|
||||
PXR_TRACKING_3DOF = 0,
|
||||
PXR_TRACKING_6DOF = 1
|
||||
};
|
||||
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrSetEngineVersionPico)(XrInstance instance,const char* version);
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrStartCVControllerThreadPico)(XrInstance instance,int headSensorState, int handSensorState);
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrStopCVControllerThreadPico)(XrInstance instance,int headSensorState, int handSensorState);
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrSetConfigPICO) (XrSession instance, enum ConfigsSetEXT configIndex, char* configData);
|
||||
|
||||
PFN_xrSetConfigPICO pfnXrSetConfigPICO = nullptr;
|
||||
PFN_xrSetEngineVersionPico pfnXrSetEngineVersionPico = nullptr;
|
||||
PFN_xrStartCVControllerThreadPico pfnXrStartCVControllerThreadPico = nullptr;
|
||||
PFN_xrStopCVControllerThreadPico pfnXrStopCVControllerThreadPico = nullptr;
|
||||
#endif
|
||||
|
||||
static engine_t vr_engine;
|
||||
int vr_initialized = 0;
|
||||
|
||||
const char* const requiredExtensionNames[] = {
|
||||
XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME,
|
||||
#ifdef OPENXR_HAS_PERFORMANCE_EXTENSION
|
||||
XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME,
|
||||
XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME,
|
||||
#endif
|
||||
#ifdef OPENXR_PLATFORM_PICO
|
||||
XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME,
|
||||
"XR_PICO_android_controller_function_ext_enable",
|
||||
"XR_PICO_view_state_ext_enable",
|
||||
"XR_PICO_frame_end_info_ext",
|
||||
"XR_PICO_configs_ext",
|
||||
"XR_PICO_reset_sensor",
|
||||
#endif
|
||||
XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME};
|
||||
const uint32_t numRequiredExtensions =
|
||||
sizeof(requiredExtensionNames) / sizeof(requiredExtensionNames[0]);
|
||||
@ -45,7 +87,14 @@ void VR_Init( ovrJava java ) {
|
||||
XrInstanceCreateInfo instanceCreateInfo;
|
||||
memset(&instanceCreateInfo, 0, sizeof(instanceCreateInfo));
|
||||
instanceCreateInfo.type = XR_TYPE_INSTANCE_CREATE_INFO;
|
||||
#ifdef OPENXR_PLATFORM_PICO
|
||||
XrInstanceCreateInfoAndroidKHR instanceCreateInfoAndroid = {XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR};
|
||||
instanceCreateInfoAndroid.applicationVM = java.Vm;
|
||||
instanceCreateInfoAndroid.applicationActivity = java.ActivityObject;
|
||||
instanceCreateInfo.next = (XrBaseInStructure*)&instanceCreateInfoAndroid;
|
||||
#else
|
||||
instanceCreateInfo.next = NULL;
|
||||
#endif
|
||||
instanceCreateInfo.createFlags = 0;
|
||||
instanceCreateInfo.applicationInfo = appInfo;
|
||||
instanceCreateInfo.enabledApiLayerCount = 0;
|
||||
@ -60,6 +109,15 @@ void VR_Init( ovrJava java ) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#ifdef OPENXR_PLATFORM_PICO
|
||||
xrGetInstanceProcAddr(vr_engine.appState.Instance, "xrSetEngineVersionPico", (PFN_xrVoidFunction*)(&pfnXrSetEngineVersionPico));
|
||||
xrGetInstanceProcAddr(vr_engine.appState.Instance, "xrStartCVControllerThreadPico", (PFN_xrVoidFunction*)(&pfnXrStartCVControllerThreadPico));
|
||||
xrGetInstanceProcAddr(vr_engine.appState.Instance, "xrStopCVControllerThreadPico", (PFN_xrVoidFunction*)(&pfnXrStopCVControllerThreadPico));
|
||||
xrGetInstanceProcAddr(vr_engine.appState.Instance,"xrSetConfigPICO", (PFN_xrVoidFunction*)(&pfnXrSetConfigPICO));
|
||||
if (pfnXrSetEngineVersionPico != nullptr) pfnXrSetEngineVersionPico(vr_engine.appState.Instance, "2.8.0.1");
|
||||
if (pfnXrStartCVControllerThreadPico != nullptr) pfnXrStartCVControllerThreadPico(vr_engine.appState.Instance, PXR_TRACKING_6DOF, PXR_TRACKING_6DOF);
|
||||
#endif
|
||||
|
||||
XrInstanceProperties instanceInfo;
|
||||
instanceInfo.type = XR_TYPE_INSTANCE_PROPERTIES;
|
||||
instanceInfo.next = NULL;
|
||||
@ -104,6 +162,11 @@ void VR_Init( ovrJava java ) {
|
||||
|
||||
void VR_Destroy( engine_t* engine ) {
|
||||
if (engine == &vr_engine) {
|
||||
#ifdef OPENXR_PLATFORM_PICO
|
||||
if (pfnXrStopCVControllerThreadPico != nullptr) {
|
||||
pfnXrStopCVControllerThreadPico(engine->appState.Instance, PXR_TRACKING_6DOF, PXR_TRACKING_6DOF);
|
||||
}
|
||||
#endif
|
||||
xrDestroyInstance(engine->appState.Instance);
|
||||
ovrApp_Destroy(&engine->appState);
|
||||
}
|
||||
@ -137,6 +200,9 @@ void VR_EnterVR( engine_t* engine ) {
|
||||
ALOGE("Failed to create XR session: %d.", initResult);
|
||||
exit(1);
|
||||
}
|
||||
#ifdef OPENXR_PLATFORM_PICO
|
||||
pfnXrSetConfigPICO(engine->appState.Session, TRACKING_ORIGIN, "1");
|
||||
#endif
|
||||
|
||||
// Create a space to the first path
|
||||
XrReferenceSpaceCreateInfo spaceCreateInfo = {};
|
||||
|
@ -1,6 +1,152 @@
|
||||
#pragma once
|
||||
|
||||
#include "VRFramebuffer.h"
|
||||
#ifdef ANDROID
|
||||
#include <android/log.h>
|
||||
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, "OpenXR", __VA_ARGS__);
|
||||
#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "OpenXR", __VA_ARGS__);
|
||||
#else
|
||||
#define ALOGE(...) printf(__VA_ARGS__)
|
||||
#define ALOGV(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
//OpenXR
|
||||
#define XR_USE_PLATFORM_ANDROID 1
|
||||
#define XR_USE_GRAPHICS_API_OPENGL_ES 1
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <jni.h>
|
||||
#include <math.h>
|
||||
#include <openxr.h>
|
||||
#include <openxr_platform.h>
|
||||
#include <GLES3/gl3.h>
|
||||
#include <GLES3/gl3ext.h>
|
||||
|
||||
#ifdef _DEBUG
|
||||
static const char* GlErrorString(GLenum error) {
|
||||
switch (error) {
|
||||
case GL_NO_ERROR:
|
||||
return "GL_NO_ERROR";
|
||||
case GL_INVALID_ENUM:
|
||||
return "GL_INVALID_ENUM";
|
||||
case GL_INVALID_VALUE:
|
||||
return "GL_INVALID_VALUE";
|
||||
case GL_INVALID_OPERATION:
|
||||
return "GL_INVALID_OPERATION";
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||
return "GL_INVALID_FRAMEBUFFER_OPERATION";
|
||||
case GL_OUT_OF_MEMORY:
|
||||
return "GL_OUT_OF_MEMORY";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void GLCheckErrors(char* file, int line) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
const GLenum error = glGetError();
|
||||
if (error == GL_NO_ERROR) {
|
||||
break;
|
||||
}
|
||||
ALOGE("GL error on line %s:%d %s", file, line, GlErrorString(error));
|
||||
}
|
||||
}
|
||||
|
||||
#define GL(func) func; GLCheckErrors(__FILE__ , __LINE__);
|
||||
#else
|
||||
#define GL(func) func;
|
||||
#endif
|
||||
|
||||
#if defined(_DEBUG)
|
||||
static void OXR_CheckErrors(XrInstance instance, XrResult result, const char* function, bool failOnError) {
|
||||
if (XR_FAILED(result)) {
|
||||
char errorBuffer[XR_MAX_RESULT_STRING_SIZE];
|
||||
xrResultToString(instance, result, errorBuffer);
|
||||
if (failOnError) {
|
||||
ALOGE("OpenXR error: %s: %s\n", function, errorBuffer);
|
||||
} else {
|
||||
ALOGV("OpenXR error: %s: %s\n", function, errorBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
#define OXR(func) OXR_CheckErrors(VR_GetEngine()->appState.Instance, func, #func, true);
|
||||
#else
|
||||
#define OXR(func) func;
|
||||
#endif
|
||||
|
||||
#ifdef OPENXR_PLATFORM_QUEST
|
||||
#define OPENXR_HAS_PERFORMANCE_EXTENSION
|
||||
#endif
|
||||
|
||||
enum { ovrMaxLayerCount = 1 };
|
||||
enum { ovrMaxNumEyes = 2 };
|
||||
|
||||
typedef union {
|
||||
XrCompositionLayerProjection Projection;
|
||||
XrCompositionLayerCylinderKHR Cylinder;
|
||||
} ovrCompositorLayer_Union;
|
||||
|
||||
typedef struct {
|
||||
XrSwapchain Handle;
|
||||
uint32_t Width;
|
||||
uint32_t Height;
|
||||
} ovrSwapChain;
|
||||
|
||||
typedef struct {
|
||||
int Width;
|
||||
int Height;
|
||||
uint32_t TextureSwapChainLength;
|
||||
uint32_t TextureSwapChainIndex;
|
||||
ovrSwapChain ColorSwapChain;
|
||||
ovrSwapChain DepthSwapChain;
|
||||
XrSwapchainImageOpenGLESKHR* ColorSwapChainImage;
|
||||
XrSwapchainImageOpenGLESKHR* DepthSwapChainImage;
|
||||
unsigned int* FrameBuffers;
|
||||
bool Acquired;
|
||||
} ovrFramebuffer;
|
||||
|
||||
typedef struct {
|
||||
bool Multiview;
|
||||
ovrFramebuffer FrameBuffer[ovrMaxNumEyes];
|
||||
} ovrRenderer;
|
||||
|
||||
typedef struct {
|
||||
int Focused;
|
||||
|
||||
XrInstance Instance;
|
||||
XrSession Session;
|
||||
XrViewConfigurationProperties ViewportConfig;
|
||||
XrViewConfigurationView ViewConfigurationView[ovrMaxNumEyes];
|
||||
XrSystemId SystemId;
|
||||
XrSpace HeadSpace;
|
||||
XrSpace StageSpace;
|
||||
XrSpace FakeStageSpace;
|
||||
XrSpace CurrentSpace;
|
||||
int SessionActive;
|
||||
|
||||
int SwapInterval;
|
||||
// These threads will be marked as performance threads.
|
||||
int MainThreadTid;
|
||||
int RenderThreadTid;
|
||||
ovrCompositorLayer_Union Layers[ovrMaxLayerCount];
|
||||
int LayerCount;
|
||||
|
||||
ovrRenderer Renderer;
|
||||
} ovrApp;
|
||||
|
||||
typedef struct {
|
||||
JavaVM* Vm;
|
||||
jobject ActivityObject;
|
||||
JNIEnv* Env;
|
||||
char AppName[64];
|
||||
int AppVersion;
|
||||
} ovrJava;
|
||||
|
||||
typedef struct {
|
||||
uint64_t frameIndex;
|
||||
ovrApp appState;
|
||||
ovrJava java;
|
||||
float predictedDisplayTime;
|
||||
} engine_t;
|
||||
|
||||
void VR_Init( ovrJava java );
|
||||
void VR_Destroy( engine_t* engine );
|
||||
@ -8,3 +154,7 @@ void VR_EnterVR( engine_t* engine );
|
||||
void VR_LeaveVR( engine_t* engine );
|
||||
|
||||
engine_t* VR_GetEngine( void );
|
||||
|
||||
void ovrApp_Clear(ovrApp* app);
|
||||
void ovrApp_Destroy(ovrApp* app);
|
||||
int ovrApp_HandleXrEvents(ovrApp* app);
|
||||
|
@ -12,6 +12,10 @@
|
||||
#include <GLES3/gl3.h>
|
||||
#include <GLES3/gl3ext.h>
|
||||
|
||||
double FromXrTime(const XrTime time) {
|
||||
return (time * 1e-9);
|
||||
}
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
@ -30,87 +34,99 @@ void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) {
|
||||
frameBuffer->ColorSwapChain.Width = 0;
|
||||
frameBuffer->ColorSwapChain.Height = 0;
|
||||
frameBuffer->ColorSwapChainImage = NULL;
|
||||
frameBuffer->DepthBuffers = NULL;
|
||||
frameBuffer->DepthSwapChain.Handle = XR_NULL_HANDLE;
|
||||
frameBuffer->DepthSwapChain.Width = 0;
|
||||
frameBuffer->DepthSwapChain.Height = 0;
|
||||
frameBuffer->DepthSwapChainImage = NULL;
|
||||
|
||||
frameBuffer->FrameBuffers = NULL;
|
||||
frameBuffer->Acquired = false;
|
||||
}
|
||||
|
||||
bool ovrFramebuffer_Create(
|
||||
XrSession session,
|
||||
ovrFramebuffer* frameBuffer,
|
||||
const int width,
|
||||
const int height) {
|
||||
bool ovrFramebuffer_Create(XrSession session, ovrFramebuffer* frameBuffer, int width, int height, bool multiview) {
|
||||
|
||||
frameBuffer->Width = width;
|
||||
frameBuffer->Height = height;
|
||||
|
||||
if (strstr((const char*)glGetString(GL_EXTENSIONS), "GL_OVR_multiview2") == nullptr)
|
||||
{
|
||||
ALOGE("OpenGL implementation does not support GL_OVR_multiview2 extension.\n");
|
||||
}
|
||||
|
||||
typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei);
|
||||
auto glFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR)eglGetProcAddress ("glFramebufferTextureMultiviewOVR");
|
||||
if (!glFramebufferTextureMultiviewOVR)
|
||||
{
|
||||
ALOGE("Can not get proc address for glFramebufferTextureMultiviewOVR.\n");
|
||||
}
|
||||
|
||||
XrSwapchainCreateInfo swapChainCreateInfo;
|
||||
memset(&swapChainCreateInfo, 0, sizeof(swapChainCreateInfo));
|
||||
swapChainCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO;
|
||||
swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
swapChainCreateInfo.format = GL_RGBA8;
|
||||
swapChainCreateInfo.sampleCount = 1;
|
||||
swapChainCreateInfo.width = width;
|
||||
swapChainCreateInfo.height = height;
|
||||
swapChainCreateInfo.faceCount = 1;
|
||||
swapChainCreateInfo.arraySize = 1;
|
||||
swapChainCreateInfo.mipCount = 1;
|
||||
swapChainCreateInfo.arraySize = multiview ? 2 : 1;
|
||||
|
||||
frameBuffer->ColorSwapChain.Width = swapChainCreateInfo.width;
|
||||
frameBuffer->ColorSwapChain.Height = swapChainCreateInfo.height;
|
||||
frameBuffer->DepthSwapChain.Width = swapChainCreateInfo.width;
|
||||
frameBuffer->DepthSwapChain.Height = swapChainCreateInfo.height;
|
||||
|
||||
// Create the swapchain.
|
||||
// Create the color swapchain.
|
||||
swapChainCreateInfo.format = GL_SRGB8_ALPHA8;
|
||||
swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->ColorSwapChain.Handle));
|
||||
// Get the number of swapchain images.
|
||||
OXR(xrEnumerateSwapchainImages(
|
||||
frameBuffer->ColorSwapChain.Handle, 0, &frameBuffer->TextureSwapChainLength, NULL));
|
||||
// Allocate the swapchain images array.
|
||||
frameBuffer->ColorSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc(
|
||||
frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR));
|
||||
OXR(xrEnumerateSwapchainImages(frameBuffer->ColorSwapChain.Handle, 0, &frameBuffer->TextureSwapChainLength, NULL));
|
||||
frameBuffer->ColorSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc(frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR));
|
||||
|
||||
// Create the depth swapchain.
|
||||
swapChainCreateInfo.format = GL_DEPTH24_STENCIL8;
|
||||
swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||
OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->DepthSwapChain.Handle));
|
||||
frameBuffer->DepthSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc(frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR));
|
||||
|
||||
// Populate the swapchain image array.
|
||||
for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) {
|
||||
frameBuffer->ColorSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR;
|
||||
frameBuffer->ColorSwapChainImage[i].next = NULL;
|
||||
frameBuffer->DepthSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR;
|
||||
frameBuffer->DepthSwapChainImage[i].next = NULL;
|
||||
}
|
||||
OXR(xrEnumerateSwapchainImages(
|
||||
frameBuffer->ColorSwapChain.Handle,
|
||||
frameBuffer->TextureSwapChainLength,
|
||||
&frameBuffer->TextureSwapChainLength,
|
||||
(XrSwapchainImageBaseHeader*)frameBuffer->ColorSwapChainImage));
|
||||
OXR(xrEnumerateSwapchainImages(
|
||||
frameBuffer->DepthSwapChain.Handle,
|
||||
frameBuffer->TextureSwapChainLength,
|
||||
&frameBuffer->TextureSwapChainLength,
|
||||
(XrSwapchainImageBaseHeader*)frameBuffer->DepthSwapChainImage));
|
||||
|
||||
frameBuffer->DepthBuffers =
|
||||
(GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint));
|
||||
frameBuffer->FrameBuffers =
|
||||
(GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint));
|
||||
|
||||
frameBuffer->FrameBuffers = (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint));
|
||||
for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) {
|
||||
// Create the color buffer texture.
|
||||
const GLuint colorTexture = frameBuffer->ColorSwapChainImage[i].image;
|
||||
GLenum colorTextureTarget = GL_TEXTURE_2D;
|
||||
GL(glBindTexture(colorTextureTarget, colorTexture));
|
||||
GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
|
||||
GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
|
||||
GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
||||
GL(glBindTexture(colorTextureTarget, 0));
|
||||
|
||||
// Create depth buffer.
|
||||
GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i]));
|
||||
GL(glBindRenderbuffer(GL_RENDERBUFFER, frameBuffer->DepthBuffers[i]));
|
||||
GL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height));
|
||||
GL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
|
||||
const GLuint depthTexture = frameBuffer->DepthSwapChainImage[i].image;
|
||||
|
||||
// Create the frame buffer.
|
||||
GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i]));
|
||||
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i]));
|
||||
GL(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, frameBuffer->DepthBuffers[i]));
|
||||
GL(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, frameBuffer->DepthBuffers[i]));
|
||||
GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0));
|
||||
if (multiview) {
|
||||
GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexture, 0, 0, 2));
|
||||
GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0, 0, 2));
|
||||
GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture, 0, 0, 2));
|
||||
} else {
|
||||
GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0));
|
||||
GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0));
|
||||
GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0));
|
||||
}
|
||||
GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER));
|
||||
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
|
||||
if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) {
|
||||
ALOGE(
|
||||
"Incomplete frame buffer object: %d", renderFramebufferStatus);
|
||||
ALOGE("Incomplete frame buffer object: %d", renderFramebufferStatus);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -120,19 +136,17 @@ bool ovrFramebuffer_Create(
|
||||
|
||||
void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer) {
|
||||
GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->FrameBuffers));
|
||||
GL(glDeleteRenderbuffers(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers));
|
||||
OXR(xrDestroySwapchain(frameBuffer->ColorSwapChain.Handle));
|
||||
OXR(xrDestroySwapchain(frameBuffer->DepthSwapChain.Handle));
|
||||
free(frameBuffer->ColorSwapChainImage);
|
||||
|
||||
free(frameBuffer->DepthBuffers);
|
||||
free(frameBuffer->DepthSwapChainImage);
|
||||
free(frameBuffer->FrameBuffers);
|
||||
|
||||
ovrFramebuffer_Clear(frameBuffer);
|
||||
}
|
||||
|
||||
void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer) {
|
||||
GL(glBindFramebuffer(
|
||||
GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[frameBuffer->TextureSwapChainIndex]));
|
||||
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[frameBuffer->TextureSwapChainIndex]));
|
||||
}
|
||||
|
||||
void ovrFramebuffer_SetNone() {
|
||||
@ -146,18 +160,16 @@ void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer) {
|
||||
}
|
||||
|
||||
void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer) {
|
||||
// Acquire the swapchain image
|
||||
XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, NULL};
|
||||
OXR(xrAcquireSwapchainImage(
|
||||
frameBuffer->ColorSwapChain.Handle, &acquireInfo, &frameBuffer->TextureSwapChainIndex));
|
||||
OXR(xrAcquireSwapchainImage(frameBuffer->ColorSwapChain.Handle, &acquireInfo, &frameBuffer->TextureSwapChainIndex));
|
||||
|
||||
XrSwapchainImageWaitInfo waitInfo;
|
||||
waitInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO;
|
||||
waitInfo.next = NULL;
|
||||
waitInfo.timeout = 1000; /* timeout in nanoseconds */
|
||||
waitInfo.timeout = 1000000; /* timeout in nanoseconds */
|
||||
XrResult res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo);
|
||||
int i = 0;
|
||||
while (res != XR_SUCCESS) {
|
||||
while ((res != XR_SUCCESS) && (i < 10)) {
|
||||
res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo);
|
||||
i++;
|
||||
ALOGV(
|
||||
@ -165,11 +177,15 @@ void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer) {
|
||||
i,
|
||||
waitInfo.timeout * (1E-9));
|
||||
}
|
||||
frameBuffer->Acquired = res == XR_SUCCESS;
|
||||
}
|
||||
|
||||
void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer) {
|
||||
XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, NULL};
|
||||
OXR(xrReleaseSwapchainImage(frameBuffer->ColorSwapChain.Handle, &releaseInfo));
|
||||
if (frameBuffer->Acquired) {
|
||||
XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, NULL};
|
||||
OXR(xrReleaseSwapchainImage(frameBuffer->ColorSwapChain.Handle, &releaseInfo));
|
||||
frameBuffer->Acquired = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -181,29 +197,23 @@ ovrRenderer
|
||||
*/
|
||||
|
||||
void ovrRenderer_Clear(ovrRenderer* renderer) {
|
||||
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
|
||||
ovrFramebuffer_Clear(&renderer->FrameBuffer[eye]);
|
||||
for (int i = 0; i < ovrMaxNumEyes; i++) {
|
||||
ovrFramebuffer_Clear(&renderer->FrameBuffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void ovrRenderer_Create(
|
||||
XrSession session,
|
||||
ovrRenderer* renderer,
|
||||
int suggestedEyeTextureWidth,
|
||||
int suggestedEyeTextureHeight) {
|
||||
// Create the frame buffers.
|
||||
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
|
||||
ovrFramebuffer_Create(
|
||||
session,
|
||||
&renderer->FrameBuffer[eye],
|
||||
suggestedEyeTextureWidth,
|
||||
suggestedEyeTextureHeight);
|
||||
void ovrRenderer_Create(XrSession session, ovrRenderer* renderer, int width, int height, bool multiview) {
|
||||
renderer->Multiview = multiview;
|
||||
int instances = renderer->Multiview ? 1 : ovrMaxNumEyes;
|
||||
for (int i = 0; i < instances; i++) {
|
||||
ovrFramebuffer_Create(session, &renderer->FrameBuffer[i], width, height, multiview);
|
||||
}
|
||||
}
|
||||
|
||||
void ovrRenderer_Destroy(ovrRenderer* renderer) {
|
||||
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
|
||||
ovrFramebuffer_Destroy(&renderer->FrameBuffer[eye]);
|
||||
int instances = renderer->Multiview ? 1 : ovrMaxNumEyes;
|
||||
for (int i = 0; i < instances; i++) {
|
||||
ovrFramebuffer_Destroy(&renderer->FrameBuffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,7 +242,6 @@ void ovrApp_Clear(ovrApp* app) {
|
||||
app->LayerCount = 0;
|
||||
app->MainThreadTid = 0;
|
||||
app->RenderThreadTid = 0;
|
||||
app->TouchPadDownLastFrame = false;
|
||||
|
||||
ovrRenderer_Clear(&app->Renderer);
|
||||
}
|
||||
@ -255,6 +264,32 @@ void ovrApp_HandleSessionStateChanges(ovrApp* app, XrSessionState state) {
|
||||
OXR(result = xrBeginSession(app->Session, &sessionBeginInfo));
|
||||
|
||||
app->SessionActive = (result == XR_SUCCESS);
|
||||
|
||||
// Set session state once we have entered VR mode and have a valid session object.
|
||||
#ifdef OPENXR_HAS_PERFORMANCE_EXTENSION
|
||||
if (app->SessionActive) {
|
||||
XrPerfSettingsLevelEXT cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT;
|
||||
XrPerfSettingsLevelEXT gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT;
|
||||
|
||||
PFN_xrPerfSettingsSetPerformanceLevelEXT pfnPerfSettingsSetPerformanceLevelEXT = NULL;
|
||||
OXR(xrGetInstanceProcAddr(
|
||||
app->Instance,
|
||||
"xrPerfSettingsSetPerformanceLevelEXT",
|
||||
(PFN_xrVoidFunction*)(&pfnPerfSettingsSetPerformanceLevelEXT)));
|
||||
|
||||
OXR(pfnPerfSettingsSetPerformanceLevelEXT(app->Session, XR_PERF_SETTINGS_DOMAIN_CPU_EXT, cpuPerfLevel));
|
||||
OXR(pfnPerfSettingsSetPerformanceLevelEXT(app->Session, XR_PERF_SETTINGS_DOMAIN_GPU_EXT, gpuPerfLevel));
|
||||
|
||||
PFN_xrSetAndroidApplicationThreadKHR pfnSetAndroidApplicationThreadKHR = NULL;
|
||||
OXR(xrGetInstanceProcAddr(
|
||||
app->Instance,
|
||||
"xrSetAndroidApplicationThreadKHR",
|
||||
(PFN_xrVoidFunction*)(&pfnSetAndroidApplicationThreadKHR)));
|
||||
|
||||
OXR(pfnSetAndroidApplicationThreadKHR(app->Session, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, app->MainThreadTid));
|
||||
OXR(pfnSetAndroidApplicationThreadKHR(app->Session, XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR, app->RenderThreadTid));
|
||||
}
|
||||
#endif
|
||||
} else if (state == XR_SESSION_STATE_STOPPING) {
|
||||
assert(app->SessionActive);
|
||||
|
||||
@ -343,193 +378,3 @@ int ovrApp_HandleXrEvents(ovrApp* app) {
|
||||
}
|
||||
return recenter;
|
||||
}
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
ovrMatrix4f
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
|
||||
ovrMatrix4f ovrMatrix4f_CreateProjectionFov(
|
||||
const float angleLeft,
|
||||
const float angleRight,
|
||||
const float angleUp,
|
||||
const float angleDown,
|
||||
const float nearZ,
|
||||
const float farZ) {
|
||||
|
||||
const float tanAngleLeft = tanf(angleLeft);
|
||||
const float tanAngleRight = tanf(angleRight);
|
||||
|
||||
const float tanAngleDown = tanf(angleDown);
|
||||
const float tanAngleUp = tanf(angleUp);
|
||||
|
||||
const float tanAngleWidth = tanAngleRight - tanAngleLeft;
|
||||
|
||||
// Set to tanAngleDown - tanAngleUp for a clip space with positive Y
|
||||
// down (Vulkan). Set to tanAngleUp - tanAngleDown for a clip space with
|
||||
// positive Y up (OpenGL / D3D / Metal).
|
||||
const float tanAngleHeight = tanAngleUp - tanAngleDown;
|
||||
|
||||
// Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES).
|
||||
// Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal).
|
||||
const float offsetZ = nearZ;
|
||||
|
||||
ovrMatrix4f result;
|
||||
if (farZ <= nearZ) {
|
||||
// place the far plane at infinity
|
||||
result.M[0][0] = 2 / tanAngleWidth;
|
||||
result.M[0][1] = 0;
|
||||
result.M[0][2] = (tanAngleRight + tanAngleLeft) / tanAngleWidth;
|
||||
result.M[0][3] = 0;
|
||||
|
||||
result.M[1][0] = 0;
|
||||
result.M[1][1] = 2 / tanAngleHeight;
|
||||
result.M[1][2] = (tanAngleUp + tanAngleDown) / tanAngleHeight;
|
||||
result.M[1][3] = 0;
|
||||
|
||||
result.M[2][0] = 0;
|
||||
result.M[2][1] = 0;
|
||||
result.M[2][2] = -1;
|
||||
result.M[2][3] = -(nearZ + offsetZ);
|
||||
|
||||
result.M[3][0] = 0;
|
||||
result.M[3][1] = 0;
|
||||
result.M[3][2] = -1;
|
||||
result.M[3][3] = 0;
|
||||
} else {
|
||||
// normal projection
|
||||
result.M[0][0] = 2 / tanAngleWidth;
|
||||
result.M[0][1] = 0;
|
||||
result.M[0][2] = (tanAngleRight + tanAngleLeft) / tanAngleWidth;
|
||||
result.M[0][3] = 0;
|
||||
|
||||
result.M[1][0] = 0;
|
||||
result.M[1][1] = 2 / tanAngleHeight;
|
||||
result.M[1][2] = (tanAngleUp + tanAngleDown) / tanAngleHeight;
|
||||
result.M[1][3] = 0;
|
||||
|
||||
result.M[2][0] = 0;
|
||||
result.M[2][1] = 0;
|
||||
result.M[2][2] = -(farZ + offsetZ) / (farZ - nearZ);
|
||||
result.M[2][3] = -(farZ * (nearZ + offsetZ)) / (farZ - nearZ);
|
||||
|
||||
result.M[3][0] = 0;
|
||||
result.M[3][1] = 0;
|
||||
result.M[3][2] = -1;
|
||||
result.M[3][3] = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q) {
|
||||
const float ww = q->w * q->w;
|
||||
const float xx = q->x * q->x;
|
||||
const float yy = q->y * q->y;
|
||||
const float zz = q->z * q->z;
|
||||
|
||||
ovrMatrix4f out;
|
||||
out.M[0][0] = ww + xx - yy - zz;
|
||||
out.M[0][1] = 2 * (q->x * q->y - q->w * q->z);
|
||||
out.M[0][2] = 2 * (q->x * q->z + q->w * q->y);
|
||||
out.M[0][3] = 0;
|
||||
|
||||
out.M[1][0] = 2 * (q->x * q->y + q->w * q->z);
|
||||
out.M[1][1] = ww - xx + yy - zz;
|
||||
out.M[1][2] = 2 * (q->y * q->z - q->w * q->x);
|
||||
out.M[1][3] = 0;
|
||||
|
||||
out.M[2][0] = 2 * (q->x * q->z - q->w * q->y);
|
||||
out.M[2][1] = 2 * (q->y * q->z + q->w * q->x);
|
||||
out.M[2][2] = ww - xx - yy + zz;
|
||||
out.M[2][3] = 0;
|
||||
|
||||
out.M[3][0] = 0;
|
||||
out.M[3][1] = 0;
|
||||
out.M[3][2] = 0;
|
||||
out.M[3][3] = 1;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/// Use left-multiplication to accumulate transformations.
|
||||
ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b) {
|
||||
ovrMatrix4f out;
|
||||
out.M[0][0] = a->M[0][0] * b->M[0][0] + a->M[0][1] * b->M[1][0] + a->M[0][2] * b->M[2][0] +
|
||||
a->M[0][3] * b->M[3][0];
|
||||
out.M[1][0] = a->M[1][0] * b->M[0][0] + a->M[1][1] * b->M[1][0] + a->M[1][2] * b->M[2][0] +
|
||||
a->M[1][3] * b->M[3][0];
|
||||
out.M[2][0] = a->M[2][0] * b->M[0][0] + a->M[2][1] * b->M[1][0] + a->M[2][2] * b->M[2][0] +
|
||||
a->M[2][3] * b->M[3][0];
|
||||
out.M[3][0] = a->M[3][0] * b->M[0][0] + a->M[3][1] * b->M[1][0] + a->M[3][2] * b->M[2][0] +
|
||||
a->M[3][3] * b->M[3][0];
|
||||
|
||||
out.M[0][1] = a->M[0][0] * b->M[0][1] + a->M[0][1] * b->M[1][1] + a->M[0][2] * b->M[2][1] +
|
||||
a->M[0][3] * b->M[3][1];
|
||||
out.M[1][1] = a->M[1][0] * b->M[0][1] + a->M[1][1] * b->M[1][1] + a->M[1][2] * b->M[2][1] +
|
||||
a->M[1][3] * b->M[3][1];
|
||||
out.M[2][1] = a->M[2][0] * b->M[0][1] + a->M[2][1] * b->M[1][1] + a->M[2][2] * b->M[2][1] +
|
||||
a->M[2][3] * b->M[3][1];
|
||||
out.M[3][1] = a->M[3][0] * b->M[0][1] + a->M[3][1] * b->M[1][1] + a->M[3][2] * b->M[2][1] +
|
||||
a->M[3][3] * b->M[3][1];
|
||||
|
||||
out.M[0][2] = a->M[0][0] * b->M[0][2] + a->M[0][1] * b->M[1][2] + a->M[0][2] * b->M[2][2] +
|
||||
a->M[0][3] * b->M[3][2];
|
||||
out.M[1][2] = a->M[1][0] * b->M[0][2] + a->M[1][1] * b->M[1][2] + a->M[1][2] * b->M[2][2] +
|
||||
a->M[1][3] * b->M[3][2];
|
||||
out.M[2][2] = a->M[2][0] * b->M[0][2] + a->M[2][1] * b->M[1][2] + a->M[2][2] * b->M[2][2] +
|
||||
a->M[2][3] * b->M[3][2];
|
||||
out.M[3][2] = a->M[3][0] * b->M[0][2] + a->M[3][1] * b->M[1][2] + a->M[3][2] * b->M[2][2] +
|
||||
a->M[3][3] * b->M[3][2];
|
||||
|
||||
out.M[0][3] = a->M[0][0] * b->M[0][3] + a->M[0][1] * b->M[1][3] + a->M[0][2] * b->M[2][3] +
|
||||
a->M[0][3] * b->M[3][3];
|
||||
out.M[1][3] = a->M[1][0] * b->M[0][3] + a->M[1][1] * b->M[1][3] + a->M[1][2] * b->M[2][3] +
|
||||
a->M[1][3] * b->M[3][3];
|
||||
out.M[2][3] = a->M[2][0] * b->M[0][3] + a->M[2][1] * b->M[1][3] + a->M[2][2] * b->M[2][3] +
|
||||
a->M[2][3] * b->M[3][3];
|
||||
out.M[3][3] = a->M[3][0] * b->M[0][3] + a->M[3][1] * b->M[1][3] + a->M[3][2] * b->M[2][3] +
|
||||
a->M[3][3] * b->M[3][3];
|
||||
return out;
|
||||
}
|
||||
|
||||
ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ) {
|
||||
const float sinX = sinf(radiansX);
|
||||
const float cosX = cosf(radiansX);
|
||||
const ovrMatrix4f rotationX = {
|
||||
{{1, 0, 0, 0}, {0, cosX, -sinX, 0}, {0, sinX, cosX, 0}, {0, 0, 0, 1}}};
|
||||
const float sinY = sinf(radiansY);
|
||||
const float cosY = cosf(radiansY);
|
||||
const ovrMatrix4f rotationY = {
|
||||
{{cosY, 0, sinY, 0}, {0, 1, 0, 0}, {-sinY, 0, cosY, 0}, {0, 0, 0, 1}}};
|
||||
const float sinZ = sinf(radiansZ);
|
||||
const float cosZ = cosf(radiansZ);
|
||||
const ovrMatrix4f rotationZ = {
|
||||
{{cosZ, -sinZ, 0, 0}, {sinZ, cosZ, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}};
|
||||
const ovrMatrix4f rotationXY = ovrMatrix4f_Multiply(&rotationY, &rotationX);
|
||||
return ovrMatrix4f_Multiply(&rotationZ, &rotationXY);
|
||||
}
|
||||
|
||||
XrVector4f XrVector4f_MultiplyMatrix4f(const ovrMatrix4f* a, const XrVector4f* v) {
|
||||
XrVector4f out;
|
||||
out.x = a->M[0][0] * v->x + a->M[0][1] * v->y + a->M[0][2] * v->z + a->M[0][3] * v->w;
|
||||
out.y = a->M[1][0] * v->x + a->M[1][1] * v->y + a->M[1][2] * v->z + a->M[1][3] * v->w;
|
||||
out.z = a->M[2][0] * v->x + a->M[2][1] * v->y + a->M[2][2] * v->z + a->M[2][3] * v->w;
|
||||
out.w = a->M[3][0] * v->x + a->M[3][1] * v->y + a->M[3][2] * v->z + a->M[3][3] * v->w;
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
ovrTrackedController
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
|
||||
void ovrTrackedController_Clear(ovrTrackedController* controller) {
|
||||
controller->Active = false;
|
||||
controller->Pose = XrPosef_Identity();
|
||||
}
|
||||
|
@ -1,102 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
//OpenXR
|
||||
#define XR_USE_GRAPHICS_API_OPENGL_ES 1
|
||||
#define XR_USE_PLATFORM_ANDROID 1
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <jni.h>
|
||||
#include <math.h>
|
||||
#include <openxr.h>
|
||||
#include <openxr_platform.h>
|
||||
|
||||
#define MATH_PI 3.14159265358979323846f
|
||||
|
||||
#define ALOGE(...) printf(__VA_ARGS__)
|
||||
#define ALOGV(...) printf(__VA_ARGS__)
|
||||
|
||||
typedef union {
|
||||
XrCompositionLayerProjection Projection;
|
||||
XrCompositionLayerCylinderKHR Cylinder;
|
||||
} ovrCompositorLayer_Union;
|
||||
|
||||
enum { ovrMaxLayerCount = 1 };
|
||||
enum { ovrMaxNumEyes = 2 };
|
||||
|
||||
#define GL(func) func;
|
||||
#define OXR(func) func;
|
||||
|
||||
typedef struct {
|
||||
JavaVM* Vm;
|
||||
jobject ActivityObject;
|
||||
JNIEnv* Env;
|
||||
char AppName[64];
|
||||
int AppVersion;
|
||||
} ovrJava;
|
||||
|
||||
typedef struct {
|
||||
XrSwapchain Handle;
|
||||
uint32_t Width;
|
||||
uint32_t Height;
|
||||
} ovrSwapChain;
|
||||
|
||||
typedef struct {
|
||||
int Width;
|
||||
int Height;
|
||||
uint32_t TextureSwapChainLength;
|
||||
uint32_t TextureSwapChainIndex;
|
||||
ovrSwapChain ColorSwapChain;
|
||||
XrSwapchainImageOpenGLESKHR* ColorSwapChainImage;
|
||||
unsigned int* DepthBuffers;
|
||||
unsigned int* FrameBuffers;
|
||||
} ovrFramebuffer;
|
||||
|
||||
typedef struct {
|
||||
ovrFramebuffer FrameBuffer[ovrMaxNumEyes];
|
||||
} ovrRenderer;
|
||||
|
||||
typedef struct {
|
||||
int Active;
|
||||
XrPosef Pose;
|
||||
} ovrTrackedController;
|
||||
|
||||
typedef struct {
|
||||
int Focused;
|
||||
|
||||
XrInstance Instance;
|
||||
XrSession Session;
|
||||
XrViewConfigurationProperties ViewportConfig;
|
||||
XrViewConfigurationView ViewConfigurationView[ovrMaxNumEyes];
|
||||
XrSystemId SystemId;
|
||||
XrSpace HeadSpace;
|
||||
XrSpace StageSpace;
|
||||
XrSpace FakeStageSpace;
|
||||
XrSpace CurrentSpace;
|
||||
int SessionActive;
|
||||
|
||||
int SwapInterval;
|
||||
// These threads will be marked as performance threads.
|
||||
int MainThreadTid;
|
||||
int RenderThreadTid;
|
||||
ovrCompositorLayer_Union Layers[ovrMaxLayerCount];
|
||||
int LayerCount;
|
||||
|
||||
int TouchPadDownLastFrame;
|
||||
ovrRenderer Renderer;
|
||||
ovrTrackedController TrackedController[2];
|
||||
} ovrApp;
|
||||
|
||||
|
||||
typedef struct {
|
||||
float M[4][4];
|
||||
} ovrMatrix4f;
|
||||
|
||||
typedef struct {
|
||||
uint64_t frameIndex;
|
||||
ovrApp appState;
|
||||
ovrJava java;
|
||||
float predictedDisplayTime;
|
||||
} engine_t;
|
||||
#include "VR/VRBase.h"
|
||||
|
||||
void ovrApp_Clear(ovrApp* app);
|
||||
void ovrApp_Destroy(ovrApp* app);
|
||||
@ -108,147 +12,5 @@ void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer);
|
||||
void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer);
|
||||
void ovrFramebuffer_SetNone();
|
||||
|
||||
void ovrRenderer_Create(
|
||||
XrSession session,
|
||||
ovrRenderer* renderer,
|
||||
int suggestedEyeTextureWidth,
|
||||
int suggestedEyeTextureHeight);
|
||||
void ovrRenderer_Create(XrSession session, ovrRenderer* renderer, int width, int height, bool multiview);
|
||||
void ovrRenderer_Destroy(ovrRenderer* renderer);
|
||||
|
||||
void ovrTrackedController_Clear(ovrTrackedController* controller);
|
||||
|
||||
ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b);
|
||||
ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ);
|
||||
ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q);
|
||||
ovrMatrix4f ovrMatrix4f_CreateProjectionFov(
|
||||
const float fovDegreesX,
|
||||
const float fovDegreesY,
|
||||
const float offsetX,
|
||||
const float offsetY,
|
||||
const float nearZ,
|
||||
const float farZ);
|
||||
|
||||
XrVector4f XrVector4f_MultiplyMatrix4f(const ovrMatrix4f* a, const XrVector4f* v);
|
||||
|
||||
|
||||
/// THESE METHODS HAVE ORIGIN IN openxr_oculus_helpers.h
|
||||
|
||||
static inline double FromXrTime(const XrTime time) {
|
||||
return (time * 1e-9);
|
||||
}
|
||||
|
||||
static inline XrTime ToXrTime(const double timeInSeconds) {
|
||||
return (timeInSeconds * 1e9);
|
||||
}
|
||||
|
||||
static inline XrPosef XrPosef_Identity() {
|
||||
XrPosef r;
|
||||
r.orientation.x = 0;
|
||||
r.orientation.y = 0;
|
||||
r.orientation.z = 0;
|
||||
r.orientation.w = 1;
|
||||
r.position.x = 0;
|
||||
r.position.y = 0;
|
||||
r.position.z = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline float XrVector3f_LengthSquared(const XrVector3f v) {
|
||||
return v.x * v.x + v.y * v.y + v.z * v.z;;
|
||||
}
|
||||
|
||||
static inline float XrVector3f_Length(const XrVector3f v) {
|
||||
return sqrtf(XrVector3f_LengthSquared(v));
|
||||
}
|
||||
|
||||
static inline XrVector3f XrVector3f_ScalarMultiply(const XrVector3f v, float scale) {
|
||||
XrVector3f u;
|
||||
u.x = v.x * scale;
|
||||
u.y = v.y * scale;
|
||||
u.z = v.z * scale;
|
||||
return u;
|
||||
}
|
||||
|
||||
static inline XrVector3f XrVector3f_Normalized(const XrVector3f v) {
|
||||
float rcpLen = 1.0f / XrVector3f_Length(v);
|
||||
return XrVector3f_ScalarMultiply(v, rcpLen);
|
||||
}
|
||||
|
||||
static inline XrQuaternionf XrQuaternionf_CreateFromVectorAngle(
|
||||
const XrVector3f axis,
|
||||
const float angle) {
|
||||
XrQuaternionf r;
|
||||
if (XrVector3f_LengthSquared(axis) == 0.0f) {
|
||||
r.x = 0;
|
||||
r.y = 0;
|
||||
r.z = 0;
|
||||
r.w = 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
XrVector3f unitAxis = XrVector3f_Normalized(axis);
|
||||
float sinHalfAngle = sinf(angle * 0.5f);
|
||||
|
||||
r.w = cosf(angle * 0.5f);
|
||||
r.x = unitAxis.x * sinHalfAngle;
|
||||
r.y = unitAxis.y * sinHalfAngle;
|
||||
r.z = unitAxis.z * sinHalfAngle;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline XrQuaternionf XrQuaternionf_Multiply(const XrQuaternionf a, const XrQuaternionf b) {
|
||||
XrQuaternionf c;
|
||||
c.x = a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y;
|
||||
c.y = a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x;
|
||||
c.z = a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w;
|
||||
c.w = a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z;
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline XrQuaternionf XrQuaternionf_Inverse(const XrQuaternionf q) {
|
||||
XrQuaternionf r;
|
||||
r.x = -q.x;
|
||||
r.y = -q.y;
|
||||
r.z = -q.z;
|
||||
r.w = q.w;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline XrVector3f XrQuaternionf_Rotate(const XrQuaternionf a, const XrVector3f v) {
|
||||
XrVector3f r;
|
||||
XrQuaternionf q = {v.x, v.y, v.z, 0.0f};
|
||||
XrQuaternionf aq = XrQuaternionf_Multiply(a, q);
|
||||
XrQuaternionf aInv = XrQuaternionf_Inverse(a);
|
||||
XrQuaternionf aqaInv = XrQuaternionf_Multiply(aq, aInv);
|
||||
r.x = aqaInv.x;
|
||||
r.y = aqaInv.y;
|
||||
r.z = aqaInv.z;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline XrVector3f XrVector3f_Add(const XrVector3f u, const XrVector3f v) {
|
||||
XrVector3f w;
|
||||
w.x = u.x + v.x;
|
||||
w.y = u.y + v.y;
|
||||
w.z = u.z + v.z;
|
||||
return w;
|
||||
}
|
||||
|
||||
static inline XrVector3f XrPosef_Transform(const XrPosef a, const XrVector3f v) {
|
||||
XrVector3f r0 = XrQuaternionf_Rotate(a.orientation, v);
|
||||
return XrVector3f_Add(r0, a.position);
|
||||
}
|
||||
|
||||
static inline XrPosef XrPosef_Multiply(const XrPosef a, const XrPosef b) {
|
||||
XrPosef c;
|
||||
c.orientation = XrQuaternionf_Multiply(a.orientation, b.orientation);
|
||||
c.position = XrPosef_Transform(a, b.position);
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline XrPosef XrPosef_Inverse(const XrPosef a) {
|
||||
XrPosef b;
|
||||
b.orientation = XrQuaternionf_Inverse(a.orientation);
|
||||
b.position = XrQuaternionf_Rotate(b.orientation, XrVector3f_ScalarMultiply(a.position, -1.0f));
|
||||
return b;
|
||||
}
|
||||
|
@ -25,9 +25,7 @@ XrAction vibrateRightFeedback;
|
||||
XrActionSet runningActionSet;
|
||||
XrSpace leftControllerAimSpace = XR_NULL_HANDLE;
|
||||
XrSpace rightControllerAimSpace = XR_NULL_HANDLE;
|
||||
int actionsAttached = 0;
|
||||
int inputInitialized = 0;
|
||||
int useSimpleProfile = 0;
|
||||
|
||||
int in_vrEventTime = 0;
|
||||
double lastframetime = 0;
|
||||
@ -40,9 +38,6 @@ XrActionStateVector2f moveJoystickState[2];
|
||||
float vibration_channel_duration[2] = {0.0f, 0.0f};
|
||||
float vibration_channel_intensity[2] = {0.0f, 0.0f};
|
||||
|
||||
float radians(float deg) {
|
||||
return (deg * M_PI) / 180.0;
|
||||
}
|
||||
|
||||
unsigned long sys_timeBase = 0;
|
||||
int milliseconds(void) {
|
||||
@ -58,98 +53,22 @@ int milliseconds(void) {
|
||||
return (tp.tv_sec - sys_timeBase)*1000 + tp.tv_usec/1000;
|
||||
}
|
||||
|
||||
#ifndef EPSILON
|
||||
#define EPSILON 0.001f
|
||||
#endif
|
||||
|
||||
XrVector3f normalizeVec(XrVector3f vec) {
|
||||
float xxyyzz = vec.x*vec.x + vec.y*vec.y + vec.z*vec.z;
|
||||
|
||||
XrVector3f result;
|
||||
float invLength = 1.0f / sqrtf(xxyyzz);
|
||||
result.x = vec.x * invLength;
|
||||
result.y = vec.y * invLength;
|
||||
result.z = vec.z * invLength;
|
||||
return result;
|
||||
XrTime ToXrTime(const double timeInSeconds) {
|
||||
return (timeInSeconds * 1e9);
|
||||
}
|
||||
|
||||
void GetAnglesFromVectors(const XrVector3f forward, const XrVector3f right, const XrVector3f up, vec3_t angles) {
|
||||
float sr, sp, sy, cr, cp, cy;
|
||||
|
||||
sp = -forward.z;
|
||||
|
||||
float cp_x_cy = forward.x;
|
||||
float cp_x_sy = forward.y;
|
||||
float cp_x_sr = -right.z;
|
||||
float cp_x_cr = up.z;
|
||||
|
||||
float yaw = atan2(cp_x_sy, cp_x_cy);
|
||||
float roll = atan2(cp_x_sr, cp_x_cr);
|
||||
|
||||
cy = cos(yaw);
|
||||
sy = sin(yaw);
|
||||
cr = cos(roll);
|
||||
sr = sin(roll);
|
||||
|
||||
if (fabs(cy) > EPSILON) {
|
||||
cp = cp_x_cy / cy;
|
||||
} else if (fabs(sy) > EPSILON) {
|
||||
cp = cp_x_sy / sy;
|
||||
} else if (fabs(sr) > EPSILON) {
|
||||
cp = cp_x_sr / sr;
|
||||
} else if (fabs(cr) > EPSILON) {
|
||||
cp = cp_x_cr / cr;
|
||||
} else {
|
||||
cp = cos(asin(sp));
|
||||
}
|
||||
|
||||
float pitch = atan2(sp, cp);
|
||||
|
||||
angles[0] = pitch / (M_PI*2.f / 360.f);
|
||||
angles[1] = yaw / (M_PI*2.f / 360.f);
|
||||
angles[2] = roll / (M_PI*2.f / 360.f);
|
||||
}
|
||||
|
||||
void QuatToYawPitchRoll(XrQuaternionf q, vec3_t rotation, vec3_t out) {
|
||||
|
||||
ovrMatrix4f mat = ovrMatrix4f_CreateFromQuaternion( &q );
|
||||
|
||||
if (rotation[0] != 0.0f || rotation[1] != 0.0f || rotation[2] != 0.0f) {
|
||||
ovrMatrix4f rot = ovrMatrix4f_CreateRotation(radians(rotation[0]), radians(rotation[1]), radians(rotation[2]));
|
||||
mat = ovrMatrix4f_Multiply(&mat, &rot);
|
||||
}
|
||||
|
||||
XrVector4f v1 = {0, 0, -1, 0};
|
||||
XrVector4f v2 = {1, 0, 0, 0};
|
||||
XrVector4f v3 = {0, 1, 0, 0};
|
||||
|
||||
XrVector4f forwardInVRSpace = XrVector4f_MultiplyMatrix4f(&mat, &v1);
|
||||
XrVector4f rightInVRSpace = XrVector4f_MultiplyMatrix4f(&mat, &v2);
|
||||
XrVector4f upInVRSpace = XrVector4f_MultiplyMatrix4f(&mat, &v3);
|
||||
|
||||
XrVector3f forward = {-forwardInVRSpace.z, -forwardInVRSpace.x, forwardInVRSpace.y};
|
||||
XrVector3f right = {-rightInVRSpace.z, -rightInVRSpace.x, rightInVRSpace.y};
|
||||
XrVector3f up = {-upInVRSpace.z, -upInVRSpace.x, upInVRSpace.y};
|
||||
|
||||
XrVector3f forwardNormal = normalizeVec(forward);
|
||||
XrVector3f rightNormal = normalizeVec(right);
|
||||
XrVector3f upNormal = normalizeVec(up);
|
||||
|
||||
GetAnglesFromVectors(forwardNormal, rightNormal, upNormal, out);
|
||||
}
|
||||
|
||||
void VR_Vibrate( int duration, int chan, float intensity ) {
|
||||
void INVR_Vibrate( int duration, int chan, float intensity ) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
int channel = (i + 1) & chan;
|
||||
int channel = i & chan;
|
||||
if (channel) {
|
||||
if (vibration_channel_duration[channel-1] > 0.0f)
|
||||
if (vibration_channel_duration[channel] > 0.0f)
|
||||
return;
|
||||
|
||||
if (vibration_channel_duration[channel-1] == -1.0f && duration != 0.0f)
|
||||
if (vibration_channel_duration[channel] == -1.0f && duration != 0.0f)
|
||||
return;
|
||||
|
||||
vibration_channel_duration[channel-1] = duration;
|
||||
vibration_channel_intensity[channel-1] = intensity;
|
||||
vibration_channel_duration[channel] = duration;
|
||||
vibration_channel_intensity[channel] = intensity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -328,174 +247,109 @@ void IN_VRInit( engine_t *engine ) {
|
||||
handPoseRightAction = CreateAction(runningActionSet, XR_ACTION_TYPE_POSE_INPUT, "hand_pose_right", NULL, 1, &rightHandPath);
|
||||
|
||||
XrPath interactionProfilePath = XR_NULL_PATH;
|
||||
XrPath interactionProfilePathTouch = XR_NULL_PATH;
|
||||
XrPath interactionProfilePathKHRSimple = XR_NULL_PATH;
|
||||
#ifdef OPENXR_PLATFORM_QUEST
|
||||
OXR(xrStringToPath(engine->appState.Instance, "/interaction_profiles/oculus/touch_controller", &interactionProfilePath));
|
||||
#elif OPENXR_PLATFORM_PICO
|
||||
OXR(xrStringToPath(engine->appState.Instance, "/interaction_profiles/pico/neo3_controller", &interactionProfilePath));
|
||||
#endif
|
||||
// Map bindings
|
||||
XrActionSuggestedBinding bindings[32]; // large enough for all profiles
|
||||
int currBinding = 0;
|
||||
|
||||
OXR(xrStringToPath(engine->appState.Instance, "/interaction_profiles/oculus/touch_controller", &interactionProfilePathTouch));
|
||||
OXR(xrStringToPath(engine->appState.Instance, "/interaction_profiles/khr/simple_controller", &interactionProfilePathKHRSimple));
|
||||
#ifdef OPENXR_PLATFORM_QUEST
|
||||
bindings[currBinding++] = ActionSuggestedBinding(indexLeftAction, "/user/hand/left/input/trigger");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(indexRightAction, "/user/hand/right/input/trigger");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(menuAction, "/user/hand/left/input/menu/click");
|
||||
#elif OPENXR_PLATFORM_PICO
|
||||
bindings[currBinding++] = ActionSuggestedBinding(indexLeftAction, "/user/hand/left/input/trigger/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(indexRightAction, "/user/hand/right/input/trigger/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(menuAction, "/user/hand/left/input/back/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(menuAction, "/user/hand/right/input/back/click");
|
||||
#endif
|
||||
bindings[currBinding++] = ActionSuggestedBinding(buttonXAction, "/user/hand/left/input/x/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(buttonYAction, "/user/hand/left/input/y/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(buttonAAction, "/user/hand/right/input/a/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(buttonBAction, "/user/hand/right/input/b/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(gripLeftAction, "/user/hand/left/input/squeeze/value");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(gripRightAction, "/user/hand/right/input/squeeze/value");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(moveOnLeftJoystickAction, "/user/hand/left/input/thumbstick");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(moveOnRightJoystickAction, "/user/hand/right/input/thumbstick");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(thumbstickLeftClickAction, "/user/hand/left/input/thumbstick/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(thumbstickRightClickAction, "/user/hand/right/input/thumbstick/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(vibrateLeftFeedback, "/user/hand/left/output/haptic");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(vibrateRightFeedback, "/user/hand/right/output/haptic");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(handPoseLeftAction, "/user/hand/left/input/aim/pose");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(handPoseRightAction, "/user/hand/right/input/aim/pose");
|
||||
|
||||
// Toggle this to force simple as a first choice, otherwise use it as a last resort
|
||||
if (useSimpleProfile) {
|
||||
ALOGV("xrSuggestInteractionProfileBindings found bindings for Khronos SIMPLE controller");
|
||||
interactionProfilePath = interactionProfilePathKHRSimple;
|
||||
} else {
|
||||
// Query Set
|
||||
XrActionSet queryActionSet = CreateActionSet(1, "query_action_set", "Action Set used to query device caps");
|
||||
XrAction dummyAction = CreateAction(queryActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "dummy_action", "Dummy Action", 0, NULL);
|
||||
XrInteractionProfileSuggestedBinding suggestedBindings = {};
|
||||
suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
|
||||
suggestedBindings.next = NULL;
|
||||
suggestedBindings.interactionProfile = interactionProfilePath;
|
||||
suggestedBindings.suggestedBindings = bindings;
|
||||
suggestedBindings.countSuggestedBindings = currBinding;
|
||||
OXR(xrSuggestInteractionProfileBindings(engine->appState.Instance, &suggestedBindings));
|
||||
|
||||
// Map bindings
|
||||
XrActionSuggestedBinding bindings[1];
|
||||
int currBinding = 0;
|
||||
bindings[currBinding++] = ActionSuggestedBinding(dummyAction, "/user/hand/right/input/system/click");
|
||||
// Attach actions
|
||||
XrSessionActionSetsAttachInfo attachInfo = {};
|
||||
attachInfo.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO;
|
||||
attachInfo.next = NULL;
|
||||
attachInfo.countActionSets = 1;
|
||||
attachInfo.actionSets = &runningActionSet;
|
||||
OXR(xrAttachSessionActionSets(engine->appState.Session, &attachInfo));
|
||||
|
||||
XrInteractionProfileSuggestedBinding suggestedBindings = {};
|
||||
suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
|
||||
suggestedBindings.next = NULL;
|
||||
suggestedBindings.suggestedBindings = bindings;
|
||||
suggestedBindings.countSuggestedBindings = currBinding;
|
||||
// Enumerate actions
|
||||
XrPath actionPathsBuffer[32];
|
||||
char stringBuffer[256];
|
||||
XrAction actionsToEnumerate[] = {
|
||||
indexLeftAction,
|
||||
indexRightAction,
|
||||
menuAction,
|
||||
buttonAAction,
|
||||
buttonBAction,
|
||||
buttonXAction,
|
||||
buttonYAction,
|
||||
gripLeftAction,
|
||||
gripRightAction,
|
||||
moveOnLeftJoystickAction,
|
||||
moveOnRightJoystickAction,
|
||||
thumbstickLeftClickAction,
|
||||
thumbstickRightClickAction,
|
||||
vibrateLeftFeedback,
|
||||
vibrateRightFeedback,
|
||||
handPoseLeftAction,
|
||||
handPoseRightAction
|
||||
};
|
||||
for (XrAction & i : actionsToEnumerate) {
|
||||
XrBoundSourcesForActionEnumerateInfo enumerateInfo = {};
|
||||
enumerateInfo.type = XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO;
|
||||
enumerateInfo.next = NULL;
|
||||
enumerateInfo.action = i;
|
||||
|
||||
// Try all
|
||||
suggestedBindings.interactionProfile = interactionProfilePathTouch;
|
||||
XrResult suggestTouchResult = xrSuggestInteractionProfileBindings(engine->appState.Instance, &suggestedBindings);
|
||||
OXR(suggestTouchResult);
|
||||
// Get Count
|
||||
uint32_t countOutput = 0;
|
||||
OXR(xrEnumerateBoundSourcesForAction(engine->appState.Session, &enumerateInfo, 0 /* request size */, &countOutput, NULL));
|
||||
ALOGV("xrEnumerateBoundSourcesForAction action=%lld count=%u", (long long)enumerateInfo.action, countOutput);
|
||||
|
||||
if (XR_SUCCESS == suggestTouchResult) {
|
||||
ALOGV("xrSuggestInteractionProfileBindings found bindings for QUEST controller");
|
||||
interactionProfilePath = interactionProfilePathTouch;
|
||||
}
|
||||
|
||||
if (interactionProfilePath == XR_NULL_PATH) {
|
||||
// Simple as a fallback
|
||||
bindings[0] = ActionSuggestedBinding(dummyAction, "/user/hand/right/input/select/click");
|
||||
suggestedBindings.interactionProfile = interactionProfilePathKHRSimple;
|
||||
XrResult suggestKHRSimpleResult = xrSuggestInteractionProfileBindings(engine->appState.Instance, &suggestedBindings);
|
||||
OXR(suggestKHRSimpleResult);
|
||||
if (XR_SUCCESS == suggestKHRSimpleResult) {
|
||||
ALOGV("xrSuggestInteractionProfileBindings found bindings for Khronos SIMPLE controller");
|
||||
interactionProfilePath = interactionProfilePathKHRSimple;
|
||||
} else {
|
||||
ALOGE("xrSuggestInteractionProfileBindings did NOT find any bindings.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Action creation
|
||||
{
|
||||
// Map bindings
|
||||
XrActionSuggestedBinding bindings[32]; // large enough for all profiles
|
||||
int currBinding = 0;
|
||||
|
||||
{
|
||||
if (interactionProfilePath == interactionProfilePathTouch) {
|
||||
bindings[currBinding++] = ActionSuggestedBinding(indexLeftAction, "/user/hand/left/input/trigger");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(indexRightAction, "/user/hand/right/input/trigger");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(menuAction, "/user/hand/left/input/menu/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(buttonXAction, "/user/hand/left/input/x/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(buttonYAction, "/user/hand/left/input/y/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(buttonAAction, "/user/hand/right/input/a/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(buttonBAction, "/user/hand/right/input/b/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(gripLeftAction, "/user/hand/left/input/squeeze/value");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(gripRightAction, "/user/hand/right/input/squeeze/value");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(moveOnLeftJoystickAction, "/user/hand/left/input/thumbstick");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(moveOnRightJoystickAction, "/user/hand/right/input/thumbstick");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(thumbstickLeftClickAction, "/user/hand/left/input/thumbstick/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(thumbstickRightClickAction, "/user/hand/right/input/thumbstick/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(vibrateLeftFeedback, "/user/hand/left/output/haptic");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(vibrateRightFeedback, "/user/hand/right/output/haptic");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(handPoseLeftAction, "/user/hand/left/input/aim/pose");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(handPoseRightAction, "/user/hand/right/input/aim/pose");
|
||||
}
|
||||
|
||||
if (interactionProfilePath == interactionProfilePathKHRSimple) {
|
||||
bindings[currBinding++] = ActionSuggestedBinding(indexLeftAction, "/user/hand/left/input/select/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(indexRightAction, "/user/hand/right/input/select/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(buttonAAction, "/user/hand/left/input/menu/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(buttonXAction, "/user/hand/right/input/menu/click");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(vibrateLeftFeedback, "/user/hand/left/output/haptic");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(vibrateRightFeedback, "/user/hand/right/output/haptic");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(handPoseLeftAction, "/user/hand/left/input/aim/pose");
|
||||
bindings[currBinding++] = ActionSuggestedBinding(handPoseRightAction, "/user/hand/right/input/aim/pose");
|
||||
}
|
||||
}
|
||||
|
||||
XrInteractionProfileSuggestedBinding suggestedBindings = {};
|
||||
suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
|
||||
suggestedBindings.next = NULL;
|
||||
suggestedBindings.interactionProfile = interactionProfilePath;
|
||||
suggestedBindings.suggestedBindings = bindings;
|
||||
suggestedBindings.countSuggestedBindings = currBinding;
|
||||
OXR(xrSuggestInteractionProfileBindings(engine->appState.Instance, &suggestedBindings));
|
||||
|
||||
// Enumerate actions
|
||||
XrPath actionPathsBuffer[32];
|
||||
char stringBuffer[256];
|
||||
XrAction actionsToEnumerate[] = {
|
||||
indexLeftAction,
|
||||
indexRightAction,
|
||||
menuAction,
|
||||
buttonAAction,
|
||||
buttonBAction,
|
||||
buttonXAction,
|
||||
buttonYAction,
|
||||
gripLeftAction,
|
||||
gripRightAction,
|
||||
moveOnLeftJoystickAction,
|
||||
moveOnRightJoystickAction,
|
||||
thumbstickLeftClickAction,
|
||||
thumbstickRightClickAction,
|
||||
vibrateLeftFeedback,
|
||||
vibrateRightFeedback,
|
||||
handPoseLeftAction,
|
||||
handPoseRightAction
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(actionsToEnumerate) / sizeof(actionsToEnumerate[0]); ++i) {
|
||||
XrBoundSourcesForActionEnumerateInfo enumerateInfo = {};
|
||||
enumerateInfo.type = XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO;
|
||||
enumerateInfo.next = NULL;
|
||||
enumerateInfo.action = actionsToEnumerate[i];
|
||||
|
||||
// Get Count
|
||||
uint32_t countOutput = 0;
|
||||
if (countOutput < 32) {
|
||||
OXR(xrEnumerateBoundSourcesForAction(
|
||||
engine->appState.Session, &enumerateInfo, 0 /* request size */, &countOutput, NULL));
|
||||
ALOGV(
|
||||
"xrEnumerateBoundSourcesForAction action=%lld count=%u",
|
||||
(long long)enumerateInfo.action,
|
||||
countOutput);
|
||||
engine->appState.Session, &enumerateInfo, 32, &countOutput, actionPathsBuffer));
|
||||
for (uint32_t a = 0; a < countOutput; ++a) {
|
||||
XrInputSourceLocalizedNameGetInfo nameGetInfo = {};
|
||||
nameGetInfo.type = XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO;
|
||||
nameGetInfo.next = NULL;
|
||||
nameGetInfo.sourcePath = actionPathsBuffer[a];
|
||||
nameGetInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT |
|
||||
XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT |
|
||||
XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT;
|
||||
|
||||
if (countOutput < 32) {
|
||||
OXR(xrEnumerateBoundSourcesForAction(
|
||||
engine->appState.Session, &enumerateInfo, 32, &countOutput, actionPathsBuffer));
|
||||
for (uint32_t a = 0; a < countOutput; ++a) {
|
||||
XrInputSourceLocalizedNameGetInfo nameGetInfo = {};
|
||||
nameGetInfo.type = XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO;
|
||||
nameGetInfo.next = NULL;
|
||||
nameGetInfo.sourcePath = actionPathsBuffer[a];
|
||||
nameGetInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT |
|
||||
XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT |
|
||||
XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT;
|
||||
|
||||
uint32_t stringCount = 0u;
|
||||
OXR(xrGetInputSourceLocalizedName(
|
||||
engine->appState.Session, &nameGetInfo, 0, &stringCount, NULL));
|
||||
if (stringCount < 256) {
|
||||
OXR(xrGetInputSourceLocalizedName(
|
||||
engine->appState.Session, &nameGetInfo, 256, &stringCount, stringBuffer));
|
||||
char pathStr[256];
|
||||
uint32_t strLen = 0;
|
||||
OXR(xrPathToString(
|
||||
engine->appState.Instance,
|
||||
actionPathsBuffer[a],
|
||||
(uint32_t)sizeof(pathStr),
|
||||
&strLen,
|
||||
pathStr));
|
||||
ALOGV(
|
||||
" -> path = %lld `%s` -> `%s`",
|
||||
(long long)actionPathsBuffer[a],
|
||||
pathStr,
|
||||
stringBuffer);
|
||||
}
|
||||
uint32_t stringCount = 0u;
|
||||
OXR(xrGetInputSourceLocalizedName(engine->appState.Session, &nameGetInfo, 0, &stringCount, NULL));
|
||||
if (stringCount < 256) {
|
||||
OXR(xrGetInputSourceLocalizedName(engine->appState.Session, &nameGetInfo, 256, &stringCount, stringBuffer));
|
||||
char pathStr[256];
|
||||
uint32_t strLen = 0;
|
||||
OXR(xrPathToString(engine->appState.Instance, actionPathsBuffer[a], (uint32_t)sizeof(pathStr), &strLen, pathStr));
|
||||
ALOGV(" -> path = %lld `%s` -> `%s`", (long long)actionPathsBuffer[a], pathStr, stringBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -504,16 +358,6 @@ void IN_VRInit( engine_t *engine ) {
|
||||
}
|
||||
|
||||
void IN_VRInputFrame( engine_t* engine ) {
|
||||
// Attach to session
|
||||
if (!actionsAttached) {
|
||||
XrSessionActionSetsAttachInfo attachInfo = {};
|
||||
attachInfo.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO;
|
||||
attachInfo.next = NULL;
|
||||
attachInfo.countActionSets = 1;
|
||||
attachInfo.actionSets = &runningActionSet;
|
||||
OXR(xrAttachSessionActionSets(engine->appState.Session, &attachInfo));
|
||||
actionsAttached = 1;
|
||||
}
|
||||
|
||||
// sync action data
|
||||
XrActiveActionSet activeActionSet = {};
|
||||
@ -588,3 +432,12 @@ uint32_t IN_VRGetButtonState( int controllerIndex ) {
|
||||
XrVector2f IN_VRGetJoystickState( int controllerIndex ) {
|
||||
return moveJoystickState[controllerIndex].currentState;
|
||||
}
|
||||
|
||||
XrPosef IN_VRGetPose( int controllerIndex ) {
|
||||
engine_t* engine = VR_GetEngine();
|
||||
XrSpaceLocation loc = {};
|
||||
loc.type = XR_TYPE_SPACE_LOCATION;
|
||||
XrSpace aimSpace[] = { leftControllerAimSpace, rightControllerAimSpace };
|
||||
xrLocateSpace(aimSpace[controllerIndex], engine->appState.CurrentSpace, engine->predictedDisplayTime, &loc);
|
||||
return loc.pose;
|
||||
}
|
||||
|
@ -2,13 +2,6 @@
|
||||
|
||||
#include "VRBase.h"
|
||||
|
||||
// angle indexes
|
||||
#define PITCH 0
|
||||
#define YAW 1
|
||||
#define ROLL 2
|
||||
|
||||
typedef float vec3_t[3];
|
||||
|
||||
typedef enum ovrButton_ {
|
||||
ovrButton_A = 0x00000001, // Set for trigger pulled on the Gear VR and Go Controllers
|
||||
ovrButton_B = 0x00000002,
|
||||
@ -39,5 +32,5 @@ void IN_VRInit( engine_t *engine );
|
||||
void IN_VRInputFrame( engine_t* engine );
|
||||
uint32_t IN_VRGetButtonState( int controllerIndex );
|
||||
XrVector2f IN_VRGetJoystickState( int controllerIndex );
|
||||
|
||||
void QuatToYawPitchRoll(XrQuaternionf q, vec3_t rotation, vec3_t out);
|
||||
XrPosef IN_VRGetPose( int controllerIndex );
|
||||
void INVR_Vibrate( int duration, int chan, float intensity );
|
||||
|
415
Common/VR/VRMath.cpp
Normal file
415
Common/VR/VRMath.cpp
Normal file
@ -0,0 +1,415 @@
|
||||
#include "VRMath.h"
|
||||
|
||||
float ToDegrees(float rad) {
|
||||
return (float)(rad / M_PI * 180.0f);
|
||||
}
|
||||
|
||||
float ToRadians(float deg) {
|
||||
return (float)(deg * M_PI / 180.0f);
|
||||
}
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
ovrMatrix4f
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
|
||||
float ovrMatrix4f_Minor(const ovrMatrix4f* m, int r0, int r1, int r2, int c0, int c1, int c2) {
|
||||
return m->M[r0][c0] * (m->M[r1][c1] * m->M[r2][c2] - m->M[r2][c1] * m->M[r1][c2]) -
|
||||
m->M[r0][c1] * (m->M[r1][c0] * m->M[r2][c2] - m->M[r2][c0] * m->M[r1][c2]) +
|
||||
m->M[r0][c2] * (m->M[r1][c0] * m->M[r2][c1] - m->M[r2][c0] * m->M[r1][c1]);
|
||||
}
|
||||
|
||||
ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q) {
|
||||
const float ww = q->w * q->w;
|
||||
const float xx = q->x * q->x;
|
||||
const float yy = q->y * q->y;
|
||||
const float zz = q->z * q->z;
|
||||
|
||||
ovrMatrix4f out;
|
||||
out.M[0][0] = ww + xx - yy - zz;
|
||||
out.M[0][1] = 2 * (q->x * q->y - q->w * q->z);
|
||||
out.M[0][2] = 2 * (q->x * q->z + q->w * q->y);
|
||||
out.M[0][3] = 0;
|
||||
|
||||
out.M[1][0] = 2 * (q->x * q->y + q->w * q->z);
|
||||
out.M[1][1] = ww - xx + yy - zz;
|
||||
out.M[1][2] = 2 * (q->y * q->z - q->w * q->x);
|
||||
out.M[1][3] = 0;
|
||||
|
||||
out.M[2][0] = 2 * (q->x * q->z - q->w * q->y);
|
||||
out.M[2][1] = 2 * (q->y * q->z + q->w * q->x);
|
||||
out.M[2][2] = ww - xx - yy + zz;
|
||||
out.M[2][3] = 0;
|
||||
|
||||
out.M[3][0] = 0;
|
||||
out.M[3][1] = 0;
|
||||
out.M[3][2] = 0;
|
||||
out.M[3][3] = 1;
|
||||
return out;
|
||||
}
|
||||
|
||||
ovrMatrix4f ovrMatrix4f_CreateProjectionFov(
|
||||
const float angleLeft,
|
||||
const float angleRight,
|
||||
const float angleUp,
|
||||
const float angleDown,
|
||||
const float nearZ,
|
||||
const float farZ) {
|
||||
|
||||
const float tanAngleLeft = tanf(angleLeft);
|
||||
const float tanAngleRight = tanf(angleRight);
|
||||
|
||||
const float tanAngleDown = tanf(angleDown);
|
||||
const float tanAngleUp = tanf(angleUp);
|
||||
|
||||
const float tanAngleWidth = tanAngleRight - tanAngleLeft;
|
||||
|
||||
// Set to tanAngleDown - tanAngleUp for a clip space with positive Y
|
||||
// down (Vulkan). Set to tanAngleUp - tanAngleDown for a clip space with
|
||||
// positive Y up (OpenGL / D3D / Metal).
|
||||
const float tanAngleHeight = tanAngleUp - tanAngleDown;
|
||||
|
||||
// Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES).
|
||||
// Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal).
|
||||
const float offsetZ = nearZ;
|
||||
|
||||
ovrMatrix4f result;
|
||||
if (farZ <= nearZ) {
|
||||
// place the far plane at infinity
|
||||
result.M[0][0] = 2 / tanAngleWidth;
|
||||
result.M[0][1] = 0;
|
||||
result.M[0][2] = (tanAngleRight + tanAngleLeft) / tanAngleWidth;
|
||||
result.M[0][3] = 0;
|
||||
|
||||
result.M[1][0] = 0;
|
||||
result.M[1][1] = 2 / tanAngleHeight;
|
||||
result.M[1][2] = (tanAngleUp + tanAngleDown) / tanAngleHeight;
|
||||
result.M[1][3] = 0;
|
||||
|
||||
result.M[2][0] = 0;
|
||||
result.M[2][1] = 0;
|
||||
result.M[2][2] = -1;
|
||||
result.M[2][3] = -(nearZ + offsetZ);
|
||||
|
||||
result.M[3][0] = 0;
|
||||
result.M[3][1] = 0;
|
||||
result.M[3][2] = -1;
|
||||
result.M[3][3] = 0;
|
||||
} else {
|
||||
// normal projection
|
||||
result.M[0][0] = 2 / tanAngleWidth;
|
||||
result.M[0][1] = 0;
|
||||
result.M[0][2] = (tanAngleRight + tanAngleLeft) / tanAngleWidth;
|
||||
result.M[0][3] = 0;
|
||||
|
||||
result.M[1][0] = 0;
|
||||
result.M[1][1] = 2 / tanAngleHeight;
|
||||
result.M[1][2] = (tanAngleUp + tanAngleDown) / tanAngleHeight;
|
||||
result.M[1][3] = 0;
|
||||
|
||||
result.M[2][0] = 0;
|
||||
result.M[2][1] = 0;
|
||||
result.M[2][2] = -(farZ + offsetZ) / (farZ - nearZ);
|
||||
result.M[2][3] = -(farZ * (nearZ + offsetZ)) / (farZ - nearZ);
|
||||
|
||||
result.M[3][0] = 0;
|
||||
result.M[3][1] = 0;
|
||||
result.M[3][2] = -1;
|
||||
result.M[3][3] = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ) {
|
||||
const float sinX = sinf(radiansX);
|
||||
const float cosX = cosf(radiansX);
|
||||
const ovrMatrix4f rotationX = {
|
||||
{{1, 0, 0, 0}, {0, cosX, -sinX, 0}, {0, sinX, cosX, 0}, {0, 0, 0, 1}}};
|
||||
const float sinY = sinf(radiansY);
|
||||
const float cosY = cosf(radiansY);
|
||||
const ovrMatrix4f rotationY = {
|
||||
{{cosY, 0, sinY, 0}, {0, 1, 0, 0}, {-sinY, 0, cosY, 0}, {0, 0, 0, 1}}};
|
||||
const float sinZ = sinf(radiansZ);
|
||||
const float cosZ = cosf(radiansZ);
|
||||
const ovrMatrix4f rotationZ = {
|
||||
{{cosZ, -sinZ, 0, 0}, {sinZ, cosZ, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}};
|
||||
const ovrMatrix4f rotationXY = ovrMatrix4f_Multiply(&rotationY, &rotationX);
|
||||
return ovrMatrix4f_Multiply(&rotationZ, &rotationXY);
|
||||
}
|
||||
|
||||
ovrMatrix4f ovrMatrix4f_Inverse(const ovrMatrix4f* m) {
|
||||
const float rcpDet = 1.0f /
|
||||
(m->M[0][0] * ovrMatrix4f_Minor(m, 1, 2, 3, 1, 2, 3) -
|
||||
m->M[0][1] * ovrMatrix4f_Minor(m, 1, 2, 3, 0, 2, 3) +
|
||||
m->M[0][2] * ovrMatrix4f_Minor(m, 1, 2, 3, 0, 1, 3) -
|
||||
m->M[0][3] * ovrMatrix4f_Minor(m, 1, 2, 3, 0, 1, 2));
|
||||
ovrMatrix4f out;
|
||||
out.M[0][0] = ovrMatrix4f_Minor(m, 1, 2, 3, 1, 2, 3) * rcpDet;
|
||||
out.M[0][1] = -ovrMatrix4f_Minor(m, 0, 2, 3, 1, 2, 3) * rcpDet;
|
||||
out.M[0][2] = ovrMatrix4f_Minor(m, 0, 1, 3, 1, 2, 3) * rcpDet;
|
||||
out.M[0][3] = -ovrMatrix4f_Minor(m, 0, 1, 2, 1, 2, 3) * rcpDet;
|
||||
out.M[1][0] = -ovrMatrix4f_Minor(m, 1, 2, 3, 0, 2, 3) * rcpDet;
|
||||
out.M[1][1] = ovrMatrix4f_Minor(m, 0, 2, 3, 0, 2, 3) * rcpDet;
|
||||
out.M[1][2] = -ovrMatrix4f_Minor(m, 0, 1, 3, 0, 2, 3) * rcpDet;
|
||||
out.M[1][3] = ovrMatrix4f_Minor(m, 0, 1, 2, 0, 2, 3) * rcpDet;
|
||||
out.M[2][0] = ovrMatrix4f_Minor(m, 1, 2, 3, 0, 1, 3) * rcpDet;
|
||||
out.M[2][1] = -ovrMatrix4f_Minor(m, 0, 2, 3, 0, 1, 3) * rcpDet;
|
||||
out.M[2][2] = ovrMatrix4f_Minor(m, 0, 1, 3, 0, 1, 3) * rcpDet;
|
||||
out.M[2][3] = -ovrMatrix4f_Minor(m, 0, 1, 2, 0, 1, 3) * rcpDet;
|
||||
out.M[3][0] = -ovrMatrix4f_Minor(m, 1, 2, 3, 0, 1, 2) * rcpDet;
|
||||
out.M[3][1] = ovrMatrix4f_Minor(m, 0, 2, 3, 0, 1, 2) * rcpDet;
|
||||
out.M[3][2] = -ovrMatrix4f_Minor(m, 0, 1, 3, 0, 1, 2) * rcpDet;
|
||||
out.M[3][3] = ovrMatrix4f_Minor(m, 0, 1, 2, 0, 1, 2) * rcpDet;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Use left-multiplication to accumulate transformations.
|
||||
ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b) {
|
||||
ovrMatrix4f out;
|
||||
out.M[0][0] = a->M[0][0] * b->M[0][0] + a->M[0][1] * b->M[1][0] + a->M[0][2] * b->M[2][0] +
|
||||
a->M[0][3] * b->M[3][0];
|
||||
out.M[1][0] = a->M[1][0] * b->M[0][0] + a->M[1][1] * b->M[1][0] + a->M[1][2] * b->M[2][0] +
|
||||
a->M[1][3] * b->M[3][0];
|
||||
out.M[2][0] = a->M[2][0] * b->M[0][0] + a->M[2][1] * b->M[1][0] + a->M[2][2] * b->M[2][0] +
|
||||
a->M[2][3] * b->M[3][0];
|
||||
out.M[3][0] = a->M[3][0] * b->M[0][0] + a->M[3][1] * b->M[1][0] + a->M[3][2] * b->M[2][0] +
|
||||
a->M[3][3] * b->M[3][0];
|
||||
|
||||
out.M[0][1] = a->M[0][0] * b->M[0][1] + a->M[0][1] * b->M[1][1] + a->M[0][2] * b->M[2][1] +
|
||||
a->M[0][3] * b->M[3][1];
|
||||
out.M[1][1] = a->M[1][0] * b->M[0][1] + a->M[1][1] * b->M[1][1] + a->M[1][2] * b->M[2][1] +
|
||||
a->M[1][3] * b->M[3][1];
|
||||
out.M[2][1] = a->M[2][0] * b->M[0][1] + a->M[2][1] * b->M[1][1] + a->M[2][2] * b->M[2][1] +
|
||||
a->M[2][3] * b->M[3][1];
|
||||
out.M[3][1] = a->M[3][0] * b->M[0][1] + a->M[3][1] * b->M[1][1] + a->M[3][2] * b->M[2][1] +
|
||||
a->M[3][3] * b->M[3][1];
|
||||
|
||||
out.M[0][2] = a->M[0][0] * b->M[0][2] + a->M[0][1] * b->M[1][2] + a->M[0][2] * b->M[2][2] +
|
||||
a->M[0][3] * b->M[3][2];
|
||||
out.M[1][2] = a->M[1][0] * b->M[0][2] + a->M[1][1] * b->M[1][2] + a->M[1][2] * b->M[2][2] +
|
||||
a->M[1][3] * b->M[3][2];
|
||||
out.M[2][2] = a->M[2][0] * b->M[0][2] + a->M[2][1] * b->M[1][2] + a->M[2][2] * b->M[2][2] +
|
||||
a->M[2][3] * b->M[3][2];
|
||||
out.M[3][2] = a->M[3][0] * b->M[0][2] + a->M[3][1] * b->M[1][2] + a->M[3][2] * b->M[2][2] +
|
||||
a->M[3][3] * b->M[3][2];
|
||||
|
||||
out.M[0][3] = a->M[0][0] * b->M[0][3] + a->M[0][1] * b->M[1][3] + a->M[0][2] * b->M[2][3] +
|
||||
a->M[0][3] * b->M[3][3];
|
||||
out.M[1][3] = a->M[1][0] * b->M[0][3] + a->M[1][1] * b->M[1][3] + a->M[1][2] * b->M[2][3] +
|
||||
a->M[1][3] * b->M[3][3];
|
||||
out.M[2][3] = a->M[2][0] * b->M[0][3] + a->M[2][1] * b->M[1][3] + a->M[2][2] * b->M[2][3] +
|
||||
a->M[2][3] * b->M[3][3];
|
||||
out.M[3][3] = a->M[3][0] * b->M[0][3] + a->M[3][1] * b->M[1][3] + a->M[3][2] * b->M[2][3] +
|
||||
a->M[3][3] * b->M[3][3];
|
||||
return out;
|
||||
}
|
||||
|
||||
XrVector3f ovrMatrix4f_ToEulerAngles(const ovrMatrix4f* m) {
|
||||
XrVector4f v1 = {0, 0, -1, 0};
|
||||
XrVector4f v2 = {1, 0, 0, 0};
|
||||
XrVector4f v3 = {0, 1, 0, 0};
|
||||
|
||||
XrVector4f forwardInVRSpace = XrVector4f_MultiplyMatrix4f(m, &v1);
|
||||
XrVector4f rightInVRSpace = XrVector4f_MultiplyMatrix4f(m, &v2);
|
||||
XrVector4f upInVRSpace = XrVector4f_MultiplyMatrix4f(m, &v3);
|
||||
|
||||
XrVector3f forward = {-forwardInVRSpace.z, -forwardInVRSpace.x, forwardInVRSpace.y};
|
||||
XrVector3f right = {-rightInVRSpace.z, -rightInVRSpace.x, rightInVRSpace.y};
|
||||
XrVector3f up = {-upInVRSpace.z, -upInVRSpace.x, upInVRSpace.y};
|
||||
|
||||
XrVector3f forwardNormal = XrVector3f_Normalized(forward);
|
||||
XrVector3f rightNormal = XrVector3f_Normalized(right);
|
||||
XrVector3f upNormal = XrVector3f_Normalized(up);
|
||||
|
||||
return XrVector3f_GetAnglesFromVectors(forwardNormal, rightNormal, upNormal);
|
||||
}
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
XrPosef
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
|
||||
XrPosef XrPosef_Identity() {
|
||||
XrPosef r;
|
||||
r.orientation.x = 0;
|
||||
r.orientation.y = 0;
|
||||
r.orientation.z = 0;
|
||||
r.orientation.w = 1;
|
||||
r.position.x = 0;
|
||||
r.position.y = 0;
|
||||
r.position.z = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
XrPosef XrPosef_Inverse(const XrPosef a) {
|
||||
XrPosef b;
|
||||
b.orientation = XrQuaternionf_Inverse(a.orientation);
|
||||
b.position = XrQuaternionf_Rotate(b.orientation, XrVector3f_ScalarMultiply(a.position, -1.0f));
|
||||
return b;
|
||||
}
|
||||
|
||||
XrPosef XrPosef_Multiply(const XrPosef a, const XrPosef b) {
|
||||
XrPosef c;
|
||||
c.orientation = XrQuaternionf_Multiply(a.orientation, b.orientation);
|
||||
c.position = XrPosef_Transform(a, b.position);
|
||||
return c;
|
||||
}
|
||||
|
||||
XrVector3f XrPosef_Transform(const XrPosef a, const XrVector3f v) {
|
||||
XrVector3f r0 = XrQuaternionf_Rotate(a.orientation, v);
|
||||
return XrVector3f_Add(r0, a.position);
|
||||
}
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
XrQuaternionf
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
|
||||
XrQuaternionf XrQuaternionf_CreateFromVectorAngle(const XrVector3f axis, const float angle) {
|
||||
XrQuaternionf r;
|
||||
if (XrVector3f_LengthSquared(axis) == 0.0f) {
|
||||
r.x = 0;
|
||||
r.y = 0;
|
||||
r.z = 0;
|
||||
r.w = 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
XrVector3f unitAxis = XrVector3f_Normalized(axis);
|
||||
float sinHalfAngle = sinf(angle * 0.5f);
|
||||
|
||||
r.w = cosf(angle * 0.5f);
|
||||
r.x = unitAxis.x * sinHalfAngle;
|
||||
r.y = unitAxis.y * sinHalfAngle;
|
||||
r.z = unitAxis.z * sinHalfAngle;
|
||||
return r;
|
||||
}
|
||||
|
||||
XrQuaternionf XrQuaternionf_Inverse(const XrQuaternionf q) {
|
||||
XrQuaternionf r;
|
||||
r.x = -q.x;
|
||||
r.y = -q.y;
|
||||
r.z = -q.z;
|
||||
r.w = q.w;
|
||||
return r;
|
||||
}
|
||||
|
||||
XrQuaternionf XrQuaternionf_Multiply(const XrQuaternionf a, const XrQuaternionf b) {
|
||||
XrQuaternionf c;
|
||||
c.x = a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y;
|
||||
c.y = a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x;
|
||||
c.z = a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w;
|
||||
c.w = a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z;
|
||||
return c;
|
||||
}
|
||||
|
||||
XrVector3f XrQuaternionf_Rotate(const XrQuaternionf a, const XrVector3f v) {
|
||||
XrVector3f r;
|
||||
XrQuaternionf q = {v.x, v.y, v.z, 0.0f};
|
||||
XrQuaternionf aq = XrQuaternionf_Multiply(a, q);
|
||||
XrQuaternionf aInv = XrQuaternionf_Inverse(a);
|
||||
XrQuaternionf aqaInv = XrQuaternionf_Multiply(aq, aInv);
|
||||
r.x = aqaInv.x;
|
||||
r.y = aqaInv.y;
|
||||
r.z = aqaInv.z;
|
||||
return r;
|
||||
}
|
||||
|
||||
XrVector3f XrQuaternionf_ToEulerAngles(const XrQuaternionf q) {
|
||||
ovrMatrix4f m = ovrMatrix4f_CreateFromQuaternion( &q );
|
||||
return ovrMatrix4f_ToEulerAngles(&m);
|
||||
}
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
XrVector3f, XrVector4f
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
|
||||
float XrVector3f_Length(const XrVector3f v) {
|
||||
return sqrtf(XrVector3f_LengthSquared(v));
|
||||
}
|
||||
|
||||
float XrVector3f_LengthSquared(const XrVector3f v) {
|
||||
return v.x * v.x + v.y * v.y + v.z * v.z;;
|
||||
}
|
||||
|
||||
XrVector3f XrVector3f_Add(const XrVector3f u, const XrVector3f v) {
|
||||
XrVector3f w;
|
||||
w.x = u.x + v.x;
|
||||
w.y = u.y + v.y;
|
||||
w.z = u.z + v.z;
|
||||
return w;
|
||||
}
|
||||
|
||||
XrVector3f XrVector3f_GetAnglesFromVectors(const XrVector3f forward, const XrVector3f right, const XrVector3f up) {
|
||||
float sr, sp, sy, cr, cp, cy;
|
||||
|
||||
sp = -forward.z;
|
||||
|
||||
float cp_x_cy = forward.x;
|
||||
float cp_x_sy = forward.y;
|
||||
float cp_x_sr = -right.z;
|
||||
float cp_x_cr = up.z;
|
||||
|
||||
float yaw = atan2(cp_x_sy, cp_x_cy);
|
||||
float roll = atan2(cp_x_sr, cp_x_cr);
|
||||
|
||||
cy = cos(yaw);
|
||||
sy = sin(yaw);
|
||||
cr = cos(roll);
|
||||
sr = sin(roll);
|
||||
|
||||
if (fabs(cy) > EPSILON) {
|
||||
cp = cp_x_cy / cy;
|
||||
} else if (fabs(sy) > EPSILON) {
|
||||
cp = cp_x_sy / sy;
|
||||
} else if (fabs(sr) > EPSILON) {
|
||||
cp = cp_x_sr / sr;
|
||||
} else if (fabs(cr) > EPSILON) {
|
||||
cp = cp_x_cr / cr;
|
||||
} else {
|
||||
cp = cos(asin(sp));
|
||||
}
|
||||
|
||||
float pitch = atan2(sp, cp);
|
||||
|
||||
XrVector3f angles;
|
||||
angles.x = ToDegrees(pitch);
|
||||
angles.y = ToDegrees(yaw);
|
||||
angles.z = ToDegrees(roll);
|
||||
return angles;
|
||||
}
|
||||
|
||||
XrVector3f XrVector3f_Normalized(const XrVector3f v) {
|
||||
float rcpLen = 1.0f / XrVector3f_Length(v);
|
||||
return XrVector3f_ScalarMultiply(v, rcpLen);
|
||||
}
|
||||
|
||||
XrVector3f XrVector3f_ScalarMultiply(const XrVector3f v, float scale) {
|
||||
XrVector3f u;
|
||||
u.x = v.x * scale;
|
||||
u.y = v.y * scale;
|
||||
u.z = v.z * scale;
|
||||
return u;
|
||||
}
|
||||
|
||||
XrVector4f XrVector4f_MultiplyMatrix4f(const ovrMatrix4f* a, const XrVector4f* v) {
|
||||
XrVector4f out;
|
||||
out.x = a->M[0][0] * v->x + a->M[0][1] * v->y + a->M[0][2] * v->z + a->M[0][3] * v->w;
|
||||
out.y = a->M[1][0] * v->x + a->M[1][1] * v->y + a->M[1][2] * v->z + a->M[1][3] * v->w;
|
||||
out.z = a->M[2][0] * v->x + a->M[2][1] * v->y + a->M[2][2] * v->z + a->M[2][3] * v->w;
|
||||
out.w = a->M[3][0] * v->x + a->M[3][1] * v->y + a->M[3][2] * v->z + a->M[3][3] * v->w;
|
||||
return out;
|
||||
}
|
46
Common/VR/VRMath.h
Normal file
46
Common/VR/VRMath.h
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <math.h>
|
||||
#include <openxr.h>
|
||||
|
||||
#ifndef EPSILON
|
||||
#define EPSILON 0.001f
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
float M[4][4];
|
||||
} ovrMatrix4f;
|
||||
|
||||
float ToDegrees(float rad);
|
||||
float ToRadians(float deg);
|
||||
|
||||
// ovrMatrix4f
|
||||
float ovrMatrix4f_Minor(const ovrMatrix4f* m, int r0, int r1, int r2, int c0, int c1, int c2);
|
||||
ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q);
|
||||
ovrMatrix4f ovrMatrix4f_CreateProjectionFov(const float angleLeft, const float angleRight, const float angleUp, const float angleDown, const float nearZ, const float farZ);
|
||||
ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ);
|
||||
ovrMatrix4f ovrMatrix4f_Inverse(const ovrMatrix4f* m);
|
||||
ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b);
|
||||
XrVector3f ovrMatrix4f_ToEulerAngles(const ovrMatrix4f* m);
|
||||
|
||||
// XrPosef
|
||||
XrPosef XrPosef_Identity();
|
||||
XrPosef XrPosef_Inverse(const XrPosef a);
|
||||
XrPosef XrPosef_Multiply(const XrPosef a, const XrPosef b);
|
||||
XrVector3f XrPosef_Transform(const XrPosef a, const XrVector3f v);
|
||||
|
||||
// XrQuaternionf
|
||||
XrQuaternionf XrQuaternionf_CreateFromVectorAngle(const XrVector3f axis, const float angle);
|
||||
XrQuaternionf XrQuaternionf_Inverse(const XrQuaternionf q);
|
||||
XrQuaternionf XrQuaternionf_Multiply(const XrQuaternionf a, const XrQuaternionf b);
|
||||
XrVector3f XrQuaternionf_Rotate(const XrQuaternionf a, const XrVector3f v);
|
||||
XrVector3f XrQuaternionf_ToEulerAngles(const XrQuaternionf q);
|
||||
|
||||
// XrVector3f, XrVector4f
|
||||
float XrVector3f_Length(const XrVector3f v);
|
||||
float XrVector3f_LengthSquared(const XrVector3f v);
|
||||
XrVector3f XrVector3f_Add(const XrVector3f u, const XrVector3f v);
|
||||
XrVector3f XrVector3f_GetAnglesFromVectors(const XrVector3f forward, const XrVector3f right, const XrVector3f up);
|
||||
XrVector3f XrVector3f_Normalized(const XrVector3f v);
|
||||
XrVector3f XrVector3f_ScalarMultiply(const XrVector3f v, float scale);
|
||||
XrVector4f XrVector4f_MultiplyMatrix4f(const ovrMatrix4f* a, const XrVector4f* v);
|
@ -9,26 +9,23 @@
|
||||
#include <GLES3/gl3.h>
|
||||
#include <GLES3/gl3ext.h>
|
||||
|
||||
XrFovf fov;
|
||||
XrView* projections;
|
||||
XrPosef invViewTransform[2];
|
||||
XrFrameState frameState = {};
|
||||
GLboolean initialized = GL_FALSE;
|
||||
GLboolean stageSupported = GL_FALSE;
|
||||
VRMode vrMode = VR_MODE_FLAT_SCREEN;
|
||||
int vrConfig[VR_CONFIG_MAX] = {};
|
||||
ovrMatrix4f vrMatrix[VR_MATRIX_COUNT];
|
||||
|
||||
float menuYaw = 0;
|
||||
float recenterYaw = 0;
|
||||
vec3_t hmdorientation;
|
||||
vec3_t hmdposition;
|
||||
|
||||
extern float radians(float deg);
|
||||
XrVector3f hmdorientation;
|
||||
XrVector3f hmdposition;
|
||||
|
||||
void VR_UpdateStageBounds(ovrApp* pappState) {
|
||||
XrExtent2Df stageBounds = {};
|
||||
|
||||
XrResult result;
|
||||
OXR(result = xrGetReferenceSpaceBoundsRect(
|
||||
pappState->Session, XR_REFERENCE_SPACE_TYPE_STAGE, &stageBounds));
|
||||
OXR(result = xrGetReferenceSpaceBoundsRect(pappState->Session, XR_REFERENCE_SPACE_TYPE_STAGE, &stageBounds));
|
||||
if (result != XR_SUCCESS) {
|
||||
ALOGV("Stage bounds query failed: using small defaults");
|
||||
stageBounds.width = 1.0f;
|
||||
@ -135,10 +132,10 @@ void VR_Recenter(engine_t* engine) {
|
||||
XrSpaceLocation loc = {};
|
||||
loc.type = XR_TYPE_SPACE_LOCATION;
|
||||
OXR(xrLocateSpace(engine->appState.HeadSpace, engine->appState.CurrentSpace, engine->predictedDisplayTime, &loc));
|
||||
vec3_t rotation = {0, 0, 0};
|
||||
QuatToYawPitchRoll(loc.pose.orientation, rotation, hmdorientation);
|
||||
hmdorientation = XrQuaternionf_ToEulerAngles(loc.pose.orientation);
|
||||
|
||||
recenterYaw += radians(hmdorientation[YAW]);
|
||||
vrConfig[VR_CONFIG_RECENTER_YAW] += (int)hmdorientation.y;
|
||||
float recenterYaw = ToRadians((float)vrConfig[VR_CONFIG_RECENTER_YAW]);
|
||||
spaceCreateInfo.poseInReferenceSpace.orientation.x = 0;
|
||||
spaceCreateInfo.poseInReferenceSpace.orientation.y = sin(recenterYaw / 2);
|
||||
spaceCreateInfo.poseInReferenceSpace.orientation.z = 0;
|
||||
@ -156,7 +153,9 @@ void VR_Recenter(engine_t* engine) {
|
||||
// Create a default stage space to use if SPACE_TYPE_STAGE is not
|
||||
// supported, or calls to xrGetReferenceSpaceBoundsRect fail.
|
||||
spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
|
||||
#ifdef OPENXR_FLOOR_STAGE
|
||||
spaceCreateInfo.poseInReferenceSpace.position.y = -1.6750f;
|
||||
#endif
|
||||
OXR(xrCreateReferenceSpace(engine->appState.Session, &spaceCreateInfo, &engine->appState.FakeStageSpace));
|
||||
ALOGV("Created fake stage space from local space with offset");
|
||||
engine->appState.CurrentSpace = engine->appState.FakeStageSpace;
|
||||
@ -166,31 +165,34 @@ void VR_Recenter(engine_t* engine) {
|
||||
spaceCreateInfo.poseInReferenceSpace.position.y = 0.0;
|
||||
OXR(xrCreateReferenceSpace(engine->appState.Session, &spaceCreateInfo, &engine->appState.StageSpace));
|
||||
ALOGV("Created stage space");
|
||||
#ifdef OPENXR_FLOOR_STAGE
|
||||
engine->appState.CurrentSpace = engine->appState.StageSpace;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Update menu orientation
|
||||
menuYaw = 0;
|
||||
vrConfig[VR_CONFIG_MENU_PITCH] = (int)hmdorientation.x;
|
||||
vrConfig[VR_CONFIG_MENU_YAW] = 0;
|
||||
}
|
||||
|
||||
void VR_InitRenderer( engine_t* engine ) {
|
||||
void VR_InitRenderer( engine_t* engine, bool multiview ) {
|
||||
if (initialized) {
|
||||
VR_DestroyRenderer(engine);
|
||||
}
|
||||
|
||||
int eyeW, eyeH;
|
||||
VR_GetResolution(engine, &eyeW, &eyeH);
|
||||
vrConfig[VR_CONFIG_VIEWPORT_WIDTH] = eyeW;
|
||||
vrConfig[VR_CONFIG_VIEWPORT_HEIGHT] = eyeH;
|
||||
|
||||
// Get the viewport configuration info for the chosen viewport configuration type.
|
||||
engine->appState.ViewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES;
|
||||
|
||||
OXR(xrGetViewConfigurationProperties(
|
||||
engine->appState.Instance, engine->appState.SystemId, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, &engine->appState.ViewportConfig));
|
||||
OXR(xrGetViewConfigurationProperties(engine->appState.Instance, engine->appState.SystemId, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, &engine->appState.ViewportConfig));
|
||||
|
||||
uint32_t numOutputSpaces = 0;
|
||||
OXR(xrEnumerateReferenceSpaces(engine->appState.Session, 0, &numOutputSpaces, NULL));
|
||||
|
||||
XrReferenceSpaceType* referenceSpaces =
|
||||
(XrReferenceSpaceType*)malloc(numOutputSpaces * sizeof(XrReferenceSpaceType));
|
||||
|
||||
OXR(xrEnumerateReferenceSpaces(
|
||||
engine->appState.Session, numOutputSpaces, &numOutputSpaces, referenceSpaces));
|
||||
XrReferenceSpaceType* referenceSpaces = (XrReferenceSpaceType*)malloc(numOutputSpaces * sizeof(XrReferenceSpaceType));
|
||||
OXR(xrEnumerateReferenceSpaces(engine->appState.Session, numOutputSpaces, &numOutputSpaces, referenceSpaces));
|
||||
|
||||
for (uint32_t i = 0; i < numOutputSpaces; i++) {
|
||||
if (referenceSpaces[i] == XR_REFERENCE_SPACE_TYPE_STAGE) {
|
||||
@ -211,7 +213,8 @@ void VR_InitRenderer( engine_t* engine ) {
|
||||
engine->appState.Session,
|
||||
&engine->appState.Renderer,
|
||||
engine->appState.ViewConfigurationView[0].recommendedImageRectWidth,
|
||||
engine->appState.ViewConfigurationView[0].recommendedImageRectHeight);
|
||||
engine->appState.ViewConfigurationView[0].recommendedImageRectHeight,
|
||||
multiview);
|
||||
initialized = GL_TRUE;
|
||||
}
|
||||
|
||||
@ -234,13 +237,13 @@ void VR_ClearFrameBuffer( int width, int height) {
|
||||
glDisable( GL_SCISSOR_TEST );
|
||||
}
|
||||
|
||||
void VR_BeginFrame( engine_t* engine ) {
|
||||
bool VR_InitFrame( engine_t* engine ) {
|
||||
GLboolean stageBoundsDirty = GL_TRUE;
|
||||
if (ovrApp_HandleXrEvents(&engine->appState)) {
|
||||
VR_Recenter(engine);
|
||||
}
|
||||
if (engine->appState.SessionActive == GL_FALSE) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stageBoundsDirty) {
|
||||
@ -260,7 +263,7 @@ void VR_BeginFrame( engine_t* engine ) {
|
||||
OXR(xrWaitFrame(engine->appState.Session, &waitFrameInfo, &frameState));
|
||||
engine->predictedDisplayTime = frameState.predictedDisplayTime;
|
||||
if (!frameState.shouldRender) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the HMD pose, predicted for the middle of the time period during which
|
||||
@ -292,62 +295,138 @@ void VR_BeginFrame( engine_t* engine ) {
|
||||
projections));
|
||||
//
|
||||
|
||||
fov = {};
|
||||
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
|
||||
fov.angleLeft += projections[eye].fov.angleLeft / 2.0f;
|
||||
fov.angleRight += projections[eye].fov.angleRight / 2.0f;
|
||||
fov.angleUp += projections[eye].fov.angleUp / 2.0f;
|
||||
fov.angleDown += projections[eye].fov.angleDown / 2.0f;
|
||||
invViewTransform[eye] = projections[eye].pose;
|
||||
}
|
||||
|
||||
// Update HMD and controllers
|
||||
vec3_t rotation = {0, 0, 0};
|
||||
QuatToYawPitchRoll(invViewTransform[0].orientation, rotation, hmdorientation);
|
||||
hmdposition[0] = invViewTransform[0].position.x;
|
||||
hmdposition[1] = invViewTransform[0].position.y;
|
||||
hmdposition[2] = invViewTransform[0].position.z;
|
||||
hmdorientation = XrQuaternionf_ToEulerAngles(invViewTransform[0].orientation);
|
||||
hmdposition = invViewTransform[0].position;
|
||||
IN_VRInputFrame(engine);
|
||||
|
||||
engine->appState.LayerCount = 0;
|
||||
memset(engine->appState.Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount);
|
||||
|
||||
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
|
||||
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[eye];
|
||||
int swapchainIndex = frameBuffer->TextureSwapChainIndex;
|
||||
int glFramebuffer = frameBuffer->FrameBuffers[swapchainIndex];
|
||||
// Update matrices
|
||||
for (int matrix = 0; matrix < VR_MATRIX_COUNT; matrix++) {
|
||||
if ((matrix == VR_PROJECTION_MATRIX_LEFT_EYE) || (matrix == VR_PROJECTION_MATRIX_RIGHT_EYE)) {
|
||||
float near = (float)vrConfig[VR_CONFIG_FOV_SCALE] / 200.0f;
|
||||
vrMatrix[matrix] = ovrMatrix4f_CreateProjectionFov(fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown, near, 0.0f );
|
||||
} else if ((matrix == VR_VIEW_MATRIX_LEFT_EYE) || (matrix == VR_VIEW_MATRIX_RIGHT_EYE)) {
|
||||
XrPosef invView = invViewTransform[0];
|
||||
|
||||
ovrFramebuffer_Acquire(frameBuffer);
|
||||
ovrFramebuffer_SetCurrent(frameBuffer);
|
||||
VR_ClearFrameBuffer(frameBuffer->ColorSwapChain.Width, frameBuffer->ColorSwapChain.Height);
|
||||
// get axis mirroring configuration
|
||||
float mx = vrConfig[VR_CONFIG_MIRROR_PITCH] ? -1 : 1;
|
||||
float my = vrConfig[VR_CONFIG_MIRROR_YAW] ? -1 : 1;
|
||||
float mz = vrConfig[VR_CONFIG_MIRROR_ROLL] ? -1 : 1;
|
||||
|
||||
// ensure there is maximally one axis to mirror rotation
|
||||
if (mx + my + mz < 0) {
|
||||
mx *= -1.0f;
|
||||
my *= -1.0f;
|
||||
mz *= -1.0f;
|
||||
} else {
|
||||
invView = XrPosef_Inverse(invView);
|
||||
}
|
||||
|
||||
// create updated quaternion
|
||||
if (mx + my + mz < 3 - EPSILON) {
|
||||
XrVector3f rotation = XrQuaternionf_ToEulerAngles(invView.orientation);
|
||||
XrQuaternionf pitch = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, mx * ToRadians(rotation.x));
|
||||
XrQuaternionf yaw = XrQuaternionf_CreateFromVectorAngle({0, 1, 0}, my * ToRadians(rotation.y));
|
||||
XrQuaternionf roll = XrQuaternionf_CreateFromVectorAngle({0, 0, 1}, mz * ToRadians(rotation.z));
|
||||
invView.orientation = XrQuaternionf_Multiply(roll, XrQuaternionf_Multiply(pitch, yaw));
|
||||
}
|
||||
|
||||
vrMatrix[matrix] = ovrMatrix4f_CreateFromQuaternion(&invView.orientation);
|
||||
float scale = (float)VR_GetConfig(VR_CONFIG_6DOF_SCALE) * 0.000001f;
|
||||
if (vrConfig[VR_CONFIG_6DOF_ENABLED]) {
|
||||
vrMatrix[matrix].M[0][3] -= hmdposition.x * (vrConfig[VR_CONFIG_MIRROR_AXIS_X] ? -1.0f : 1.0f) * scale;
|
||||
vrMatrix[matrix].M[1][3] -= hmdposition.y * (vrConfig[VR_CONFIG_MIRROR_AXIS_Y] ? -1.0f : 1.0f) * scale;
|
||||
vrMatrix[matrix].M[2][3] -= hmdposition.z * (vrConfig[VR_CONFIG_MIRROR_AXIS_Z] ? -1.0f : 1.0f) * scale;
|
||||
}
|
||||
if (vrConfig[VR_CONFIG_6DOF_PRECISE] && (matrix == VR_VIEW_MATRIX_RIGHT_EYE)) {
|
||||
float dx = fabs(invViewTransform[1].position.x - invViewTransform[0].position.x);
|
||||
float dy = fabs(invViewTransform[1].position.y - invViewTransform[0].position.y);
|
||||
float dz = fabs(invViewTransform[1].position.z - invViewTransform[0].position.z);
|
||||
float ipd = sqrt(dx * dx + dy * dy + dz * dz);
|
||||
XrVector3f separation = {ipd * scale, 0.0f, 0.0f};
|
||||
separation = XrQuaternionf_Rotate(invView.orientation, separation);
|
||||
separation = XrVector3f_ScalarMultiply(separation, vrConfig[VR_CONFIG_MIRROR_AXIS_Z] ? -1.0f : 1.0f);
|
||||
vrMatrix[matrix].M[0][3] -= separation.x;
|
||||
vrMatrix[matrix].M[1][3] -= separation.y;
|
||||
vrMatrix[matrix].M[2][3] -= separation.z;
|
||||
}
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VR_BeginFrame( engine_t* engine, int fboIndex ) {
|
||||
vrConfig[VR_CONFIG_CURRENT_FBO] = fboIndex;
|
||||
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[fboIndex];
|
||||
ovrFramebuffer_Acquire(frameBuffer);
|
||||
ovrFramebuffer_SetCurrent(frameBuffer);
|
||||
VR_ClearFrameBuffer(frameBuffer->ColorSwapChain.Width, frameBuffer->ColorSwapChain.Height);
|
||||
}
|
||||
|
||||
void VR_EndFrame( engine_t* engine ) {
|
||||
|
||||
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
|
||||
int fboIndex = vrConfig[VR_CONFIG_CURRENT_FBO];
|
||||
|
||||
// Clear the alpha channel, other way OpenXR would not transfer the framebuffer fully
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
// Clear the alpha channel, other way OpenXR would not transfer the framebuffer fully
|
||||
VR_BindFramebuffer(engine);
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
|
||||
// Show mouse cursor
|
||||
int size = vrConfig[VR_CONFIG_MOUSE_SIZE];
|
||||
if ((vrConfig[VR_CONFIG_MODE] == VR_MODE_FLAT_SCREEN) && (size > 0)) {
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissor(vrConfig[VR_CONFIG_MOUSE_X], vrConfig[VR_CONFIG_MOUSE_Y], size, size);
|
||||
glViewport(vrConfig[VR_CONFIG_MOUSE_X], vrConfig[VR_CONFIG_MOUSE_Y], size, size);
|
||||
glClearColor(1, 1, 1, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
|
||||
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[eye];
|
||||
//TODO:ovrFramebuffer_Resolve(frameBuffer);
|
||||
ovrFramebuffer_Release(frameBuffer);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
ovrFramebuffer_SetNone();
|
||||
|
||||
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[fboIndex];
|
||||
//ovrFramebuffer_Resolve(frameBuffer);
|
||||
ovrFramebuffer_Release(frameBuffer);
|
||||
ovrFramebuffer_SetNone();
|
||||
}
|
||||
|
||||
void VR_FinishFrame( engine_t* engine ) {
|
||||
|
||||
int vrMode = vrConfig[VR_CONFIG_MODE];
|
||||
XrCompositionLayerProjectionView projection_layer_elements[2] = {};
|
||||
if ((vrMode == VR_MODE_MONO_6DOF) || (vrMode == VR_MODE_STEREO_6DOF)) {
|
||||
menuYaw = hmdorientation[YAW];
|
||||
vrConfig[VR_CONFIG_MENU_YAW] = (int)hmdorientation.y;
|
||||
|
||||
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
|
||||
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[eye];
|
||||
if (vrMode == VR_MODE_MONO_6DOF) {
|
||||
frameBuffer = &engine->appState.Renderer.FrameBuffer[0];
|
||||
int imageLayer = engine->appState.Renderer.Multiview ? eye : 0;
|
||||
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[0];
|
||||
XrPosef pose = invViewTransform[0];
|
||||
if (vrMode != VR_MODE_MONO_6DOF) {
|
||||
if (!engine->appState.Renderer.Multiview) {
|
||||
frameBuffer = &engine->appState.Renderer.FrameBuffer[eye];
|
||||
}
|
||||
pose = invViewTransform[eye];
|
||||
}
|
||||
|
||||
memset(&projection_layer_elements[eye], 0, sizeof(XrCompositionLayerProjectionView));
|
||||
projection_layer_elements[eye].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
|
||||
projection_layer_elements[eye].pose = invViewTransform[eye];
|
||||
projection_layer_elements[eye].fov = projections[eye].fov;
|
||||
projection_layer_elements[eye].pose = pose;
|
||||
projection_layer_elements[eye].fov = fov;
|
||||
|
||||
memset(&projection_layer_elements[eye].subImage, 0, sizeof(XrSwapchainSubImage));
|
||||
projection_layer_elements[eye].subImage.swapchain = frameBuffer->ColorSwapChain.Handle;
|
||||
@ -355,7 +434,7 @@ void VR_EndFrame( engine_t* engine ) {
|
||||
projection_layer_elements[eye].subImage.imageRect.offset.y = 0;
|
||||
projection_layer_elements[eye].subImage.imageRect.extent.width = frameBuffer->ColorSwapChain.Width;
|
||||
projection_layer_elements[eye].subImage.imageRect.extent.height = frameBuffer->ColorSwapChain.Height;
|
||||
projection_layer_elements[eye].subImage.imageArrayIndex = 0;
|
||||
projection_layer_elements[eye].subImage.imageArrayIndex = imageLayer;
|
||||
}
|
||||
|
||||
XrCompositionLayerProjection projection_layer = {};
|
||||
@ -384,17 +463,21 @@ void VR_EndFrame( engine_t* engine ) {
|
||||
cylinder_layer.subImage.imageRect.extent.width = width;
|
||||
cylinder_layer.subImage.imageRect.extent.height = height;
|
||||
cylinder_layer.subImage.imageArrayIndex = 0;
|
||||
const XrVector3f axis = {0.0f, 1.0f, 0.0f};
|
||||
float distance = (float)vrConfig[VR_CONFIG_CANVAS_DISTANCE];
|
||||
float menuPitch = ToRadians((float)vrConfig[VR_CONFIG_MENU_PITCH]);
|
||||
float menuYaw = ToRadians((float)vrConfig[VR_CONFIG_MENU_YAW]);
|
||||
XrVector3f pos = {
|
||||
invViewTransform[0].position.x - sin(radians(menuYaw)) * 6.0f,
|
||||
invViewTransform[0].position.x - sin(menuYaw) * distance,
|
||||
invViewTransform[0].position.y,
|
||||
invViewTransform[0].position.z - cos(radians(menuYaw)) * 6.0f
|
||||
invViewTransform[0].position.z - cos(menuYaw) * distance
|
||||
};
|
||||
cylinder_layer.pose.orientation = XrQuaternionf_CreateFromVectorAngle(axis, radians(menuYaw));
|
||||
XrQuaternionf pitch = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, -menuPitch);
|
||||
XrQuaternionf yaw = XrQuaternionf_CreateFromVectorAngle({0, 1, 0}, menuYaw);
|
||||
cylinder_layer.pose.orientation = XrQuaternionf_Multiply(pitch, yaw);
|
||||
cylinder_layer.pose.position = pos;
|
||||
cylinder_layer.radius = 12.0f;
|
||||
cylinder_layer.centralAngle = MATH_PI * 0.5f;
|
||||
cylinder_layer.aspectRatio = height / (float)width;
|
||||
cylinder_layer.centralAngle = M_PI * 0.5f;
|
||||
cylinder_layer.aspectRatio = 1;
|
||||
|
||||
engine->appState.Layers[engine->appState.LayerCount++].Cylinder = cylinder_layer;
|
||||
} else {
|
||||
@ -415,38 +498,31 @@ void VR_EndFrame( engine_t* engine ) {
|
||||
endFrameInfo.layers = layers;
|
||||
|
||||
OXR(xrEndFrame(engine->appState.Session, &endFrameInfo));
|
||||
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
|
||||
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[eye];
|
||||
int instances = engine->appState.Renderer.Multiview ? 1 : ovrMaxNumEyes;
|
||||
for (int i = 0; i < instances; i++) {
|
||||
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[instances];
|
||||
frameBuffer->TextureSwapChainIndex++;
|
||||
frameBuffer->TextureSwapChainIndex %= frameBuffer->TextureSwapChainLength;
|
||||
}
|
||||
}
|
||||
|
||||
void VR_BindFramebuffer( engine_t* engine, int eye ) {
|
||||
int VR_GetConfig( VRConfig config ) {
|
||||
return vrConfig[config];
|
||||
}
|
||||
|
||||
void VR_SetConfig( VRConfig config, int value) {
|
||||
vrConfig[config] = value;
|
||||
}
|
||||
|
||||
void VR_BindFramebuffer(engine_t *engine) {
|
||||
if (!initialized) return;
|
||||
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[eye];
|
||||
int swapchainIndex = frameBuffer->TextureSwapChainIndex;
|
||||
int glFramebuffer = frameBuffer->FrameBuffers[swapchainIndex];
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, glFramebuffer);
|
||||
int fboIndex = vrConfig[VR_CONFIG_CURRENT_FBO];
|
||||
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[fboIndex];
|
||||
unsigned int swapchainIndex = frameBuffer->TextureSwapChainIndex;
|
||||
unsigned int glFramebuffer = frameBuffer->FrameBuffers[swapchainIndex];
|
||||
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, glFramebuffer));
|
||||
}
|
||||
|
||||
ovrMatrix4f VR_GetMatrix( VRMatrix matrix ) {
|
||||
ovrMatrix4f output;
|
||||
if (matrix == VR_PROJECTION_MATRIX_HUD) {
|
||||
float hudScale = radians(15.0f);
|
||||
output = ovrMatrix4f_CreateProjectionFov(-hudScale, hudScale, hudScale, -hudScale, 1.0f, 0.0f );
|
||||
} else if ((matrix == VR_PROJECTION_MATRIX_LEFT_EYE) || (matrix == VR_PROJECTION_MATRIX_RIGHT_EYE)) {
|
||||
XrFovf fov = matrix == VR_PROJECTION_MATRIX_LEFT_EYE ? projections[0].fov : projections[1].fov;
|
||||
output = ovrMatrix4f_CreateProjectionFov(fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown, 1.0f, 0.0f );
|
||||
} else if ((matrix == VR_VIEW_MATRIX_LEFT_EYE) || (matrix == VR_VIEW_MATRIX_RIGHT_EYE)) {
|
||||
XrPosef invView = matrix == VR_VIEW_MATRIX_LEFT_EYE ? invViewTransform[0] : invViewTransform[1];
|
||||
XrPosef view = XrPosef_Inverse(invView);
|
||||
output = ovrMatrix4f_CreateFromQuaternion(&view.orientation);
|
||||
output.M[3][0] = view.position.x;
|
||||
output.M[3][1] = view.position.y;
|
||||
output.M[3][2] = view.position.z;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
return output;
|
||||
return vrMatrix[matrix];
|
||||
}
|
||||
|
@ -1,13 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "VRFramebuffer.h"
|
||||
#include "VRMath.h"
|
||||
|
||||
enum VRConfig {
|
||||
//switching between 2D and 3D
|
||||
VR_CONFIG_MODE, VR_CONFIG_3D_GEOMETRY_COUNT, VR_CONFIG_FORCE_2D,
|
||||
//camera setup
|
||||
VR_CONFIG_FOV_SCALE, VR_CONFIG_CANVAS_DISTANCE,
|
||||
//6DoF
|
||||
VR_CONFIG_6DOF_ENABLED, VR_CONFIG_6DOF_SCALE, VR_CONFIG_6DOF_PRECISE,
|
||||
VR_CONFIG_MIRROR_AXIS_X, VR_CONFIG_MIRROR_AXIS_Y, VR_CONFIG_MIRROR_AXIS_Z,
|
||||
VR_CONFIG_MIRROR_PITCH, VR_CONFIG_MIRROR_YAW, VR_CONFIG_MIRROR_ROLL,
|
||||
//2D canvas positioning
|
||||
VR_CONFIG_MENU_PITCH, VR_CONFIG_MENU_YAW, VR_CONFIG_RECENTER_YAW,
|
||||
//mouse cursor
|
||||
VR_CONFIG_MOUSE_SIZE, VR_CONFIG_MOUSE_X, VR_CONFIG_MOUSE_Y,
|
||||
//viewport setup
|
||||
VR_CONFIG_VIEWPORT_WIDTH, VR_CONFIG_VIEWPORT_HEIGHT, VR_CONFIG_VIEWPORT_VALID,
|
||||
//render status
|
||||
VR_CONFIG_CURRENT_FBO,
|
||||
|
||||
//end
|
||||
VR_CONFIG_MAX
|
||||
};
|
||||
|
||||
enum VRMatrix {
|
||||
VR_PROJECTION_MATRIX_HUD = 0,
|
||||
VR_PROJECTION_MATRIX_LEFT_EYE = 1,
|
||||
VR_PROJECTION_MATRIX_RIGHT_EYE = 2,
|
||||
VR_VIEW_MATRIX_LEFT_EYE = 3,
|
||||
VR_VIEW_MATRIX_RIGHT_EYE = 4
|
||||
VR_PROJECTION_MATRIX_LEFT_EYE,
|
||||
VR_PROJECTION_MATRIX_RIGHT_EYE,
|
||||
VR_VIEW_MATRIX_LEFT_EYE,
|
||||
VR_VIEW_MATRIX_RIGHT_EYE,
|
||||
VR_MATRIX_COUNT
|
||||
};
|
||||
|
||||
enum VRMode {
|
||||
@ -17,12 +40,16 @@ enum VRMode {
|
||||
};
|
||||
|
||||
void VR_GetResolution( engine_t* engine, int *pWidth, int *pHeight );
|
||||
void VR_InitRenderer( engine_t* engine );
|
||||
void VR_InitRenderer( engine_t* engine, bool multiview );
|
||||
void VR_DestroyRenderer( engine_t* engine );
|
||||
|
||||
void VR_BeginFrame( engine_t* engine );
|
||||
bool VR_InitFrame( engine_t* engine );
|
||||
void VR_BeginFrame( engine_t* engine, int fboIndex );
|
||||
void VR_EndFrame( engine_t* engine );
|
||||
void VR_SetMode( VRMode mode );
|
||||
void VR_FinishFrame( engine_t* engine );
|
||||
|
||||
void VR_BindFramebuffer( engine_t* engine, int eye );
|
||||
int VR_GetConfig( VRConfig config );
|
||||
void VR_SetConfig( VRConfig config, int value);
|
||||
|
||||
void VR_BindFramebuffer(engine_t *engine);
|
||||
ovrMatrix4f VR_GetMatrix( VRMatrix matrix );
|
||||
|
93
Common/VR/VRTweaks.cpp
Normal file
93
Common/VR/VRTweaks.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
#include "VRTweaks.h"
|
||||
#include <iostream>
|
||||
|
||||
bool VR_TweakIsMatrixBigScale(float* matrix) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
float value = matrix[i * 4 + i];
|
||||
if (fabs(value) < 10.0f) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VR_TweakIsMatrixIdentity(float* matrix) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
float value = matrix[i * 4 + j];
|
||||
|
||||
// Other number than zero on non-diagonale
|
||||
if ((i != j) && (fabs(value) > EPSILON)) return false;
|
||||
// Other number than one on diagonale
|
||||
if ((i == j) && (fabs(value - 1.0f) > EPSILON)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VR_TweakIsMatrixOneOrtho(float* matrix) {
|
||||
float value = matrix[15];
|
||||
return fabs(value - 1) < EPSILON;
|
||||
}
|
||||
|
||||
bool VR_TweakIsMatrixOneScale(float* matrix) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
float value = matrix[i * 4 + i];
|
||||
if (fabs(value - 1) > EPSILON) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VR_TweakIsMatrixOneTransform(float* matrix) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
float value = matrix[12 + j];
|
||||
if (fabs(fabs(value) - 1.0f) > EPSILON) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VR_TweakMirroring(float* projMatrix) {
|
||||
VR_SetConfig(VR_CONFIG_MIRROR_AXIS_X, projMatrix[0] < 0);
|
||||
VR_SetConfig(VR_CONFIG_MIRROR_AXIS_Y, projMatrix[5] < 0);
|
||||
VR_SetConfig(VR_CONFIG_MIRROR_AXIS_Z, projMatrix[10] > 0);
|
||||
if ((projMatrix[0] < 0) && (projMatrix[10] < 0)) { //e.g. Dante's inferno
|
||||
VR_SetConfig(VR_CONFIG_MIRROR_PITCH, true);
|
||||
VR_SetConfig(VR_CONFIG_MIRROR_YAW, true);
|
||||
VR_SetConfig(VR_CONFIG_MIRROR_ROLL, false);
|
||||
} else if (projMatrix[10] < 0) { //e.g. GTA - Liberty city
|
||||
VR_SetConfig(VR_CONFIG_MIRROR_PITCH, false);
|
||||
VR_SetConfig(VR_CONFIG_MIRROR_YAW, false);
|
||||
VR_SetConfig(VR_CONFIG_MIRROR_ROLL, false);
|
||||
} else if (projMatrix[5] < 0) { //e.g. PES 2014
|
||||
VR_SetConfig(VR_CONFIG_MIRROR_PITCH, true);
|
||||
VR_SetConfig(VR_CONFIG_MIRROR_YAW, true);
|
||||
VR_SetConfig(VR_CONFIG_MIRROR_ROLL, false);
|
||||
} else { //e.g. Lego Pirates
|
||||
VR_SetConfig(VR_CONFIG_MIRROR_PITCH, false);
|
||||
VR_SetConfig(VR_CONFIG_MIRROR_YAW, true);
|
||||
VR_SetConfig(VR_CONFIG_MIRROR_ROLL, true);
|
||||
}
|
||||
}
|
||||
|
||||
void VR_TweakProjection(float* src, float* dst, VRMatrix matrix) {
|
||||
ovrMatrix4f hmdProjection = VR_GetMatrix(matrix);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
if ((hmdProjection.M[i][j] > 0) != (src[i * 4 + j] > 0)) {
|
||||
hmdProjection.M[i][j] *= -1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
memcpy(dst, hmdProjection.M, 16 * sizeof(float));
|
||||
}
|
||||
|
||||
void VR_TweakView(float* view, VRMatrix matrix) {
|
||||
// Get view matrix from the game
|
||||
ovrMatrix4f gameView;
|
||||
memcpy(gameView.M, view, 16 * sizeof(float));
|
||||
|
||||
// Get view matrix from the headset
|
||||
ovrMatrix4f hmdView = VR_GetMatrix(matrix);
|
||||
|
||||
// Combine the matrices
|
||||
ovrMatrix4f renderView = ovrMatrix4f_Multiply(&hmdView, &gameView);
|
||||
memcpy(view, renderView.M, 16 * sizeof(float));
|
||||
}
|
12
Common/VR/VRTweaks.h
Normal file
12
Common/VR/VRTweaks.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "VRRenderer.h"
|
||||
|
||||
bool VR_TweakIsMatrixBigScale(float* matrix);
|
||||
bool VR_TweakIsMatrixIdentity(float* matrix);
|
||||
bool VR_TweakIsMatrixOneOrtho(float* matrix);
|
||||
bool VR_TweakIsMatrixOneScale(float* matrix);
|
||||
bool VR_TweakIsMatrixOneTransform(float* matrix);
|
||||
void VR_TweakMirroring(float* projMatrix);
|
||||
void VR_TweakProjection(float* src, float* dst, VRMatrix matrix);
|
||||
void VR_TweakView(float* view, VRMatrix matrix);
|
@ -42,6 +42,15 @@ void Compatibility::Load(const std::string &gameID) {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
IniFile compat;
|
||||
// This loads from assets.
|
||||
if (compat.LoadFromVFS("compatvr.ini")) {
|
||||
CheckSetting(compat, gameID, "Skyplane", &vrCompat_.Skyplane);
|
||||
CheckSetting(compat, gameID, "UnitsPerMeter", &vrCompat_.UnitsPerMeter);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
IniFile compat2;
|
||||
// This one is user-editable. Need to load it after the system one.
|
||||
@ -54,6 +63,7 @@ void Compatibility::Load(const std::string &gameID) {
|
||||
|
||||
void Compatibility::Clear() {
|
||||
memset(&flags_, 0, sizeof(flags_));
|
||||
memset(&vrCompat_, 0, sizeof(vrCompat_));
|
||||
}
|
||||
|
||||
void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
|
||||
@ -67,7 +77,6 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
|
||||
CheckSetting(iniFile, gameID, "RequireBufferedRendering", &flags_.RequireBufferedRendering);
|
||||
CheckSetting(iniFile, gameID, "RequireBlockTransfer", &flags_.RequireBlockTransfer);
|
||||
CheckSetting(iniFile, gameID, "RequireDefaultCPUClock", &flags_.RequireDefaultCPUClock);
|
||||
CheckSetting(iniFile, gameID, "DisableReadbacks", &flags_.DisableReadbacks);
|
||||
CheckSetting(iniFile, gameID, "DisableAccurateDepth", &flags_.DisableAccurateDepth);
|
||||
CheckSetting(iniFile, gameID, "MGS2AcidHack", &flags_.MGS2AcidHack);
|
||||
CheckSetting(iniFile, gameID, "SonicRivalsHack", &flags_.SonicRivalsHack);
|
||||
@ -86,10 +95,8 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
|
||||
CheckSetting(iniFile, gameID, "ReportSmallMemstick", &flags_.ReportSmallMemstick);
|
||||
CheckSetting(iniFile, gameID, "MemstickFixedFree", &flags_.MemstickFixedFree);
|
||||
CheckSetting(iniFile, gameID, "DateLimited", &flags_.DateLimited);
|
||||
CheckSetting(iniFile, gameID, "ReinterpretFramebuffers", &flags_.ReinterpretFramebuffers);
|
||||
CheckSetting(iniFile, gameID, "ShaderColorBitmask", &flags_.ShaderColorBitmask);
|
||||
CheckSetting(iniFile, gameID, "DisableFirstFrameReadback", &flags_.DisableFirstFrameReadback);
|
||||
CheckSetting(iniFile, gameID, "DisableRangeCulling", &flags_.DisableRangeCulling);
|
||||
CheckSetting(iniFile, gameID, "MpegAvcWarmUp", &flags_.MpegAvcWarmUp);
|
||||
CheckSetting(iniFile, gameID, "BlueToAlpha", &flags_.BlueToAlpha);
|
||||
CheckSetting(iniFile, gameID, "CenteredLines", &flags_.CenteredLines);
|
||||
@ -97,11 +104,27 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
|
||||
CheckSetting(iniFile, gameID, "ZZT3SelectHack", &flags_.ZZT3SelectHack);
|
||||
CheckSetting(iniFile, gameID, "AllowLargeFBTextureOffsets", &flags_.AllowLargeFBTextureOffsets);
|
||||
CheckSetting(iniFile, gameID, "AtracLoopHack", &flags_.AtracLoopHack);
|
||||
CheckSetting(iniFile, gameID, "DeswizzleDepth", &flags_.DeswizzleDepth);
|
||||
CheckSetting(iniFile, gameID, "SplitFramebufferMargin", &flags_.SplitFramebufferMargin);
|
||||
CheckSetting(iniFile, gameID, "ForceLowerResolutionForEffectsOn", &flags_.ForceLowerResolutionForEffectsOn);
|
||||
CheckSetting(iniFile, gameID, "AllowDownloadCLUT", &flags_.AllowDownloadCLUT);
|
||||
CheckSetting(iniFile, gameID, "NearestFilteringOnFramebufferCreate", &flags_.NearestFilteringOnFramebufferCreate);
|
||||
CheckSetting(iniFile, gameID, "Fontltn12Hack", &flags_.Fontltn12Hack);
|
||||
}
|
||||
|
||||
void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) {
|
||||
if (ignored_.find(option) == ignored_.end()) {
|
||||
iniFile.Get(option, gameID.c_str(), flag, *flag);
|
||||
|
||||
// Shortcut for debugging, sometimes useful to globally enable compat flags.
|
||||
bool all = false;
|
||||
iniFile.Get(option, "ALL", &all, false);
|
||||
*flag |= all;
|
||||
}
|
||||
}
|
||||
|
||||
void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, float *flag) {
|
||||
std::string value;
|
||||
iniFile.Get(option, gameID.c_str(), &value, "0");
|
||||
*flag = stof(value);
|
||||
}
|
||||
|
@ -57,7 +57,6 @@ struct CompatFlags {
|
||||
bool RequireBufferedRendering;
|
||||
bool RequireBlockTransfer;
|
||||
bool RequireDefaultCPUClock;
|
||||
bool DisableReadbacks;
|
||||
bool DisableAccurateDepth;
|
||||
bool MGS2AcidHack;
|
||||
bool SonicRivalsHack;
|
||||
@ -76,10 +75,8 @@ struct CompatFlags {
|
||||
bool ReportSmallMemstick;
|
||||
bool MemstickFixedFree;
|
||||
bool DateLimited;
|
||||
bool ReinterpretFramebuffers;
|
||||
bool ShaderColorBitmask;
|
||||
bool DisableFirstFrameReadback;
|
||||
bool DisableRangeCulling;
|
||||
bool MpegAvcWarmUp;
|
||||
bool BlueToAlpha;
|
||||
bool CenteredLines;
|
||||
@ -87,9 +84,19 @@ struct CompatFlags {
|
||||
bool ZZT3SelectHack;
|
||||
bool AllowLargeFBTextureOffsets;
|
||||
bool AtracLoopHack;
|
||||
bool DeswizzleDepth;
|
||||
bool SplitFramebufferMargin;
|
||||
bool ForceLowerResolutionForEffectsOn;
|
||||
bool AllowDownloadCLUT;
|
||||
bool NearestFilteringOnFramebufferCreate;
|
||||
bool Fontltn12Hack;
|
||||
};
|
||||
|
||||
struct VRCompat {
|
||||
bool Skyplane;
|
||||
float UnitsPerMeter;
|
||||
};
|
||||
|
||||
class IniFile;
|
||||
|
||||
class Compatibility {
|
||||
@ -101,13 +108,17 @@ public:
|
||||
// Flags enforced read-only through const. Only way to change them is to load assets/compat.ini.
|
||||
const CompatFlags &flags() const { return flags_; }
|
||||
|
||||
const VRCompat &vrCompat() const { return vrCompat_; }
|
||||
|
||||
void Load(const std::string &gameID);
|
||||
|
||||
private:
|
||||
void Clear();
|
||||
void CheckSettings(IniFile &iniFile, const std::string &gameID);
|
||||
void CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag);
|
||||
void CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, float *value);
|
||||
|
||||
CompatFlags flags_{};
|
||||
VRCompat vrCompat_{};
|
||||
std::set<std::string> ignored_;
|
||||
};
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "Common/System/System.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/GPU/Vulkan/VulkanLoader.h"
|
||||
#include "Common/VR/PPSSPPVR.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/ConfigValues.h"
|
||||
#include "Core/Loaders.h"
|
||||
@ -331,7 +332,7 @@ struct ConfigSetting {
|
||||
section->Get(ini5_, &ptr_.customButton->repeat, default_.customButton.repeat);
|
||||
return true;
|
||||
default:
|
||||
_dbg_assert_msg_(false, "Unexpected ini setting type");
|
||||
_dbg_assert_msg_(false, "Get(%s): Unexpected ini setting type: %d", iniKey_, (int)type_);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -375,7 +376,7 @@ struct ConfigSetting {
|
||||
section->Set(ini5_, ptr_.customButton->repeat);
|
||||
return;
|
||||
default:
|
||||
_dbg_assert_msg_(false, "Unexpected ini setting type");
|
||||
_dbg_assert_msg_(false, "Set(%s): Unexpected ini setting type: %d", iniKey_, (int)type_);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -406,22 +407,22 @@ struct ConfigSetting {
|
||||
// Doesn't report.
|
||||
return;
|
||||
default:
|
||||
_dbg_assert_msg_(false, "Unexpected ini setting type");
|
||||
_dbg_assert_msg_(false, "Report(%s): Unexpected ini setting type: %d", iniKey_, (int)type_);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const char *iniKey_;
|
||||
const char *ini2_;
|
||||
const char *ini3_;
|
||||
const char *ini4_;
|
||||
const char *ini5_;
|
||||
const char *iniKey_ = nullptr;
|
||||
const char *ini2_ = nullptr;
|
||||
const char *ini3_ = nullptr;
|
||||
const char *ini4_ = nullptr;
|
||||
const char *ini5_ = nullptr;
|
||||
Type type_;
|
||||
bool report_;
|
||||
bool save_;
|
||||
bool perGame_;
|
||||
SettingPtr ptr_;
|
||||
DefaultValue default_;
|
||||
DefaultValue default_{};
|
||||
Callback cb_;
|
||||
|
||||
// We only support transform for ints.
|
||||
@ -609,6 +610,7 @@ static ConfigSetting generalSettings[] = {
|
||||
ConfigSetting("PauseWhenMinimized", &g_Config.bPauseWhenMinimized, false, true, true),
|
||||
ConfigSetting("DumpDecryptedEboots", &g_Config.bDumpDecryptedEboot, false, true, true),
|
||||
ConfigSetting("FullscreenOnDoubleclick", &g_Config.bFullscreenOnDoubleclick, true, false, false),
|
||||
ConfigSetting("ShowMenuBar", &g_Config.bShowMenuBar, true, true, false),
|
||||
|
||||
ReportedConfigSetting("MemStickInserted", &g_Config.bMemStickInserted, true, true, true),
|
||||
ConfigSetting("EnablePlugins", &g_Config.bLoadPlugins, true, true, true),
|
||||
@ -695,9 +697,9 @@ const char * const vulkanDefaultBlacklist[] = {
|
||||
};
|
||||
|
||||
static int DefaultGPUBackend() {
|
||||
#ifdef OPENXR
|
||||
return (int)GPUBackend::OPENGL;
|
||||
#endif
|
||||
if (IsVRBuild()) {
|
||||
return (int)GPUBackend::OPENGL;
|
||||
}
|
||||
|
||||
#if PPSSPP_PLATFORM(WINDOWS)
|
||||
// If no Vulkan, use Direct3D 11 on Windows 8+ (most importantly 10.)
|
||||
@ -748,7 +750,7 @@ int Config::NextValidBackend() {
|
||||
if (failed.count((GPUBackend)iGPUBackend)) {
|
||||
ERROR_LOG(LOADER, "Graphics backend failed for %d, trying another", iGPUBackend);
|
||||
|
||||
#if (PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(ANDROID)) && !PPSSPP_PLATFORM(UWP)
|
||||
#if !PPSSPP_PLATFORM(UWP)
|
||||
if (!failed.count(GPUBackend::VULKAN) && VulkanMayBeAvailable()) {
|
||||
return (int)GPUBackend::VULKAN;
|
||||
}
|
||||
@ -797,6 +799,9 @@ bool Config::IsBackendEnabled(GPUBackend backend, bool validate) {
|
||||
#if PPSSPP_PLATFORM(UWP)
|
||||
if (backend != GPUBackend::DIRECT3D11)
|
||||
return false;
|
||||
#elif PPSSPP_PLATFORM(SWITCH)
|
||||
if (backend != GPUBackend::OPENGL)
|
||||
return false;
|
||||
#elif PPSSPP_PLATFORM(WINDOWS)
|
||||
if (validate) {
|
||||
if (backend == GPUBackend::DIRECT3D11 && !DoesVersionMatchWindows(6, 0, 0, 0, true))
|
||||
@ -869,6 +874,7 @@ static ConfigSetting graphicsSettings[] = {
|
||||
#endif
|
||||
ConfigSetting("CameraDevice", &g_Config.sCameraDevice, "", true, false),
|
||||
ConfigSetting("VendorBugChecksEnabled", &g_Config.bVendorBugChecksEnabled, true, false, false),
|
||||
ConfigSetting("UseGeometryShader", &g_Config.bUseGeometryShader, true, true, true),
|
||||
ReportedConfigSetting("RenderingMode", &g_Config.iRenderingMode, 1, true, true),
|
||||
ConfigSetting("SoftwareRenderer", &g_Config.bSoftwareRendering, false, true, true),
|
||||
ConfigSetting("SoftwareRendererJit", &g_Config.bSoftwareRenderingJit, true, true, true),
|
||||
@ -931,11 +937,8 @@ static ConfigSetting graphicsSettings[] = {
|
||||
ConfigSetting("ShaderChainRequires60FPS", &g_Config.bShaderChainRequires60FPS, false, true, true),
|
||||
|
||||
ReportedConfigSetting("MemBlockTransferGPU", &g_Config.bBlockTransferGPU, true, true, true),
|
||||
ReportedConfigSetting("DisableSlowFramebufEffects", &g_Config.bDisableSlowFramebufEffects, false, true, true),
|
||||
ReportedConfigSetting("FragmentTestCache", &g_Config.bFragmentTestCache, true, true, true),
|
||||
|
||||
ConfigSetting("GfxDebugOutput", &g_Config.bGfxDebugOutput, false, false, false),
|
||||
ConfigSetting("GfxDebugSplitSubmit", &g_Config.bGfxDebugSplitSubmit, false, false, false),
|
||||
ConfigSetting("LogFrameDrops", &g_Config.bLogFrameDrops, false, true, false),
|
||||
|
||||
ConfigSetting("InflightFrames", &g_Config.iInflightFrames, 3, true, false),
|
||||
@ -1113,15 +1116,6 @@ static ConfigSetting networkSettings[] = {
|
||||
ConfigSetting(false),
|
||||
};
|
||||
|
||||
static int DefaultPSPModel() {
|
||||
// TODO: Can probably default this on, but not sure about its memory differences.
|
||||
#if !PPSSPP_ARCH(AMD64) && !defined(_WIN32)
|
||||
return PSP_MODEL_FAT;
|
||||
#else
|
||||
return PSP_MODEL_SLIM;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int DefaultSystemParamLanguage() {
|
||||
int defaultLang = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;
|
||||
if (g_Config.bFirstRun) {
|
||||
@ -1135,7 +1129,7 @@ static int DefaultSystemParamLanguage() {
|
||||
}
|
||||
|
||||
static ConfigSetting systemParamSettings[] = {
|
||||
ReportedConfigSetting("PSPModel", &g_Config.iPSPModel, &DefaultPSPModel, true, true),
|
||||
ReportedConfigSetting("PSPModel", &g_Config.iPSPModel, PSP_MODEL_SLIM, true, true),
|
||||
ReportedConfigSetting("PSPFirmwareVersion", &g_Config.iFirmwareVersion, PSP_DEFAULT_FIRMWARE, true, true),
|
||||
ConfigSetting("NickName", &g_Config.sNickName, "PPSSPP", true, true),
|
||||
ConfigSetting("MacAddress", &g_Config.sMACAddress, "", true, true),
|
||||
@ -1167,6 +1161,9 @@ static ConfigSetting debuggerSettings[] = {
|
||||
ConfigSetting("GEWindowY", &g_Config.iGEWindowY, -1),
|
||||
ConfigSetting("GEWindowW", &g_Config.iGEWindowW, -1),
|
||||
ConfigSetting("GEWindowH", &g_Config.iGEWindowH, -1),
|
||||
ConfigSetting("GEWindowTabsBL", &g_Config.uGETabsLeft, (uint32_t)0),
|
||||
ConfigSetting("GEWindowTabsBR", &g_Config.uGETabsRight, (uint32_t)0),
|
||||
ConfigSetting("GEWindowTabsTR", &g_Config.uGETabsTopRight, (uint32_t)0),
|
||||
ConfigSetting("ConsoleWindowX", &g_Config.iConsoleWindowX, -1),
|
||||
ConfigSetting("ConsoleWindowY", &g_Config.iConsoleWindowY, -1),
|
||||
ConfigSetting("FontWidth", &g_Config.iFontWidth, 8),
|
||||
@ -1204,6 +1201,17 @@ static ConfigSetting themeSettings[] = {
|
||||
ConfigSetting(false),
|
||||
};
|
||||
|
||||
|
||||
static ConfigSetting vrSettings[] = {
|
||||
ConfigSetting("VREnable", &g_Config.bEnableVR, true),
|
||||
ConfigSetting("VREnable6DoF", &g_Config.bEnable6DoF, true),
|
||||
ConfigSetting("VREnableStereo", &g_Config.bEnableStereo, false),
|
||||
ConfigSetting("VRCanvasDistance", &g_Config.iCanvasDistance, 6),
|
||||
ConfigSetting("VRFieldOfView", &g_Config.iFieldOfViewPercentage, 100),
|
||||
|
||||
ConfigSetting(false),
|
||||
};
|
||||
|
||||
static ConfigSectionSettings sections[] = {
|
||||
{"General", generalSettings},
|
||||
{"CPU", cpuSettings},
|
||||
@ -1216,6 +1224,7 @@ static ConfigSectionSettings sections[] = {
|
||||
{"JIT", jitSettings},
|
||||
{"Upgrade", upgradeSettings},
|
||||
{"Theme", themeSettings},
|
||||
{"VR", vrSettings},
|
||||
};
|
||||
|
||||
static void IterateSettings(IniFile &iniFile, std::function<void(Section *section, ConfigSetting *setting)> func) {
|
||||
@ -1415,7 +1424,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
|
||||
// build of PPSSPP, receive an upgrade notice, then start a newer version, and still receive the upgrade notice,
|
||||
// even if said newer version is >= the upgrade found online.
|
||||
if ((dismissedVersion == upgradeVersion) || (versionsValid && (installed >= upgrade))) {
|
||||
upgradeMessage = "";
|
||||
upgradeMessage.clear();
|
||||
}
|
||||
|
||||
// Check for new version on every 10 runs.
|
||||
@ -1627,16 +1636,16 @@ void Config::DownloadCompletedCallback(http::Download &download) {
|
||||
|
||||
if (installed >= upgrade) {
|
||||
INFO_LOG(LOADER, "Version check: Already up to date, erasing any upgrade message");
|
||||
g_Config.upgradeMessage = "";
|
||||
g_Config.upgradeMessage.clear();
|
||||
g_Config.upgradeVersion = upgrade.ToString();
|
||||
g_Config.dismissedVersion = "";
|
||||
g_Config.dismissedVersion.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (installed < upgrade && dismissed != upgrade) {
|
||||
g_Config.upgradeMessage = "New version of PPSSPP available!";
|
||||
g_Config.upgradeVersion = upgrade.ToString();
|
||||
g_Config.dismissedVersion = "";
|
||||
g_Config.dismissedVersion.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,6 +164,7 @@ public:
|
||||
bool bHardwareTransform; // only used in the GLES backend
|
||||
bool bSoftwareSkinning; // may speed up some games
|
||||
bool bVendorBugChecksEnabled;
|
||||
bool bUseGeometryShader;
|
||||
|
||||
int iRenderingMode; // 0 = non-buffered rendering 1 = buffered rendering
|
||||
int iTexFiltering; // 1 = auto , 2 = nearest , 3 = linear , 4 = auto max quality
|
||||
@ -176,6 +177,7 @@ public:
|
||||
bool bSustainedPerformanceMode; // Android: Slows clocks down to avoid overheating/speed fluctuations.
|
||||
bool bIgnoreScreenInsets; // Android: Center screen disregarding insets if this is enabled.
|
||||
bool bVSync;
|
||||
|
||||
int iFrameSkip;
|
||||
int iFrameSkipType;
|
||||
int iFastForwardMode; // See FastForwardMode in ConfigValues.h.
|
||||
@ -190,6 +192,7 @@ public:
|
||||
int iWindowY;
|
||||
int iWindowWidth; // Windows and other windowed environments
|
||||
int iWindowHeight;
|
||||
bool bShowMenuBar; // Windows-only
|
||||
|
||||
float fUITint;
|
||||
float fUISaturation;
|
||||
@ -232,8 +235,6 @@ public:
|
||||
float fGameListScrollPosition;
|
||||
int iBloomHack; //0 = off, 1 = safe, 2 = balanced, 3 = aggressive
|
||||
bool bBlockTransferGPU;
|
||||
bool bDisableSlowFramebufEffects;
|
||||
bool bFragmentTestCache;
|
||||
int iSplineBezierQuality; // 0 = low , 1 = Intermediate , 2 = High
|
||||
bool bHardwareTessellation;
|
||||
bool bShaderCache; // Hidden ini-only setting, useful for debugging shader compile times.
|
||||
@ -243,7 +244,6 @@ public:
|
||||
bool bShaderChainRequires60FPS;
|
||||
std::string sTextureShaderName;
|
||||
bool bGfxDebugOutput;
|
||||
bool bGfxDebugSplitSubmit;
|
||||
int iInflightFrames;
|
||||
bool bRenderDuplicateFrames;
|
||||
|
||||
@ -453,6 +453,13 @@ public:
|
||||
int iFirmwareVersion;
|
||||
bool bBypassOSKWithKeyboard;
|
||||
|
||||
// Virtual reality
|
||||
bool bEnableVR;
|
||||
bool bEnable6DoF;
|
||||
bool bEnableStereo;
|
||||
int iCanvasDistance;
|
||||
int iFieldOfViewPercentage;
|
||||
|
||||
// Debugger
|
||||
int iDisasmWindowX;
|
||||
int iDisasmWindowY;
|
||||
@ -462,6 +469,9 @@ public:
|
||||
int iGEWindowY;
|
||||
int iGEWindowW;
|
||||
int iGEWindowH;
|
||||
uint32_t uGETabsLeft;
|
||||
uint32_t uGETabsRight;
|
||||
uint32_t uGETabsTopRight;
|
||||
int iConsoleWindowX;
|
||||
int iConsoleWindowY;
|
||||
int iFontWidth;
|
||||
|
@ -419,6 +419,7 @@ const char *MemoryExceptionTypeAsString(MemoryExceptionType type) {
|
||||
case MemoryExceptionType::WRITE_WORD: return "Write Word";
|
||||
case MemoryExceptionType::READ_BLOCK: return "Read Block";
|
||||
case MemoryExceptionType::WRITE_BLOCK: return "Read/Write Block";
|
||||
case MemoryExceptionType::ALIGNMENT: return "Alignment";
|
||||
default:
|
||||
return "N/A";
|
||||
}
|
||||
@ -446,7 +447,7 @@ void Core_MemoryException(u32 address, u32 pc, MemoryExceptionType type) {
|
||||
ExceptionInfo &e = g_exceptionInfo;
|
||||
e = {};
|
||||
e.type = ExceptionType::MEMORY;
|
||||
e.info = "";
|
||||
e.info.clear();
|
||||
e.memory_type = type;
|
||||
e.address = address;
|
||||
e.pc = pc;
|
||||
@ -482,20 +483,23 @@ void Core_ExecException(u32 address, u32 pc, ExecExceptionType type) {
|
||||
ExceptionInfo &e = g_exceptionInfo;
|
||||
e = {};
|
||||
e.type = ExceptionType::BAD_EXEC_ADDR;
|
||||
e.info = "";
|
||||
e.info.clear();
|
||||
e.exec_type = type;
|
||||
e.address = address;
|
||||
e.pc = pc;
|
||||
Core_EnableStepping(true, "cpu.exception", pc);
|
||||
// This just records the closest value that could be useful as reference.
|
||||
e.ra = currentMIPS->r[MIPS_REG_RA];
|
||||
Core_EnableStepping(true, "cpu.exception", address);
|
||||
}
|
||||
|
||||
void Core_Break() {
|
||||
void Core_Break(u32 pc) {
|
||||
ERROR_LOG(CPU, "BREAK!");
|
||||
|
||||
ExceptionInfo &e = g_exceptionInfo;
|
||||
e = {};
|
||||
e.type = ExceptionType::BREAK;
|
||||
e.info = "";
|
||||
e.info.clear();
|
||||
e.pc = pc;
|
||||
|
||||
if (!g_Config.bIgnoreBadMemAccess) {
|
||||
Core_EnableStepping(true, "cpu.breakInstruction", currentMIPS->pc);
|
||||
|
@ -93,6 +93,7 @@ enum class MemoryExceptionType {
|
||||
WRITE_WORD,
|
||||
READ_BLOCK,
|
||||
WRITE_BLOCK,
|
||||
ALIGNMENT,
|
||||
};
|
||||
enum class ExecExceptionType {
|
||||
JUMP,
|
||||
@ -105,7 +106,7 @@ void Core_MemoryException(u32 address, u32 pc, MemoryExceptionType type);
|
||||
void Core_MemoryExceptionInfo(u32 address, u32 pc, MemoryExceptionType type, std::string additionalInfo);
|
||||
|
||||
void Core_ExecException(u32 address, u32 pc, ExecExceptionType type);
|
||||
void Core_Break();
|
||||
void Core_Break(u32 pc);
|
||||
// Call when loading save states, etc.
|
||||
void Core_ResetException();
|
||||
|
||||
@ -124,6 +125,7 @@ struct ExceptionInfo {
|
||||
MemoryExceptionType memory_type;
|
||||
uint32_t pc;
|
||||
uint32_t address;
|
||||
uint32_t ra = 0;
|
||||
|
||||
// Reuses pc and address from memory type, where address is the failed destination.
|
||||
ExecExceptionType exec_type;
|
||||
|
@ -139,7 +139,7 @@
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
@ -148,6 +148,7 @@
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<ForcedIncludeFiles>Common/DbgNew.h</ForcedIncludeFiles>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
@ -157,12 +158,15 @@
|
||||
<Outputs>../git-version.cpp;%(Outputs)</Outputs>
|
||||
<Message>Updating git-version.cpp</Message>
|
||||
</CustomBuildStep>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib;../ext/zstd/lib</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
<OmitFramePointers>false</OmitFramePointers>
|
||||
@ -172,6 +176,7 @@
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<ForcedIncludeFiles>Common/DbgNew.h</ForcedIncludeFiles>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
@ -181,12 +186,15 @@
|
||||
<Outputs>../git-version.cpp;%(Outputs)</Outputs>
|
||||
<Message>Updating git-version.cpp</Message>
|
||||
</CustomBuildStep>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_DEBUG;_LIB;_UNICODE;UNICODE;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
<OmitFramePointers>false</OmitFramePointers>
|
||||
@ -196,6 +204,7 @@
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<ForcedIncludeFiles>Common/DbgNew.h</ForcedIncludeFiles>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
@ -205,12 +214,15 @@
|
||||
<Outputs>../git-version.cpp;%(Outputs)</Outputs>
|
||||
<Message>Updating git-version.cpp</Message>
|
||||
</CustomBuildStep>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_DEBUG;_LIB;_UNICODE;UNICODE;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
<OmitFramePointers>false</OmitFramePointers>
|
||||
@ -221,6 +233,7 @@
|
||||
<ForcedIncludeFiles>
|
||||
</ForcedIncludeFiles>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
@ -230,6 +243,9 @@
|
||||
<Outputs>../git-version.cpp;%(Outputs)</Outputs>
|
||||
<Message>Updating git-version.cpp</Message>
|
||||
</CustomBuildStep>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
@ -241,12 +257,13 @@
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_M_IX86=1;_LIB;NDEBUG;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_M_IX86=1;_LIB;NDEBUG;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
@ -255,6 +272,7 @@
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
<CustomBuildStep>
|
||||
<Command>../Windows/git-version-gen.cmd</Command>
|
||||
@ -276,11 +294,12 @@
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_M_X64=1;_LIB;NDEBUG;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_M_X64=1;_LIB;NDEBUG;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StringPooling>true</StringPooling>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
@ -292,6 +311,9 @@
|
||||
<Outputs>../git-version.cpp;%(Outputs)</Outputs>
|
||||
<Message>Updating git-version.cpp</Message>
|
||||
</CustomBuildStep>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<ClCompile>
|
||||
@ -307,11 +329,12 @@
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_LIB;NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_LIB;NDEBUG;_UNICODE;UNICODE;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StringPooling>true</StringPooling>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
@ -323,6 +346,9 @@
|
||||
<Outputs>../git-version.cpp;%(Outputs)</Outputs>
|
||||
<Message>Updating git-version.cpp</Message>
|
||||
</CustomBuildStep>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<ClCompile>
|
||||
@ -338,11 +364,12 @@
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_LIB;NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_LIB;NDEBUG;_UNICODE;UNICODE;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StringPooling>true</StringPooling>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
@ -354,6 +381,9 @@
|
||||
<Outputs>../git-version.cpp;%(Outputs)</Outputs>
|
||||
<Message>Updating git-version.cpp</Message>
|
||||
</CustomBuildStep>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\ext\cityhash\city.cpp" />
|
||||
@ -568,7 +598,16 @@
|
||||
<ClCompile Include="Cwcheat.cpp" />
|
||||
<ClCompile Include="Debugger\Breakpoints.cpp" />
|
||||
<ClCompile Include="Debugger\DisassemblyManager.cpp" />
|
||||
<ClCompile Include="Debugger\SymbolMap.cpp" />
|
||||
<ClCompile Include="Debugger\SymbolMap.cpp">
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Dialog\PSPGamedataInstallDialog.cpp" />
|
||||
<ClCompile Include="Dialog\PSPDialog.cpp" />
|
||||
<ClCompile Include="Dialog\PSPMsgDialog.cpp" />
|
||||
@ -900,7 +939,16 @@
|
||||
<ClCompile Include="MIPS\JitCommon\JitState.cpp" />
|
||||
<ClCompile Include="MIPS\MIPS.cpp" />
|
||||
<ClCompile Include="MIPS\MIPSAnalyst.cpp" />
|
||||
<ClCompile Include="MIPS\MIPSAsm.cpp" />
|
||||
<ClCompile Include="MIPS\MIPSAsm.cpp">
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\MIPSCodeUtils.cpp" />
|
||||
<ClCompile Include="MIPS\MIPSDebugInterface.cpp" />
|
||||
<ClCompile Include="MIPS\MIPSDis.cpp" />
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user