Merge branch 'master' into feature_openxr_vulkan

This commit is contained in:
Lubos 2022-10-09 17:48:23 +02:00
commit 7c6cff5ae9
320 changed files with 7874 additions and 6856 deletions

View File

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

View File

@ -1,11 +1,11 @@
# vim:noexpandtab:
cmake_minimum_required(VERSION 3.6)
cmake_minimum_required(VERSION 3.8)
project(PPSSPP)
enable_testing()
#This is supposed to work but doesn't!
if(NOT ANDROID)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
@ -120,8 +120,16 @@ if(OPENXR)
add_definitions(-DOPENXR)
add_library(openxr SHARED IMPORTED)
include_directories(ext/openxr)
set_property(TARGET openxr PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/ext/openxr/libs/arm64-v8a/libopenxr_loader.so")
message("OpenXR enabled")
if(OPENXR_PLATFORM_PICO)
add_definitions(-DOPENXR_PLATFORM_PICO)
set_property(TARGET openxr PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/ext/openxr/libs/pico/arm64-v8a/libopenxr_loader.so")
message("OpenXR for Pico enabled")
endif()
if(OPENXR_PLATFORM_QUEST)
add_definitions(-DOPENXR_PLATFORM_QUEST)
set_property(TARGET openxr PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/ext/openxr/libs/quest/arm64-v8a/libopenxr_loader.so")
message("OpenXR for Quest enabled")
endif()
endif()
if(GOLD)
@ -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()

View File

@ -153,6 +153,7 @@
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
<AdditionalLibraryDirectories>
</AdditionalLibraryDirectories>
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -179,6 +180,7 @@
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
<AdditionalLibraryDirectories>
</AdditionalLibraryDirectories>
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
@ -205,6 +207,7 @@
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
<AdditionalLibraryDirectories>
</AdditionalLibraryDirectories>
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
@ -232,6 +235,7 @@
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
<AdditionalLibraryDirectories>
</AdditionalLibraryDirectories>
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@ -264,6 +268,7 @@
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
<AdditionalLibraryDirectories>
</AdditionalLibraryDirectories>
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -298,6 +303,7 @@
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
<AdditionalLibraryDirectories>
</AdditionalLibraryDirectories>
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
@ -332,6 +338,7 @@
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
<AdditionalLibraryDirectories>
</AdditionalLibraryDirectories>
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
@ -366,6 +373,7 @@
<AdditionalDependencies>ws2_32.lib</AdditionalDependencies>
<AdditionalLibraryDirectories>
</AdditionalLibraryDirectories>
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
@ -441,6 +449,7 @@
<ClInclude Include="GPU\Vulkan\VulkanBarrier.h" />
<ClInclude Include="GPU\Vulkan\VulkanContext.h" />
<ClInclude Include="GPU\Vulkan\VulkanDebug.h" />
<ClInclude Include="GPU\Vulkan\VulkanFrameData.h" />
<ClInclude Include="GPU\Vulkan\VulkanImage.h" />
<ClInclude Include="GPU\Vulkan\VulkanLoader.h" />
<ClInclude Include="GPU\Vulkan\VulkanMemory.h" />
@ -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" />

View File

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

View File

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

View File

@ -82,6 +82,20 @@ inline void Uint8x3ToFloat4(float f[4], uint32_t u) {
#endif
}
inline void Uint8x3ToFloat3(float f[4], uint32_t u) {
#if defined(_M_SSE) || PPSSPP_ARCH(ARM_NEON)
float temp[4];
Uint8x4ToFloat4(temp, u & 0xFFFFFF);
f[0] = temp[0];
f[1] = temp[1];
f[2] = temp[2];
#else
f[0] = ((u >> 0) & 0xFF) * (1.0f / 255.0f);
f[1] = ((u >> 8) & 0xFF) * (1.0f / 255.0f);
f[2] = ((u >> 16) & 0xFF) * (1.0f / 255.0f);
#endif
}
inline void Uint8x3ToInt4(int i[4], uint32_t u) {
i[0] = ((u >> 0) & 0xFF);
i[1] = ((u >> 8) & 0xFF);

View File

@ -95,7 +95,6 @@ bool decompress_string(const std::string& str, std::string *dest) {
inflateEnd(&zs);
if (ret != Z_STREAM_END) { // an error occurred that was not EOF
std::ostringstream oss;
ERROR_LOG(IO, "Exception during zlib decompression: (%i) %s", ret, zs.msg);
return false;
}

View File

@ -206,7 +206,7 @@ void Section::Set(const char* key, const char* newValue)
else
{
// The key did not already exist in this section - let's add it.
lines.push_back(std::string(key) + " = " + EscapeComments(newValue));
lines.emplace_back(std::string(key) + " = " + EscapeComments(newValue));
}
}
@ -272,7 +272,7 @@ void Section::Set(const char* key, const std::vector<std::string>& newValues)
}
void Section::AddComment(const std::string &comment) {
lines.push_back("# " + comment);
lines.emplace_back("# " + comment);
}
bool Section::Get(const char* key, std::vector<std::string>& values)

View File

@ -39,7 +39,7 @@ const char *I18NCategory::T(const char *key, const char *def) {
if (def)
missedKeyLog_[key] = def;
else
missedKeyLog_[key] = modifiedKey.c_str();
missedKeyLog_[key] = modifiedKey;
// INFO_LOG(SYSTEM, "Missed translation key in %s: %s", name_.c_str(), key);
return def ? def : key;
}

View File

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

View File

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

View File

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

View File

@ -47,7 +47,7 @@ LPD3DBLOB CompileShaderToByteCodeD3D9(const char *code, const char *target, std:
pShaderCode = nullptr;
}
} else {
*errorMessage = "";
errorMessage->clear();
}
return pShaderCode;

View File

@ -23,6 +23,7 @@
#include "Common/Math/lin/matrix4x4.h"
#include "Common/GPU/thin3d.h"
#include "Common/GPU/D3D9/D3D9StateCache.h"
#include "Common/OSVersion.h"
#include "Common/StringUtils.h"
#include "Common/Log.h"
@ -114,6 +115,7 @@ static const D3DSTENCILOP stencilOpToD3D9[] = {
D3DFORMAT FormatToD3DFMT(DataFormat fmt) {
switch (fmt) {
case DataFormat::R16_UNORM: return D3DFMT_L16; // closest match, should be a fine substitution if we ignore channels except R.
case DataFormat::R8G8B8A8_UNORM: return D3DFMT_A8R8G8B8;
case DataFormat::B8G8R8A8_UNORM: return D3DFMT_A8R8G8B8;
case DataFormat::R4G4B4A4_UNORM_PACK16: return D3DFMT_A4R4G4B4; // emulated
@ -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;

View File

@ -32,6 +32,8 @@ enum class DataFormat : uint8_t {
A1R5G5B5_UNORM_PACK16, // A1 in the UPPER bit.
A1B5G5R5_UNORM_PACK16, // A1 in the UPPER bit. OpenGL-only.
R16_UNORM,
R16_FLOAT,
R16G16_FLOAT,
R16G16B16A16_FLOAT,

View File

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

View File

@ -5,6 +5,6 @@
namespace Draw {
bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment);
bool Thin3DFormatToGLFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment);
}

View File

@ -51,7 +51,7 @@ static void ParseExtensionsString(const std::string& str, std::set<std::string>
size_t next = 0;
for (size_t pos = 0, len = str.length(); pos < len; ++pos) {
if (str[pos] == ' ') {
output.insert(str.substr(next, pos - next));
output.emplace(str.substr(next, pos - next));
// Skip the delimiter itself.
next = pos + 1;
}
@ -60,7 +60,7 @@ static void ParseExtensionsString(const std::string& str, std::set<std::string>
if (next == 0 && str.length() != 0) {
output.insert(str);
} else if (next < str.length()) {
output.insert(str.substr(next));
output.emplace(str.substr(next));
}
}

View File

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

View File

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

View File

@ -91,6 +91,13 @@ public:
std::string error;
};
struct GLRProgramFlags {
bool supportDualSource : 1;
bool useClipDistance0 : 1;
bool useClipDistance1 : 1;
bool useClipDistance2 : 1;
};
class GLRProgram {
public:
~GLRProgram() {
@ -119,7 +126,7 @@ public:
std::vector<Semantic> semantics_;
std::vector<UniformLocQuery> queries_;
std::vector<Initializer> initialize_;
bool use_clip_distance0 = false;
bool use_clip_distance[8]{};
struct UniformInfo {
int loc_;
@ -427,15 +434,17 @@ public:
// not be an active render pass.
GLRProgram *CreateProgram(
std::vector<GLRShader *> shaders, std::vector<GLRProgram::Semantic> semantics, std::vector<GLRProgram::UniformLocQuery> queries,
std::vector<GLRProgram::Initializer> initializers, bool supportDualSource, bool useClipDistance0) {
std::vector<GLRProgram::Initializer> initializers, const GLRProgramFlags &flags) {
GLRInitStep step{ GLRInitStepType::CREATE_PROGRAM };
_assert_(shaders.size() <= ARRAY_SIZE(step.create_program.shaders));
step.create_program.program = new GLRProgram();
step.create_program.program->semantics_ = semantics;
step.create_program.program->queries_ = queries;
step.create_program.program->initialize_ = initializers;
step.create_program.program->use_clip_distance0 = useClipDistance0;
step.create_program.support_dual_source = supportDualSource;
step.create_program.program->use_clip_distance[0] = flags.useClipDistance0;
step.create_program.program->use_clip_distance[1] = flags.useClipDistance1;
step.create_program.program->use_clip_distance[2] = flags.useClipDistance2;
step.create_program.support_dual_source = flags.supportDualSource;
_assert_msg_(shaders.size() > 0, "Can't create a program with zero shaders");
for (size_t i = 0; i < shaders.size(); i++) {
step.create_program.shaders[i] = shaders[i];
@ -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;

View File

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

View File

@ -152,16 +152,18 @@ std::string Postprocess(std::string code, ShaderLanguage lang, ShaderStage stage
return output;
}
static_assert(Draw::SEM_TEXCOORD0 == 3, "Semantic shader hardcoded in glsl below.");
bool ConvertToVulkanGLSL(std::string *dest, TranslatedShaderMetadata *destMetadata, std::string src, ShaderStage stage, std::string *errorMessage) {
std::stringstream out;
static struct {
static const struct {
ShaderStage stage;
const char *needle;
const char *replacement;
} replacements[] = {
{ ShaderStage::Vertex, "attribute vec4 a_position;", "layout(location = 0) in vec4 a_position;" },
{ ShaderStage::Vertex, "attribute vec2 a_texcoord0;", "layout(location = 2) in vec2 a_texcoord0;"},
{ ShaderStage::Vertex, "attribute vec2 a_texcoord0;", "layout(location = 3) in vec2 a_texcoord0;"},
{ ShaderStage::Vertex, "varying vec2 v_position;", "layout(location = 0) out vec2 v_position;" },
{ ShaderStage::Fragment, "varying vec2 v_position;", "layout(location = 0) in vec2 v_position;" },
{ ShaderStage::Fragment, "texture2D(", "texture(" },
@ -233,7 +235,7 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, const ShaderLan
return false;
#endif
*errorMessage = "";
errorMessage->clear();
glslang::TProgram program;
const char *shaderStrings[1]{};

View File

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

View File

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

View File

@ -4,7 +4,7 @@
void VulkanBarrier::Flush(VkCommandBuffer cmd) {
if (!imageBarriers_.empty()) {
vkCmdPipelineBarrier(cmd, srcStageMask_, dstStageMask_, 0, 0, nullptr, 0, nullptr, (uint32_t)imageBarriers_.size(), imageBarriers_.data());
vkCmdPipelineBarrier(cmd, srcStageMask_, dstStageMask_, dependencyFlags_, 0, nullptr, 0, nullptr, (uint32_t)imageBarriers_.size(), imageBarriers_.data());
}
imageBarriers_.clear();
srcStageMask_ = 0;

View File

@ -19,8 +19,11 @@ public:
VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask,
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask
) {
_dbg_assert_(image != VK_NULL_HANDLE);
srcStageMask_ |= srcStageMask;
dstStageMask_ |= dstStageMask;
dependencyFlags_ |= VK_DEPENDENCY_BY_REGION_BIT;
VkImageMemoryBarrier imageBarrier;
imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
@ -45,6 +48,8 @@ public:
void TransitionImageAuto(
VkImage image, int baseMip, int numMipLevels, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout
) {
_dbg_assert_(image != VK_NULL_HANDLE);
VkAccessFlags srcAccessMask = 0;
VkAccessFlags dstAccessMask = 0;
switch (oldImageLayout) {
@ -112,4 +117,5 @@ private:
VkPipelineStageFlags srcStageMask_ = 0;
VkPipelineStageFlags dstStageMask_ = 0;
std::vector<VkImageMemoryBarrier> imageBarriers_;
VkDependencyFlags dependencyFlags_ = 0;
};

View File

@ -600,8 +600,7 @@ void VulkanContext::ChooseDevice(int physical_device) {
deviceFeatures_.enabled.samplerAnisotropy = deviceFeatures_.available.samplerAnisotropy;
deviceFeatures_.enabled.shaderClipDistance = deviceFeatures_.available.shaderClipDistance;
deviceFeatures_.enabled.shaderCullDistance = deviceFeatures_.available.shaderCullDistance;
// For easy wireframe mode, someday.
deviceFeatures_.enabled.fillModeNonSolid = deviceFeatures_.available.fillModeNonSolid;
deviceFeatures_.enabled.geometryShader = deviceFeatures_.available.geometryShader;
GetDeviceLayerExtensionList(nullptr, device_extension_properties_);
@ -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;

View File

@ -86,7 +86,6 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback(
} else {
WARN_LOG(G3D, "VKDEBUG: %s", msg.c_str());
}
// false indicates that layer should not bail-out of an
// API call that had validation failures. This may mean that the
// app dies inside the driver due to invalid parameter(s).

View File

@ -0,0 +1,224 @@
#include <mutex>
#include "VulkanFrameData.h"
#include "Common/Log.h"
#include "Common/StringUtils.h"
void FrameData::Init(VulkanContext *vulkan, int index) {
this->index = index;
VkDevice device = vulkan->GetDevice();
VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
cmd_pool_info.queueFamilyIndex = vulkan->GetGraphicsQueueFamilyIndex();
cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &cmdPoolInit);
_dbg_assert_(res == VK_SUCCESS);
res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &cmdPoolMain);
_dbg_assert_(res == VK_SUCCESS);
VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
cmd_alloc.commandPool = cmdPoolInit;
cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
cmd_alloc.commandBufferCount = 1;
res = vkAllocateCommandBuffers(device, &cmd_alloc, &initCmd);
_dbg_assert_(res == VK_SUCCESS);
cmd_alloc.commandPool = cmdPoolMain;
res = vkAllocateCommandBuffers(device, &cmd_alloc, &mainCmd);
res = vkAllocateCommandBuffers(device, &cmd_alloc, &presentCmd);
_dbg_assert_(res == VK_SUCCESS);
vulkan->SetDebugName(initCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("initCmd%d", index).c_str());
vulkan->SetDebugName(mainCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("mainCmd%d", index).c_str());
vulkan->SetDebugName(presentCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("presentCmd%d", index).c_str());
// Creating the frame fence with true so they can be instantly waited on the first frame
fence = vulkan->CreateFence(true);
vulkan->SetDebugName(fence, VK_OBJECT_TYPE_FENCE, StringFromFormat("fence%d", index).c_str());
readyForFence = true;
// This fence is used for synchronizing readbacks. Does not need preinitialization.
// TODO: Put this in frameDataShared, only one is needed.
readbackFence = vulkan->CreateFence(false);
vulkan->SetDebugName(fence, VK_OBJECT_TYPE_FENCE, "readbackFence");
VkQueryPoolCreateInfo query_ci{ VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO };
query_ci.queryCount = MAX_TIMESTAMP_QUERIES;
query_ci.queryType = VK_QUERY_TYPE_TIMESTAMP;
res = vkCreateQueryPool(device, &query_ci, nullptr, &profile.queryPool);
}
void FrameData::Destroy(VulkanContext *vulkan) {
VkDevice device = vulkan->GetDevice();
vkDestroyCommandPool(device, cmdPoolInit, nullptr);
vkDestroyCommandPool(device, cmdPoolMain, nullptr);
vkDestroyFence(device, fence, nullptr);
vkDestroyFence(device, readbackFence, nullptr);
vkDestroyQueryPool(device, profile.queryPool, nullptr);
}
void FrameData::AcquireNextImage(VulkanContext *vulkan, FrameDataShared &shared) {
_dbg_assert_(!hasAcquired);
// Get the index of the next available swapchain image, and a semaphore to block command buffer execution on.
VkResult res = vkAcquireNextImageKHR(vulkan->GetDevice(), vulkan->GetSwapchain(), UINT64_MAX, shared.acquireSemaphore, (VkFence)VK_NULL_HANDLE, &curSwapchainImage);
switch (res) {
case VK_SUCCESS:
hasAcquired = true;
break;
case VK_SUBOPTIMAL_KHR:
hasAcquired = true;
// Hopefully the resize will happen shortly. Ignore - one frame might look bad or something.
WARN_LOG(G3D, "VK_SUBOPTIMAL_KHR returned - ignoring");
break;
case VK_ERROR_OUT_OF_DATE_KHR:
// We do not set hasAcquired here!
WARN_LOG(G3D, "VK_ERROR_OUT_OF_DATE_KHR returned from AcquireNextImage - processing the frame, but not presenting");
skipSwap = true;
break;
default:
// Weird, shouldn't get any other values. Maybe lost device?
_assert_msg_(false, "vkAcquireNextImageKHR failed! result=%s", VulkanResultToString(res));
break;
}
}
VkResult FrameData::QueuePresent(VulkanContext *vulkan, FrameDataShared &shared) {
_dbg_assert_(hasAcquired);
hasAcquired = false;
_dbg_assert_(!skipSwap);
VkSwapchainKHR swapchain = vulkan->GetSwapchain();
VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
present.swapchainCount = 1;
present.pSwapchains = &swapchain;
present.pImageIndices = &curSwapchainImage;
present.pWaitSemaphores = &shared.renderingCompleteSemaphore;
present.waitSemaphoreCount = 1;
return vkQueuePresentKHR(vulkan->GetGraphicsQueue(), &present);
}
VkCommandBuffer FrameData::GetInitCmd(VulkanContext *vulkan) {
if (!hasInitCommands) {
VkCommandBufferBeginInfo begin = {
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
nullptr,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
};
vkResetCommandPool(vulkan->GetDevice(), cmdPoolInit, 0);
VkResult res = vkBeginCommandBuffer(initCmd, &begin);
if (res != VK_SUCCESS) {
return VK_NULL_HANDLE;
}
// Good spot to reset the query pool.
vkCmdResetQueryPool(initCmd, profile.queryPool, 0, MAX_TIMESTAMP_QUERIES);
vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, profile.queryPool, 0);
hasInitCommands = true;
}
return initCmd;
}
void FrameData::SubmitPending(VulkanContext *vulkan, FrameSubmitType type, FrameDataShared &sharedData) {
VkCommandBuffer cmdBufs[2];
int numCmdBufs = 0;
VkFence fenceToTrigger = VK_NULL_HANDLE;
if (hasInitCommands) {
if (profilingEnabled_) {
// Pre-allocated query ID 1 - end of init cmdbuf.
vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, profile.queryPool, 1);
}
VkResult res = vkEndCommandBuffer(initCmd);
cmdBufs[numCmdBufs++] = initCmd;
_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (init)! result=%s", VulkanResultToString(res));
hasInitCommands = false;
}
if ((hasMainCommands || hasPresentCommands) && type == FrameSubmitType::Sync) {
fenceToTrigger = readbackFence;
}
if (hasMainCommands) {
VkResult res = vkEndCommandBuffer(mainCmd);
_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (main)! result=%s", VulkanResultToString(res));
cmdBufs[numCmdBufs++] = mainCmd;
hasMainCommands = false;
}
if (hasPresentCommands && type != FrameSubmitType::Pending) {
VkResult res = vkEndCommandBuffer(presentCmd);
_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (present)! result=%s", VulkanResultToString(res));
cmdBufs[numCmdBufs++] = presentCmd;
hasPresentCommands = false;
if (type == FrameSubmitType::Present) {
fenceToTrigger = fence;
}
}
if (!numCmdBufs && fenceToTrigger == VK_NULL_HANDLE) {
// Nothing to do.
return;
}
VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO };
VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
if (type == FrameSubmitType::Present && !skipSwap) {
_dbg_assert_(hasAcquired);
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &sharedData.acquireSemaphore;
submit_info.pWaitDstStageMask = waitStage;
}
submit_info.commandBufferCount = (uint32_t)numCmdBufs;
submit_info.pCommandBuffers = cmdBufs;
if (type == FrameSubmitType::Present && !skipSwap) {
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &sharedData.renderingCompleteSemaphore;
}
VkResult res;
if (fenceToTrigger == fence) {
// The fence is waited on by the main thread, they are not allowed to access it simultaneously.
res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger);
std::lock_guard<std::mutex> lock(fenceMutex);
readyForFence = true;
fenceCondVar.notify_one();
} else {
res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger);
}
if (res == VK_ERROR_DEVICE_LOST) {
_assert_msg_(false, "Lost the Vulkan device in vkQueueSubmit! If this happens again, switch Graphics Backend away from Vulkan");
} else {
_assert_msg_(res == VK_SUCCESS, "vkQueueSubmit failed (main)! result=%s", VulkanResultToString(res));
}
if (type == FrameSubmitType::Sync) {
// Hard stall of the GPU, not ideal, but necessary so the CPU has the contents of the readback.
vkWaitForFences(vulkan->GetDevice(), 1, &readbackFence, true, UINT64_MAX);
vkResetFences(vulkan->GetDevice(), 1, &readbackFence);
syncDone = true;
}
}
void FrameDataShared::Init(VulkanContext *vulkan) {
VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
semaphoreCreateInfo.flags = 0;
VkResult res = vkCreateSemaphore(vulkan->GetDevice(), &semaphoreCreateInfo, nullptr, &acquireSemaphore);
_dbg_assert_(res == VK_SUCCESS);
res = vkCreateSemaphore(vulkan->GetDevice(), &semaphoreCreateInfo, nullptr, &renderingCompleteSemaphore);
_dbg_assert_(res == VK_SUCCESS);
}
void FrameDataShared::Destroy(VulkanContext *vulkan) {
VkDevice device = vulkan->GetDevice();
vkDestroySemaphore(device, acquireSemaphore, nullptr);
vkDestroySemaphore(device, renderingCompleteSemaphore, nullptr);
}

View File

@ -0,0 +1,93 @@
#pragma once
#include <cstdint>
#include <mutex>
#include <condition_variable>
#include "Common/GPU/Vulkan/VulkanContext.h"
enum {
MAX_TIMESTAMP_QUERIES = 128,
};
enum class VKRRunType {
PRESENT,
SYNC,
EXIT,
};
struct QueueProfileContext {
VkQueryPool queryPool;
std::vector<std::string> timestampDescriptions;
std::string profileSummary;
double cpuStartTime;
double cpuEndTime;
};
struct FrameDataShared {
// Permanent objects
VkSemaphore acquireSemaphore = VK_NULL_HANDLE;
VkSemaphore renderingCompleteSemaphore = VK_NULL_HANDLE;
void Init(VulkanContext *vulkan);
void Destroy(VulkanContext *vulkan);
};
enum class FrameSubmitType {
Pending,
Sync,
Present,
};
// Per-frame data, round-robin so we can overlap submission with execution of the previous frame.
struct FrameData {
bool skipSwap = false;
std::mutex fenceMutex;
std::condition_variable fenceCondVar;
bool readyForFence = true;
VkFence fence = VK_NULL_HANDLE;
VkFence readbackFence = VK_NULL_HANDLE; // Strictly speaking we might only need one global of these.
// These are on different threads so need separate pools.
VkCommandPool cmdPoolInit = VK_NULL_HANDLE; // Written to from main thread
VkCommandPool cmdPoolMain = VK_NULL_HANDLE; // Written to from render thread, which also submits
VkCommandBuffer initCmd = VK_NULL_HANDLE;
VkCommandBuffer mainCmd = VK_NULL_HANDLE;
VkCommandBuffer presentCmd = VK_NULL_HANDLE;
bool hasInitCommands = false;
bool hasMainCommands = false;
bool hasPresentCommands = false;
bool hasFencePending = false;
bool hasAcquired = false;
bool syncDone = false;
// Swapchain.
uint32_t curSwapchainImage = -1;
// Profiling.
QueueProfileContext profile;
bool profilingEnabled_ = false;
void Init(VulkanContext *vulkan, int index);
void Destroy(VulkanContext *vulkan);
void AcquireNextImage(VulkanContext *vulkan, FrameDataShared &shared);
VkResult QueuePresent(VulkanContext *vulkan, FrameDataShared &shared);
// Generally called from the main thread, unlike most of the rest.
VkCommandBuffer GetInitCmd(VulkanContext *vulkan);
// This will only submit if we are actually recording init commands.
void SubmitPending(VulkanContext *vulkan, FrameSubmitType type, FrameDataShared &shared);
private:
// Metadata for logging etc
int index;
};

View File

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

View File

@ -34,13 +34,13 @@ void VulkanProfiler::BeginFrame(VulkanContext *vulkan, VkCommandBuffer firstComm
static const char * const indent[4] = { "", " ", " ", " " };
if (!scopes_.empty()) {
NOTICE_LOG(G3D, "Profiling events this frame:");
INFO_LOG(G3D, "Profiling events this frame:");
}
// Log it all out.
for (auto &scope : scopes_) {
if (scope.endQueryId == -1) {
NOTICE_LOG(G3D, "Unclosed scope: %s", scope.name.c_str());
WARN_LOG(G3D, "Unclosed scope: %s", scope.name.c_str());
continue;
}
uint64_t startTime = results[scope.startQueryId];
@ -50,7 +50,7 @@ void VulkanProfiler::BeginFrame(VulkanContext *vulkan, VkCommandBuffer firstComm
double milliseconds = (double)delta * timestampConversionFactor;
NOTICE_LOG(G3D, "%s%s (%0.3f ms)", indent[scope.level & 3], scope.name.c_str(), milliseconds);
INFO_LOG(G3D, "%s%s (%0.3f ms)", indent[scope.level & 3], scope.name.c_str(), milliseconds);
}
scopes_.clear();

View File

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

View File

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

View File

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

View File

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

View File

@ -33,6 +33,9 @@ size_t DataFormatSizeInBytes(DataFormat fmt) {
case DataFormat::R8G8B8A8_SNORM: return 4;
case DataFormat::R8G8B8A8_UINT: return 4;
case DataFormat::R8G8B8A8_SINT: return 4;
case DataFormat::R16_UNORM: return 2;
case DataFormat::R16_FLOAT: return 2;
case DataFormat::R16G16_FLOAT: return 4;
case DataFormat::R16G16B16A16_FLOAT: return 8;
@ -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)";
}
}

View File

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

View File

@ -31,6 +31,6 @@ DrawContext *T3DCreateDX9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapt
DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context, ID3D11Device1 *device1, ID3D11DeviceContext1 *context1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector<std::string> adapterNames);
#endif
DrawContext *T3DCreateVulkanContext(VulkanContext *context, bool splitSubmit);
DrawContext *T3DCreateVulkanContext(VulkanContext *context);
} // namespace Draw

View File

@ -35,7 +35,7 @@ bool ShouldLogNTimes(const char *identifier, int count) {
std::lock_guard<std::mutex> lock(logNTimesLock);
auto iter = logNTimes.find(identifier);
if (iter == logNTimes.end()) {
logNTimes.insert(std::pair<const char*, int>(identifier, 1));
logNTimes.emplace(identifier, 1);
return true;
} else {
if (iter->second >= count) {

View File

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

View File

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

View File

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

View File

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

View File

@ -74,7 +74,6 @@ public:
const UI::Theme *theme;
// Utility methods
TextDrawer *Text() const { return textDrawer_; }
void SetFontStyle(const UI::FontStyle &style);
@ -103,6 +102,10 @@ public:
void setUIAtlas(const std::string &name);
void SetScreenTag(const char *tag) {
screenTag_ = tag;
}
private:
Draw::DrawContext *draw_ = nullptr;
Bounds bounds_;
@ -126,4 +129,6 @@ private:
std::string lastUIAtlas_;
std::string UIAtlas_ = "ui_atlas.zim";
const char *screenTag_ = nullptr;
};

View File

@ -71,7 +71,7 @@ public:
// what screen it is.
virtual void *dialogData() { return 0; }
virtual std::string tag() const { return std::string(""); }
virtual const char *tag() const = 0;
virtual bool isTransparent() const { return false; }
virtual bool isTopLevel() const { return false; }

View File

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

View File

@ -136,7 +136,7 @@ public:
void SetHiddenChoices(std::set<int> hidden) {
hidden_ = hidden;
}
virtual std::string tag() const override { return std::string("listpopup"); }
const char *tag() const override { return "listpopup"; }
UI::Event OnChoice;
@ -187,6 +187,8 @@ public:
disabled_ = *value_ < 0;
}
const char *tag() const override { return "SliderPopup"; }
Event OnChange;
private:
@ -214,6 +216,8 @@ public:
: PopupScreen(title, "OK", "Cancel"), units_(units), value_(value), originalValue_(*value), minValue_(minValue), maxValue_(maxValue), step_(step), changing_(false), liveUpdate_(liveUpdate) {}
void CreatePopupContents(UI::ViewGroup *parent) override;
const char *tag() const override { return "SliderFloatPopup"; }
Event OnChange;
private:
@ -241,6 +245,8 @@ public:
: PopupScreen(title, "OK", "Cancel"), value_(value), placeholder_(placeholder), maxLen_(maxLen) {}
virtual void CreatePopupContents(ViewGroup *parent) override;
const char *tag() const override { return "TextEditPopup"; }
Event OnChange;
private:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -164,6 +164,7 @@ public:
bool bHardwareTransform; // only used in the GLES backend
bool bSoftwareSkinning; // may speed up some games
bool bVendorBugChecksEnabled;
bool bUseGeometryShader;
int iRenderingMode; // 0 = non-buffered rendering 1 = buffered rendering
int iTexFiltering; // 1 = auto , 2 = nearest , 3 = linear , 4 = auto max quality
@ -176,6 +177,7 @@ public:
bool bSustainedPerformanceMode; // Android: Slows clocks down to avoid overheating/speed fluctuations.
bool bIgnoreScreenInsets; // Android: Center screen disregarding insets if this is enabled.
bool bVSync;
int iFrameSkip;
int iFrameSkipType;
int iFastForwardMode; // See FastForwardMode in ConfigValues.h.
@ -190,6 +192,7 @@ public:
int iWindowY;
int iWindowWidth; // Windows and other windowed environments
int iWindowHeight;
bool bShowMenuBar; // Windows-only
float fUITint;
float fUISaturation;
@ -242,7 +245,6 @@ public:
bool bShaderChainRequires60FPS;
std::string sTextureShaderName;
bool bGfxDebugOutput;
bool bGfxDebugSplitSubmit;
int iInflightFrames;
bool bRenderDuplicateFrames;

View File

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

View File

@ -139,7 +139,7 @@
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
@ -148,6 +148,7 @@
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<ForcedIncludeFiles>Common/DbgNew.h</ForcedIncludeFiles>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -157,12 +158,15 @@
<Outputs>../git-version.cpp;%(Outputs)</Outputs>
<Message>Updating git-version.cpp</Message>
</CustomBuildStep>
<Lib>
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib;../ext/zstd/lib</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<OmitFramePointers>false</OmitFramePointers>
@ -172,6 +176,7 @@
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<ForcedIncludeFiles>Common/DbgNew.h</ForcedIncludeFiles>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -181,12 +186,15 @@
<Outputs>../git-version.cpp;%(Outputs)</Outputs>
<Message>Updating git-version.cpp</Message>
</CustomBuildStep>
<Lib>
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_DEBUG;_LIB;_UNICODE;UNICODE;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<OmitFramePointers>false</OmitFramePointers>
@ -196,6 +204,7 @@
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<ForcedIncludeFiles>Common/DbgNew.h</ForcedIncludeFiles>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -205,12 +214,15 @@
<Outputs>../git-version.cpp;%(Outputs)</Outputs>
<Message>Updating git-version.cpp</Message>
</CustomBuildStep>
<Lib>
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_DEBUG;_LIB;_UNICODE;UNICODE;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<OmitFramePointers>false</OmitFramePointers>
@ -221,6 +233,7 @@
<ForcedIncludeFiles>
</ForcedIncludeFiles>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -230,6 +243,9 @@
<Outputs>../git-version.cpp;%(Outputs)</Outputs>
<Message>Updating git-version.cpp</Message>
</CustomBuildStep>
<Lib>
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
@ -241,12 +257,13 @@
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_M_IX86=1;_LIB;NDEBUG;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_M_IX86=1;_LIB;NDEBUG;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -255,6 +272,7 @@
</Link>
<Lib>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
</Lib>
<CustomBuildStep>
<Command>../Windows/git-version-gen.cmd</Command>
@ -276,11 +294,12 @@
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<WholeProgramOptimization>false</WholeProgramOptimization>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_M_X64=1;_LIB;NDEBUG;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_M_X64=1;_LIB;NDEBUG;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StringPooling>true</StringPooling>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -292,6 +311,9 @@
<Outputs>../git-version.cpp;%(Outputs)</Outputs>
<Message>Updating git-version.cpp</Message>
</CustomBuildStep>
<Lib>
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
@ -307,11 +329,12 @@
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<WholeProgramOptimization>false</WholeProgramOptimization>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_LIB;NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_LIB;NDEBUG;_UNICODE;UNICODE;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StringPooling>true</StringPooling>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -323,6 +346,9 @@
<Outputs>../git-version.cpp;%(Outputs)</Outputs>
<Message>Updating git-version.cpp</Message>
</CustomBuildStep>
<Lib>
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<ClCompile>
@ -338,11 +364,12 @@
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<WholeProgramOptimization>false</WholeProgramOptimization>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_LIB;NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_LIB;NDEBUG;_UNICODE;UNICODE;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StringPooling>true</StringPooling>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -354,6 +381,9 @@
<Outputs>../git-version.cpp;%(Outputs)</Outputs>
<Message>Updating git-version.cpp</Message>
</CustomBuildStep>
<Lib>
<AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\ext\cityhash\city.cpp" />
@ -568,7 +598,16 @@
<ClCompile Include="Cwcheat.cpp" />
<ClCompile Include="Debugger\Breakpoints.cpp" />
<ClCompile Include="Debugger\DisassemblyManager.cpp" />
<ClCompile Include="Debugger\SymbolMap.cpp" />
<ClCompile Include="Debugger\SymbolMap.cpp">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
</ClCompile>
<ClCompile Include="Dialog\PSPGamedataInstallDialog.cpp" />
<ClCompile Include="Dialog\PSPDialog.cpp" />
<ClCompile Include="Dialog\PSPMsgDialog.cpp" />
@ -900,7 +939,16 @@
<ClCompile Include="MIPS\JitCommon\JitState.cpp" />
<ClCompile Include="MIPS\MIPS.cpp" />
<ClCompile Include="MIPS\MIPSAnalyst.cpp" />
<ClCompile Include="MIPS\MIPSAsm.cpp" />
<ClCompile Include="MIPS\MIPSAsm.cpp">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/native/ext/libpng17;../ext/zlib;../ext/native/ext;../ext/armips/;../ext/armips/ext/filesystem/include/;../ext/armips/ext/tinyformat/</AdditionalIncludeDirectories>
</ClCompile>
<ClCompile Include="MIPS\MIPSCodeUtils.cpp" />
<ClCompile Include="MIPS\MIPSDebugInterface.cpp" />
<ClCompile Include="MIPS\MIPSDis.cpp" />

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,7 +22,7 @@
struct SceUtilityGamedataInstallParam {
pspUtilityDialogCommon common;
u32_le unknown1;
s32_le mode;
char gameName[13];
char ignore1[3];
char dataName[20];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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