mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-10-07 10:53:31 +00:00
Merge branch 'master' into feature_openxr_vulkan
This commit is contained in:
commit
7c6cff5ae9
@ -6,7 +6,7 @@
|
||||
|
||||
# Core definitions
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: normal
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
|
||||
.core-defs:
|
||||
variables:
|
||||
|
@ -1,11 +1,11 @@
|
||||
# vim:noexpandtab:
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(PPSSPP)
|
||||
enable_testing()
|
||||
|
||||
#This is supposed to work but doesn't!
|
||||
if(NOT ANDROID)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
|
||||
@ -120,8 +120,16 @@ if(OPENXR)
|
||||
add_definitions(-DOPENXR)
|
||||
add_library(openxr SHARED IMPORTED)
|
||||
include_directories(ext/openxr)
|
||||
set_property(TARGET openxr PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/ext/openxr/libs/arm64-v8a/libopenxr_loader.so")
|
||||
message("OpenXR enabled")
|
||||
if(OPENXR_PLATFORM_PICO)
|
||||
add_definitions(-DOPENXR_PLATFORM_PICO)
|
||||
set_property(TARGET openxr PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/ext/openxr/libs/pico/arm64-v8a/libopenxr_loader.so")
|
||||
message("OpenXR for Pico enabled")
|
||||
endif()
|
||||
if(OPENXR_PLATFORM_QUEST)
|
||||
add_definitions(-DOPENXR_PLATFORM_QUEST)
|
||||
set_property(TARGET openxr PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/ext/openxr/libs/quest/arm64-v8a/libopenxr_loader.so")
|
||||
message("OpenXR for Quest enabled")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(GOLD)
|
||||
@ -315,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)
|
||||
@ -370,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)
|
||||
@ -623,6 +632,8 @@ 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
|
||||
@ -1551,6 +1562,8 @@ set(GPU_SOURCES
|
||||
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
|
||||
@ -2056,6 +2069,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)
|
||||
@ -2444,7 +2459,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)
|
||||
@ -2498,6 +2513,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"
|
||||
@ -2510,7 +2527,11 @@ if(UNIX AND NOT ANDROID AND NOT APPLE)
|
||||
)
|
||||
install(
|
||||
FILES "${CMAKE_SOURCE_DIR}/icons/icon-512.svg"
|
||||
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pixmaps"
|
||||
RENAME ppsspp.svg
|
||||
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps"
|
||||
RENAME "ppsspp.svg"
|
||||
)
|
||||
install(
|
||||
FILES "${CMAKE_SOURCE_DIR}/assets/mime/ppsspp.xml"
|
||||
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/mime/packages"
|
||||
)
|
||||
endif()
|
||||
|
@ -153,6 +153,7 @@
|
||||
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
@ -179,6 +180,7 @@
|
||||
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
@ -205,6 +207,7 @@
|
||||
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
@ -232,6 +235,7 @@
|
||||
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
@ -264,6 +268,7 @@
|
||||
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
@ -298,6 +303,7 @@
|
||||
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
@ -332,6 +338,7 @@
|
||||
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
@ -366,6 +373,7 @@
|
||||
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
@ -441,6 +449,7 @@
|
||||
<ClInclude Include="GPU\Vulkan\VulkanBarrier.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanContext.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanDebug.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanFrameData.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanImage.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanLoader.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanMemory.h" />
|
||||
@ -861,6 +870,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" />
|
||||
|
@ -419,6 +419,9 @@
|
||||
<Filter>GPU\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RiscVEmitter.h" />
|
||||
<ClInclude Include="GPU\Vulkan\VulkanFrameData.h">
|
||||
<Filter>GPU\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ABI.cpp" />
|
||||
@ -791,6 +794,9 @@
|
||||
<Filter>GPU\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RiscVEmitter.cpp" />
|
||||
<ClCompile Include="GPU\Vulkan\VulkanFrameData.cpp">
|
||||
<Filter>GPU\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Crypto">
|
||||
|
@ -10,21 +10,21 @@ struct TinySet {
|
||||
~TinySet() { delete slowLookup_; }
|
||||
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;
|
||||
if (fastCount_ < MaxFastSize) {
|
||||
fastLookup_[fastCount_++] = t;
|
||||
return;
|
||||
}
|
||||
if (!slowLookup_) {
|
||||
@ -33,8 +33,8 @@ struct TinySet {
|
||||
slowLookup_->push_back(t);
|
||||
}
|
||||
inline T *add_back() {
|
||||
if (fastCount < MaxFastSize) {
|
||||
return &fastLookup_[fastCount++];
|
||||
if (fastCount_ < MaxFastSize) {
|
||||
return &fastLookup_[fastCount_++];
|
||||
}
|
||||
if (!slowLookup_) {
|
||||
slowLookup_ = new std::vector<T>();
|
||||
@ -48,9 +48,9 @@ struct TinySet {
|
||||
if (size() + otherSize <= MaxFastSize) {
|
||||
// Fast case
|
||||
for (size_t i = 0; i < otherSize; i++) {
|
||||
fastLookup_[fastCount + i] = other.fastLookup_[i];
|
||||
fastLookup_[fastCount_ + i] = other.fastLookup_[i];
|
||||
}
|
||||
fastCount += other.fastCount;
|
||||
fastCount_ += other.fastCount_;
|
||||
} else {
|
||||
for (size_t i = 0; i < otherSize; i++) {
|
||||
push_back(other[i]);
|
||||
@ -58,7 +58,7 @@ struct TinySet {
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
@ -72,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;
|
||||
}
|
||||
@ -85,16 +85,17 @@ 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;
|
||||
return fastCount_ == 0;
|
||||
}
|
||||
size_t size() const {
|
||||
if (!slowLookup_) {
|
||||
return fastCount;
|
||||
return fastCount_;
|
||||
} else {
|
||||
return slowLookup_->size() + MaxFastSize;
|
||||
}
|
||||
@ -129,7 +130,7 @@ private:
|
||||
}
|
||||
slowLookup_->push_back(t);
|
||||
}
|
||||
int fastCount_ = 0; // first in the struct just so it's more visible in the VS debugger.
|
||||
T fastLookup_[MaxFastSize];
|
||||
int fastCount = 0;
|
||||
std::vector<T> *slowLookup_ = nullptr;
|
||||
};
|
||||
|
@ -82,6 +82,20 @@ inline void Uint8x3ToFloat4(float f[4], uint32_t u) {
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void Uint8x3ToFloat3(float f[4], uint32_t u) {
|
||||
#if defined(_M_SSE) || PPSSPP_ARCH(ARM_NEON)
|
||||
float temp[4];
|
||||
Uint8x4ToFloat4(temp, u & 0xFFFFFF);
|
||||
f[0] = temp[0];
|
||||
f[1] = temp[1];
|
||||
f[2] = temp[2];
|
||||
#else
|
||||
f[0] = ((u >> 0) & 0xFF) * (1.0f / 255.0f);
|
||||
f[1] = ((u >> 8) & 0xFF) * (1.0f / 255.0f);
|
||||
f[2] = ((u >> 16) & 0xFF) * (1.0f / 255.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void Uint8x3ToInt4(int i[4], uint32_t u) {
|
||||
i[0] = ((u >> 0) & 0xFF);
|
||||
i[1] = ((u >> 8) & 0xFF);
|
||||
|
@ -95,7 +95,6 @@ bool decompress_string(const std::string& str, std::string *dest) {
|
||||
inflateEnd(&zs);
|
||||
|
||||
if (ret != Z_STREAM_END) { // an error occurred that was not EOF
|
||||
std::ostringstream oss;
|
||||
ERROR_LOG(IO, "Exception during zlib decompression: (%i) %s", ret, zs.msg);
|
||||
return false;
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ void Section::Set(const char* key, const char* newValue)
|
||||
else
|
||||
{
|
||||
// The key did not already exist in this section - let's add it.
|
||||
lines.push_back(std::string(key) + " = " + EscapeComments(newValue));
|
||||
lines.emplace_back(std::string(key) + " = " + EscapeComments(newValue));
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,7 +272,7 @@ void Section::Set(const char* key, const std::vector<std::string>& newValues)
|
||||
}
|
||||
|
||||
void Section::AddComment(const std::string &comment) {
|
||||
lines.push_back("# " + comment);
|
||||
lines.emplace_back("# " + comment);
|
||||
}
|
||||
|
||||
bool Section::Get(const char* key, std::vector<std::string>& values)
|
||||
|
@ -39,7 +39,7 @@ const char *I18NCategory::T(const char *key, const char *def) {
|
||||
if (def)
|
||||
missedKeyLog_[key] = def;
|
||||
else
|
||||
missedKeyLog_[key] = modifiedKey.c_str();
|
||||
missedKeyLog_[key] = modifiedKey;
|
||||
// INFO_LOG(SYSTEM, "Missed translation key in %s: %s", name_.c_str(), key);
|
||||
return def ? def : key;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -271,7 +271,7 @@ 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) {
|
||||
@ -357,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_);
|
||||
|
@ -105,6 +105,7 @@ public:
|
||||
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;
|
||||
@ -269,6 +270,7 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de
|
||||
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));
|
||||
@ -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";
|
||||
@ -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));
|
||||
|
@ -47,7 +47,7 @@ LPD3DBLOB CompileShaderToByteCodeD3D9(const char *code, const char *target, std:
|
||||
pShaderCode = nullptr;
|
||||
}
|
||||
} else {
|
||||
*errorMessage = "";
|
||||
errorMessage->clear();
|
||||
}
|
||||
|
||||
return pShaderCode;
|
||||
|
@ -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
|
||||
@ -442,6 +444,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();
|
||||
@ -520,6 +533,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) {
|
||||
@ -632,6 +647,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')))
|
||||
|
||||
@ -651,14 +723,24 @@ 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;
|
||||
@ -670,8 +752,30 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID
|
||||
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;
|
||||
@ -679,8 +783,9 @@ 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;
|
||||
|
||||
@ -813,6 +918,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;
|
||||
}
|
||||
@ -835,6 +945,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;
|
||||
|
@ -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,
|
||||
|
@ -4,9 +4,23 @@
|
||||
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:
|
||||
internalFormat = GL_RGBA;
|
||||
format = GL_RED;
|
||||
type = GL_UNSIGNED_BYTE;
|
||||
alignment = 1;
|
||||
break;
|
||||
|
||||
case DataFormat::R8G8B8A8_UNORM:
|
||||
internalFormat = GL_RGBA;
|
||||
format = GL_RGBA;
|
||||
|
@ -5,6 +5,6 @@
|
||||
|
||||
namespace Draw {
|
||||
|
||||
bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment);
|
||||
bool Thin3DFormatToGLFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment);
|
||||
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ static void ParseExtensionsString(const std::string& str, std::set<std::string>
|
||||
size_t next = 0;
|
||||
for (size_t pos = 0, len = str.length(); pos < len; ++pos) {
|
||||
if (str[pos] == ' ') {
|
||||
output.insert(str.substr(next, pos - next));
|
||||
output.emplace(str.substr(next, pos - next));
|
||||
// Skip the delimiter itself.
|
||||
next = pos + 1;
|
||||
}
|
||||
@ -60,7 +60,7 @@ static void ParseExtensionsString(const std::string& str, std::set<std::string>
|
||||
if (next == 0 && str.length() != 0) {
|
||||
output.insert(str);
|
||||
} else if (next < str.length()) {
|
||||
output.insert(str.substr(next));
|
||||
output.emplace(str.substr(next));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,8 +252,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());
|
||||
@ -385,7 +385,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,
|
||||
@ -701,7 +701,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);
|
||||
@ -811,7 +817,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;
|
||||
|
||||
@ -1119,14 +1125,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;
|
||||
@ -1279,7 +1289,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
|
||||
// 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);
|
||||
@ -1366,8 +1376,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();
|
||||
|
@ -236,28 +236,12 @@ bool GLRenderManager::ThreadFrame() {
|
||||
firstFrame = false;
|
||||
}
|
||||
|
||||
// Start of an OpenXR frame. This updates user's head pose and VR timestamps.
|
||||
// For fluent rendering, delay between StartVRRender and FinishVRRender must be very short.
|
||||
if (IsVRBuild() && !vrRenderStarted) {
|
||||
if (StartVRRender()) {
|
||||
vrRenderStarted = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Render the scene.
|
||||
Run(threadFrame_);
|
||||
|
||||
VLOG("PULL: Finished frame %d", threadFrame_);
|
||||
} while (!nextFrame);
|
||||
|
||||
// Post OpenXR frame on a screen.
|
||||
if (IsVRBuild() && vrRenderStarted) {
|
||||
FinishVRRender();
|
||||
vrRenderStarted = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -579,7 +563,6 @@ void GLRenderManager::EndSubmitFrame(int frame) {
|
||||
void GLRenderManager::Run(int frame) {
|
||||
BeginSubmitFrame(frame);
|
||||
|
||||
|
||||
FrameData &frameData = frameData_[frame];
|
||||
|
||||
auto &stepsOnThread = frameData_[frame].steps;
|
||||
|
@ -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];
|
||||
@ -1003,6 +1012,7 @@ private:
|
||||
bool readyForFence = true;
|
||||
bool readyForRun = false;
|
||||
bool readyForSubmit = false;
|
||||
|
||||
bool skipSwap = false;
|
||||
GLRRunType type = GLRRunType::END;
|
||||
|
||||
@ -1041,7 +1051,6 @@ private:
|
||||
|
||||
bool nextFrame = false;
|
||||
bool firstFrame = true;
|
||||
bool vrRenderStarted = false;
|
||||
|
||||
GLDeleter deleter_;
|
||||
bool skipGLCalls_ = false;
|
||||
|
@ -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_));
|
||||
@ -550,6 +552,8 @@ OpenGLContext::OpenGLContext() {
|
||||
caps_.framebufferDepthBlitSupported = caps_.framebufferBlitSupported;
|
||||
caps_.framebufferStencilBlitSupported = caps_.framebufferBlitSupported;
|
||||
caps_.depthClampSupported = gl_extensions.ARB_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;
|
||||
@ -709,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]";
|
||||
@ -1138,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];
|
||||
@ -1191,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" });
|
||||
@ -1226,7 +1240,8 @@ bool OpenGLPipeline::LinkShaders() {
|
||||
}
|
||||
}
|
||||
|
||||
program_ = render_->CreateProgram(linkShaders, semantics, queries, initialize, false, false);
|
||||
GLRProgramFlags flags{};
|
||||
program_ = render_->CreateProgram(linkShaders, semantics, queries, initialize, flags);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1483,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:
|
||||
|
@ -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]{};
|
||||
|
@ -53,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"
|
||||
@ -68,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, ...) {
|
||||
@ -97,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;
|
||||
}
|
||||
@ -115,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;
|
||||
}
|
||||
@ -145,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;
|
||||
}
|
||||
@ -172,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, ");
|
||||
}
|
||||
@ -308,6 +345,43 @@ 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) {
|
||||
@ -349,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");
|
||||
|
@ -85,10 +85,12 @@ public:
|
||||
// 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_;
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
void VulkanBarrier::Flush(VkCommandBuffer cmd) {
|
||||
if (!imageBarriers_.empty()) {
|
||||
vkCmdPipelineBarrier(cmd, srcStageMask_, dstStageMask_, 0, 0, nullptr, 0, nullptr, (uint32_t)imageBarriers_.size(), imageBarriers_.data());
|
||||
vkCmdPipelineBarrier(cmd, srcStageMask_, dstStageMask_, dependencyFlags_, 0, nullptr, 0, nullptr, (uint32_t)imageBarriers_.size(), imageBarriers_.data());
|
||||
}
|
||||
imageBarriers_.clear();
|
||||
srcStageMask_ = 0;
|
||||
|
@ -19,8 +19,11 @@ public:
|
||||
VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask,
|
||||
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask
|
||||
) {
|
||||
_dbg_assert_(image != VK_NULL_HANDLE);
|
||||
|
||||
srcStageMask_ |= srcStageMask;
|
||||
dstStageMask_ |= dstStageMask;
|
||||
dependencyFlags_ |= VK_DEPENDENCY_BY_REGION_BIT;
|
||||
|
||||
VkImageMemoryBarrier imageBarrier;
|
||||
imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
@ -45,6 +48,8 @@ public:
|
||||
void TransitionImageAuto(
|
||||
VkImage image, int baseMip, int numMipLevels, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout
|
||||
) {
|
||||
_dbg_assert_(image != VK_NULL_HANDLE);
|
||||
|
||||
VkAccessFlags srcAccessMask = 0;
|
||||
VkAccessFlags dstAccessMask = 0;
|
||||
switch (oldImageLayout) {
|
||||
@ -112,4 +117,5 @@ private:
|
||||
VkPipelineStageFlags srcStageMask_ = 0;
|
||||
VkPipelineStageFlags dstStageMask_ = 0;
|
||||
std::vector<VkImageMemoryBarrier> imageBarriers_;
|
||||
VkDependencyFlags dependencyFlags_ = 0;
|
||||
};
|
||||
|
@ -600,8 +600,7 @@ void VulkanContext::ChooseDevice(int physical_device) {
|
||||
deviceFeatures_.enabled.samplerAnisotropy = deviceFeatures_.available.samplerAnisotropy;
|
||||
deviceFeatures_.enabled.shaderClipDistance = deviceFeatures_.available.shaderClipDistance;
|
||||
deviceFeatures_.enabled.shaderCullDistance = deviceFeatures_.available.shaderCullDistance;
|
||||
// For easy wireframe mode, someday.
|
||||
deviceFeatures_.enabled.fillModeNonSolid = deviceFeatures_.available.fillModeNonSolid;
|
||||
deviceFeatures_.enabled.geometryShader = deviceFeatures_.available.geometryShader;
|
||||
|
||||
GetDeviceLayerExtensionList(nullptr, device_extension_properties_);
|
||||
|
||||
@ -667,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;
|
||||
@ -1263,10 +1265,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;
|
||||
|
||||
|
@ -86,7 +86,6 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback(
|
||||
} else {
|
||||
WARN_LOG(G3D, "VKDEBUG: %s", msg.c_str());
|
||||
}
|
||||
|
||||
// false indicates that layer should not bail-out of an
|
||||
// API call that had validation failures. This may mean that the
|
||||
// app dies inside the driver due to invalid parameter(s).
|
||||
|
224
Common/GPU/Vulkan/VulkanFrameData.cpp
Normal file
224
Common/GPU/Vulkan/VulkanFrameData.cpp
Normal file
@ -0,0 +1,224 @@
|
||||
#include <mutex>
|
||||
|
||||
#include "VulkanFrameData.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/StringUtils.h"
|
||||
|
||||
void FrameData::Init(VulkanContext *vulkan, int index) {
|
||||
this->index = index;
|
||||
VkDevice device = vulkan->GetDevice();
|
||||
|
||||
VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
|
||||
cmd_pool_info.queueFamilyIndex = vulkan->GetGraphicsQueueFamilyIndex();
|
||||
cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
||||
VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &cmdPoolInit);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &cmdPoolMain);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
|
||||
VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
|
||||
cmd_alloc.commandPool = cmdPoolInit;
|
||||
cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
cmd_alloc.commandBufferCount = 1;
|
||||
res = vkAllocateCommandBuffers(device, &cmd_alloc, &initCmd);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
cmd_alloc.commandPool = cmdPoolMain;
|
||||
res = vkAllocateCommandBuffers(device, &cmd_alloc, &mainCmd);
|
||||
res = vkAllocateCommandBuffers(device, &cmd_alloc, &presentCmd);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
|
||||
vulkan->SetDebugName(initCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("initCmd%d", index).c_str());
|
||||
vulkan->SetDebugName(mainCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("mainCmd%d", index).c_str());
|
||||
vulkan->SetDebugName(presentCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("presentCmd%d", index).c_str());
|
||||
|
||||
// Creating the frame fence with true so they can be instantly waited on the first frame
|
||||
fence = vulkan->CreateFence(true);
|
||||
vulkan->SetDebugName(fence, VK_OBJECT_TYPE_FENCE, StringFromFormat("fence%d", index).c_str());
|
||||
readyForFence = true;
|
||||
|
||||
// This fence is used for synchronizing readbacks. Does not need preinitialization.
|
||||
// TODO: Put this in frameDataShared, only one is needed.
|
||||
readbackFence = vulkan->CreateFence(false);
|
||||
vulkan->SetDebugName(fence, VK_OBJECT_TYPE_FENCE, "readbackFence");
|
||||
|
||||
VkQueryPoolCreateInfo query_ci{ VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO };
|
||||
query_ci.queryCount = MAX_TIMESTAMP_QUERIES;
|
||||
query_ci.queryType = VK_QUERY_TYPE_TIMESTAMP;
|
||||
res = vkCreateQueryPool(device, &query_ci, nullptr, &profile.queryPool);
|
||||
}
|
||||
|
||||
void FrameData::Destroy(VulkanContext *vulkan) {
|
||||
VkDevice device = vulkan->GetDevice();
|
||||
vkDestroyCommandPool(device, cmdPoolInit, nullptr);
|
||||
vkDestroyCommandPool(device, cmdPoolMain, nullptr);
|
||||
vkDestroyFence(device, fence, nullptr);
|
||||
vkDestroyFence(device, readbackFence, nullptr);
|
||||
vkDestroyQueryPool(device, profile.queryPool, nullptr);
|
||||
}
|
||||
|
||||
void FrameData::AcquireNextImage(VulkanContext *vulkan, FrameDataShared &shared) {
|
||||
_dbg_assert_(!hasAcquired);
|
||||
|
||||
// Get the index of the next available swapchain image, and a semaphore to block command buffer execution on.
|
||||
VkResult res = vkAcquireNextImageKHR(vulkan->GetDevice(), vulkan->GetSwapchain(), UINT64_MAX, shared.acquireSemaphore, (VkFence)VK_NULL_HANDLE, &curSwapchainImage);
|
||||
switch (res) {
|
||||
case VK_SUCCESS:
|
||||
hasAcquired = true;
|
||||
break;
|
||||
case VK_SUBOPTIMAL_KHR:
|
||||
hasAcquired = true;
|
||||
// Hopefully the resize will happen shortly. Ignore - one frame might look bad or something.
|
||||
WARN_LOG(G3D, "VK_SUBOPTIMAL_KHR returned - ignoring");
|
||||
break;
|
||||
case VK_ERROR_OUT_OF_DATE_KHR:
|
||||
// We do not set hasAcquired here!
|
||||
WARN_LOG(G3D, "VK_ERROR_OUT_OF_DATE_KHR returned from AcquireNextImage - processing the frame, but not presenting");
|
||||
skipSwap = true;
|
||||
break;
|
||||
default:
|
||||
// Weird, shouldn't get any other values. Maybe lost device?
|
||||
_assert_msg_(false, "vkAcquireNextImageKHR failed! result=%s", VulkanResultToString(res));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VkResult FrameData::QueuePresent(VulkanContext *vulkan, FrameDataShared &shared) {
|
||||
_dbg_assert_(hasAcquired);
|
||||
hasAcquired = false;
|
||||
_dbg_assert_(!skipSwap);
|
||||
|
||||
VkSwapchainKHR swapchain = vulkan->GetSwapchain();
|
||||
VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
|
||||
present.swapchainCount = 1;
|
||||
present.pSwapchains = &swapchain;
|
||||
present.pImageIndices = &curSwapchainImage;
|
||||
present.pWaitSemaphores = &shared.renderingCompleteSemaphore;
|
||||
present.waitSemaphoreCount = 1;
|
||||
|
||||
return vkQueuePresentKHR(vulkan->GetGraphicsQueue(), &present);
|
||||
}
|
||||
|
||||
VkCommandBuffer FrameData::GetInitCmd(VulkanContext *vulkan) {
|
||||
if (!hasInitCommands) {
|
||||
VkCommandBufferBeginInfo begin = {
|
||||
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
nullptr,
|
||||
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
|
||||
};
|
||||
vkResetCommandPool(vulkan->GetDevice(), cmdPoolInit, 0);
|
||||
VkResult res = vkBeginCommandBuffer(initCmd, &begin);
|
||||
if (res != VK_SUCCESS) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// Good spot to reset the query pool.
|
||||
vkCmdResetQueryPool(initCmd, profile.queryPool, 0, MAX_TIMESTAMP_QUERIES);
|
||||
vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, profile.queryPool, 0);
|
||||
|
||||
hasInitCommands = true;
|
||||
}
|
||||
return initCmd;
|
||||
}
|
||||
|
||||
void FrameData::SubmitPending(VulkanContext *vulkan, FrameSubmitType type, FrameDataShared &sharedData) {
|
||||
VkCommandBuffer cmdBufs[2];
|
||||
int numCmdBufs = 0;
|
||||
|
||||
VkFence fenceToTrigger = VK_NULL_HANDLE;
|
||||
|
||||
if (hasInitCommands) {
|
||||
if (profilingEnabled_) {
|
||||
// Pre-allocated query ID 1 - end of init cmdbuf.
|
||||
vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, profile.queryPool, 1);
|
||||
}
|
||||
|
||||
VkResult res = vkEndCommandBuffer(initCmd);
|
||||
cmdBufs[numCmdBufs++] = initCmd;
|
||||
|
||||
_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (init)! result=%s", VulkanResultToString(res));
|
||||
hasInitCommands = false;
|
||||
}
|
||||
|
||||
if ((hasMainCommands || hasPresentCommands) && type == FrameSubmitType::Sync) {
|
||||
fenceToTrigger = readbackFence;
|
||||
}
|
||||
|
||||
if (hasMainCommands) {
|
||||
VkResult res = vkEndCommandBuffer(mainCmd);
|
||||
_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (main)! result=%s", VulkanResultToString(res));
|
||||
|
||||
cmdBufs[numCmdBufs++] = mainCmd;
|
||||
hasMainCommands = false;
|
||||
}
|
||||
|
||||
if (hasPresentCommands && type != FrameSubmitType::Pending) {
|
||||
VkResult res = vkEndCommandBuffer(presentCmd);
|
||||
_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (present)! result=%s", VulkanResultToString(res));
|
||||
|
||||
cmdBufs[numCmdBufs++] = presentCmd;
|
||||
hasPresentCommands = false;
|
||||
|
||||
if (type == FrameSubmitType::Present) {
|
||||
fenceToTrigger = fence;
|
||||
}
|
||||
}
|
||||
|
||||
if (!numCmdBufs && fenceToTrigger == VK_NULL_HANDLE) {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO };
|
||||
VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
|
||||
if (type == FrameSubmitType::Present && !skipSwap) {
|
||||
_dbg_assert_(hasAcquired);
|
||||
submit_info.waitSemaphoreCount = 1;
|
||||
submit_info.pWaitSemaphores = &sharedData.acquireSemaphore;
|
||||
submit_info.pWaitDstStageMask = waitStage;
|
||||
}
|
||||
submit_info.commandBufferCount = (uint32_t)numCmdBufs;
|
||||
submit_info.pCommandBuffers = cmdBufs;
|
||||
if (type == FrameSubmitType::Present && !skipSwap) {
|
||||
submit_info.signalSemaphoreCount = 1;
|
||||
submit_info.pSignalSemaphores = &sharedData.renderingCompleteSemaphore;
|
||||
}
|
||||
|
||||
VkResult res;
|
||||
if (fenceToTrigger == fence) {
|
||||
// The fence is waited on by the main thread, they are not allowed to access it simultaneously.
|
||||
res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger);
|
||||
std::lock_guard<std::mutex> lock(fenceMutex);
|
||||
readyForFence = true;
|
||||
fenceCondVar.notify_one();
|
||||
} else {
|
||||
res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger);
|
||||
}
|
||||
|
||||
if (res == VK_ERROR_DEVICE_LOST) {
|
||||
_assert_msg_(false, "Lost the Vulkan device in vkQueueSubmit! If this happens again, switch Graphics Backend away from Vulkan");
|
||||
} else {
|
||||
_assert_msg_(res == VK_SUCCESS, "vkQueueSubmit failed (main)! result=%s", VulkanResultToString(res));
|
||||
}
|
||||
|
||||
if (type == FrameSubmitType::Sync) {
|
||||
// Hard stall of the GPU, not ideal, but necessary so the CPU has the contents of the readback.
|
||||
vkWaitForFences(vulkan->GetDevice(), 1, &readbackFence, true, UINT64_MAX);
|
||||
vkResetFences(vulkan->GetDevice(), 1, &readbackFence);
|
||||
syncDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
void FrameDataShared::Init(VulkanContext *vulkan) {
|
||||
VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
|
||||
semaphoreCreateInfo.flags = 0;
|
||||
VkResult res = vkCreateSemaphore(vulkan->GetDevice(), &semaphoreCreateInfo, nullptr, &acquireSemaphore);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
res = vkCreateSemaphore(vulkan->GetDevice(), &semaphoreCreateInfo, nullptr, &renderingCompleteSemaphore);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
void FrameDataShared::Destroy(VulkanContext *vulkan) {
|
||||
VkDevice device = vulkan->GetDevice();
|
||||
vkDestroySemaphore(device, acquireSemaphore, nullptr);
|
||||
vkDestroySemaphore(device, renderingCompleteSemaphore, nullptr);
|
||||
}
|
93
Common/GPU/Vulkan/VulkanFrameData.h
Normal file
93
Common/GPU/Vulkan/VulkanFrameData.h
Normal file
@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "Common/GPU/Vulkan/VulkanContext.h"
|
||||
|
||||
enum {
|
||||
MAX_TIMESTAMP_QUERIES = 128,
|
||||
};
|
||||
|
||||
enum class VKRRunType {
|
||||
PRESENT,
|
||||
SYNC,
|
||||
EXIT,
|
||||
};
|
||||
|
||||
struct QueueProfileContext {
|
||||
VkQueryPool queryPool;
|
||||
std::vector<std::string> timestampDescriptions;
|
||||
std::string profileSummary;
|
||||
double cpuStartTime;
|
||||
double cpuEndTime;
|
||||
};
|
||||
|
||||
struct FrameDataShared {
|
||||
// Permanent objects
|
||||
VkSemaphore acquireSemaphore = VK_NULL_HANDLE;
|
||||
VkSemaphore renderingCompleteSemaphore = VK_NULL_HANDLE;
|
||||
|
||||
void Init(VulkanContext *vulkan);
|
||||
void Destroy(VulkanContext *vulkan);
|
||||
};
|
||||
|
||||
enum class FrameSubmitType {
|
||||
Pending,
|
||||
Sync,
|
||||
Present,
|
||||
};
|
||||
|
||||
// Per-frame data, round-robin so we can overlap submission with execution of the previous frame.
|
||||
struct FrameData {
|
||||
bool skipSwap = false;
|
||||
|
||||
std::mutex fenceMutex;
|
||||
std::condition_variable fenceCondVar;
|
||||
bool readyForFence = true;
|
||||
|
||||
VkFence fence = VK_NULL_HANDLE;
|
||||
VkFence readbackFence = VK_NULL_HANDLE; // Strictly speaking we might only need one global of these.
|
||||
|
||||
// These are on different threads so need separate pools.
|
||||
VkCommandPool cmdPoolInit = VK_NULL_HANDLE; // Written to from main thread
|
||||
VkCommandPool cmdPoolMain = VK_NULL_HANDLE; // Written to from render thread, which also submits
|
||||
|
||||
VkCommandBuffer initCmd = VK_NULL_HANDLE;
|
||||
VkCommandBuffer mainCmd = VK_NULL_HANDLE;
|
||||
VkCommandBuffer presentCmd = VK_NULL_HANDLE;
|
||||
|
||||
bool hasInitCommands = false;
|
||||
bool hasMainCommands = false;
|
||||
bool hasPresentCommands = false;
|
||||
|
||||
bool hasFencePending = false;
|
||||
bool hasAcquired = false;
|
||||
|
||||
bool syncDone = false;
|
||||
|
||||
// Swapchain.
|
||||
uint32_t curSwapchainImage = -1;
|
||||
|
||||
// Profiling.
|
||||
QueueProfileContext profile;
|
||||
bool profilingEnabled_ = false;
|
||||
|
||||
void Init(VulkanContext *vulkan, int index);
|
||||
void Destroy(VulkanContext *vulkan);
|
||||
|
||||
void AcquireNextImage(VulkanContext *vulkan, FrameDataShared &shared);
|
||||
VkResult QueuePresent(VulkanContext *vulkan, FrameDataShared &shared);
|
||||
|
||||
// Generally called from the main thread, unlike most of the rest.
|
||||
VkCommandBuffer GetInitCmd(VulkanContext *vulkan);
|
||||
|
||||
// This will only submit if we are actually recording init commands.
|
||||
void SubmitPending(VulkanContext *vulkan, FrameSubmitType type, FrameDataShared &shared);
|
||||
|
||||
private:
|
||||
// Metadata for logging etc
|
||||
int index;
|
||||
};
|
@ -241,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.
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
|
@ -30,13 +30,14 @@ static void MergeRenderAreaRectInto(VkRect2D *dest, VkRect2D &src) {
|
||||
// We need to take the "max" of the features used in the two render passes.
|
||||
RenderPassType MergeRPTypes(RenderPassType a, RenderPassType b) {
|
||||
// Either both are backbuffer type, or neither are.
|
||||
_dbg_assert_((a == RP_TYPE_BACKBUFFER) == (b == RP_TYPE_BACKBUFFER));
|
||||
if (a == b) {
|
||||
// Trivial merging case.
|
||||
// These can't merge with other renderpasses
|
||||
if (a == RP_TYPE_BACKBUFFER || b == RP_TYPE_BACKBUFFER) {
|
||||
_dbg_assert_(a == b);
|
||||
return a;
|
||||
}
|
||||
// More cases to be added later.
|
||||
return a;
|
||||
|
||||
// The rest we can just OR together to get the maximum feature set.
|
||||
return (RenderPassType)((u32)a | (u32)b);
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::CreateDeviceObjects() {
|
||||
@ -138,6 +139,171 @@ void VulkanQueueRunner::DestroyDeviceObjects() {
|
||||
renderPasses_.Clear();
|
||||
}
|
||||
|
||||
bool VulkanQueueRunner::CreateSwapchain(VkCommandBuffer cmdInit) {
|
||||
VkResult res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, nullptr);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
|
||||
VkImage *swapchainImages = new VkImage[swapchainImageCount_];
|
||||
res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, swapchainImages);
|
||||
if (res != VK_SUCCESS) {
|
||||
ERROR_LOG(G3D, "vkGetSwapchainImagesKHR failed");
|
||||
delete[] swapchainImages;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < swapchainImageCount_; i++) {
|
||||
SwapchainImageData sc_buffer{};
|
||||
sc_buffer.image = swapchainImages[i];
|
||||
|
||||
VkImageViewCreateInfo color_image_view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
|
||||
color_image_view.format = vulkan_->GetSwapchainFormat();
|
||||
color_image_view.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
color_image_view.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
color_image_view.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
color_image_view.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
color_image_view.subresourceRange.baseMipLevel = 0;
|
||||
color_image_view.subresourceRange.levelCount = 1;
|
||||
color_image_view.subresourceRange.baseArrayLayer = 0;
|
||||
color_image_view.subresourceRange.layerCount = 1;
|
||||
color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
color_image_view.flags = 0;
|
||||
color_image_view.image = sc_buffer.image;
|
||||
|
||||
// We leave the images as UNDEFINED, there's no need to pre-transition them as
|
||||
// the backbuffer renderpass starts out with them being auto-transitioned from UNDEFINED anyway.
|
||||
// Also, turns out it's illegal to transition un-acquired images, thanks Hans-Kristian. See #11417.
|
||||
|
||||
res = vkCreateImageView(vulkan_->GetDevice(), &color_image_view, nullptr, &sc_buffer.view);
|
||||
swapchainImages_.push_back(sc_buffer);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
}
|
||||
delete[] swapchainImages;
|
||||
|
||||
// Must be before InitBackbufferRenderPass.
|
||||
if (InitDepthStencilBuffer(cmdInit)) {
|
||||
InitBackbufferFramebuffers(vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool VulkanQueueRunner::InitBackbufferFramebuffers(int width, int height) {
|
||||
VkResult res;
|
||||
// We share the same depth buffer but have multiple color buffers, see the loop below.
|
||||
VkImageView attachments[2] = { VK_NULL_HANDLE, depth_.view };
|
||||
|
||||
VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
|
||||
fb_info.renderPass = GetCompatibleRenderPass()->Get(vulkan_, RP_TYPE_BACKBUFFER);
|
||||
fb_info.attachmentCount = 2;
|
||||
fb_info.pAttachments = attachments;
|
||||
fb_info.width = width;
|
||||
fb_info.height = height;
|
||||
fb_info.layers = 1;
|
||||
|
||||
framebuffers_.resize(swapchainImageCount_);
|
||||
|
||||
for (uint32_t i = 0; i < swapchainImageCount_; i++) {
|
||||
attachments[0] = swapchainImages_[i].view;
|
||||
res = vkCreateFramebuffer(vulkan_->GetDevice(), &fb_info, nullptr, &framebuffers_[i]);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
if (res != VK_SUCCESS) {
|
||||
framebuffers_.clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanQueueRunner::InitDepthStencilBuffer(VkCommandBuffer cmd) {
|
||||
const VkFormat depth_format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat;
|
||||
int aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
VkImageCreateInfo image_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
||||
image_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
image_info.format = depth_format;
|
||||
image_info.extent.width = vulkan_->GetBackbufferWidth();
|
||||
image_info.extent.height = vulkan_->GetBackbufferHeight();
|
||||
image_info.extent.depth = 1;
|
||||
image_info.mipLevels = 1;
|
||||
image_info.arrayLayers = 1;
|
||||
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
image_info.queueFamilyIndexCount = 0;
|
||||
image_info.pQueueFamilyIndices = nullptr;
|
||||
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
image_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||
image_info.flags = 0;
|
||||
|
||||
depth_.format = depth_format;
|
||||
|
||||
VmaAllocationCreateInfo allocCreateInfo{};
|
||||
VmaAllocationInfo allocInfo{};
|
||||
|
||||
allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
|
||||
|
||||
VkResult res = vmaCreateImage(vulkan_->Allocator(), &image_info, &allocCreateInfo, &depth_.image, &depth_.alloc, &allocInfo);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
if (res != VK_SUCCESS)
|
||||
return false;
|
||||
|
||||
vulkan_->SetDebugName(depth_.image, VK_OBJECT_TYPE_IMAGE, "BackbufferDepth");
|
||||
|
||||
TransitionImageLayout2(cmd, depth_.image, 0, 1,
|
||||
aspectMask,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
||||
0, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT);
|
||||
|
||||
VkImageViewCreateInfo depth_view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
|
||||
depth_view_info.image = depth_.image;
|
||||
depth_view_info.format = depth_format;
|
||||
depth_view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
depth_view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
depth_view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
depth_view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
depth_view_info.subresourceRange.aspectMask = aspectMask;
|
||||
depth_view_info.subresourceRange.baseMipLevel = 0;
|
||||
depth_view_info.subresourceRange.levelCount = 1;
|
||||
depth_view_info.subresourceRange.baseArrayLayer = 0;
|
||||
depth_view_info.subresourceRange.layerCount = 1;
|
||||
depth_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
depth_view_info.flags = 0;
|
||||
|
||||
VkDevice device = vulkan_->GetDevice();
|
||||
|
||||
res = vkCreateImageView(device, &depth_view_info, NULL, &depth_.view);
|
||||
_dbg_assert_(res == VK_SUCCESS);
|
||||
if (res != VK_SUCCESS)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void VulkanQueueRunner::DestroyBackBuffers() {
|
||||
for (auto &image : swapchainImages_) {
|
||||
vulkan_->Delete().QueueDeleteImageView(image.view);
|
||||
}
|
||||
swapchainImages_.clear();
|
||||
|
||||
if (depth_.view) {
|
||||
vulkan_->Delete().QueueDeleteImageView(depth_.view);
|
||||
}
|
||||
if (depth_.image) {
|
||||
_dbg_assert_(depth_.alloc);
|
||||
vulkan_->Delete().QueueDeleteImageAllocation(depth_.image, depth_.alloc);
|
||||
}
|
||||
depth_ = {};
|
||||
for (uint32_t i = 0; i < framebuffers_.size(); i++) {
|
||||
_dbg_assert_(framebuffers_[i] != VK_NULL_HANDLE);
|
||||
vulkan_->Delete().QueueDeleteFramebuffer(framebuffers_[i]);
|
||||
}
|
||||
framebuffers_.clear();
|
||||
|
||||
INFO_LOG(G3D, "Backbuffers destroyed");
|
||||
}
|
||||
|
||||
static VkAttachmentLoadOp ConvertLoadAction(VKRRenderPassLoadAction action) {
|
||||
switch (action) {
|
||||
case VKRRenderPassLoadAction::CLEAR: return VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
@ -155,31 +321,40 @@ static VkAttachmentStoreOp ConvertStoreAction(VKRRenderPassStoreAction action) {
|
||||
return VK_ATTACHMENT_STORE_OP_DONT_CARE; // avoid compiler warning
|
||||
}
|
||||
|
||||
VkRenderPass CreateRP(VulkanContext *vulkan, const RPKey &key, RenderPassType rpType) {
|
||||
// Self-dependency: https://github.com/gpuweb/gpuweb/issues/442#issuecomment-547604827
|
||||
// Also see https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#synchronization-pipeline-barriers-subpass-self-dependencies
|
||||
|
||||
VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPassType rpType) {
|
||||
bool selfDependency = rpType == RP_TYPE_COLOR_INPUT || rpType == RP_TYPE_COLOR_DEPTH_INPUT;
|
||||
bool isBackbuffer = rpType == RP_TYPE_BACKBUFFER;
|
||||
bool hasDepth = rpType == RP_TYPE_BACKBUFFER || rpType == RP_TYPE_COLOR_DEPTH || rpType == RP_TYPE_COLOR_DEPTH_INPUT;
|
||||
|
||||
VkAttachmentDescription attachments[2] = {};
|
||||
attachments[0].format = rpType == RP_TYPE_BACKBUFFER ? vulkan->GetSwapchainFormat() : VK_FORMAT_R8G8B8A8_UNORM;
|
||||
attachments[0].format = isBackbuffer ? vulkan->GetSwapchainFormat() : VK_FORMAT_R8G8B8A8_UNORM;
|
||||
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachments[0].loadOp = ConvertLoadAction(key.colorLoadAction);
|
||||
attachments[0].storeOp = ConvertStoreAction(key.colorStoreAction);
|
||||
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachments[0].initialLayout = rpType == RP_TYPE_BACKBUFFER ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attachments[0].finalLayout = rpType == RP_TYPE_BACKBUFFER ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attachments[0].initialLayout = isBackbuffer ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attachments[0].finalLayout = isBackbuffer ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attachments[0].flags = 0;
|
||||
|
||||
attachments[1].format = vulkan->GetDeviceInfo().preferredDepthStencilFormat;
|
||||
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachments[1].loadOp = ConvertLoadAction(key.depthLoadAction);
|
||||
attachments[1].storeOp = ConvertStoreAction(key.depthStoreAction);
|
||||
attachments[1].stencilLoadOp = ConvertLoadAction(key.stencilLoadAction);
|
||||
attachments[1].stencilStoreOp = ConvertStoreAction(key.stencilStoreAction);
|
||||
attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
attachments[1].flags = 0;
|
||||
if (hasDepth) {
|
||||
attachments[1].format = vulkan->GetDeviceInfo().preferredDepthStencilFormat;
|
||||
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachments[1].loadOp = ConvertLoadAction(key.depthLoadAction);
|
||||
attachments[1].storeOp = ConvertStoreAction(key.depthStoreAction);
|
||||
attachments[1].stencilLoadOp = ConvertLoadAction(key.stencilLoadAction);
|
||||
attachments[1].stencilStoreOp = ConvertStoreAction(key.stencilStoreAction);
|
||||
attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
attachments[1].flags = 0;
|
||||
}
|
||||
|
||||
VkAttachmentReference color_reference{};
|
||||
color_reference.attachment = 0;
|
||||
color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
color_reference.layout = selfDependency ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkAttachmentReference depth_reference{};
|
||||
depth_reference.attachment = 1;
|
||||
@ -188,32 +363,56 @@ VkRenderPass CreateRP(VulkanContext *vulkan, const RPKey &key, RenderPassType rp
|
||||
VkSubpassDescription subpass{};
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.flags = 0;
|
||||
subpass.inputAttachmentCount = 0;
|
||||
subpass.pInputAttachments = nullptr;
|
||||
if (selfDependency) {
|
||||
subpass.inputAttachmentCount = 1;
|
||||
subpass.pInputAttachments = &color_reference;
|
||||
} else {
|
||||
subpass.inputAttachmentCount = 0;
|
||||
subpass.pInputAttachments = nullptr;
|
||||
}
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &color_reference;
|
||||
subpass.pResolveAttachments = nullptr;
|
||||
subpass.pDepthStencilAttachment = &depth_reference;
|
||||
if (hasDepth) {
|
||||
subpass.pDepthStencilAttachment = &depth_reference;
|
||||
}
|
||||
subpass.preserveAttachmentCount = 0;
|
||||
subpass.pPreserveAttachments = nullptr;
|
||||
|
||||
// Not sure if this is really necessary.
|
||||
VkSubpassDependency dep{};
|
||||
dep.srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
dep.dstSubpass = 0;
|
||||
dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dep.srcAccessMask = 0;
|
||||
dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
VkSubpassDependency deps[2]{};
|
||||
size_t numDeps = 0;
|
||||
|
||||
VkRenderPassCreateInfo rp{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
|
||||
rp.attachmentCount = 2;
|
||||
rp.attachmentCount = hasDepth ? 2 : 1;
|
||||
rp.pAttachments = attachments;
|
||||
rp.subpassCount = 1;
|
||||
rp.pSubpasses = &subpass;
|
||||
if (rpType == RP_TYPE_BACKBUFFER) {
|
||||
rp.dependencyCount = 1;
|
||||
rp.pDependencies = &dep;
|
||||
|
||||
if (isBackbuffer) {
|
||||
deps[numDeps].srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
deps[numDeps].dstSubpass = 0;
|
||||
deps[numDeps].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
deps[numDeps].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
deps[numDeps].srcAccessMask = 0;
|
||||
deps[numDeps].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
numDeps++;
|
||||
}
|
||||
|
||||
if (selfDependency) {
|
||||
deps[numDeps].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
|
||||
deps[numDeps].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
deps[numDeps].dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
|
||||
deps[numDeps].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
deps[numDeps].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
deps[numDeps].srcSubpass = 0;
|
||||
deps[numDeps].dstSubpass = 0;
|
||||
numDeps++;
|
||||
}
|
||||
|
||||
if (numDeps > 0) {
|
||||
rp.dependencyCount = (u32)numDeps;
|
||||
rp.pDependencies = deps;
|
||||
}
|
||||
|
||||
VkRenderPass pass;
|
||||
@ -228,7 +427,7 @@ VkRenderPass VKRRenderPass::Get(VulkanContext *vulkan, RenderPassType rpType) {
|
||||
// practical later when referring to it. Could change to on-demand if it feels motivated
|
||||
// but I think the render pass objects are cheap.
|
||||
if (!pass[(int)rpType]) {
|
||||
pass[(int)rpType] = CreateRP(vulkan, key_, (RenderPassType)rpType);
|
||||
pass[(int)rpType] = CreateRenderPass(vulkan, key_, (RenderPassType)rpType);
|
||||
}
|
||||
return pass[(int)rpType];
|
||||
}
|
||||
@ -246,6 +445,30 @@ VKRRenderPass *VulkanQueueRunner::GetRenderPass(const RPKey &key) {
|
||||
return pass;
|
||||
}
|
||||
|
||||
// Must match the subpass self-dependency declared above.
|
||||
void VulkanQueueRunner::SelfDependencyBarrier(VKRImage &img, VkImageAspectFlags aspect, VulkanBarrier *recordBarrier) {
|
||||
if (aspect & VK_IMAGE_ASPECT_COLOR_BIT) {
|
||||
VkAccessFlags srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
VkAccessFlags dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
|
||||
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
recordBarrier->TransitionImage(
|
||||
img.image,
|
||||
0,
|
||||
1,
|
||||
aspect,
|
||||
VK_IMAGE_LAYOUT_GENERAL,
|
||||
VK_IMAGE_LAYOUT_GENERAL,
|
||||
srcAccessMask,
|
||||
dstAccessMask,
|
||||
srcStageMask,
|
||||
dstStageMask
|
||||
);
|
||||
} else {
|
||||
_assert_msg_(false, "Depth self-dependencies not yet supported");
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::PreprocessSteps(std::vector<VKRStep *> &steps) {
|
||||
// Optimizes renderpasses, then sequences them.
|
||||
// Planned optimizations:
|
||||
@ -321,23 +544,54 @@ void VulkanQueueRunner::PreprocessSteps(std::vector<VKRStep *> &steps) {
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector<VKRStep *> &steps, QueueProfileContext *profile) {
|
||||
void VulkanQueueRunner::RunSteps(std::vector<VKRStep *> &steps, FrameData &frameData, FrameDataShared &frameDataShared) {
|
||||
QueueProfileContext *profile = frameData.profilingEnabled_ ? &frameData.profile : nullptr;
|
||||
|
||||
if (profile)
|
||||
profile->cpuStartTime = time_now_d();
|
||||
|
||||
bool emitLabels = vulkan_->Extensions().EXT_debug_utils;
|
||||
|
||||
VkCommandBuffer cmd = frameData.hasPresentCommands ? frameData.presentCmd : frameData.mainCmd;
|
||||
|
||||
for (size_t i = 0; i < steps.size(); i++) {
|
||||
const VKRStep &step = *steps[i];
|
||||
|
||||
if (emitLabels) {
|
||||
VkDebugUtilsLabelEXT labelInfo{ VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT };
|
||||
labelInfo.pLabelName = step.tag;
|
||||
vkCmdBeginDebugUtilsLabelEXT(cmd, &labelInfo);
|
||||
char temp[128];
|
||||
if (step.stepType == VKRStepType::RENDER && step.render.framebuffer) {
|
||||
snprintf(temp, sizeof(temp), "%s: %s", step.tag, step.render.framebuffer->Tag());
|
||||
labelInfo.pLabelName = temp;
|
||||
} else {
|
||||
labelInfo.pLabelName = step.tag;
|
||||
}
|
||||
vkCmdBeginDebugUtilsLabelEXT(frameData.mainCmd, &labelInfo);
|
||||
}
|
||||
|
||||
switch (step.stepType) {
|
||||
case VKRStepType::RENDER:
|
||||
if (!step.render.framebuffer) {
|
||||
frameData.SubmitPending(vulkan_, FrameSubmitType::Pending, frameDataShared);
|
||||
|
||||
// When stepping in the GE debugger, we can end up here multiple times in a "frame".
|
||||
// So only acquire once.
|
||||
if (!frameData.hasAcquired) {
|
||||
frameData.AcquireNextImage(vulkan_, frameDataShared);
|
||||
SetBackbuffer(framebuffers_[frameData.curSwapchainImage], swapchainImages_[frameData.curSwapchainImage].image);
|
||||
}
|
||||
|
||||
if (!frameData.hasPresentCommands) {
|
||||
// A RENDER step rendering to the backbuffer is normally the last step that happens in a frame,
|
||||
// unless taking a screenshot, in which case there might be a READBACK_IMAGE after it.
|
||||
// This is why we have to switch cmd to presentCmd, in this case.
|
||||
VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
||||
begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||
vkBeginCommandBuffer(frameData.presentCmd, &begin);
|
||||
frameData.hasPresentCommands = true;
|
||||
}
|
||||
cmd = frameData.presentCmd;
|
||||
}
|
||||
PerformRenderPass(step, cmd);
|
||||
break;
|
||||
case VKRStepType::COPY:
|
||||
@ -368,10 +622,12 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector<VKRStep *> &st
|
||||
|
||||
// Deleting all in one go should be easier on the instruction cache than deleting
|
||||
// them as we go - and easier to debug because we can look backwards in the frame.
|
||||
for (size_t i = 0; i < steps.size(); i++) {
|
||||
delete steps[i];
|
||||
for (auto step : steps) {
|
||||
delete step;
|
||||
}
|
||||
|
||||
steps.clear();
|
||||
|
||||
if (profile)
|
||||
profile->cpuEndTime = time_now_d();
|
||||
}
|
||||
@ -627,10 +883,13 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const {
|
||||
const char *renderCmd;
|
||||
switch (step.render.renderPassType) {
|
||||
case RP_TYPE_BACKBUFFER: renderCmd = "BACKBUF"; break;
|
||||
case RP_TYPE_COLOR_DEPTH: renderCmd = "RENDER"; break;
|
||||
case RP_TYPE_COLOR: renderCmd = "RENDER"; break;
|
||||
case RP_TYPE_COLOR_DEPTH: renderCmd = "RENDER_DEPTH"; break;
|
||||
case RP_TYPE_COLOR_INPUT: renderCmd = "RENDER_INPUT"; break;
|
||||
case RP_TYPE_COLOR_DEPTH_INPUT: renderCmd = "RENDER_DEPTH_INPUT"; break;
|
||||
default: renderCmd = "N/A";
|
||||
}
|
||||
snprintf(buffer, sizeof(buffer), "%s %s (draws: %d, %dx%d/%dx%d, fb: %p, )", renderCmd, step.tag, step.render.numDraws, actual_w, actual_h, w, h, step.render.framebuffer);
|
||||
snprintf(buffer, sizeof(buffer), "%s %s %s (draws: %d, %dx%d/%dx%d)", renderCmd, step.tag, step.render.framebuffer ? step.render.framebuffer->Tag() : "", step.render.numDraws, actual_w, actual_h, w, h);
|
||||
break;
|
||||
}
|
||||
case VKRStepType::COPY:
|
||||
@ -640,7 +899,7 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const {
|
||||
snprintf(buffer, sizeof(buffer), "BLIT '%s' %s -> %s (%dx%d->%dx%d, %s)", step.tag, step.copy.src->Tag(), step.copy.dst->Tag(), step.blit.srcRect.extent.width, step.blit.srcRect.extent.height, step.blit.dstRect.extent.width, step.blit.dstRect.extent.height, AspectToString(step.blit.aspectMask));
|
||||
break;
|
||||
case VKRStepType::READBACK:
|
||||
snprintf(buffer, sizeof(buffer), "READBACK '%s' %s (%dx%d, %s)", step.tag, step.readback.src->Tag(), step.readback.srcRect.extent.width, step.readback.srcRect.extent.height, AspectToString(step.readback.aspectMask));
|
||||
snprintf(buffer, sizeof(buffer), "READBACK '%s' %s (%dx%d, %s)", step.tag, step.readback.src ? step.readback.src->Tag() : "(backbuffer)", step.readback.srcRect.extent.width, step.readback.srcRect.extent.height, AspectToString(step.readback.aspectMask));
|
||||
break;
|
||||
case VKRStepType::READBACK_IMAGE:
|
||||
snprintf(buffer, sizeof(buffer), "READBACK_IMAGE '%s' (%dx%d)", step.tag, step.readback_image.srcRect.extent.width, step.readback_image.srcRect.extent.height);
|
||||
@ -817,6 +1076,9 @@ void VulkanQueueRunner::LogRenderPass(const VKRStep &pass, bool verbose) {
|
||||
case VKRRenderCommand::REMOVED:
|
||||
INFO_LOG(G3D, " (Removed)");
|
||||
break;
|
||||
case VKRRenderCommand::SELF_DEPENDENCY_BARRIER:
|
||||
INFO_LOG(G3D, " SelfBarrier()");
|
||||
break;
|
||||
case VKRRenderCommand::BIND_GRAPHICS_PIPELINE:
|
||||
INFO_LOG(G3D, " BindGraphicsPipeline(%x)", (int)(intptr_t)cmd.graphics_pipeline.pipeline);
|
||||
break;
|
||||
@ -903,7 +1165,7 @@ void TransitionToOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayout
|
||||
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
break;
|
||||
default:
|
||||
_dbg_assert_msg_(false, "GetRenderPass: Unexpected color layout %d", (int)colorLayout);
|
||||
_dbg_assert_msg_(false, "TransitionToOptimal: Unexpected color layout %d", (int)colorLayout);
|
||||
break;
|
||||
}
|
||||
recordBarrier->TransitionImage(
|
||||
@ -939,7 +1201,7 @@ void TransitionToOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayout
|
||||
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
break;
|
||||
default:
|
||||
_dbg_assert_msg_(false, "GetRenderPass: Unexpected depth layout %d", (int)depthStencilLayout);
|
||||
_dbg_assert_msg_(false, "TransitionToOptimal: Unexpected depth layout %d", (int)depthStencilLayout);
|
||||
break;
|
||||
}
|
||||
recordBarrier->TransitionImage(
|
||||
@ -986,7 +1248,7 @@ void TransitionFromOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayou
|
||||
// Nothing to do.
|
||||
break;
|
||||
default:
|
||||
_dbg_assert_msg_(false, "GetRenderPass: Unexpected final color layout %d", (int)colorLayout);
|
||||
_dbg_assert_msg_(false, "TransitionFromOptimal: Unexpected final color layout %d", (int)colorLayout);
|
||||
break;
|
||||
}
|
||||
barrier[0].oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
@ -1025,7 +1287,7 @@ void TransitionFromOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayou
|
||||
// Nothing to do.
|
||||
break;
|
||||
default:
|
||||
_dbg_assert_msg_(false, "GetRenderPass: Unexpected final depth layout %d", (int)depthStencilLayout);
|
||||
_dbg_assert_msg_(false, "TransitionFromOptimal: Unexpected final depth layout %d", (int)depthStencilLayout);
|
||||
break;
|
||||
}
|
||||
barrier[barrierCount].oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
@ -1057,7 +1319,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
iter.targetLayout
|
||||
);
|
||||
iter.fb->color.layout = iter.targetLayout;
|
||||
} else if ((iter.aspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) && iter.fb->depth.layout != iter.targetLayout) {
|
||||
} else if (iter.fb->depth.image != VK_NULL_HANDLE && (iter.aspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) && iter.fb->depth.layout != iter.targetLayout) {
|
||||
recordBarrier_.TransitionImageAuto(
|
||||
iter.fb->depth.image,
|
||||
0,
|
||||
@ -1070,7 +1332,6 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Don't execute empty renderpasses that keep the contents.
|
||||
if (step.commands.empty() && step.render.colorLoad == VKRRenderPassLoadAction::KEEP && step.render.depthLoad == VKRRenderPassLoadAction::KEEP && step.render.stencilLoad == VKRRenderPassLoadAction::KEEP) {
|
||||
// Flush the pending barrier
|
||||
@ -1087,7 +1348,6 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
int n = 0;
|
||||
int stage = 0;
|
||||
|
||||
VkImageMemoryBarrier barriers[2]{};
|
||||
if (step.render.framebuffer->color.layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
|
||||
recordBarrier_.TransitionImage(
|
||||
step.render.framebuffer->color.image,
|
||||
@ -1102,7 +1362,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
|
||||
);
|
||||
}
|
||||
if (step.render.framebuffer->depth.layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
|
||||
if (step.render.framebuffer->depth.image != VK_NULL_HANDLE && step.render.framebuffer->depth.layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
|
||||
recordBarrier_.TransitionImage(
|
||||
step.render.framebuffer->depth.image,
|
||||
0,
|
||||
@ -1120,6 +1380,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
|
||||
// This reads the layout of the color and depth images, and chooses a render pass using them that
|
||||
// will transition to the desired final layout.
|
||||
//
|
||||
// NOTE: Flushes recordBarrier_.
|
||||
VKRRenderPass *renderPass = PerformBindFramebufferAsRenderTarget(step, cmd);
|
||||
|
||||
@ -1235,6 +1496,15 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
break;
|
||||
}
|
||||
|
||||
case VKRRenderCommand::SELF_DEPENDENCY_BARRIER:
|
||||
{
|
||||
_assert_(step.render.pipelineFlags & PipelineFlags::USES_INPUT_ATTACHMENT);
|
||||
VulkanBarrier barrier;
|
||||
SelfDependencyBarrier(step.render.framebuffer->color, VK_IMAGE_ASPECT_COLOR_BIT, &barrier);
|
||||
barrier.Flush(cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
case VKRRenderCommand::PUSH_CONSTANTS:
|
||||
vkCmdPushConstants(cmd, pipelineLayout, c.push.stages, c.push.offset, c.push.size, c.push.data);
|
||||
break;
|
||||
@ -1327,6 +1597,9 @@ VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKR
|
||||
VkFramebuffer framebuf;
|
||||
int w;
|
||||
int h;
|
||||
|
||||
bool hasDepth = RenderPassTypeHasDepth(step.render.renderPassType);
|
||||
|
||||
if (step.render.framebuffer) {
|
||||
_dbg_assert_(step.render.finalColorLayout != VK_IMAGE_LAYOUT_UNDEFINED);
|
||||
_dbg_assert_(step.render.finalDepthStencilLayout != VK_IMAGE_LAYOUT_UNDEFINED);
|
||||
@ -1367,7 +1640,7 @@ VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKR
|
||||
Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor);
|
||||
numClearVals = 1;
|
||||
}
|
||||
if (step.render.depthLoad == VKRRenderPassLoadAction::CLEAR || step.render.stencilLoad == VKRRenderPassLoadAction::CLEAR) {
|
||||
if (hasDepth && (step.render.depthLoad == VKRRenderPassLoadAction::CLEAR || step.render.stencilLoad == VKRRenderPassLoadAction::CLEAR)) {
|
||||
clearVal[1].depthStencil.depth = step.render.clearDepth;
|
||||
clearVal[1].depthStencil.stencil = step.render.clearStencil;
|
||||
numClearVals = 2;
|
||||
@ -1386,7 +1659,7 @@ VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKR
|
||||
h = vulkan_->GetBackbufferHeight();
|
||||
|
||||
Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor);
|
||||
numClearVals = 2; // We don't bother with a depth buffer here.
|
||||
numClearVals = hasDepth ? 2 : 1; // We might do depth-less backbuffer in the future, though doubtful of the value.
|
||||
clearVal[1].depthStencil.depth = 0.0f;
|
||||
clearVal[1].depthStencil.stencil = 0;
|
||||
}
|
||||
@ -1457,6 +1730,10 @@ void VulkanQueueRunner::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) {
|
||||
if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
|
||||
SetupTransitionToTransferDst(dst->depth, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, &recordBarrier_);
|
||||
_dbg_assert_(dst->depth.layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
} else {
|
||||
// Kingdom Hearts: Subsequent copies to the same depth buffer without any other use.
|
||||
// Not super sure how that happens, but we need a barrier to pass sync validation.
|
||||
SetupTransferDstWriteAfterWrite(dst->depth, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, &recordBarrier_);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1468,6 +1745,8 @@ void VulkanQueueRunner::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) {
|
||||
vkCmdCopyImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, ©);
|
||||
}
|
||||
if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
|
||||
_dbg_assert_(src->depth.image != VK_NULL_HANDLE);
|
||||
_dbg_assert_(dst->depth.image != VK_NULL_HANDLE);
|
||||
copy.srcSubresource.aspectMask = step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
|
||||
copy.dstSubresource.aspectMask = step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
|
||||
vkCmdCopyImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, ©);
|
||||
@ -1478,9 +1757,6 @@ void VulkanQueueRunner::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) {
|
||||
// The barrier code doesn't handle this case. We'd need to transition to GENERAL to do an intra-image copy.
|
||||
_dbg_assert_(step.blit.src != step.blit.dst);
|
||||
|
||||
VkImageMemoryBarrier srcBarriers[2]{};
|
||||
VkImageMemoryBarrier dstBarriers[2]{};
|
||||
|
||||
VKRFramebuffer *src = step.blit.src;
|
||||
VKRFramebuffer *dst = step.blit.dst;
|
||||
|
||||
@ -1522,6 +1798,9 @@ void VulkanQueueRunner::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) {
|
||||
|
||||
// We can't copy only depth or only stencil unfortunately.
|
||||
if (step.blit.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
|
||||
_dbg_assert_(src->depth.image != VK_NULL_HANDLE);
|
||||
_dbg_assert_(dst->depth.image != VK_NULL_HANDLE);
|
||||
|
||||
if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) {
|
||||
SetupTransitionToTransferSrc(src->depth, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, &recordBarrier_);
|
||||
}
|
||||
@ -1652,6 +1931,33 @@ void VulkanQueueRunner::SetupTransitionToTransferDst(VKRImage &img, VkImageAspec
|
||||
img.layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::SetupTransferDstWriteAfterWrite(VKRImage &img, VkImageAspectFlags aspect, VulkanBarrier *recordBarrier) {
|
||||
VkImageAspectFlags imageAspect = aspect;
|
||||
VkAccessFlags srcAccessMask = 0;
|
||||
VkPipelineStageFlags srcStageMask = 0;
|
||||
if (img.format == VK_FORMAT_D16_UNORM_S8_UINT || img.format == VK_FORMAT_D24_UNORM_S8_UINT || img.format == VK_FORMAT_D32_SFLOAT_S8_UINT) {
|
||||
// Barrier must specify both for combined depth/stencil buffers.
|
||||
imageAspect = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
} else {
|
||||
imageAspect = aspect;
|
||||
}
|
||||
_dbg_assert_(img.layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
recordBarrier->TransitionImage(
|
||||
img.image,
|
||||
0,
|
||||
1,
|
||||
aspect,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT
|
||||
);
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd) {
|
||||
ResizeReadbackBuffer(sizeof(uint32_t) * step.readback.srcRect.extent.width * step.readback.srcRect.extent.height);
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#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,11 +17,11 @@ 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,
|
||||
};
|
||||
|
||||
@ -36,24 +37,43 @@ enum class VKRRenderCommand : uint8_t {
|
||||
DRAW,
|
||||
DRAW_INDEXED,
|
||||
PUSH_CONSTANTS,
|
||||
SELF_DEPENDENCY_BARRIER,
|
||||
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 {
|
||||
RP_TYPE_BACKBUFFER,
|
||||
// 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 {
|
||||
@ -144,14 +164,10 @@ struct TransitionRequest {
|
||||
VKRFramebuffer *fb;
|
||||
VkImageAspectFlags aspect; // COLOR or DEPTH
|
||||
VkImageLayout targetLayout;
|
||||
};
|
||||
|
||||
struct QueueProfileContext {
|
||||
VkQueryPool queryPool;
|
||||
std::vector<std::string> timestampDescriptions;
|
||||
std::string profileSummary;
|
||||
double cpuStartTime;
|
||||
double cpuEndTime;
|
||||
bool operator == (const TransitionRequest &other) const {
|
||||
return fb == other.fb && aspect == other.aspect && targetLayout == other.targetLayout;
|
||||
}
|
||||
};
|
||||
|
||||
class VKRRenderPass;
|
||||
@ -168,7 +184,6 @@ struct VKRStep {
|
||||
union {
|
||||
struct {
|
||||
VKRFramebuffer *framebuffer;
|
||||
// TODO: Look these up through renderPass?
|
||||
VKRRenderPassLoadAction colorLoad;
|
||||
VKRRenderPassLoadAction depthLoad;
|
||||
VKRRenderPassLoadAction stencilLoad;
|
||||
@ -183,7 +198,7 @@ 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.
|
||||
@ -245,6 +260,14 @@ private:
|
||||
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) {}
|
||||
@ -255,7 +278,7 @@ public:
|
||||
}
|
||||
|
||||
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;
|
||||
@ -263,6 +286,14 @@ public:
|
||||
void CreateDeviceObjects();
|
||||
void DestroyDeviceObjects();
|
||||
|
||||
// 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.
|
||||
VKRRenderPass *GetCompatibleRenderPass() const {
|
||||
@ -302,6 +333,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool InitBackbufferFramebuffers(int width, int height);
|
||||
bool InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering.
|
||||
|
||||
VKRRenderPass *PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
void PerformCopy(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
@ -323,6 +357,9 @@ 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_;
|
||||
|
||||
@ -354,4 +391,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
@ -43,20 +43,22 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int
|
||||
|
||||
class VKRFramebuffer {
|
||||
public:
|
||||
VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, 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 Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType);
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
VKRImage color{};
|
||||
VKRImage depth{};
|
||||
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_;
|
||||
private:
|
||||
@ -65,15 +67,6 @@ private:
|
||||
std::string tag_;
|
||||
};
|
||||
|
||||
enum class VKRRunType {
|
||||
END,
|
||||
SYNC,
|
||||
};
|
||||
|
||||
enum {
|
||||
MAX_TIMESTAMP_QUERIES = 128,
|
||||
};
|
||||
|
||||
struct BoundingRect {
|
||||
int x1;
|
||||
int y1;
|
||||
@ -135,6 +128,7 @@ struct VKRGraphicsPipelineDesc {
|
||||
// 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]{};
|
||||
@ -157,6 +151,12 @@ struct VKRComputePipelineDesc {
|
||||
|
||||
// Wrapped pipeline. Doesn't own desc.
|
||||
struct VKRGraphicsPipeline {
|
||||
~VKRGraphicsPipeline() {
|
||||
for (int i = 0; i < RP_TYPE_COUNT; i++) {
|
||||
delete pipeline[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType);
|
||||
|
||||
// This deletes the whole VKRGraphicsPipeline, you must remove your last pointer to it when doing this.
|
||||
@ -170,11 +170,12 @@ struct VKRGraphicsPipeline {
|
||||
};
|
||||
|
||||
struct VKRComputePipeline {
|
||||
VKRComputePipeline() {
|
||||
pipeline = VK_NULL_HANDLE;
|
||||
~VKRComputePipeline() {
|
||||
delete pipeline;
|
||||
}
|
||||
|
||||
VKRComputePipelineDesc *desc = nullptr;
|
||||
Promise<VkPipeline> *pipeline;
|
||||
Promise<VkPipeline> *pipeline = nullptr;
|
||||
|
||||
bool Create(VulkanContext *vulkan);
|
||||
bool Pending() const {
|
||||
@ -202,16 +203,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();
|
||||
|
||||
@ -236,6 +231,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);
|
||||
|
||||
@ -247,7 +244,7 @@ public:
|
||||
// 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, uint32_t variantBitmask, const char *tag);
|
||||
VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, PipelineFlags pipelineFlags, uint32_t variantBitmask, const char *tag);
|
||||
VKRComputePipeline *CreateComputePipeline(VKRComputePipelineDesc *desc);
|
||||
|
||||
void NudgeCompilerThread() {
|
||||
@ -440,11 +437,7 @@ public:
|
||||
void DestroyBackbuffers();
|
||||
|
||||
bool HasBackbuffers() {
|
||||
return !framebuffers_.empty();
|
||||
}
|
||||
|
||||
void SetSplitSubmit(bool split) {
|
||||
splitSubmit_ = split;
|
||||
return queueRunner_.HasBackbuffers();
|
||||
}
|
||||
|
||||
void SetInflightFrames(int f) {
|
||||
@ -470,57 +463,20 @@ public:
|
||||
}
|
||||
|
||||
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;
|
||||
@ -539,25 +495,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_;
|
||||
@ -568,23 +532,4 @@ private:
|
||||
|
||||
// pipelines to check and possibly create at the end of the current render pass.
|
||||
std::vector<VKRGraphicsPipeline *> pipelinesToCheck_;
|
||||
|
||||
// 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_;
|
||||
|
||||
// This works great - except see issue #10097. WTF?
|
||||
bool useThread_ = true;
|
||||
};
|
||||
|
@ -177,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
|
||||
@ -194,17 +194,19 @@ public:
|
||||
~VKShaderModule() {
|
||||
if (module_) {
|
||||
DEBUG_LOG(G3D, "Queueing %s (shmodule %p) for release", tag_.c_str(), module_);
|
||||
vulkan_->Delete().QueueDeleteShaderModule(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_;
|
||||
@ -213,8 +215,8 @@ 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;
|
||||
@ -232,7 +234,9 @@ bool VKShaderModule::Compile(VulkanContext *vulkan, ShaderLanguage language, con
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vulkan->CreateShaderModule(spirv, &module_, vkstage_ == VK_SHADER_STAGE_VERTEX_BIT ? "thin3d_vs" : "thin3d_fs")) {
|
||||
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 (%s)", tag_.c_str());
|
||||
@ -274,7 +278,6 @@ public:
|
||||
return buf->PushAligned(ubo_, uboSize_, vulkan->GetPhysicalDeviceProperties().properties.limits.minUniformBufferOffsetAlignment, vkbuf);
|
||||
}
|
||||
|
||||
int GetUniformLoc(const char *name);
|
||||
int GetUBOSize() const {
|
||||
return uboSize_;
|
||||
}
|
||||
@ -361,7 +364,7 @@ class VKFramebuffer;
|
||||
|
||||
class VKContext : public DrawContext {
|
||||
public:
|
||||
VKContext(VulkanContext *vulkan, bool splitSubmit);
|
||||
VKContext(VulkanContext *vulkan);
|
||||
virtual ~VKContext();
|
||||
|
||||
const DeviceCaps &GetDeviceCaps() const override {
|
||||
@ -401,9 +404,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;
|
||||
void BindCurrentFramebufferForColorInput() override;
|
||||
|
||||
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
|
||||
|
||||
@ -414,6 +418,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;
|
||||
@ -472,27 +477,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::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;
|
||||
|
||||
@ -521,7 +506,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_;
|
||||
@ -563,6 +548,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:
|
||||
@ -586,6 +576,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;
|
||||
@ -772,7 +765,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);
|
||||
|
||||
@ -798,9 +791,11 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
|
||||
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;
|
||||
@ -822,6 +817,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) {
|
||||
@ -831,19 +831,32 @@ 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.
|
||||
if (majorVersion <= 15) {
|
||||
bugs_.Infest(Bugs::GEOMETRY_SHADERS_SLOW);
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
@ -911,8 +924,6 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
|
||||
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() {
|
||||
@ -973,7 +984,7 @@ void VKContext::WipeQueue() {
|
||||
}
|
||||
|
||||
VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) {
|
||||
DescriptorSetKey key;
|
||||
DescriptorSetKey key{};
|
||||
|
||||
FrameData *frame = &frame_[vulkan_->GetCurFrame()];
|
||||
|
||||
@ -1049,12 +1060,13 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char
|
||||
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, tag);
|
||||
VKPipeline *pipeline = new VKPipeline(vulkan_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float), pipelineFlags, tag);
|
||||
|
||||
VKRGraphicsPipelineDesc &gDesc = pipeline->vkrDesc;
|
||||
|
||||
@ -1066,11 +1078,12 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char
|
||||
vkshader->AddRef();
|
||||
pipeline->deps.push_back(vkshader);
|
||||
if (vkshader->GetStage() == ShaderStage::Vertex) {
|
||||
gDesc.vertexShader = Promise<VkShaderModule>::AlreadyDone(vkshader->Get());
|
||||
gDesc.vertexShader = vkshader->Get();
|
||||
} else if (vkshader->GetStage() == ShaderStage::Fragment) {
|
||||
gDesc.fragmentShader = Promise<VkShaderModule>::AlreadyDone(vkshader->Get());
|
||||
gDesc.fragmentShader = vkshader->Get();
|
||||
} else {
|
||||
ERROR_LOG(G3D, "Bad stage");
|
||||
delete pipeline;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@ -1126,7 +1139,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char
|
||||
VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
|
||||
raster->ToVulkan(&gDesc.rs);
|
||||
|
||||
pipeline->pipeline = renderManager_.CreateGraphicsPipeline(&gDesc, 1 << RP_TYPE_BACKBUFFER, tag ? tag : "thin3d");
|
||||
pipeline->pipeline = renderManager_.CreateGraphicsPipeline(&gDesc, pipelineFlags, 1 << RP_TYPE_BACKBUFFER, tag ? tag : "thin3d");
|
||||
|
||||
if (desc.uniformDesc) {
|
||||
pipeline->dynamicUniformSize = (int)desc.uniformDesc->uniformBufferSize;
|
||||
@ -1284,6 +1297,11 @@ void VKContext::BindTextures(int start, int count, Texture **textures) {
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
@ -1295,17 +1313,6 @@ ShaderModule *VKContext::CreateShaderModule(ShaderStage stage, ShaderLanguage la
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@ -1387,8 +1394,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) {
|
||||
@ -1418,7 +1425,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;
|
||||
}
|
||||
@ -1477,15 +1484,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_.GetQueueRunner()->GetCompatibleRenderPass(), 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);
|
||||
}
|
||||
|
||||
@ -1570,6 +1578,10 @@ void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChanne
|
||||
boundImageView_[binding] = renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, attachment);
|
||||
}
|
||||
|
||||
void VKContext::BindCurrentFramebufferForColorInput() {
|
||||
renderManager_.BindCurrentFramebufferAsInputAttachment0(VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
}
|
||||
|
||||
void VKContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) {
|
||||
VKFramebuffer *fb = (VKFramebuffer *)fbo;
|
||||
if (fb) {
|
||||
@ -1610,4 +1622,28 @@ void VKContext::InvalidateFramebuffer(FBInvalidationStage stage, uint32_t channe
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Draw
|
||||
|
@ -33,6 +33,9 @@ size_t DataFormatSizeInBytes(DataFormat fmt) {
|
||||
case DataFormat::R8G8B8A8_SNORM: return 4;
|
||||
case DataFormat::R8G8B8A8_UINT: return 4;
|
||||
case DataFormat::R8G8B8A8_SINT: return 4;
|
||||
|
||||
case DataFormat::R16_UNORM: return 2;
|
||||
|
||||
case DataFormat::R16_FLOAT: return 2;
|
||||
case DataFormat::R16G16_FLOAT: return 4;
|
||||
case DataFormat::R16G16B16A16_FLOAT: return 8;
|
||||
@ -432,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; };
|
||||
@ -447,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 },
|
||||
@ -675,9 +680,9 @@ const char *Bugs::GetBugName(uint32_t bug) {
|
||||
case COLORWRITEMASK_BROKEN_WITH_DEPTHTEST: return "COLORWRITEMASK_BROKEN_WITH_DEPTHTEST";
|
||||
case BROKEN_FLAT_IN_SHADER: return "BROKEN_FLAT_IN_SHADER";
|
||||
case EQUAL_WZ_CORRUPTS_DEPTH: return "EQUAL_WZ_CORRUPTS_DEPTH";
|
||||
case MALI_STENCIL_DISCARD_BUG: return "MALI_STENCIL_DISCARD_BUG";
|
||||
case RASPBERRY_SHADER_COMP_HANG: return "RASPBERRY_SHADER_COMP_HANG";
|
||||
case MALI_CONSTANT_LOAD_BUG: return "MALI_CONSTANT_LOAD_BUG";
|
||||
case SUBPASS_FEEDBACK_BROKEN: return "SUBPASS_FEEDBACK_BROKEN";
|
||||
default: return "(N/A)";
|
||||
}
|
||||
}
|
||||
|
@ -114,6 +114,7 @@ enum BufferUsageFlag : int {
|
||||
enum Semantic : int {
|
||||
SEM_POSITION,
|
||||
SEM_COLOR0,
|
||||
SEM_COLOR1,
|
||||
SEM_TEXCOORD0,
|
||||
SEM_TEXCOORD1,
|
||||
SEM_NORMAL,
|
||||
@ -242,6 +243,7 @@ enum class NativeObject {
|
||||
INIT_COMMANDBUFFER,
|
||||
BOUND_TEXTURE0_IMAGEVIEW,
|
||||
BOUND_TEXTURE1_IMAGEVIEW,
|
||||
BOUND_FRAMEBUFFER_COLOR_IMAGEVIEW,
|
||||
RENDER_MANAGER,
|
||||
TEXTURE_VIEW,
|
||||
NULL_IMAGEVIEW,
|
||||
@ -328,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 = 11,
|
||||
MAX_BUG,
|
||||
};
|
||||
|
||||
protected:
|
||||
uint32_t flags_ = 0;
|
||||
|
||||
static_assert(sizeof(flags_) * 8 > MAX_BUG, "Ran out of space for bugs.");
|
||||
};
|
||||
|
||||
class RefCountedObject {
|
||||
@ -410,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;
|
||||
};
|
||||
@ -546,6 +552,7 @@ struct DeviceCaps {
|
||||
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.
|
||||
};
|
||||
@ -557,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;
|
||||
@ -650,6 +658,9 @@ 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;
|
||||
|
||||
// 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;
|
||||
@ -674,6 +685,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;
|
||||
|
@ -31,6 +31,6 @@ DrawContext *T3DCreateDX9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapt
|
||||
DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context, ID3D11Device1 *device1, ID3D11DeviceContext1 *context1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector<std::string> adapterNames);
|
||||
#endif
|
||||
|
||||
DrawContext *T3DCreateVulkanContext(VulkanContext *context, bool splitSubmit);
|
||||
DrawContext *T3DCreateVulkanContext(VulkanContext *context);
|
||||
|
||||
} // namespace Draw
|
||||
|
@ -35,7 +35,7 @@ bool ShouldLogNTimes(const char *identifier, int count) {
|
||||
std::lock_guard<std::mutex> lock(logNTimesLock);
|
||||
auto iter = logNTimes.find(identifier);
|
||||
if (iter == logNTimes.end()) {
|
||||
logNTimes.insert(std::pair<const char*, int>(identifier, 1));
|
||||
logNTimes.emplace(identifier, 1);
|
||||
return true;
|
||||
} else {
|
||||
if (iter->second >= count) {
|
||||
|
@ -628,7 +628,7 @@ void RiscVEmitter::FlushIcache() {
|
||||
|
||||
void RiscVEmitter::FlushIcacheSection(const u8 *start, const u8 *end) {
|
||||
#if PPSSPP_ARCH(RISCV64)
|
||||
__builtin___clear_cache(start, end);
|
||||
__builtin___clear_cache((void *)start, (void *)end);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -273,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;
|
||||
}
|
||||
@ -282,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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,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
|
||||
|
@ -79,7 +79,6 @@ public:
|
||||
|
||||
// Returns T if the data is ready, nullptr if it's not.
|
||||
T Poll() {
|
||||
_dbg_assert_(this != nullptr);
|
||||
std::lock_guard<std::mutex> guard(readyMutex_);
|
||||
if (ready_) {
|
||||
return data_;
|
||||
@ -96,7 +95,6 @@ public:
|
||||
}
|
||||
|
||||
T BlockUntilReady() {
|
||||
_dbg_assert_(this != nullptr);
|
||||
std::lock_guard<std::mutex> guard(readyMutex_);
|
||||
if (ready_) {
|
||||
return data_;
|
||||
|
@ -170,7 +170,7 @@ void UIContext::ActivateTopScissor() {
|
||||
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: %d,%d-%d,%d / %d,%d", x, y, w, h, pixel_xres, pixel_yres);
|
||||
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);
|
||||
|
@ -74,7 +74,6 @@ public:
|
||||
const UI::Theme *theme;
|
||||
|
||||
// Utility methods
|
||||
|
||||
TextDrawer *Text() const { return textDrawer_; }
|
||||
|
||||
void SetFontStyle(const UI::FontStyle &style);
|
||||
@ -103,6 +102,10 @@ public:
|
||||
|
||||
void setUIAtlas(const std::string &name);
|
||||
|
||||
void SetScreenTag(const char *tag) {
|
||||
screenTag_ = tag;
|
||||
}
|
||||
|
||||
private:
|
||||
Draw::DrawContext *draw_ = nullptr;
|
||||
Bounds bounds_;
|
||||
@ -126,4 +129,6 @@ private:
|
||||
|
||||
std::string lastUIAtlas_;
|
||||
std::string UIAtlas_ = "ui_atlas.zim";
|
||||
|
||||
const char *screenTag_ = nullptr;
|
||||
};
|
||||
|
@ -71,7 +71,7 @@ public:
|
||||
// what screen it is.
|
||||
virtual void *dialogData() { return 0; }
|
||||
|
||||
virtual std::string tag() const { return std::string(""); }
|
||||
virtual const char *tag() const = 0;
|
||||
|
||||
virtual bool isTransparent() const { return false; }
|
||||
virtual bool isTopLevel() const { return false; }
|
||||
|
@ -117,6 +117,9 @@ void UIScreen::render() {
|
||||
|
||||
if (root_) {
|
||||
UIContext *uiContext = screenManager()->getUIContext();
|
||||
|
||||
uiContext->SetScreenTag(tag());
|
||||
|
||||
UI::LayoutViewHierarchy(*uiContext, root_, ignoreInsets_);
|
||||
|
||||
uiContext->PushTransform({translation_, scale_, alpha_});
|
||||
|
@ -136,7 +136,7 @@ public:
|
||||
void SetHiddenChoices(std::set<int> hidden) {
|
||||
hidden_ = hidden;
|
||||
}
|
||||
virtual std::string tag() const override { return std::string("listpopup"); }
|
||||
const char *tag() const override { return "listpopup"; }
|
||||
|
||||
UI::Event OnChoice;
|
||||
|
||||
@ -187,6 +187,8 @@ public:
|
||||
disabled_ = *value_ < 0;
|
||||
}
|
||||
|
||||
const char *tag() const override { return "SliderPopup"; }
|
||||
|
||||
Event OnChange;
|
||||
|
||||
private:
|
||||
@ -214,6 +216,8 @@ public:
|
||||
: PopupScreen(title, "OK", "Cancel"), units_(units), value_(value), originalValue_(*value), minValue_(minValue), maxValue_(maxValue), step_(step), changing_(false), liveUpdate_(liveUpdate) {}
|
||||
void CreatePopupContents(UI::ViewGroup *parent) override;
|
||||
|
||||
const char *tag() const override { return "SliderFloatPopup"; }
|
||||
|
||||
Event OnChange;
|
||||
|
||||
private:
|
||||
@ -241,6 +245,8 @@ public:
|
||||
: PopupScreen(title, "OK", "Cancel"), value_(value), placeholder_(placeholder), maxLen_(maxLen) {}
|
||||
virtual void CreatePopupContents(ViewGroup *parent) override;
|
||||
|
||||
const char *tag() const override { return "TextEditPopup"; }
|
||||
|
||||
Event OnChange;
|
||||
|
||||
private:
|
||||
|
@ -1,4 +1,6 @@
|
||||
#include "Common/GPU/OpenGL/GLRenderManager.h"
|
||||
#include "Common/GPU/Vulkan/VulkanContext.h"
|
||||
|
||||
#include "Common/VR/PPSSPPVR.h"
|
||||
#include "Common/VR/VRBase.h"
|
||||
#include "Common/VR/VRInput.h"
|
||||
@ -11,6 +13,7 @@
|
||||
#include "Core/KeyMap.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
static long vrCompat[VR_COMPAT_MAX];
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
@ -229,6 +232,59 @@ void UpdateVRScreenKey(const KeyInput &key) {
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
// 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
|
||||
|
||||
================================================================================
|
||||
@ -248,12 +304,16 @@ bool StartVRRender() {
|
||||
|
||||
// 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)) {
|
||||
VR_SetConfig(VR_CONFIG_MODE, g_Config.bEnableStereo ? VR_MODE_STEREO_6DOF : VR_MODE_MONO_6DOF);
|
||||
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);
|
||||
@ -269,6 +329,7 @@ void FinishVRRender() {
|
||||
|
||||
void PreVRFrameRender(int fboIndex) {
|
||||
VR_BeginFrame(VR_GetEngine(), fboIndex);
|
||||
vrCompat[VR_COMPAT_FBO_CLEAR] = false;
|
||||
}
|
||||
|
||||
void PostVRFrameRender() {
|
||||
|
@ -3,6 +3,20 @@
|
||||
#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
|
||||
@ -13,6 +27,10 @@ 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();
|
||||
@ -36,6 +54,10 @@ 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; }
|
||||
|
@ -6,6 +6,36 @@
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
#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;
|
||||
|
||||
@ -39,6 +69,14 @@ void VR_Init( ovrJava java, bool useVulkan ) {
|
||||
extensions.push_back(XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME);
|
||||
extensions.push_back(XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME);
|
||||
#endif
|
||||
#ifdef OPENXR_PLATFORM_PICO
|
||||
extensions.push_back(XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME);
|
||||
extensions.push_back("XR_PICO_android_controller_function_ext_enable");
|
||||
extensions.push_back("XR_PICO_view_state_ext_enable");
|
||||
extensions.push_back("XR_PICO_frame_end_info_ext");
|
||||
extensions.push_back("XR_PICO_configs_ext");
|
||||
extensions.push_back("XR_PICO_reset_sensor");
|
||||
#endif
|
||||
|
||||
// Create the OpenXR instance.
|
||||
XrApplicationInfo appInfo;
|
||||
@ -52,7 +90,14 @@ void VR_Init( ovrJava java, bool useVulkan ) {
|
||||
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;
|
||||
@ -67,6 +112,15 @@ void VR_Init( ovrJava java, bool useVulkan ) {
|
||||
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;
|
||||
@ -124,6 +178,11 @@ void VR_Init( ovrJava java, bool useVulkan ) {
|
||||
|
||||
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);
|
||||
}
|
||||
@ -160,6 +219,9 @@ void VR_EnterVR( engine_t* engine, XrGraphicsBindingVulkanKHR* graphicsBindingVu
|
||||
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 = {};
|
||||
|
@ -82,7 +82,9 @@ static void OXR_CheckErrors(XrInstance instance, XrResult result, const char* fu
|
||||
#define OXR(func) func;
|
||||
#endif
|
||||
|
||||
#ifdef OPENXR_PLATFORM_QUEST
|
||||
#define OPENXR_HAS_PERFORMANCE_EXTENSION
|
||||
#endif
|
||||
|
||||
enum { ovrMaxLayerCount = 1 };
|
||||
enum { ovrMaxNumEyes = 2 };
|
||||
|
@ -75,7 +75,7 @@ bool ovrFramebuffer_CreateGL(XrSession session, ovrFramebuffer* frameBuffer, int
|
||||
frameBuffer->DepthSwapChain.Height = swapChainCreateInfo.height;
|
||||
|
||||
// Create the color swapchain.
|
||||
swapChainCreateInfo.format = GL_RGBA8;
|
||||
swapChainCreateInfo.format = GL_SRGB8_ALPHA8;
|
||||
swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->ColorSwapChain.Handle));
|
||||
OXR(xrEnumerateSwapchainImages(frameBuffer->ColorSwapChain.Handle, 0, &frameBuffer->TextureSwapChainLength, NULL));
|
||||
|
@ -26,7 +26,6 @@ XrActionSet runningActionSet;
|
||||
XrSpace leftControllerAimSpace = XR_NULL_HANDLE;
|
||||
XrSpace rightControllerAimSpace = XR_NULL_HANDLE;
|
||||
int inputInitialized = 0;
|
||||
int useSimpleProfile = 0;
|
||||
|
||||
int in_vrEventTime = 0;
|
||||
double lastframetime = 0;
|
||||
@ -248,182 +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));
|
||||
|
||||
// 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));
|
||||
|
||||
// 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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,14 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
XrFovf fov;
|
||||
XrView* projections;
|
||||
XrPosef invViewTransform[2];
|
||||
XrFrameState frameState = {};
|
||||
bool initialized = false;
|
||||
bool stageSupported = false;
|
||||
int vrConfig[VR_CONFIG_MAX] = {};
|
||||
ovrMatrix4f vrMatrix[VR_MATRIX_COUNT];
|
||||
|
||||
XrVector3f hmdorientation;
|
||||
XrVector3f hmdposition;
|
||||
@ -279,7 +281,12 @@ bool VR_InitFrame( 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;
|
||||
}
|
||||
|
||||
@ -290,6 +297,61 @@ bool VR_InitFrame( engine_t* engine ) {
|
||||
|
||||
engine->appState.LayerCount = 0;
|
||||
memset(engine->appState.Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount);
|
||||
|
||||
// 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];
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -326,16 +388,17 @@ void VR_FinishFrame( engine_t* engine ) {
|
||||
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
|
||||
int imageLayer = engine->appState.Renderer.Multiview ? eye : 0;
|
||||
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[0];
|
||||
XrFovf fov = projections[eye].fov;
|
||||
if (vrMode == VR_MODE_MONO_6DOF) {
|
||||
fov = projections[0].fov;
|
||||
} else if (!engine->appState.Renderer.Multiview) {
|
||||
frameBuffer = &engine->appState.Renderer.FrameBuffer[eye];
|
||||
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].pose = pose;
|
||||
projection_layer_elements[eye].fov = fov;
|
||||
|
||||
memset(&projection_layer_elements[eye].subImage, 0, sizeof(XrSwapchainSubImage));
|
||||
@ -431,51 +494,5 @@ void VR_BindFramebuffer(engine_t *engine) {
|
||||
}
|
||||
|
||||
ovrMatrix4f VR_GetMatrix( VRMatrix matrix ) {
|
||||
ovrMatrix4f output;
|
||||
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;
|
||||
float near = (float)vrConfig[VR_CONFIG_FOV_SCALE] / 200.0f;
|
||||
output = 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];
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
output = ovrMatrix4f_CreateFromQuaternion(&invView.orientation);
|
||||
float scale = (float)VR_GetConfig(VR_CONFIG_6DOF_SCALE) * 0.000001f;
|
||||
if (vrConfig[VR_CONFIG_6DOF_ENABLED]) {
|
||||
output.M[0][3] -= hmdposition.x * (vrConfig[VR_CONFIG_MIRROR_AXIS_X] ? -1.0f : 1.0f) * scale;
|
||||
output.M[1][3] -= hmdposition.y * (vrConfig[VR_CONFIG_MIRROR_AXIS_Y] ? -1.0f : 1.0f) * scale;
|
||||
output.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)) {
|
||||
output.M[0][3] += (invViewTransform[1].position.x - invViewTransform[0].position.x) * scale;
|
||||
output.M[1][3] += (invViewTransform[1].position.y - invViewTransform[0].position.y) * scale;
|
||||
output.M[2][3] += (invViewTransform[1].position.z - invViewTransform[0].position.z) * scale;
|
||||
}
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
return output;
|
||||
return vrMatrix[matrix];
|
||||
}
|
||||
|
@ -26,10 +26,11 @@ enum VRConfig {
|
||||
};
|
||||
|
||||
enum VRMatrix {
|
||||
VR_PROJECTION_MATRIX_LEFT_EYE = 0,
|
||||
VR_PROJECTION_MATRIX_RIGHT_EYE = 1,
|
||||
VR_VIEW_MATRIX_LEFT_EYE = 2,
|
||||
VR_VIEW_MATRIX_RIGHT_EYE = 3
|
||||
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 {
|
||||
|
@ -46,6 +46,7 @@ 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);
|
||||
}
|
||||
}
|
||||
@ -96,7 +97,6 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
|
||||
CheckSetting(iniFile, gameID, "DateLimited", &flags_.DateLimited);
|
||||
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);
|
||||
@ -108,6 +108,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
|
||||
CheckSetting(iniFile, gameID, "SplitFramebufferMargin", &flags_.SplitFramebufferMargin);
|
||||
CheckSetting(iniFile, gameID, "ForceLowerResolutionForEffectsOn", &flags_.ForceLowerResolutionForEffectsOn);
|
||||
CheckSetting(iniFile, gameID, "AllowDownloadCLUT", &flags_.AllowDownloadCLUT);
|
||||
CheckSetting(iniFile, gameID, "NearestFilteringOnFramebufferCreate", &flags_.NearestFilteringOnFramebufferCreate);
|
||||
}
|
||||
|
||||
void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) {
|
||||
|
@ -77,7 +77,6 @@ struct CompatFlags {
|
||||
bool DateLimited;
|
||||
bool ShaderColorBitmask;
|
||||
bool DisableFirstFrameReadback;
|
||||
bool DisableRangeCulling;
|
||||
bool MpegAvcWarmUp;
|
||||
bool BlueToAlpha;
|
||||
bool CenteredLines;
|
||||
@ -89,9 +88,11 @@ struct CompatFlags {
|
||||
bool SplitFramebufferMargin;
|
||||
bool ForceLowerResolutionForEffectsOn;
|
||||
bool AllowDownloadCLUT;
|
||||
bool NearestFilteringOnFramebufferCreate;
|
||||
};
|
||||
|
||||
struct VRCompat {
|
||||
bool Skyplane;
|
||||
float UnitsPerMeter;
|
||||
};
|
||||
|
||||
|
@ -610,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),
|
||||
@ -873,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),
|
||||
@ -938,7 +940,6 @@ static ConfigSetting graphicsSettings[] = {
|
||||
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),
|
||||
@ -1424,7 +1425,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.
|
||||
@ -1636,16 +1637,16 @@ void Config::DownloadCompletedCallback(http::Download &download) {
|
||||
|
||||
if (installed >= upgrade) {
|
||||
INFO_LOG(LOADER, "Version check: Already up to date, erasing any upgrade message");
|
||||
g_Config.upgradeMessage = "";
|
||||
g_Config.upgradeMessage.clear();
|
||||
g_Config.upgradeVersion = upgrade.ToString();
|
||||
g_Config.dismissedVersion = "";
|
||||
g_Config.dismissedVersion.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (installed < upgrade && dismissed != upgrade) {
|
||||
g_Config.upgradeMessage = "New version of PPSSPP available!";
|
||||
g_Config.upgradeVersion = upgrade.ToString();
|
||||
g_Config.dismissedVersion = "";
|
||||
g_Config.dismissedVersion.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,6 +164,7 @@ public:
|
||||
bool bHardwareTransform; // only used in the GLES backend
|
||||
bool bSoftwareSkinning; // may speed up some games
|
||||
bool bVendorBugChecksEnabled;
|
||||
bool bUseGeometryShader;
|
||||
|
||||
int iRenderingMode; // 0 = non-buffered rendering 1 = buffered rendering
|
||||
int iTexFiltering; // 1 = auto , 2 = nearest , 3 = linear , 4 = auto max quality
|
||||
@ -176,6 +177,7 @@ public:
|
||||
bool bSustainedPerformanceMode; // Android: Slows clocks down to avoid overheating/speed fluctuations.
|
||||
bool bIgnoreScreenInsets; // Android: Center screen disregarding insets if this is enabled.
|
||||
bool bVSync;
|
||||
|
||||
int iFrameSkip;
|
||||
int iFrameSkipType;
|
||||
int iFastForwardMode; // See FastForwardMode in ConfigValues.h.
|
||||
@ -190,6 +192,7 @@ public:
|
||||
int iWindowY;
|
||||
int iWindowWidth; // Windows and other windowed environments
|
||||
int iWindowHeight;
|
||||
bool bShowMenuBar; // Windows-only
|
||||
|
||||
float fUITint;
|
||||
float fUISaturation;
|
||||
@ -242,7 +245,6 @@ public:
|
||||
bool bShaderChainRequires60FPS;
|
||||
std::string sTextureShaderName;
|
||||
bool bGfxDebugOutput;
|
||||
bool bGfxDebugSplitSubmit;
|
||||
int iInflightFrames;
|
||||
bool bRenderDuplicateFrames;
|
||||
|
||||
|
@ -447,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;
|
||||
@ -483,7 +483,7 @@ 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;
|
||||
@ -498,7 +498,7 @@ void Core_Break(u32 pc) {
|
||||
ExceptionInfo &e = g_exceptionInfo;
|
||||
e = {};
|
||||
e.type = ExceptionType::BREAK;
|
||||
e.info = "";
|
||||
e.info.clear();
|
||||
e.pc = pc;
|
||||
|
||||
if (!g_Config.bIgnoreBadMemAccess) {
|
||||
|
@ -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" />
|
||||
|
@ -316,7 +316,7 @@ void DisassemblyManager::getLine(u32 address, bool insertSymbols, DisassemblyLin
|
||||
dest.params = "Disassembly failure";
|
||||
} else {
|
||||
dest.name = "-";
|
||||
dest.params = "";
|
||||
dest.params.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1003,7 +1003,7 @@ void DisassemblyData::createLines()
|
||||
lines[currentLineStart] = entry;
|
||||
lineAddresses.push_back(currentLineStart);
|
||||
|
||||
currentLine = "";
|
||||
currentLine.clear();
|
||||
currentLineStart = pos-1;
|
||||
inString = false;
|
||||
}
|
||||
@ -1028,7 +1028,7 @@ void DisassemblyData::createLines()
|
||||
lines[currentLineStart] = entry;
|
||||
lineAddresses.push_back(currentLineStart);
|
||||
|
||||
currentLine = "";
|
||||
currentLine.clear();
|
||||
currentLineStart = pos-1;
|
||||
inString = false;
|
||||
}
|
||||
@ -1099,7 +1099,7 @@ void DisassemblyData::createLines()
|
||||
lines[currentLineStart] = entry;
|
||||
lineAddresses.push_back(currentLineStart);
|
||||
|
||||
currentLine = "";
|
||||
currentLine.clear();
|
||||
currentLineStart = currentPos;
|
||||
}
|
||||
|
||||
|
@ -399,12 +399,18 @@ void FlushPendingMemInfo() {
|
||||
pendingNotifyMaxAddr2 = 0;
|
||||
}
|
||||
|
||||
static inline uint32_t NormalizeAddress(uint32_t addr) {
|
||||
if ((addr & 0x3F000000) == 0x04000000)
|
||||
return addr & 0x041FFFFF;
|
||||
return addr & 0x3FFFFFFF;
|
||||
}
|
||||
|
||||
void NotifyMemInfoPC(MemBlockFlags flags, uint32_t start, uint32_t size, uint32_t pc, const char *tagStr, size_t strLength) {
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
// Clear the uncached and kernel bits.
|
||||
start &= ~0xC0000000;
|
||||
start = NormalizeAddress(start);
|
||||
|
||||
bool needFlush = false;
|
||||
// When the setting is off, we skip smaller info to keep things fast.
|
||||
@ -450,7 +456,7 @@ void NotifyMemInfo(MemBlockFlags flags, uint32_t start, uint32_t size, const cha
|
||||
}
|
||||
|
||||
std::vector<MemBlockInfo> FindMemInfo(uint32_t start, uint32_t size) {
|
||||
start &= ~0xC0000000;
|
||||
start = NormalizeAddress(start);
|
||||
|
||||
if (pendingNotifyMinAddr1 < start + size && pendingNotifyMaxAddr1 >= start)
|
||||
FlushPendingMemInfo();
|
||||
@ -466,7 +472,7 @@ std::vector<MemBlockInfo> FindMemInfo(uint32_t start, uint32_t size) {
|
||||
}
|
||||
|
||||
std::vector<MemBlockInfo> FindMemInfoByFlag(MemBlockFlags flags, uint32_t start, uint32_t size) {
|
||||
start &= ~0xC0000000;
|
||||
start = NormalizeAddress(start);
|
||||
|
||||
if (pendingNotifyMinAddr1 < start + size && pendingNotifyMaxAddr1 >= start)
|
||||
FlushPendingMemInfo();
|
||||
@ -486,7 +492,7 @@ std::vector<MemBlockInfo> FindMemInfoByFlag(MemBlockFlags flags, uint32_t start,
|
||||
}
|
||||
|
||||
static const char *FindWriteTagByFlag(MemBlockFlags flags, uint32_t start, uint32_t size) {
|
||||
start &= ~0xC0000000;
|
||||
start = NormalizeAddress(start);
|
||||
|
||||
if (pendingNotifyMinAddr1 < start + size && pendingNotifyMaxAddr1 >= start)
|
||||
FlushPendingMemInfo();
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "zlib.h"
|
||||
@ -439,7 +440,7 @@ void SymbolMap::AddModule(const char *name, u32 address, u32 size) {
|
||||
// Just reactivate that one.
|
||||
it->start = address;
|
||||
it->size = size;
|
||||
activeModuleEnds.insert(std::make_pair(it->start + it->size, *it));
|
||||
activeModuleEnds.emplace(it->start + it->size, *it);
|
||||
activeNeedUpdate_ = true;
|
||||
return;
|
||||
}
|
||||
@ -452,7 +453,7 @@ void SymbolMap::AddModule(const char *name, u32 address, u32 size) {
|
||||
mod.index = (int)modules.size() + 1;
|
||||
|
||||
modules.push_back(mod);
|
||||
activeModuleEnds.insert(std::make_pair(mod.start + mod.size, mod));
|
||||
activeModuleEnds.emplace(mod.start + mod.size, mod);
|
||||
activeNeedUpdate_ = true;
|
||||
}
|
||||
|
||||
@ -559,7 +560,7 @@ void SymbolMap::AddFunction(const char* name, u32 address, u32 size, int moduleI
|
||||
auto active = activeFunctions.find(address);
|
||||
if (active != activeFunctions.end() && active->second.module == moduleIndex) {
|
||||
activeFunctions.erase(active);
|
||||
activeFunctions.insert(std::make_pair(address, existing->second));
|
||||
activeFunctions.emplace(address, existing->second);
|
||||
}
|
||||
} else {
|
||||
FunctionEntry func;
|
||||
@ -570,7 +571,7 @@ void SymbolMap::AddFunction(const char* name, u32 address, u32 size, int moduleI
|
||||
functions[symbolKey] = func;
|
||||
|
||||
if (IsModuleActive(moduleIndex)) {
|
||||
activeFunctions.insert(std::make_pair(address, func));
|
||||
activeFunctions.emplace(address, func);
|
||||
}
|
||||
}
|
||||
|
||||
@ -716,27 +717,27 @@ void SymbolMap::UpdateActiveSymbols() {
|
||||
for (auto it = functions.begin(), end = functions.end(); it != end; ++it) {
|
||||
const auto mod = activeModuleIndexes.find(it->second.module);
|
||||
if (it->second.module == 0) {
|
||||
activeFunctions.insert(std::make_pair(it->second.start, it->second));
|
||||
activeFunctions.emplace(it->second.start, it->second);
|
||||
} else if (mod != activeModuleIndexes.end()) {
|
||||
activeFunctions.insert(std::make_pair(mod->second + it->second.start, it->second));
|
||||
activeFunctions.emplace(mod->second + it->second.start, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = labels.begin(), end = labels.end(); it != end; ++it) {
|
||||
const auto mod = activeModuleIndexes.find(it->second.module);
|
||||
if (it->second.module == 0) {
|
||||
activeLabels.insert(std::make_pair(it->second.addr, it->second));
|
||||
activeLabels.emplace(it->second.addr, it->second);
|
||||
} else if (mod != activeModuleIndexes.end()) {
|
||||
activeLabels.insert(std::make_pair(mod->second + it->second.addr, it->second));
|
||||
activeLabels.emplace(mod->second + it->second.addr, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = data.begin(), end = data.end(); it != end; ++it) {
|
||||
const auto mod = activeModuleIndexes.find(it->second.module);
|
||||
if (it->second.module == 0) {
|
||||
activeData.insert(std::make_pair(it->second.start, it->second));
|
||||
activeData.emplace(it->second.start, it->second);
|
||||
} else if (mod != activeModuleIndexes.end()) {
|
||||
activeData.insert(std::make_pair(mod->second + it->second.start, it->second));
|
||||
activeData.emplace(mod->second + it->second.start, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
@ -757,7 +758,7 @@ bool SymbolMap::SetFunctionSize(u32 startAddress, u32 newSize) {
|
||||
if (func != functions.end()) {
|
||||
func->second.size = newSize;
|
||||
activeFunctions.erase(funcInfo);
|
||||
activeFunctions.insert(std::make_pair(startAddress, func->second));
|
||||
activeFunctions.emplace(startAddress, func->second);
|
||||
}
|
||||
}
|
||||
|
||||
@ -829,7 +830,7 @@ void SymbolMap::AddLabel(const char* name, u32 address, int moduleIndex) {
|
||||
auto active = activeLabels.find(address);
|
||||
if (active != activeLabels.end() && active->second.module == moduleIndex) {
|
||||
activeLabels.erase(active);
|
||||
activeLabels.insert(std::make_pair(address, label));
|
||||
activeLabels.emplace(address, label);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -840,7 +841,7 @@ void SymbolMap::AddLabel(const char* name, u32 address, int moduleIndex) {
|
||||
|
||||
labels[symbolKey] = label;
|
||||
if (IsModuleActive(moduleIndex)) {
|
||||
activeLabels.insert(std::make_pair(address, label));
|
||||
activeLabels.emplace(address, label);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -864,7 +865,7 @@ void SymbolMap::SetLabelName(const char* name, u32 address) {
|
||||
auto active = activeLabels.find(address);
|
||||
if (active != activeLabels.end() && active->second.module == label->second.module) {
|
||||
activeLabels.erase(active);
|
||||
activeLabels.insert(std::make_pair(address, label->second));
|
||||
activeLabels.emplace(address, label->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -947,7 +948,7 @@ void SymbolMap::AddData(u32 address, u32 size, DataType type, int moduleIndex) {
|
||||
auto active = activeData.find(address);
|
||||
if (active != activeData.end() && active->second.module == moduleIndex) {
|
||||
activeData.erase(active);
|
||||
activeData.insert(std::make_pair(address, existing->second));
|
||||
activeData.emplace(address, existing->second);
|
||||
}
|
||||
} else {
|
||||
DataEntry entry;
|
||||
@ -958,7 +959,7 @@ void SymbolMap::AddData(u32 address, u32 size, DataType type, int moduleIndex) {
|
||||
|
||||
data[symbolKey] = entry;
|
||||
if (IsModuleActive(moduleIndex)) {
|
||||
activeData.insert(std::make_pair(address, entry));
|
||||
activeData.emplace(address, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1037,8 +1038,9 @@ void SymbolMap::GetLabels(std::vector<LabelDefinition> &dest) {
|
||||
for (auto it = activeLabels.begin(); it != activeLabels.end(); it++) {
|
||||
LabelDefinition entry;
|
||||
entry.value = it->first;
|
||||
entry.name = ConvertUTF8ToWString(it->second.name);
|
||||
std::transform(entry.name.begin(), entry.name.end(), entry.name.begin(), ::towlower);
|
||||
std::string name = it->second.name;
|
||||
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
|
||||
entry.name = Identifier(name);
|
||||
dest.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
@ -97,6 +97,10 @@ static void UpdateConnected(int delta) {
|
||||
}
|
||||
|
||||
static void WebSocketNotifyLifecycle(CoreLifecycle stage) {
|
||||
// We'll likely already be locked during the reboot.
|
||||
if (PSP_IsRebooting())
|
||||
return;
|
||||
|
||||
switch (stage) {
|
||||
case CoreLifecycle::STARTING:
|
||||
case CoreLifecycle::STOPPING:
|
||||
|
@ -379,7 +379,7 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) {
|
||||
json.pop();
|
||||
}
|
||||
|
||||
// Search disassembly for some text (cpu.searchDisasm)
|
||||
// Search disassembly for some text (memory.searchDisasm)
|
||||
//
|
||||
// Parameters:
|
||||
// - thread: optional number indicating the thread id (may not affect search much.)
|
||||
@ -461,7 +461,7 @@ void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) {
|
||||
json.writeNull("address");
|
||||
}
|
||||
|
||||
// Assemble an instruction (cpu.assemble)
|
||||
// Assemble an instruction (memory.assemble)
|
||||
//
|
||||
// Parameters:
|
||||
// - address: number indicating the address to write to.
|
||||
@ -482,7 +482,7 @@ void WebSocketDisasmState::Assemble(DebuggerRequest &req) {
|
||||
return;
|
||||
|
||||
if (!MIPSAsm::MipsAssembleOpcode(code.c_str(), currentDebugMIPS, address))
|
||||
return req.Fail(StringFromFormat("Could not assemble: %s", ConvertWStringToUTF8(MIPSAsm::GetAssembleError()).c_str()));
|
||||
return req.Fail(StringFromFormat("Could not assemble: %s", MIPSAsm::GetAssembleError().c_str()));
|
||||
|
||||
JsonWriter &json = req.Respond();
|
||||
Reporting::NotifyDebugger();
|
||||
|
@ -86,7 +86,7 @@ void GameBroadcaster::Broadcast(net::WebSocketServer *ws) {
|
||||
} else if (state == UISTATE_INGAME && PSP_IsInited()) {
|
||||
ws->Send(GameStatusEvent{"game.start"});
|
||||
prevState_ = state;
|
||||
} else if (state == UISTATE_MENU && !PSP_IsInited() && !PSP_IsQuitting()) {
|
||||
} else if (state == UISTATE_MENU && !PSP_IsInited() && !PSP_IsQuitting() && !PSP_IsRebooting()) {
|
||||
ws->Send(GameStatusEvent{"game.quit"});
|
||||
prevState_ = state;
|
||||
}
|
||||
|
@ -49,9 +49,8 @@ void WebSocketGameReset(DebuggerRequest &req) {
|
||||
if (needBreak)
|
||||
PSP_CoreParameter().startBreak = true;
|
||||
|
||||
PSP_Shutdown();
|
||||
std::string resetError;
|
||||
if (!PSP_Init(PSP_CoreParameter(), &resetError)) {
|
||||
if (!PSP_Reboot(&resetError)) {
|
||||
ERROR_LOG(BOOT, "Error resetting: %s", resetError.c_str());
|
||||
return req.Fail("Could not reset");
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "Core/Debugger/SymbolMap.h"
|
||||
#include "Core/Debugger/WebSocket/HLESubscriber.h"
|
||||
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/MIPSAnalyst.h"
|
||||
#include "Core/MIPS/MIPSDebugInterface.h"
|
||||
#include "Core/MIPS/MIPSStackWalk.h"
|
||||
@ -36,6 +37,7 @@ DebuggerSubscriber *WebSocketHLEInit(DebuggerEventHandlerMap &map) {
|
||||
map["hle.func.add"] = &WebSocketHLEFuncAdd;
|
||||
map["hle.func.remove"] = &WebSocketHLEFuncRemove;
|
||||
map["hle.func.rename"] = &WebSocketHLEFuncRename;
|
||||
map["hle.func.scan"] = &WebSocketHLEFuncScan;
|
||||
map["hle.module.list"] = &WebSocketHLEModuleList;
|
||||
map["hle.backtrace"] = &WebSocketHLEBacktrace;
|
||||
|
||||
@ -403,6 +405,35 @@ void WebSocketHLEFuncRename(DebuggerRequest &req) {
|
||||
json.writeString("name", name);
|
||||
}
|
||||
|
||||
// Auto-detect functions in a memory range (hle.func.scan)
|
||||
//
|
||||
// Parameters:
|
||||
// - address: unsigned integer address within function to rename.
|
||||
// - size: unsigned integer size in bytes for scan.
|
||||
//
|
||||
// Response (same event name) with no extra data.
|
||||
void WebSocketHLEFuncScan(DebuggerRequest &req) {
|
||||
if (!g_symbolMap)
|
||||
return req.Fail("CPU not active");
|
||||
if (!Core_IsStepping())
|
||||
return req.Fail("CPU currently running (cpu.stepping first)");
|
||||
|
||||
u32 addr;
|
||||
if (!req.ParamU32("address", &addr))
|
||||
return;
|
||||
u32 size;
|
||||
if (!req.ParamU32("size", &size))
|
||||
return;
|
||||
|
||||
if (!Memory::IsValidRange(addr, size))
|
||||
return req.Fail("Address or size outside valid memory");
|
||||
|
||||
bool insertSymbols = MIPSAnalyst::ScanForFunctions(addr, addr + size, true);
|
||||
MIPSAnalyst::FinalizeScan(insertSymbols);
|
||||
|
||||
req.Respond();
|
||||
}
|
||||
|
||||
// List all known user modules (hle.module.list)
|
||||
//
|
||||
// No parameters.
|
||||
|
@ -28,5 +28,6 @@ void WebSocketHLEFuncList(DebuggerRequest &req);
|
||||
void WebSocketHLEFuncAdd(DebuggerRequest &req);
|
||||
void WebSocketHLEFuncRemove(DebuggerRequest &req);
|
||||
void WebSocketHLEFuncRename(DebuggerRequest &req);
|
||||
void WebSocketHLEFuncScan(DebuggerRequest &req);
|
||||
void WebSocketHLEModuleList(DebuggerRequest &req);
|
||||
void WebSocketHLEBacktrace(DebuggerRequest &req);
|
||||
|
@ -216,7 +216,7 @@ bool DebuggerRequest::ParamBool(const char *name, bool *out, DebuggerParamType t
|
||||
*out = true;
|
||||
return true;
|
||||
}
|
||||
if (s == "0" || s == "false" || (s == "" && allowLoose)) {
|
||||
if (s == "0" || s == "false" || (s.empty() && allowLoose)) {
|
||||
*out = false;
|
||||
return true;
|
||||
}
|
||||
@ -262,7 +262,7 @@ bool DebuggerRequest::ParamString(const char *name, std::string *out, DebuggerPa
|
||||
return true;
|
||||
} else if (tag == JSON_NULL) {
|
||||
if (required) {
|
||||
*out = "";
|
||||
out->clear();
|
||||
}
|
||||
return true;
|
||||
} else if (tag == JSON_NUMBER) {
|
||||
|
@ -36,8 +36,9 @@ const static u32 GAMEDATA_BYTES_PER_READ = 32768;
|
||||
// If this is too high, some games (e.g. Senjou no Valkyria 3) will lag.
|
||||
const static u32 GAMEDATA_READS_PER_UPDATE = 20;
|
||||
|
||||
const u32 ERROR_UTILITY_GAMEDATA_MEMSTRICK_WRITE_PROTECTED = 0x80111903;
|
||||
const u32 ERROR_UTILITY_GAMEDATA_MEMSTRICK_REMOVED = 0x80111901;
|
||||
const u32 ERROR_UTILITY_GAMEDATA_MEMSTRICK_WRITE_PROTECTED = 0x80111903;
|
||||
const u32 ERROR_UTILITY_GAMEDATA_INVALID_MODE = 0x80111908;
|
||||
|
||||
static const std::string SFO_FILENAME = "PARAM.SFO";
|
||||
|
||||
@ -88,9 +89,14 @@ int PSPGamedataInstallDialog::Init(u32 paramAddr) {
|
||||
}
|
||||
|
||||
int size = Memory::Read_U32(paramAddr);
|
||||
if (size != 1424 && size != 1432) {
|
||||
ERROR_LOG_REPORT(SCEUTILITY, "sceGamedataInstallInitStart: invalid param size %d", size);
|
||||
return SCE_ERROR_UTILITY_INVALID_PARAM_SIZE;
|
||||
}
|
||||
|
||||
memset(&request, 0, sizeof(request));
|
||||
// Only copy the right size to support different request format
|
||||
Memory::Memcpy(&request, paramAddr, size);
|
||||
Memory::Memcpy(&request, paramAddr, size, "sceGamedataInstallInitStart");
|
||||
|
||||
ChangeStatusInit(GAMEDATA_INIT_DELAY_US);
|
||||
return 0;
|
||||
@ -100,6 +106,17 @@ int PSPGamedataInstallDialog::Update(int animSpeed) {
|
||||
if (GetStatus() != SCE_UTILITY_STATUS_RUNNING)
|
||||
return SCE_ERROR_UTILITY_INVALID_STATUS;
|
||||
|
||||
if (param->mode >= 2) {
|
||||
param->common.result = ERROR_UTILITY_GAMEDATA_INVALID_MODE;
|
||||
param.NotifyWrite("DialogResult");
|
||||
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);
|
||||
WARN_LOG_REPORT(SCEUTILITY, "sceUtilityGamedataInstallUpdate: invalid mode %d", param->mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: param->mode == 1 should show a prompt to confirm, then a progress bar.
|
||||
// Any other mode (i.e. 0 or negative) should proceed and show no UI.
|
||||
|
||||
// TODO: This should return error codes in some cases, like write failure.
|
||||
// request.common.result must be updated for errors as well.
|
||||
|
||||
@ -222,6 +239,9 @@ void PSPGamedataInstallDialog::WriteSfoFile() {
|
||||
}
|
||||
|
||||
int PSPGamedataInstallDialog::Abort() {
|
||||
param->common.result = 1;
|
||||
param.NotifyWrite("DialogResult");
|
||||
|
||||
// TODO: Delete the files or anything?
|
||||
return PSPDialog::Shutdown();
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
struct SceUtilityGamedataInstallParam {
|
||||
pspUtilityDialogCommon common;
|
||||
u32_le unknown1;
|
||||
s32_le mode;
|
||||
char gameName[13];
|
||||
char ignore1[3];
|
||||
char dataName[20];
|
||||
|
@ -194,21 +194,21 @@ void PSPNetconfDialog::DisplayMessage(std::string text1, std::string text2a, std
|
||||
|
||||
PPGeScissor(0, (int)(centerY - h2 - 2), 480, (int)(centerY + h2 + 2));
|
||||
PPGeDrawTextWrapped(text1.c_str(), 240.0f, centerY - h2 - scrollPos_, WRAP_WIDTH, 0, messageStyle);
|
||||
if (text2a != "") {
|
||||
if (text2b != "")
|
||||
if (!text2a.empty()) {
|
||||
if (!text2b.empty())
|
||||
PPGeDrawTextWrapped(text2a.c_str(), 240.0f - 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyleRight);
|
||||
else
|
||||
PPGeDrawTextWrapped(text2a.c_str(), 240.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyle);
|
||||
}
|
||||
if (text2b != "")
|
||||
if (!text2b.empty())
|
||||
PPGeDrawTextWrapped(text2b.c_str(), 240.0f + 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyleLeft);
|
||||
if (text3a != "") {
|
||||
if (text3b != "")
|
||||
if (!text3a.empty()) {
|
||||
if (!text3b.empty())
|
||||
PPGeDrawTextWrapped(text3a.c_str(), 240.0f - 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyleRight);
|
||||
else
|
||||
PPGeDrawTextWrapped(text3a.c_str(), 240.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyle);
|
||||
}
|
||||
if (text3b != "")
|
||||
if (!text3b.empty())
|
||||
PPGeDrawTextWrapped(text3b.c_str(), 240.0f + 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyleLeft);
|
||||
PPGeScissorReset();
|
||||
|
||||
|
@ -191,21 +191,21 @@ void PSPNpSigninDialog::DisplayMessage(std::string text1, std::string text2a, st
|
||||
|
||||
PPGeScissor(0, (int)(centerY - h2 - 2), 480, (int)(centerY + h2 + 2));
|
||||
PPGeDrawTextWrapped(text1.c_str(), 240.0f, centerY - h2 - scrollPos_, WRAP_WIDTH, 0, messageStyle);
|
||||
if (text2a != "") {
|
||||
if (text2b != "")
|
||||
if (!text2a.empty()) {
|
||||
if (!text2b.empty())
|
||||
PPGeDrawTextWrapped(text2a.c_str(), 240.0f - 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyleRight);
|
||||
else
|
||||
PPGeDrawTextWrapped(text2a.c_str(), 240.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyle);
|
||||
}
|
||||
if (text2b != "")
|
||||
if (!text2b.empty())
|
||||
PPGeDrawTextWrapped(text2b.c_str(), 240.0f + 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyleLeft);
|
||||
if (text3a != "") {
|
||||
if (text3b != "")
|
||||
if (!text3a.empty()) {
|
||||
if (!text3b.empty())
|
||||
PPGeDrawTextWrapped(text3a.c_str(), 240.0f - 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyleRight);
|
||||
else
|
||||
PPGeDrawTextWrapped(text3a.c_str(), 240.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyle);
|
||||
}
|
||||
if (text3b != "")
|
||||
if (!text3b.empty())
|
||||
PPGeDrawTextWrapped(text3b.c_str(), 240.0f + 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyleLeft);
|
||||
PPGeScissorReset();
|
||||
|
||||
|
@ -258,7 +258,7 @@ std::string SavedataParam::GetSaveFilePath(const SceUtilitySavedataParam *param,
|
||||
inline static std::string FixedToString(const char *str, size_t n)
|
||||
{
|
||||
if (!str) {
|
||||
return std::string("");
|
||||
return std::string();
|
||||
} else {
|
||||
return std::string(str, strnlen(str, n));
|
||||
}
|
||||
@ -353,11 +353,11 @@ int SavedataParam::DeleteData(SceUtilitySavedataParam* param) {
|
||||
if (!pspFileSystem.GetFileInfo(sfoPath).exists)
|
||||
return SCE_UTILITY_SAVEDATA_ERROR_RW_DATA_BROKEN;
|
||||
|
||||
if (fileName != "" && !pspFileSystem.GetFileInfo(filePath).exists) {
|
||||
if (!fileName.empty() && !pspFileSystem.GetFileInfo(filePath).exists) {
|
||||
return SCE_UTILITY_SAVEDATA_ERROR_RW_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (fileName == "") {
|
||||
if (fileName.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -533,7 +533,7 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD
|
||||
// copy back save name in request
|
||||
strncpy(param->saveName, saveDirName.c_str(), 20);
|
||||
|
||||
if (fileName == "") {
|
||||
if (fileName.empty()) {
|
||||
delete[] cryptedData;
|
||||
} else {
|
||||
if (!WritePSPFile(filePath, data_, saveSize)) {
|
||||
@ -583,13 +583,12 @@ int SavedataParam::Load(SceUtilitySavedataParam *param, const std::string &saveD
|
||||
std::string dirPath = GetSaveFilePath(param, GetSaveDir(param, saveDirName));
|
||||
std::string fileName = GetFileName(param);
|
||||
std::string filePath = dirPath + "/" + fileName;
|
||||
std::string sfoPath = dirPath + "/" + SFO_FILENAME;
|
||||
|
||||
if (!pspFileSystem.GetFileInfo(dirPath).exists) {
|
||||
return isRWMode ? SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA : SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA;
|
||||
}
|
||||
|
||||
if (fileName != "" && !pspFileSystem.GetFileInfo(filePath).exists) {
|
||||
if (!fileName.empty() && !pspFileSystem.GetFileInfo(filePath).exists) {
|
||||
return isRWMode ? SCE_UTILITY_SAVEDATA_ERROR_RW_FILE_NOT_FOUND : SCE_UTILITY_SAVEDATA_ERROR_LOAD_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
@ -637,7 +636,7 @@ int SavedataParam::LoadSaveData(SceUtilitySavedataParam *param, const std::strin
|
||||
std::string filename = GetFileName(param);
|
||||
std::string filePath = dirPath + "/" + filename;
|
||||
// Blank filename always means success, if secureVersion was correct.
|
||||
if (filename == "")
|
||||
if (filename.empty())
|
||||
return 0;
|
||||
|
||||
s64 readSize;
|
||||
@ -1576,7 +1575,7 @@ void SavedataParam::SetFileInfo(SaveFileInfo &saveInfo, PSPFileInfo &info, std::
|
||||
saveInfo.idx = 0;
|
||||
saveInfo.modif_time = info.mtime;
|
||||
|
||||
std::string saveDir = savrDir == "" ? GetGameName(pspParam) + saveName : savrDir;
|
||||
std::string saveDir = savrDir.empty() ? GetGameName(pspParam) + saveName : savrDir;
|
||||
saveInfo.saveDir = saveDir;
|
||||
|
||||
// Start with a blank slate.
|
||||
|
@ -583,7 +583,8 @@ int ElfReader::LoadInto(u32 loadAddress, bool fromTop)
|
||||
{
|
||||
if (!(sections[sectionToModify].sh_flags & SHF_ALLOC))
|
||||
{
|
||||
ERROR_LOG_REPORT(LOADER, "Trying to relocate non-loaded section %s, ignoring", GetSectionName(sectionToModify));
|
||||
// Generally stuff like debug info. We don't need it.
|
||||
INFO_LOG(LOADER, "Skipping relocation of non-loaded section %s", GetSectionName(sectionToModify));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -283,7 +283,7 @@ std::string ParamSFOData::GenerateFakeID(std::string filename) {
|
||||
// Generates fake gameID for homebrew based on it's folder name.
|
||||
// Should probably not be a part of ParamSFO, but it'll be called in same places.
|
||||
std::string file = PSP_CoreParameter().fileToStart.ToString();
|
||||
if (filename != "")
|
||||
if (!filename.empty())
|
||||
file = filename;
|
||||
|
||||
std::size_t lslash = file.find_last_of("/");
|
||||
|
@ -148,7 +148,7 @@ ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevic
|
||||
if (!blockDevice->ReadBlock(16, (u8*)&desc))
|
||||
blockDevice->NotifyReadError();
|
||||
|
||||
entireISO.name = "";
|
||||
entireISO.name.clear();
|
||||
entireISO.isDirectory = false;
|
||||
entireISO.startingPosition = 0;
|
||||
entireISO.size = _blockDevice->GetNumBlocks();
|
||||
|
@ -81,7 +81,6 @@ void VirtualDiscFileSystem::LoadFileListIndex() {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string buf;
|
||||
static const int MAX_LINE_SIZE = 2048;
|
||||
char linebuf[MAX_LINE_SIZE]{};
|
||||
while (fgets(linebuf, MAX_LINE_SIZE, f)) {
|
||||
@ -330,7 +329,7 @@ int VirtualDiscFileSystem::OpenFile(std::string filename, FileAccess access, con
|
||||
entry.size = 0;
|
||||
entry.startOffset = 0;
|
||||
|
||||
if (filename == "")
|
||||
if (filename.empty())
|
||||
{
|
||||
entry.type = VFILETYPE_ISO;
|
||||
entry.fileIndex = -1;
|
||||
|
@ -864,6 +864,8 @@ void hleDoLogInternal(LogTypes::LOG_TYPE t, LogTypes::LOG_LEVELS level, u64 res,
|
||||
|
||||
funcName = latestSyscall->name;
|
||||
funcFlags = latestSyscall->flags;
|
||||
} else {
|
||||
strcpy(formatted_args, "?");
|
||||
}
|
||||
|
||||
const char *fmt;
|
||||
|
@ -189,7 +189,7 @@ T hleDoLog(LogTypes::LOG_TYPE t, LogTypes::LOG_LEVELS level, T res, const char *
|
||||
|
||||
template <typename T>
|
||||
T hleDoLog(LogTypes::LOG_TYPE t, LogTypes::LOG_LEVELS level, T res, const char *file, int line, const char *reportTag, char retmask) {
|
||||
if (level > MAX_LOGLEVEL || !GenericLogEnabled(level, t)) {
|
||||
if ((level > MAX_LOGLEVEL || !GenericLogEnabled(level, t)) && !reportTag) {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -1325,10 +1325,10 @@ void sendChat(std::string chatString) {
|
||||
if (IsSocketReady((int)metasocket, false, true) > 0) {
|
||||
int chatResult = send((int)metasocket, (const char*)&chat, sizeof(chat), MSG_NOSIGNAL);
|
||||
NOTICE_LOG(SCENET, "Send Chat %s to Adhoc Server", chat.message);
|
||||
std::string name = g_Config.sNickName.c_str();
|
||||
std::string name = g_Config.sNickName;
|
||||
|
||||
std::lock_guard<std::mutex> guard(chatLogLock);
|
||||
chatLog.push_back(name.substr(0, 8) + ": " + chat.message);
|
||||
chatLog.emplace_back(name.substr(0, 8) + ": " + chat.message);
|
||||
chatMessageGeneration++;
|
||||
}
|
||||
}
|
||||
@ -1891,6 +1891,7 @@ uint32_t getLocalIp(int sock) {
|
||||
static std::vector<std::pair<uint32_t, uint32_t>> InitPrivateIPRanges() {
|
||||
struct sockaddr_in saNet {}, saMask{};
|
||||
std::vector<std::pair<uint32_t, uint32_t>> ip_ranges;
|
||||
ip_ranges.reserve(5);
|
||||
|
||||
if (1 == inet_pton(AF_INET, "192.168.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.255.0.0", &(saMask.sin_addr)))
|
||||
ip_ranges.push_back({saNet.sin_addr.s_addr, saMask.sin_addr.s_addr});
|
||||
@ -1928,7 +1929,7 @@ void getLocalMac(SceNetEtherAddr * addr){
|
||||
mac[0] &= 0xfc;
|
||||
}
|
||||
else
|
||||
if (!ParseMacAddress(g_Config.sMACAddress.c_str(), mac)) {
|
||||
if (!ParseMacAddress(g_Config.sMACAddress, mac)) {
|
||||
ERROR_LOG(SCENET, "Error parsing mac address %s", g_Config.sMACAddress.c_str());
|
||||
memset(&mac, 0, sizeof(mac));
|
||||
}
|
||||
|
@ -518,49 +518,19 @@ u32 sceGeRestoreContext(u32 ctxAddr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __GeCopyMatrix(u32 matrixPtr, float *mtx, u32 size) {
|
||||
for (u32 i = 0; i < size / sizeof(float); ++i) {
|
||||
Memory::Write_U32(toFloat24(mtx[i]), matrixPtr + i * sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
static int sceGeGetMtx(int type, u32 matrixPtr) {
|
||||
if (!Memory::IsValidAddress(matrixPtr)) {
|
||||
ERROR_LOG(SCEGE, "sceGeGetMtx(%d, %08x) - bad matrix ptr", type, matrixPtr);
|
||||
return -1;
|
||||
int size = type == GE_MTX_PROJECTION ? 16 : 12;
|
||||
if (!Memory::IsValidRange(matrixPtr, size * sizeof(float))) {
|
||||
return hleLogError(SCEGE, -1, "bad matrix ptr");
|
||||
}
|
||||
|
||||
INFO_LOG(SCEGE, "sceGeGetMtx(%d, %08x)", type, matrixPtr);
|
||||
switch (type) {
|
||||
case GE_MTX_BONE0:
|
||||
case GE_MTX_BONE1:
|
||||
case GE_MTX_BONE2:
|
||||
case GE_MTX_BONE3:
|
||||
case GE_MTX_BONE4:
|
||||
case GE_MTX_BONE5:
|
||||
case GE_MTX_BONE6:
|
||||
case GE_MTX_BONE7:
|
||||
{
|
||||
int n = type - GE_MTX_BONE0;
|
||||
__GeCopyMatrix(matrixPtr, gstate.boneMatrix + n * 12, 12 * sizeof(float));
|
||||
}
|
||||
break;
|
||||
case GE_MTX_TEXGEN:
|
||||
__GeCopyMatrix(matrixPtr, gstate.tgenMatrix, 12 * sizeof(float));
|
||||
break;
|
||||
case GE_MTX_WORLD:
|
||||
__GeCopyMatrix(matrixPtr, gstate.worldMatrix, 12 * sizeof(float));
|
||||
break;
|
||||
case GE_MTX_VIEW:
|
||||
__GeCopyMatrix(matrixPtr, gstate.viewMatrix, 12 * sizeof(float));
|
||||
break;
|
||||
case GE_MTX_PROJECTION:
|
||||
__GeCopyMatrix(matrixPtr, gstate.projMatrix, 16 * sizeof(float));
|
||||
break;
|
||||
default:
|
||||
return SCE_KERNEL_ERROR_INVALID_INDEX;
|
||||
}
|
||||
return 0;
|
||||
u32_le *dest = (u32_le *)Memory::GetPointerWriteUnchecked(matrixPtr);
|
||||
// Note: this reads the CPU-visible matrix values, which may differ from the actual used values.
|
||||
// They only differ when more DATA commands are sent than are valid for a matrix.
|
||||
if (!gpu || !gpu->GetMatrix24(GEMatrixType(type), dest, 0))
|
||||
return hleLogError(SCEGE, SCE_KERNEL_ERROR_INVALID_INDEX, "invalid matrix");
|
||||
|
||||
return hleLogSuccessInfoI(SCEGE, 0);
|
||||
}
|
||||
|
||||
static u32 sceGeGetCmd(int cmd) {
|
||||
@ -600,21 +570,17 @@ static int sceGeGetStack(int index, u32 stackPtr) {
|
||||
return gpu->GetStack(index, stackPtr);
|
||||
}
|
||||
|
||||
static u32 sceGeEdramSetAddrTranslation(int new_size) {
|
||||
static u32 sceGeEdramSetAddrTranslation(u32 new_size) {
|
||||
bool outsideRange = new_size != 0 && (new_size < 0x200 || new_size > 0x1000);
|
||||
bool notPowerOfTwo = (new_size & (new_size - 1)) != 0;
|
||||
if (outsideRange || notPowerOfTwo) {
|
||||
WARN_LOG(SCEGE, "sceGeEdramSetAddrTranslation(%i): invalid value", new_size);
|
||||
return SCE_KERNEL_ERROR_INVALID_VALUE;
|
||||
return hleLogWarning(SCEGE, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid value");
|
||||
}
|
||||
if (!gpu) {
|
||||
return hleLogError(SCEGE, -1, "GPUInterface not available");
|
||||
}
|
||||
|
||||
DEBUG_LOG(SCEGE, "sceGeEdramSetAddrTranslation(%i)", new_size);
|
||||
|
||||
// TODO: This isn't safe. EDRamWidth should be global and saved.
|
||||
static int EDRamWidth = 0x400;
|
||||
int last = EDRamWidth;
|
||||
EDRamWidth = new_size;
|
||||
return last;
|
||||
return hleReportDebug(SCEGE, gpu->SetAddrTranslation(new_size));
|
||||
}
|
||||
|
||||
const HLEFunction sceGe_user[] = {
|
||||
@ -629,9 +595,9 @@ const HLEFunction sceGe_user[] = {
|
||||
{0XA4FC06A4, &WrapU_U<sceGeSetCallback>, "sceGeSetCallback", 'i', "p" },
|
||||
{0X05DB22CE, &WrapI_U<sceGeUnsetCallback>, "sceGeUnsetCallback", 'i', "x" },
|
||||
{0X1F6752AD, &WrapU_V<sceGeEdramGetSize>, "sceGeEdramGetSize", 'x', "" },
|
||||
{0XB77905EA, &WrapU_I<sceGeEdramSetAddrTranslation>, "sceGeEdramSetAddrTranslation", 'x', "i" },
|
||||
{0XB77905EA, &WrapU_U<sceGeEdramSetAddrTranslation>, "sceGeEdramSetAddrTranslation", 'x', "x" },
|
||||
{0XDC93CFEF, &WrapU_I<sceGeGetCmd>, "sceGeGetCmd", 'x', "i" },
|
||||
{0X57C8945B, &WrapI_IU<sceGeGetMtx>, "sceGeGetMtx", 'i', "ix" },
|
||||
{0X57C8945B, &WrapI_IU<sceGeGetMtx>, "sceGeGetMtx", 'i', "ip" },
|
||||
{0X438A385A, &WrapU_U<sceGeSaveContext>, "sceGeSaveContext", 'x', "x" },
|
||||
{0X0BF608FB, &WrapU_U<sceGeRestoreContext>, "sceGeRestoreContext", 'x', "x" },
|
||||
{0X5FB86AB0, &WrapI_U<sceGeListDeQueue>, "sceGeListDeQueue", 'i', "x" },
|
||||
|
@ -820,7 +820,7 @@ const HLEFunction ThreadManForUser[] =
|
||||
{0X87D4DD36, &WrapI_IU<sceKernelCancelReceiveMbx>, "sceKernelCancelReceiveMbx", 'i', "ix" },
|
||||
{0XA8E8C846, &WrapI_IU<sceKernelReferMbxStatus>, "sceKernelReferMbxStatus", 'i', "ip" },
|
||||
|
||||
{0X7C0DC2A0, &WrapI_CIUUU<sceKernelCreateMsgPipe>, "sceKernelCreateMsgPipe", 'i', "sixxx" },
|
||||
{0X7C0DC2A0, &WrapI_CIUUU<sceKernelCreateMsgPipe>, "sceKernelCreateMsgPipe", 'i', "sixxp" },
|
||||
{0XF0B7DA1C, &WrapI_I<sceKernelDeleteMsgPipe>, "sceKernelDeleteMsgPipe", 'i', "i" },
|
||||
{0X876DBFAD, &WrapI_IUUUUU<sceKernelSendMsgPipe>, "sceKernelSendMsgPipe", 'i', "ixxxxx" },
|
||||
{0X7C41F2C2, &WrapI_IUUUUU<sceKernelSendMsgPipeCB>, "sceKernelSendMsgPipeCB", 'i', "ixxxxx" },
|
||||
@ -831,7 +831,7 @@ const HLEFunction ThreadManForUser[] =
|
||||
{0X349B864D, &WrapI_IUU<sceKernelCancelMsgPipe>, "sceKernelCancelMsgPipe", 'i', "ixx" },
|
||||
{0X33BE4024, &WrapI_IU<sceKernelReferMsgPipeStatus>, "sceKernelReferMsgPipeStatus", 'i', "ip" },
|
||||
|
||||
{0X56C039B5, &WrapI_CIUUU<sceKernelCreateVpl>, "sceKernelCreateVpl", 'i', "sixxx" },
|
||||
{0X56C039B5, &WrapI_CIUUU<sceKernelCreateVpl>, "sceKernelCreateVpl", 'i', "sixxp" },
|
||||
{0X89B3D48C, &WrapI_I<sceKernelDeleteVpl>, "sceKernelDeleteVpl", 'i', "i" },
|
||||
{0XBED27435, &WrapI_IUUU<sceKernelAllocateVpl>, "sceKernelAllocateVpl", 'i', "ixxx", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
|
||||
{0XEC0A693F, &WrapI_IUUU<sceKernelAllocateVplCB>, "sceKernelAllocateVplCB", 'i', "ixxx", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
|
||||
@ -840,7 +840,7 @@ const HLEFunction ThreadManForUser[] =
|
||||
{0X1D371B8A, &WrapI_IU<sceKernelCancelVpl>, "sceKernelCancelVpl", 'i', "ix" },
|
||||
{0X39810265, &WrapI_IU<sceKernelReferVplStatus>, "sceKernelReferVplStatus", 'i', "ip" },
|
||||
|
||||
{0XC07BB470, &WrapI_CUUUUU<sceKernelCreateFpl>, "sceKernelCreateFpl", 'i', "sxxxxx" },
|
||||
{0XC07BB470, &WrapI_CUUUUU<sceKernelCreateFpl>, "sceKernelCreateFpl", 'i', "sixxxp" },
|
||||
{0XED1410E0, &WrapI_I<sceKernelDeleteFpl>, "sceKernelDeleteFpl", 'i', "i" },
|
||||
{0XD979E9BF, &WrapI_IUU<sceKernelAllocateFpl>, "sceKernelAllocateFpl", 'i', "ixx", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
|
||||
{0XE7282CB6, &WrapI_IUU<sceKernelAllocateFplCB>, "sceKernelAllocateFplCB", 'i', "ixx", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
|
||||
@ -864,7 +864,7 @@ const HLEFunction ThreadManForUser[] =
|
||||
{0XD8B299AE, &WrapU_IUUU<sceKernelSetVTimerHandler>, "sceKernelSetVTimerHandler", 'x', "ixxx" },
|
||||
{0X53B00E9A, &WrapU_IU64UU<sceKernelSetVTimerHandlerWide>, "sceKernelSetVTimerHandlerWide", 'x', "iXxx" },
|
||||
|
||||
{0X8DAFF657, &WrapI_CUUUUU<sceKernelCreateTlspl>, "sceKernelCreateTlspl", 'i', "sxxxxx" },
|
||||
{0X8DAFF657, &WrapI_CUUUUU<sceKernelCreateTlspl>, "sceKernelCreateTlspl", 'i', "sixxxp" },
|
||||
{0X32BF938E, &WrapI_I<sceKernelDeleteTlspl>, "sceKernelDeleteTlspl", 'i', "i" },
|
||||
{0X721067F3, &WrapI_IU<sceKernelReferTlsplStatus>, "sceKernelReferTlsplStatus", 'i', "xp" },
|
||||
// Not completely certain about args.
|
||||
@ -908,7 +908,7 @@ const HLEFunction ThreadManForKernel[] =
|
||||
{0x1fb15a32, &WrapU_IU<sceKernelSetEventFlag>, "sceKernelSetEventFlag", 'x', "ix", HLE_KERNEL_SYSCALL },
|
||||
{0x812346e4, &WrapU_IU<sceKernelClearEventFlag>, "sceKernelClearEventFlag", 'x', "ix", HLE_KERNEL_SYSCALL },
|
||||
{0x402fcf22, &WrapI_IUUUU<sceKernelWaitEventFlag>, "sceKernelWaitEventFlag", 'i', "ixxpp", HLE_NOT_IN_INTERRUPT | HLE_KERNEL_SYSCALL},
|
||||
{0xc07bb470, &WrapI_CUUUUU<sceKernelCreateFpl>, "sceKernelCreateFpl", 'i', "sxxxxx" ,HLE_KERNEL_SYSCALL },
|
||||
{0xc07bb470, &WrapI_CUUUUU<sceKernelCreateFpl>, "sceKernelCreateFpl", 'i', "sixxxp" ,HLE_KERNEL_SYSCALL },
|
||||
{0xed1410e0, &WrapI_I<sceKernelDeleteFpl>, "sceKernelDeleteFpl", 'i', "i" ,HLE_KERNEL_SYSCALL },
|
||||
{0x623ae665, &WrapI_IU<sceKernelTryAllocateFpl>, "sceKernelTryAllocateFpl", 'i', "ix" ,HLE_KERNEL_SYSCALL },
|
||||
{0x616403ba, &WrapI_I<sceKernelTerminateThread>, "sceKernelTerminateThread", 'i', "i" ,HLE_KERNEL_SYSCALL },
|
||||
@ -932,7 +932,7 @@ const HLEFunction ThreadManForKernel[] =
|
||||
{0x0D81716A, &WrapI_IU<sceKernelPollMbx>, "sceKernelPollMbx", 'i', "ix", HLE_KERNEL_SYSCALL },
|
||||
{0x87D4DD36, &WrapI_IU<sceKernelCancelReceiveMbx>, "sceKernelCancelReceiveMbx", 'i', "ix", HLE_KERNEL_SYSCALL },
|
||||
{0xA8E8C846, &WrapI_IU<sceKernelReferMbxStatus>, "sceKernelReferMbxStatus", 'i', "ip", HLE_KERNEL_SYSCALL },
|
||||
{0x56C039B5, &WrapI_CIUUU<sceKernelCreateVpl>, "sceKernelCreateVpl", 'i', "sixxx", HLE_KERNEL_SYSCALL },
|
||||
{0x56C039B5, &WrapI_CIUUU<sceKernelCreateVpl>, "sceKernelCreateVpl", 'i', "sixxp", HLE_KERNEL_SYSCALL },
|
||||
{0x89B3D48C, &WrapI_I<sceKernelDeleteVpl>, "sceKernelDeleteVpl", 'i', "i", HLE_KERNEL_SYSCALL },
|
||||
{0xBED27435, &WrapI_IUUU<sceKernelAllocateVpl>, "sceKernelAllocateVpl", 'i', "ixxx", HLE_KERNEL_SYSCALL | HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
|
||||
{0xEC0A693F, &WrapI_IUUU<sceKernelAllocateVplCB>, "sceKernelAllocateVplCB", 'i', "ixxx", HLE_KERNEL_SYSCALL | HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
|
||||
|
@ -46,6 +46,7 @@ const int TLSPL_NUM_INDEXES = 16;
|
||||
// STATE BEGIN
|
||||
BlockAllocator userMemory(256);
|
||||
BlockAllocator kernelMemory(256);
|
||||
BlockAllocator volatileMemory(256);
|
||||
|
||||
static int vplWaitTimer = -1;
|
||||
static int fplWaitTimer = -1;
|
||||
@ -432,6 +433,7 @@ void __KernelMemoryInit()
|
||||
MemBlockInfoInit();
|
||||
kernelMemory.Init(PSP_GetKernelMemoryBase(), PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase(), false);
|
||||
userMemory.Init(PSP_GetUserMemoryBase(), PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase(), false);
|
||||
volatileMemory.Init(PSP_GetVolatileMemoryStart(), PSP_GetVolatileMemoryEnd() - PSP_GetVolatileMemoryStart(), false);
|
||||
ParallelMemset(&g_threadManager, Memory::GetPointerWrite(PSP_GetKernelMemoryBase()), 0, PSP_GetUserMemoryEnd() - PSP_GetKernelMemoryBase());
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd() - PSP_GetKernelMemoryBase(), "MemInit");
|
||||
INFO_LOG(SCEKERNEL, "Kernel and user memory pools initialized");
|
||||
@ -457,12 +459,14 @@ void __KernelMemoryInit()
|
||||
|
||||
void __KernelMemoryDoState(PointerWrap &p)
|
||||
{
|
||||
auto s = p.Section("sceKernelMemory", 1, 2);
|
||||
auto s = p.Section("sceKernelMemory", 1, 3);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
kernelMemory.DoState(p);
|
||||
userMemory.DoState(p);
|
||||
if (s >= 3)
|
||||
volatileMemory.DoState(p);
|
||||
|
||||
Do(p, vplWaitTimer);
|
||||
CoreTiming::RestoreRegisterEvent(vplWaitTimer, "VplTimeout", __KernelVplTimeout);
|
||||
@ -481,6 +485,11 @@ void __KernelMemoryDoState(PointerWrap &p)
|
||||
|
||||
void __KernelMemoryShutdown()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
INFO_LOG(SCEKERNEL, "Shutting down volatile memory pool: ");
|
||||
volatileMemory.ListBlocks();
|
||||
#endif
|
||||
volatileMemory.Shutdown();
|
||||
#ifdef _DEBUG
|
||||
INFO_LOG(SCEKERNEL,"Shutting down user memory pool: ");
|
||||
userMemory.ListBlocks();
|
||||
@ -495,6 +504,56 @@ void __KernelMemoryShutdown()
|
||||
MemBlockInfoShutdown();
|
||||
}
|
||||
|
||||
BlockAllocator *BlockAllocatorFromID(int id) {
|
||||
switch (id) {
|
||||
case 1:
|
||||
case 3:
|
||||
case 4:
|
||||
if (hleIsKernelMode())
|
||||
return &kernelMemory;
|
||||
return nullptr;
|
||||
|
||||
case 2:
|
||||
case 6:
|
||||
return &userMemory;
|
||||
|
||||
case 8:
|
||||
case 10:
|
||||
if (hleIsKernelMode())
|
||||
return &userMemory;
|
||||
return nullptr;
|
||||
|
||||
case 5:
|
||||
return &volatileMemory;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int BlockAllocatorToID(const BlockAllocator *alloc) {
|
||||
if (alloc == &kernelMemory)
|
||||
return 1;
|
||||
if (alloc == &userMemory)
|
||||
return 2;
|
||||
if (alloc == &volatileMemory)
|
||||
return 5;
|
||||
return 0;
|
||||
}
|
||||
|
||||
BlockAllocator *BlockAllocatorFromAddr(u32 addr) {
|
||||
addr &= 0x3FFFFFFF;
|
||||
if (Memory::IsKernelAndNotVolatileAddress(addr))
|
||||
return &kernelMemory;
|
||||
if (Memory::IsKernelAddress(addr))
|
||||
return &volatileMemory;
|
||||
if (Memory::IsRAMAddress(addr))
|
||||
return &userMemory;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
enum SceKernelFplAttr
|
||||
{
|
||||
PSP_FPL_ATTR_FIFO = 0x0000,
|
||||
@ -580,29 +639,18 @@ static void __KernelSortFplThreads(FPL *fpl)
|
||||
std::stable_sort(fpl->waitingThreads.begin(), fpl->waitingThreads.end(), __FplThreadSortPriority);
|
||||
}
|
||||
|
||||
int sceKernelCreateFpl(const char *name, u32 mpid, u32 attr, u32 blockSize, u32 numBlocks, u32 optPtr)
|
||||
{
|
||||
int sceKernelCreateFpl(const char *name, u32 mpid, u32 attr, u32 blockSize, u32 numBlocks, u32 optPtr) {
|
||||
if (!name)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateFpl(): invalid name", SCE_KERNEL_ERROR_NO_MEMORY);
|
||||
return SCE_KERNEL_ERROR_NO_MEMORY;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_NO_MEMORY, "invalid name");
|
||||
if (mpid < 1 || mpid > 9 || mpid == 7)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateFpl(): invalid partition %d", SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, mpid);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
// We only support user right now.
|
||||
if (mpid != 2 && mpid != 6)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateFpl(): invalid partition %d", SCE_KERNEL_ERROR_ILLEGAL_PERM, mpid);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_PERM;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "invalid partition %d", mpid);
|
||||
|
||||
BlockAllocator *allocator = BlockAllocatorFromID(mpid);
|
||||
if (allocator == nullptr)
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_PERM, "invalid partition %d", mpid);
|
||||
if (((attr & ~PSP_FPL_ATTR_KNOWN) & ~0xFF) != 0)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateFpl(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, attr);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ATTR, "invalid attr parameter: %08x", attr);
|
||||
|
||||
// There's probably a simpler way to get this same basic formula...
|
||||
// This is based on results from a PSP.
|
||||
bool illegalMemSize = blockSize == 0 || numBlocks == 0;
|
||||
@ -611,25 +659,16 @@ int sceKernelCreateFpl(const char *name, u32 mpid, u32 attr, u32 blockSize, u32
|
||||
if (!illegalMemSize && (u64) numBlocks >= 0x100000000ULL / (((u64) blockSize + 3ULL) & ~3ULL))
|
||||
illegalMemSize = true;
|
||||
if (illegalMemSize)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateFpl(): invalid blockSize/count", SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE;
|
||||
}
|
||||
return hleReportWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE, "invalid blockSize/count");
|
||||
|
||||
int alignment = 4;
|
||||
if (optPtr != 0)
|
||||
{
|
||||
u32 size = Memory::Read_U32(optPtr);
|
||||
if (size > 8)
|
||||
WARN_LOG_REPORT(SCEKERNEL, "sceKernelCreateFpl(): unsupported extra options, size = %d", size);
|
||||
if (Memory::IsValidRange(optPtr, 4)) {
|
||||
u32 size = Memory::ReadUnchecked_U32(optPtr);
|
||||
if (size >= 4)
|
||||
alignment = Memory::Read_U32(optPtr + 4);
|
||||
// Must be a power of 2 to be valid.
|
||||
if ((alignment & (alignment - 1)) != 0)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateFpl(): invalid alignment %d", SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, alignment);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "invalid alignment %d", alignment);
|
||||
}
|
||||
|
||||
if (alignment < 4)
|
||||
@ -638,9 +677,8 @@ int sceKernelCreateFpl(const char *name, u32 mpid, u32 attr, u32 blockSize, u32
|
||||
int alignedSize = ((int)blockSize + alignment - 1) & ~(alignment - 1);
|
||||
u32 totalSize = alignedSize * numBlocks;
|
||||
bool atEnd = (attr & PSP_FPL_ATTR_HIGHMEM) != 0;
|
||||
u32 address = userMemory.Alloc(totalSize, atEnd, "FPL");
|
||||
if (address == (u32)-1)
|
||||
{
|
||||
u32 address = allocator->Alloc(totalSize, atEnd, "FPL");
|
||||
if (address == (u32)-1) {
|
||||
DEBUG_LOG(SCEKERNEL, "sceKernelCreateFpl(\"%s\", partition=%i, attr=%08x, bsize=%i, nb=%i) FAILED - out of ram",
|
||||
name, mpid, attr, blockSize, numBlocks);
|
||||
return SCE_KERNEL_ERROR_NO_MEMORY;
|
||||
@ -682,7 +720,10 @@ int sceKernelDeleteFpl(SceUID uid)
|
||||
if (wokeThreads)
|
||||
hleReSchedule("fpl deleted");
|
||||
|
||||
userMemory.Free(fpl->address);
|
||||
BlockAllocator *alloc = BlockAllocatorFromAddr(fpl->address);
|
||||
_assert_msg_(alloc != nullptr, "Should always have a valid allocator/address");
|
||||
if (alloc)
|
||||
alloc->Free(fpl->address);
|
||||
return kernelObjects.Destroy<FPL>(uid);
|
||||
}
|
||||
else
|
||||
@ -955,18 +996,23 @@ public:
|
||||
alloc->Free(address);
|
||||
}
|
||||
bool IsValid() {return address != (u32)-1;}
|
||||
BlockAllocator *alloc;
|
||||
|
||||
void DoState(PointerWrap &p) override
|
||||
{
|
||||
auto s = p.Section("PMB", 1);
|
||||
auto s = p.Section("PMB", 1, 2);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
Do(p, address);
|
||||
DoArray(p, name, sizeof(name));
|
||||
if (s >= 2) {
|
||||
int allocType = BlockAllocatorToID(alloc);
|
||||
Do(p, allocType);
|
||||
alloc = BlockAllocatorFromID(allocType);
|
||||
}
|
||||
}
|
||||
|
||||
BlockAllocator *alloc;
|
||||
u32 address;
|
||||
char name[32];
|
||||
};
|
||||
@ -986,44 +1032,28 @@ static u32 sceKernelTotalFreeMemSize()
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int sceKernelAllocPartitionMemory(int partition, const char *name, int type, u32 size, u32 addr)
|
||||
{
|
||||
if (name == NULL)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelAllocPartitionMemory(): invalid name", SCE_KERNEL_ERROR_ERROR);
|
||||
return SCE_KERNEL_ERROR_ERROR;
|
||||
}
|
||||
if (size == 0)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelAllocPartitionMemory(): invalid size %x", SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED, size);
|
||||
return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED;
|
||||
int sceKernelAllocPartitionMemory(int partition, const char *name, int type, u32 size, u32 addr) {
|
||||
if (type < PSP_SMEM_Low || type > PSP_SMEM_HighAligned)
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_MEMBLOCKTYPE, "invalid type %x", type);
|
||||
// Alignment is only allowed for powers of 2.
|
||||
if (type == PSP_SMEM_LowAligned || type == PSP_SMEM_HighAligned) {
|
||||
if ((addr & (addr - 1)) != 0 || addr == 0)
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ALIGNMENT_SIZE, "invalid alignment %x", addr);
|
||||
}
|
||||
if (partition < 1 || partition > 9 || partition == 7)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelAllocPartitionMemory(): invalid partition %x", SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, partition);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
// We only support user right now.
|
||||
if (partition != 2 && partition != 5 && partition != 6)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelAllocPartitionMemory(): invalid partition %x", SCE_KERNEL_ERROR_ILLEGAL_PARTITION, partition);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_PARTITION;
|
||||
}
|
||||
if (type < PSP_SMEM_Low || type > PSP_SMEM_HighAligned)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelAllocPartitionMemory(): invalid type %x", SCE_KERNEL_ERROR_ILLEGAL_MEMBLOCKTYPE, type);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_MEMBLOCKTYPE;
|
||||
}
|
||||
// Alignment is only allowed for powers of 2.
|
||||
if ((type == PSP_SMEM_LowAligned || type == PSP_SMEM_HighAligned) && ((addr & (addr - 1)) != 0 || addr == 0))
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelAllocPartitionMemory(): invalid alignment %x", SCE_KERNEL_ERROR_ILLEGAL_ALIGNMENT_SIZE, addr);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_ALIGNMENT_SIZE;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "invalid partition %x", partition);
|
||||
|
||||
PartitionMemoryBlock *block = new PartitionMemoryBlock(&userMemory, name, size, (MemblockType)type, addr);
|
||||
if (!block->IsValid())
|
||||
{
|
||||
BlockAllocator *allocator = BlockAllocatorFromID(partition);
|
||||
if (allocator == nullptr)
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_PARTITION, "invalid partition %x", partition);
|
||||
|
||||
if (name == nullptr)
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ERROR, "invalid name");
|
||||
if (size == 0)
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED, "invalid size %x", size);
|
||||
|
||||
PartitionMemoryBlock *block = new PartitionMemoryBlock(allocator, name, size, (MemblockType)type, addr);
|
||||
if (!block->IsValid()) {
|
||||
delete block;
|
||||
ERROR_LOG(SCEKERNEL, "sceKernelAllocPartitionMemory(partition = %i, %s, type= %i, size= %i, addr= %08x): allocation failed", partition, name, type, size, addr);
|
||||
return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED;
|
||||
@ -1451,40 +1481,23 @@ static void __KernelSortVplThreads(VPL *vpl)
|
||||
std::stable_sort(vpl->waitingThreads.begin(), vpl->waitingThreads.end(), __VplThreadSortPriority);
|
||||
}
|
||||
|
||||
SceUID sceKernelCreateVpl(const char *name, int partition, u32 attr, u32 vplSize, u32 optPtr)
|
||||
{
|
||||
SceUID sceKernelCreateVpl(const char *name, int partition, u32 attr, u32 vplSize, u32 optPtr) {
|
||||
if (!name)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateVpl(): invalid name", SCE_KERNEL_ERROR_ERROR);
|
||||
return SCE_KERNEL_ERROR_ERROR;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ERROR, "invalid name");
|
||||
if (partition < 1 || partition > 9 || partition == 7)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateVpl(): invalid partition %d", SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, partition);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
// We only support user right now.
|
||||
if (partition != 2 && partition != 6)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateVpl(): invalid partition %d", SCE_KERNEL_ERROR_ILLEGAL_PERM, partition);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_PERM;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "invalid partition %d", partition);
|
||||
|
||||
BlockAllocator *allocator = BlockAllocatorFromID(partition);
|
||||
if (allocator == nullptr)
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_PERM, "invalid partition %d", partition);
|
||||
|
||||
if (((attr & ~PSP_VPL_ATTR_KNOWN) & ~0xFF) != 0)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateVpl(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, attr);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ATTR, "invalid attr parameter: %08x", attr);
|
||||
if (vplSize == 0)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateVpl(): invalid size", SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE, "invalid size");
|
||||
// Block Allocator seems to A-OK this, let's stop it here.
|
||||
if (vplSize >= 0x80000000)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateVpl(): way too big size", SCE_KERNEL_ERROR_NO_MEMORY);
|
||||
return SCE_KERNEL_ERROR_NO_MEMORY;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_NO_MEMORY, "way too big size");
|
||||
|
||||
// Can't have that little space in a Vpl, sorry.
|
||||
if (vplSize <= 0x30)
|
||||
@ -1493,12 +1506,9 @@ SceUID sceKernelCreateVpl(const char *name, int partition, u32 attr, u32 vplSize
|
||||
|
||||
// We ignore the upalign to 256 and do it ourselves by 8.
|
||||
u32 allocSize = vplSize;
|
||||
u32 memBlockPtr = userMemory.Alloc(allocSize, (attr & PSP_VPL_ATTR_HIGHMEM) != 0, "VPL");
|
||||
u32 memBlockPtr = allocator->Alloc(allocSize, (attr & PSP_VPL_ATTR_HIGHMEM) != 0, "VPL");
|
||||
if (memBlockPtr == (u32)-1)
|
||||
{
|
||||
ERROR_LOG(SCEKERNEL, "sceKernelCreateVpl(): Failed to allocate %i bytes of pool data", vplSize);
|
||||
return SCE_KERNEL_ERROR_NO_MEMORY;
|
||||
}
|
||||
return hleLogError(SCEKERNEL, SCE_KERNEL_ERROR_NO_MEMORY, "failed to allocate %i bytes of pool data", vplSize);
|
||||
|
||||
VPL *vpl = new VPL;
|
||||
SceUID id = kernelObjects.Create(vpl);
|
||||
@ -1542,7 +1552,10 @@ int sceKernelDeleteVpl(SceUID uid)
|
||||
if (wokeThreads)
|
||||
hleReSchedule("vpl deleted");
|
||||
|
||||
userMemory.Free(vpl->address);
|
||||
BlockAllocator *alloc = BlockAllocatorFromAddr(vpl->address);
|
||||
_assert_msg_(alloc != nullptr, "Should always have a valid allocator/address");
|
||||
if (alloc)
|
||||
alloc->Free(vpl->address);
|
||||
kernelObjects.Destroy<VPL>(uid);
|
||||
return 0;
|
||||
}
|
||||
@ -1994,7 +2007,7 @@ int __KernelFreeTls(TLSPL *tls, SceUID threadID)
|
||||
__KernelResumeThreadFromWait(waitingThreadID, freedAddress);
|
||||
|
||||
// Gotta watch the thread to quit as well, since they've allocated now.
|
||||
tlsplThreadEndChecks.insert(std::make_pair(waitingThreadID, uid));
|
||||
tlsplThreadEndChecks.emplace(waitingThreadID, uid);
|
||||
|
||||
// No need to continue or free it, we're done.
|
||||
return 0;
|
||||
@ -2044,29 +2057,17 @@ void __KernelTlsplThreadEnd(SceUID threadID)
|
||||
tlsplThreadEndChecks.erase(locked.first, locked.second);
|
||||
}
|
||||
|
||||
SceUID sceKernelCreateTlspl(const char *name, u32 partition, u32 attr, u32 blockSize, u32 count, u32 optionsPtr)
|
||||
{
|
||||
SceUID sceKernelCreateTlspl(const char *name, u32 partition, u32 attr, u32 blockSize, u32 count, u32 optionsPtr) {
|
||||
if (!name)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateTlspl(): invalid name", SCE_KERNEL_ERROR_NO_MEMORY);
|
||||
return SCE_KERNEL_ERROR_NO_MEMORY;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_NO_MEMORY, "invalid name");
|
||||
if ((attr & ~PSP_TLSPL_ATTR_KNOWN) >= 0x100)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateTlspl(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, attr);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ATTR, "invalid attr parameter: %08x", attr);
|
||||
if (partition < 1 || partition > 9 || partition == 7)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateTlspl(): invalid partition %d", SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, partition);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
// We only support user right now.
|
||||
if (partition != 2 && partition != 6)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateTlspl(): invalid partition %d", SCE_KERNEL_ERROR_ILLEGAL_PERM, partition);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_PERM;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "invalid partition %d", partition);
|
||||
|
||||
BlockAllocator *allocator = BlockAllocatorFromID(partition);
|
||||
if (allocator == nullptr)
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_PERM, "invalid partition %x", partition);
|
||||
|
||||
// There's probably a simpler way to get this same basic formula...
|
||||
// This is based on results from a PSP.
|
||||
@ -2076,41 +2077,29 @@ SceUID sceKernelCreateTlspl(const char *name, u32 partition, u32 attr, u32 block
|
||||
if (!illegalMemSize && (u64) count >= 0x100000000ULL / (((u64) blockSize + 3ULL) & ~3ULL))
|
||||
illegalMemSize = true;
|
||||
if (illegalMemSize)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateTlspl(): invalid blockSize/count", SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE, "invalid blockSize/count");
|
||||
|
||||
int index = -1;
|
||||
for (int i = 0; i < TLSPL_NUM_INDEXES; ++i)
|
||||
if (tlsplUsedIndexes[i] == false)
|
||||
{
|
||||
for (int i = 0; i < TLSPL_NUM_INDEXES; ++i) {
|
||||
if (tlsplUsedIndexes[i] == false) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateTlspl(): ran out of indexes for TLS pools", PSP_ERROR_TOO_MANY_TLSPL);
|
||||
return PSP_ERROR_TOO_MANY_TLSPL;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, PSP_ERROR_TOO_MANY_TLSPL, "ran out of indexes for TLS pools");
|
||||
|
||||
// Unless otherwise specified, we align to 4 bytes (a mips word.)
|
||||
u32 alignment = 4;
|
||||
if (optionsPtr != 0)
|
||||
{
|
||||
u32 size = Memory::Read_U32(optionsPtr);
|
||||
if (size > 8)
|
||||
WARN_LOG_REPORT(SCEKERNEL, "sceKernelCreateTlspl(%s) unsupported options parameter, size = %d", name, size);
|
||||
if (Memory::IsValidRange(optionsPtr, 4)) {
|
||||
u32 size = Memory::ReadUnchecked_U32(optionsPtr);
|
||||
if (size >= 8)
|
||||
alignment = Memory::Read_U32(optionsPtr + 4);
|
||||
|
||||
// Note that 0 intentionally is allowed.
|
||||
if ((alignment & (alignment - 1)) != 0)
|
||||
{
|
||||
ERROR_LOG_REPORT(SCEKERNEL, "sceKernelCreateTlspl(%s): alignment is not a power of 2: %d", name, alignment);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
return hleLogError(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "alignment is not a power of 2: %d", alignment);
|
||||
// This goes for 0, 1, and 2. Can't have less than 4 byte alignment.
|
||||
if (alignment < 4)
|
||||
alignment = 4;
|
||||
@ -2120,16 +2109,13 @@ SceUID sceKernelCreateTlspl(const char *name, u32 partition, u32 attr, u32 block
|
||||
u32 alignedSize = (blockSize + alignment - 1) & ~(alignment - 1);
|
||||
|
||||
u32 totalSize = alignedSize * count;
|
||||
u32 blockPtr = userMemory.Alloc(totalSize, (attr & PSP_TLSPL_ATTR_HIGHMEM) != 0, name);
|
||||
u32 blockPtr = allocator->Alloc(totalSize, (attr & PSP_TLSPL_ATTR_HIGHMEM) != 0, name);
|
||||
#ifdef _DEBUG
|
||||
userMemory.ListBlocks();
|
||||
allocator->ListBlocks();
|
||||
#endif
|
||||
|
||||
if (blockPtr == (u32) -1)
|
||||
{
|
||||
ERROR_LOG(SCEKERNEL, "%08x=sceKernelCreateTlspl(%s, %d, %08x, %d, %d, %08x): failed to allocate memory", SCE_KERNEL_ERROR_NO_MEMORY, name, partition, attr, blockSize, count, optionsPtr);
|
||||
return SCE_KERNEL_ERROR_NO_MEMORY;
|
||||
}
|
||||
if (blockPtr == (u32)-1)
|
||||
return hleLogError(SCEKERNEL, SCE_KERNEL_ERROR_NO_MEMORY, "failed to allocate memory");
|
||||
|
||||
TLSPL *tls = new TLSPL();
|
||||
SceUID id = kernelObjects.Create(tls);
|
||||
@ -2148,9 +2134,7 @@ SceUID sceKernelCreateTlspl(const char *name, u32 partition, u32 attr, u32 block
|
||||
tls->alignment = alignment;
|
||||
tls->usage.resize(count, 0);
|
||||
|
||||
WARN_LOG(SCEKERNEL, "%08x=sceKernelCreateTlspl(%s, %d, %08x, %d, %d, %08x)", id, name, partition, attr, blockSize, count, optionsPtr);
|
||||
|
||||
return id;
|
||||
return hleLogSuccessInfoI(SCEKERNEL, id);
|
||||
}
|
||||
|
||||
int sceKernelDeleteTlspl(SceUID uid)
|
||||
@ -2178,7 +2162,10 @@ int sceKernelDeleteTlspl(SceUID uid)
|
||||
HLEKernel::ResumeFromWait(threadID, WAITTYPE_TLSPL, uid, 0);
|
||||
hleReSchedule("deleted tlspl");
|
||||
|
||||
userMemory.Free(tls->address);
|
||||
BlockAllocator *allocator = BlockAllocatorFromAddr(tls->address);
|
||||
_assert_msg_(allocator != nullptr, "Should always have a valid allocator/address");
|
||||
if (allocator)
|
||||
allocator->Free(tls->address);
|
||||
tlsplUsedIndexes[tls->ntls.index] = false;
|
||||
kernelObjects.Destroy<TLSPL>(uid);
|
||||
}
|
||||
@ -2187,68 +2174,89 @@ int sceKernelDeleteTlspl(SceUID uid)
|
||||
return error;
|
||||
}
|
||||
|
||||
int sceKernelGetTlsAddr(SceUID uid)
|
||||
{
|
||||
// TODO: Allocate downward if PSP_TLSPL_ATTR_HIGHMEM?
|
||||
DEBUG_LOG(SCEKERNEL, "sceKernelGetTlsAddr(%08x)", uid);
|
||||
struct FindTLSByIndexArg {
|
||||
int index;
|
||||
TLSPL *result = nullptr;
|
||||
};
|
||||
|
||||
static bool FindTLSByIndex(TLSPL *possible, FindTLSByIndexArg *state) {
|
||||
if (possible->ntls.index == state->index) {
|
||||
state->result = possible;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int sceKernelGetTlsAddr(SceUID uid) {
|
||||
if (!__KernelIsDispatchEnabled() || __IsInInterrupt())
|
||||
return 0;
|
||||
return hleLogWarning(SCEKERNEL, 0, "dispatch disabled");
|
||||
|
||||
u32 error;
|
||||
TLSPL *tls = kernelObjects.Get<TLSPL>(uid, error);
|
||||
if (tls)
|
||||
{
|
||||
SceUID threadID = __KernelGetCurThread();
|
||||
int allocBlock = -1;
|
||||
bool needsClear = false;
|
||||
if (!tls) {
|
||||
if (uid < 0)
|
||||
return hleLogError(SCEKERNEL, 0, "tlspl not found");
|
||||
|
||||
// If the thread already has one, return it.
|
||||
// There's this weird behavior where it looks up by index. Maybe we shouldn't use uids...
|
||||
if (!tlsplUsedIndexes[(uid >> 3) & 15])
|
||||
return hleLogError(SCEKERNEL, 0, "tlspl not found");
|
||||
|
||||
FindTLSByIndexArg state;
|
||||
state.index = (uid >> 3) & 15;
|
||||
kernelObjects.Iterate<TLSPL>(&FindTLSByIndex, &state);
|
||||
if (!state.result)
|
||||
return hleLogError(SCEKERNEL, 0, "tlspl not found");
|
||||
|
||||
tls = state.result;
|
||||
}
|
||||
|
||||
SceUID threadID = __KernelGetCurThread();
|
||||
int allocBlock = -1;
|
||||
bool needsClear = false;
|
||||
|
||||
// If the thread already has one, return it.
|
||||
for (size_t i = 0; i < tls->ntls.totalBlocks && allocBlock == -1; ++i)
|
||||
{
|
||||
if (tls->usage[i] == threadID)
|
||||
allocBlock = (int) i;
|
||||
}
|
||||
|
||||
if (allocBlock == -1)
|
||||
{
|
||||
for (size_t i = 0; i < tls->ntls.totalBlocks && allocBlock == -1; ++i)
|
||||
{
|
||||
if (tls->usage[i] == threadID)
|
||||
allocBlock = (int) i;
|
||||
// The PSP doesn't give the same block out twice in a row, even if freed.
|
||||
if (tls->usage[tls->next] == 0)
|
||||
allocBlock = tls->next;
|
||||
tls->next = (tls->next + 1) % tls->ntls.totalBlocks;
|
||||
}
|
||||
|
||||
if (allocBlock == -1)
|
||||
if (allocBlock != -1)
|
||||
{
|
||||
for (size_t i = 0; i < tls->ntls.totalBlocks && allocBlock == -1; ++i)
|
||||
{
|
||||
// The PSP doesn't give the same block out twice in a row, even if freed.
|
||||
if (tls->usage[tls->next] == 0)
|
||||
allocBlock = tls->next;
|
||||
tls->next = (tls->next + 1) % tls->ntls.totalBlocks;
|
||||
}
|
||||
|
||||
if (allocBlock != -1)
|
||||
{
|
||||
tls->usage[allocBlock] = threadID;
|
||||
tlsplThreadEndChecks.insert(std::make_pair(threadID, uid));
|
||||
--tls->ntls.freeBlocks;
|
||||
needsClear = true;
|
||||
}
|
||||
tls->usage[allocBlock] = threadID;
|
||||
tlsplThreadEndChecks.emplace(threadID, uid);
|
||||
--tls->ntls.freeBlocks;
|
||||
needsClear = true;
|
||||
}
|
||||
|
||||
if (allocBlock == -1)
|
||||
{
|
||||
tls->waitingThreads.push_back(threadID);
|
||||
__KernelWaitCurThread(WAITTYPE_TLSPL, uid, 1, 0, false, "allocate tls");
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 alignedSize = (tls->ntls.blockSize + tls->alignment - 1) & ~(tls->alignment - 1);
|
||||
u32 allocAddress = tls->address + allocBlock * alignedSize;
|
||||
NotifyMemInfo(MemBlockFlags::SUB_ALLOC, allocAddress, tls->ntls.blockSize, "TlsAddr");
|
||||
|
||||
// We clear the blocks upon first allocation (and also when they are freed, both are necessary.)
|
||||
if (needsClear) {
|
||||
Memory::Memset(allocAddress, 0, tls->ntls.blockSize, "TlsAddr");
|
||||
}
|
||||
|
||||
return allocAddress;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
if (allocBlock == -1)
|
||||
{
|
||||
tls->waitingThreads.push_back(threadID);
|
||||
__KernelWaitCurThread(WAITTYPE_TLSPL, uid, 1, 0, false, "allocate tls");
|
||||
return hleLogDebug(SCEKERNEL, 0, "waiting for tls alloc");
|
||||
}
|
||||
|
||||
u32 alignedSize = (tls->ntls.blockSize + tls->alignment - 1) & ~(tls->alignment - 1);
|
||||
u32 allocAddress = tls->address + allocBlock * alignedSize;
|
||||
NotifyMemInfo(MemBlockFlags::SUB_ALLOC, allocAddress, tls->ntls.blockSize, "TlsAddr");
|
||||
|
||||
// We clear the blocks upon first allocation (and also when they are freed, both are necessary.)
|
||||
if (needsClear) {
|
||||
Memory::Memset(allocAddress, 0, tls->ntls.blockSize, "TlsAddr");
|
||||
}
|
||||
|
||||
return hleLogDebug(SCEKERNEL, allocAddress);
|
||||
}
|
||||
|
||||
// Parameters are an educated guess.
|
||||
|
@ -40,6 +40,10 @@ KernelObject *__KernelMemoryVPLObject();
|
||||
KernelObject *__KernelMemoryPMBObject();
|
||||
KernelObject *__KernelTlsplObject();
|
||||
|
||||
BlockAllocator *BlockAllocatorFromID(int id);
|
||||
int BlockAllocatorToID(const BlockAllocator *alloc);
|
||||
BlockAllocator *BlockAllocatorFromAddr(u32 addr);
|
||||
|
||||
SceUID sceKernelCreateVpl(const char *name, int partition, u32 attr, u32 vplSize, u32 optPtr);
|
||||
int sceKernelDeleteVpl(SceUID uid);
|
||||
int sceKernelAllocateVpl(SceUID uid, u32 size, u32 addrPtr, u32 timeoutPtr);
|
||||
|
@ -1144,6 +1144,7 @@ static int gzipDecompress(u8 *OutBuffer, int OutBufferLength, u8 *InBuffer) {
|
||||
}
|
||||
|
||||
static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 loadAddress, bool fromTop, std::string *error_string, u32 *magic, u32 &error) {
|
||||
u32 crc = crc32(0, ptr, (uInt)elfSize);
|
||||
PSPModule *module = new PSPModule();
|
||||
kernelObjects.Create(module);
|
||||
loadedModules.insert(module->GetUID());
|
||||
@ -1170,14 +1171,14 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
|
||||
|
||||
if (IsHLEVersionedModule(head->modname)) {
|
||||
int ver = (head->module_ver_hi << 8) | head->module_ver_lo;
|
||||
INFO_LOG(SCEMODULE, "Loading module %s with version %04x, devkit %08x", head->modname, ver, head->devkitversion);
|
||||
INFO_LOG(SCEMODULE, "Loading module %s with version %04x, devkit %08x, crc %x", head->modname, ver, head->devkitversion, crc);
|
||||
reportedModule = true;
|
||||
|
||||
if (!strcmp(head->modname, "sceMpeg_library")) {
|
||||
__MpegLoadModule(ver);
|
||||
__MpegLoadModule(ver, crc);
|
||||
}
|
||||
if (!strcmp(head->modname, "scePsmfP_library") || !strcmp(head->modname, "scePsmfPlayer")) {
|
||||
__PsmfPlayerLoadModule(head->devkitversion);
|
||||
__PsmfPlayerLoadModule(head->devkitversion, crc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1610,10 +1611,10 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
|
||||
INFO_LOG(SCEMODULE, "Loading module %s with version %04x, devkit %08x", modinfo->name, modinfo->moduleVersion, devkitVersion);
|
||||
|
||||
if (!strcmp(modinfo->name, "sceMpeg_library")) {
|
||||
__MpegLoadModule(modinfo->moduleVersion);
|
||||
__MpegLoadModule(modinfo->moduleVersion, crc);
|
||||
}
|
||||
if (!strcmp(modinfo->name, "scePsmfP_library") || !strcmp(modinfo->name, "scePsmfPlayer")) {
|
||||
__PsmfPlayerLoadModule(devkitVersion);
|
||||
__PsmfPlayerLoadModule(devkitVersion, crc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,10 +140,13 @@ struct MsgPipe : public KernelObject
|
||||
int GetIDType() const override { return SCE_KERNEL_TMID_Mpipe; }
|
||||
|
||||
MsgPipe() : buffer(0) {}
|
||||
~MsgPipe()
|
||||
{
|
||||
if (buffer != 0)
|
||||
userMemory.Free(buffer);
|
||||
~MsgPipe() {
|
||||
if (buffer != 0) {
|
||||
BlockAllocator *alloc = BlockAllocatorFromAddr(buffer);
|
||||
_assert_msg_(alloc != nullptr, "Should always have a valid allocator/address");
|
||||
if (alloc)
|
||||
alloc->Free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
u32 GetUsedSize()
|
||||
@ -667,41 +670,26 @@ void __KernelMsgPipeDoState(PointerWrap &p)
|
||||
CoreTiming::RestoreRegisterEvent(waitTimer, "MsgPipeTimeout", __KernelMsgPipeTimeout);
|
||||
}
|
||||
|
||||
int sceKernelCreateMsgPipe(const char *name, int partition, u32 attr, u32 size, u32 optionsPtr)
|
||||
{
|
||||
int sceKernelCreateMsgPipe(const char *name, int partition, u32 attr, u32 size, u32 optionsPtr) {
|
||||
if (!name)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateMsgPipe(): invalid name", SCE_KERNEL_ERROR_NO_MEMORY);
|
||||
return SCE_KERNEL_ERROR_NO_MEMORY;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_NO_MEMORY, "invalid name");
|
||||
if (partition < 1 || partition > 9 || partition == 7)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateMsgPipe(): invalid partition %d", SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, partition);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
// We only support user right now.
|
||||
if (partition != 2 && partition != 6)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateMsgPipe(): invalid partition %d", SCE_KERNEL_ERROR_ILLEGAL_PERM, partition);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_PERM;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "invalid partition %d", partition);
|
||||
|
||||
BlockAllocator *allocator = BlockAllocatorFromID(partition);
|
||||
if (allocator == nullptr)
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_PERM, "invalid partition %d", partition);
|
||||
|
||||
if ((attr & ~SCE_KERNEL_MPA_KNOWN) >= 0x100)
|
||||
{
|
||||
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateEventFlag(%s): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, name, attr);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
|
||||
}
|
||||
return hleLogWarning(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ATTR, "invalid attr parameter: %08x", attr);
|
||||
|
||||
u32 memBlockPtr = 0;
|
||||
if (size != 0)
|
||||
{
|
||||
if (size != 0) {
|
||||
// We ignore the upalign to 256.
|
||||
u32 allocSize = size;
|
||||
memBlockPtr = userMemory.Alloc(allocSize, (attr & SCE_KERNEL_MPA_HIGHMEM) != 0, "MsgPipe");
|
||||
memBlockPtr = allocator->Alloc(allocSize, (attr & SCE_KERNEL_MPA_HIGHMEM) != 0, "MsgPipe");
|
||||
if (memBlockPtr == (u32)-1)
|
||||
{
|
||||
ERROR_LOG(SCEKERNEL, "%08x=sceKernelCreateEventFlag(%s): Failed to allocate %i bytes for buffer", SCE_KERNEL_ERROR_NO_MEMORY, name, size);
|
||||
return SCE_KERNEL_ERROR_NO_MEMORY;
|
||||
}
|
||||
return hleLogError(SCEKERNEL, SCE_KERNEL_ERROR_NO_MEMORY, "failed to allocate %i bytes for buffer", size);
|
||||
}
|
||||
|
||||
MsgPipe *m = new MsgPipe();
|
||||
|
@ -213,7 +213,7 @@ static void __KernelMutexAcquireLock(PSPMutex *mutex, int count, SceUID thread)
|
||||
_dbg_assert_msg_((*iter).second != mutex->GetUID(), "Thread %d / mutex %d wasn't removed from mutexHeldLocks properly.", thread, mutex->GetUID());
|
||||
#endif
|
||||
|
||||
mutexHeldLocks.insert(std::make_pair(thread, mutex->GetUID()));
|
||||
mutexHeldLocks.emplace(thread, mutex->GetUID());
|
||||
|
||||
mutex->nm.lockLevel = count;
|
||||
mutex->nm.lockThread = thread;
|
||||
|
@ -230,7 +230,7 @@ public:
|
||||
MipsCallManager() : idGen_(0) {}
|
||||
u32 add(MipsCall *call) {
|
||||
u32 id = genId();
|
||||
calls_.insert(std::pair<int, MipsCall *>(id, call));
|
||||
calls_.emplace(id, call);
|
||||
return id;
|
||||
}
|
||||
MipsCall *get(u32 id) {
|
||||
|
@ -125,7 +125,7 @@ struct SceMpegLLI
|
||||
};
|
||||
|
||||
void SceMpegAu::read(u32 addr) {
|
||||
Memory::Memcpy(this, addr, sizeof(this), "SceMpegAu");
|
||||
Memory::Memcpy(this, addr, sizeof(*this), "SceMpegAu");
|
||||
pts = (pts & 0xFFFFFFFFULL) << 32 | (((u64)pts) >> 32);
|
||||
dts = (dts & 0xFFFFFFFFULL) << 32 | (((u64)dts) >> 32);
|
||||
}
|
||||
@ -133,7 +133,7 @@ void SceMpegAu::read(u32 addr) {
|
||||
void SceMpegAu::write(u32 addr) {
|
||||
pts = (pts & 0xFFFFFFFFULL) << 32 | (((u64)pts) >> 32);
|
||||
dts = (dts & 0xFFFFFFFFULL) << 32 | (((u64)dts) >> 32);
|
||||
Memory::Memcpy(addr, this, sizeof(this), "SceMpegAu");
|
||||
Memory::Memcpy(addr, this, sizeof(*this), "SceMpegAu");
|
||||
}
|
||||
|
||||
/*
|
||||
@ -258,7 +258,8 @@ struct MpegContext {
|
||||
};
|
||||
|
||||
static bool isMpegInit;
|
||||
static int mpegLibVersion;
|
||||
static int mpegLibVersion = 0;
|
||||
static u32 mpegLibCrc = 0;
|
||||
static u32 streamIdGen;
|
||||
static int actionPostPut;
|
||||
static std::map<u32, MpegContext *> mpegMap;
|
||||
@ -403,7 +404,7 @@ void __MpegInit() {
|
||||
}
|
||||
|
||||
void __MpegDoState(PointerWrap &p) {
|
||||
auto s = p.Section("sceMpeg", 1, 3);
|
||||
auto s = p.Section("sceMpeg", 1, 4);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
@ -421,7 +422,14 @@ void __MpegDoState(PointerWrap &p) {
|
||||
ringbufferPutPacketsAdded = 0;
|
||||
} else {
|
||||
Do(p, ringbufferPutPacketsAdded);
|
||||
}
|
||||
if (s < 4) {
|
||||
mpegLibCrc = 0;
|
||||
}
|
||||
else {
|
||||
Do(p, mpegLibCrc);
|
||||
}
|
||||
|
||||
Do(p, streamIdGen);
|
||||
Do(p, mpegLibVersion);
|
||||
}
|
||||
@ -440,8 +448,9 @@ void __MpegShutdown() {
|
||||
mpegMap.clear();
|
||||
}
|
||||
|
||||
void __MpegLoadModule(int version) {
|
||||
void __MpegLoadModule(int version,u32 crc) {
|
||||
mpegLibVersion = version;
|
||||
mpegLibCrc = crc;
|
||||
}
|
||||
|
||||
static u32 sceMpegInit() {
|
||||
@ -450,7 +459,7 @@ static u32 sceMpegInit() {
|
||||
// TODO: Need to properly hook module load/unload for this to work right.
|
||||
//return ERROR_MPEG_ALREADY_INIT;
|
||||
} else {
|
||||
INFO_LOG(ME, "sceMpegInit()");
|
||||
INFO_LOG(ME, "sceMpegInit(), mpegLibVersion 0x%0x, mpegLibcrc %x", mpegLibVersion, mpegLibCrc);
|
||||
}
|
||||
isMpegInit = true;
|
||||
return hleDelayResult(0, "mpeg init", 750);
|
||||
@ -1361,8 +1370,14 @@ static int sceMpegAvcDecodeYCbCr(u32 mpeg, u32 auAddr, u32 bufferAddr, u32 initA
|
||||
// Flush structs back to memory
|
||||
avcAu.write(auAddr);
|
||||
|
||||
// Save the current frame's status to initAddr
|
||||
Memory::Write_U32(ctx->avc.avcFrameStatus, initAddr);
|
||||
if (mpegLibVersion >= 0x010A) {
|
||||
// Sunday Vs Magazine Shuuketsu! Choujou Daikessen expect, issue #11060
|
||||
Memory::Write_U32(1, initAddr);
|
||||
}
|
||||
else {
|
||||
// Save the current frame's status to initAddr
|
||||
Memory::Write_U32(ctx->avc.avcFrameStatus, initAddr);
|
||||
}
|
||||
ctx->avc.avcDecodeResult = MPEG_AVC_DECODE_SUCCESS;
|
||||
|
||||
DEBUG_LOG(ME, "sceMpegAvcDecodeYCbCr(%08x, %08x, %08x, %08x)", mpeg, auAddr, bufferAddr, initAddr);
|
||||
|
@ -79,7 +79,7 @@ void __MpegInit();
|
||||
void __MpegDoState(PointerWrap &p);
|
||||
void __MpegShutdown();
|
||||
|
||||
void __MpegLoadModule(int version);
|
||||
void __MpegLoadModule(int version, u32 crc);
|
||||
|
||||
void Register_sceMpeg();
|
||||
|
||||
|
@ -691,7 +691,7 @@ static u32 sceWlanGetEtherAddr(u32 addrAddr) {
|
||||
addr[0] &= 0xfc;
|
||||
} else {
|
||||
// Read MAC Address from config
|
||||
if (!ParseMacAddress(g_Config.sMACAddress.c_str(), addr)) {
|
||||
if (!ParseMacAddress(g_Config.sMACAddress, addr)) {
|
||||
ERROR_LOG(SCENET, "Error parsing mac address %s", g_Config.sMACAddress.c_str());
|
||||
Memory::Memset(addrAddr, 0, 6);
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ static const int audioSamplesBytes = audioSamples * 4;
|
||||
static int videoPixelMode = GE_CMODE_32BIT_ABGR8888;
|
||||
static int videoLoopStatus = PSMF_PLAYER_CONFIG_NO_LOOP;
|
||||
static int psmfPlayerLibVersion = 0;
|
||||
static u32 psmfPlayerLibcrc = 0;
|
||||
|
||||
int eventPsmfPlayerStatusChange = -1;
|
||||
|
||||
@ -175,35 +176,35 @@ public:
|
||||
|
||||
int FindEPWithTimestamp(int pts) const;
|
||||
|
||||
u32 magic;
|
||||
u32 version;
|
||||
u32 streamOffset;
|
||||
u32 streamSize;
|
||||
u32 headerSize;
|
||||
u32 headerOffset;
|
||||
u32 streamType;
|
||||
u32 streamChannel;
|
||||
u32 magic = 0;
|
||||
u32 version = 0;
|
||||
u32 streamOffset = 0;
|
||||
u32 streamSize = 0;
|
||||
u32 headerSize = 0;
|
||||
u32 headerOffset = 0;
|
||||
u32 streamType = 0;
|
||||
u32 streamChannel = 0;
|
||||
// 0x50
|
||||
u32 streamDataTotalSize;
|
||||
u32 presentationStartTime;
|
||||
u32 presentationEndTime;
|
||||
u32 streamDataNextBlockSize;
|
||||
u32 streamDataNextInnerBlockSize;
|
||||
u32 streamDataTotalSize = 0;
|
||||
u32 presentationStartTime = 0;
|
||||
u32 presentationEndTime = 0;
|
||||
u32 streamDataNextBlockSize = 0;
|
||||
u32 streamDataNextInnerBlockSize = 0;
|
||||
|
||||
int numStreams;
|
||||
int currentStreamNum;
|
||||
int currentStreamType;
|
||||
int currentStreamChannel;
|
||||
int numStreams = 0;
|
||||
int currentStreamNum = 0;
|
||||
int currentStreamType = 0;
|
||||
int currentStreamChannel = 0;
|
||||
|
||||
// parameters gotten from streams
|
||||
// I guess this is the seek information?
|
||||
u32 EPMapOffset;
|
||||
u32 EPMapEntriesNum;
|
||||
u32 EPMapOffset = 0;
|
||||
u32 EPMapEntriesNum = 0;
|
||||
// These shouldn't be here, just here for convenience with old states.
|
||||
int videoWidth;
|
||||
int videoHeight;
|
||||
int audioChannels;
|
||||
int audioFrequency;
|
||||
int videoWidth = 0;
|
||||
int videoHeight = 0;
|
||||
int audioChannels = 0;
|
||||
int audioFrequency = 0;
|
||||
std::vector<PsmfEntry> EPMap;
|
||||
|
||||
PsmfStreamMap streamMap;
|
||||
@ -696,8 +697,9 @@ void __PsmfInit() {
|
||||
eventPsmfPlayerStatusChange = CoreTiming::RegisterEvent("PsmfPlayerStatusChange", &__PsmfPlayerStatusChange);
|
||||
}
|
||||
|
||||
void __PsmfPlayerLoadModule(int devkitVersion) {
|
||||
void __PsmfPlayerLoadModule(int devkitVersion, u32 crc) {
|
||||
psmfPlayerLibVersion = devkitVersion;
|
||||
psmfPlayerLibcrc = crc;
|
||||
}
|
||||
|
||||
void __PsmfDoState(PointerWrap &p) {
|
||||
@ -709,7 +711,7 @@ void __PsmfDoState(PointerWrap &p) {
|
||||
}
|
||||
|
||||
void __PsmfPlayerDoState(PointerWrap &p) {
|
||||
auto s = p.Section("scePsmfPlayer", 1, 3);
|
||||
auto s = p.Section("scePsmfPlayer", 1, 4);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
@ -722,6 +724,11 @@ void __PsmfPlayerDoState(PointerWrap &p) {
|
||||
Do(p, eventPsmfPlayerStatusChange);
|
||||
}
|
||||
CoreTiming::RestoreRegisterEvent(eventPsmfPlayerStatusChange, "PsmfPlayerStatusChangeEvent", &__PsmfPlayerStatusChange);
|
||||
if (s < 4) {
|
||||
psmfPlayerLibcrc = 0;
|
||||
} else {
|
||||
Do(p, psmfPlayerLibcrc);
|
||||
}
|
||||
if (s < 2) {
|
||||
// Assume the latest, which is what we were emulating before.
|
||||
psmfPlayerLibVersion = 0x06060010;
|
||||
@ -1157,7 +1164,8 @@ static int scePsmfPlayerCreate(u32 psmfPlayer, u32 dataPtr) {
|
||||
|
||||
int delayUs = 20000;
|
||||
DelayPsmfStateChange(psmfPlayer, PSMF_PLAYER_STATUS_INIT, delayUs);
|
||||
return hleLogSuccessInfoI(ME, hleDelayResult(0, "player create", delayUs));
|
||||
INFO_LOG(ME, "psmfplayer create, psmfPlayerLibVersion 0x%0x, psmfPlayerLibcrc %x", psmfPlayerLibVersion, psmfPlayerLibcrc);
|
||||
return hleDelayResult(0, "player create", delayUs);
|
||||
}
|
||||
|
||||
static int scePsmfPlayerStop(u32 psmfPlayer) {
|
||||
@ -1427,7 +1435,7 @@ static int scePsmfPlayerStart(u32 psmfPlayer, u32 psmfPlayerData, int initPts)
|
||||
psmfplayer->playMode = playerData->playMode;
|
||||
psmfplayer->playSpeed = playerData->playSpeed;
|
||||
|
||||
WARN_LOG(ME, "scePsmfPlayerStart(%08x, %08x, %d,(mode %d, speed %d)", psmfPlayer, psmfPlayerData, initPts, playerData->playMode, playerData->playSpeed);
|
||||
WARN_LOG(ME, "scePsmfPlayerStart(%08x, %08x, %d (mode %d, speed %d)", psmfPlayer, psmfPlayerData, initPts, playerData->playMode, playerData->playSpeed);
|
||||
|
||||
// Does not alter current pts, it just catches up when Update()/etc. get there.
|
||||
|
||||
|
@ -21,7 +21,7 @@ void Register_scePsmf();
|
||||
void Register_scePsmfPlayer();
|
||||
|
||||
void __PsmfInit();
|
||||
void __PsmfPlayerLoadModule(int devkitVersion);
|
||||
void __PsmfPlayerLoadModule(int devkitVersion, u32 crc);
|
||||
void __PsmfDoState(PointerWrap &p);
|
||||
void __PsmfPlayerDoState(PointerWrap &p);
|
||||
void __PsmfShutdown();
|
||||
|
@ -744,11 +744,14 @@ static int sceUtilityGamedataInstallInitStart(u32 paramsAddr) {
|
||||
}
|
||||
|
||||
ActivateDialog(UtilityDialogType::GAMEDATAINSTALL);
|
||||
return hleLogSuccessInfoX(SCEUTILITY, gamedataInstallDialog->Init(paramsAddr));
|
||||
int result = gamedataInstallDialog->Init(paramsAddr);
|
||||
if (result < 0)
|
||||
DeactivateDialog();
|
||||
return hleLogSuccessInfoX(SCEUTILITY, result);
|
||||
}
|
||||
|
||||
static int sceUtilityGamedataInstallShutdownStart() {
|
||||
if (currentDialogType != UtilityDialogType::GAMEDATAINSTALL) {
|
||||
if (!currentDialogActive || currentDialogType != UtilityDialogType::GAMEDATAINSTALL) {
|
||||
return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type");
|
||||
}
|
||||
|
||||
@ -757,7 +760,7 @@ static int sceUtilityGamedataInstallShutdownStart() {
|
||||
}
|
||||
|
||||
static int sceUtilityGamedataInstallUpdate(int animSpeed) {
|
||||
if (currentDialogType != UtilityDialogType::GAMEDATAINSTALL) {
|
||||
if (!currentDialogActive || currentDialogType != UtilityDialogType::GAMEDATAINSTALL) {
|
||||
return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type");
|
||||
}
|
||||
|
||||
@ -765,8 +768,9 @@ static int sceUtilityGamedataInstallUpdate(int animSpeed) {
|
||||
}
|
||||
|
||||
static int sceUtilityGamedataInstallGetStatus() {
|
||||
if (currentDialogType != UtilityDialogType::GAMEDATAINSTALL) {
|
||||
if (!currentDialogActive || currentDialogType != UtilityDialogType::GAMEDATAINSTALL) {
|
||||
// This is called incorrectly all the time by some games. So let's not bother warning.
|
||||
hleEatCycles(200);
|
||||
return hleLogDebug(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type");
|
||||
}
|
||||
|
||||
@ -776,7 +780,7 @@ static int sceUtilityGamedataInstallGetStatus() {
|
||||
}
|
||||
|
||||
static int sceUtilityGamedataInstallAbort() {
|
||||
if (currentDialogType != UtilityDialogType::GAMEDATAINSTALL) {
|
||||
if (!currentDialogActive || currentDialogType != UtilityDialogType::GAMEDATAINSTALL) {
|
||||
return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type");
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user