Merge remote-tracking branch 'upstream/master' into Font-ltn12-hack

This commit is contained in:
sum2012 2022-10-16 07:27:12 +08:00
commit 0e52856faf
552 changed files with 39420 additions and 29519 deletions

View File

@ -6,7 +6,7 @@
# Core definitions
variables:
GIT_SUBMODULE_STRATEGY: normal
GIT_SUBMODULE_STRATEGY: recursive
.core-defs:
variables:

View File

@ -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()

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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;
};

View File

@ -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);

View File

@ -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;
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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>();
}

View File

@ -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 ".."

View File

@ -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_);

View File

@ -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) {

View File

@ -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);

View File

@ -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

View File

@ -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();
};

View File

@ -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:

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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_) {

View File

@ -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;

View File

@ -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:

View File

@ -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;

View File

@ -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.

View File

@ -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]{};

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
};

View File

@ -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";

View File

@ -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;

View File

@ -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).

View 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);
}

View 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;
};

View File

@ -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();

View File

@ -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);

View File

@ -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.
};

View File

@ -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;
}

View File

@ -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().

View File

@ -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

View File

@ -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

View File

@ -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_;
};

View File

@ -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

View File

@ -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)";
}
}

View File

@ -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:

View File

@ -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

View File

@ -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) {

View 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
View 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_;
};

View File

@ -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);

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

File diff suppressed because it is too large Load Diff

472
Common/RiscVEmitter.h Normal file
View 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;
};
};

View File

@ -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

View File

@ -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);

View File

@ -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.

View File

@ -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;
};

View File

@ -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

View File

@ -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;
};

View File

@ -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; }

View File

@ -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_});

View File

@ -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
View 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
View 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

View File

@ -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 = {};

View File

@ -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);

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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
View 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
View 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);

View File

@ -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];
}

View File

@ -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
View 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
View 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);

View File

@ -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);
}

View File

@ -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_;
};

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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