diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8648726fd7..32ee69b692 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,7 +6,7 @@ # Core definitions variables: - GIT_SUBMODULE_STRATEGY: normal + GIT_SUBMODULE_STRATEGY: recursive .core-defs: variables: diff --git a/CMakeLists.txt b/CMakeLists.txt index bc81d9be2b..6cb38fb017 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 2895cdc837..901006a0d9 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -153,6 +153,7 @@ ws2_32.lib + /ignore:4221 %(AdditionalOptions) @@ -179,6 +180,7 @@ ws2_32.lib + /ignore:4221 %(AdditionalOptions) @@ -205,6 +207,7 @@ ws2_32.lib + /ignore:4221 %(AdditionalOptions) @@ -232,6 +235,7 @@ ws2_32.lib + /ignore:4221 %(AdditionalOptions) @@ -264,6 +268,7 @@ ws2_32.lib + /ignore:4221 %(AdditionalOptions) @@ -298,6 +303,7 @@ ws2_32.lib + /ignore:4221 %(AdditionalOptions) @@ -332,6 +338,7 @@ ws2_32.lib + /ignore:4221 %(AdditionalOptions) @@ -366,6 +373,7 @@ ws2_32.lib + /ignore:4221 %(AdditionalOptions) @@ -441,6 +449,7 @@ + @@ -861,6 +870,7 @@ + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index ff9fd9eaa6..991b03a71f 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -419,6 +419,9 @@ GPU\Vulkan + + GPU\Vulkan + @@ -791,6 +794,9 @@ GPU\Vulkan + + GPU\Vulkan + diff --git a/Common/Data/Collections/TinySet.h b/Common/Data/Collections/TinySet.h index 9b4d91030d..4daf8279a3 100644 --- a/Common/Data/Collections/TinySet.h +++ b/Common/Data/Collections/TinySet.h @@ -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(); @@ -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 &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 *slowLookup_ = nullptr; }; diff --git a/Common/Data/Convert/SmallDataConvert.h b/Common/Data/Convert/SmallDataConvert.h index a877295918..95558a17df 100644 --- a/Common/Data/Convert/SmallDataConvert.h +++ b/Common/Data/Convert/SmallDataConvert.h @@ -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); diff --git a/Common/Data/Encoding/Compression.cpp b/Common/Data/Encoding/Compression.cpp index 751a9595cf..3e2e499a18 100644 --- a/Common/Data/Encoding/Compression.cpp +++ b/Common/Data/Encoding/Compression.cpp @@ -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; } diff --git a/Common/Data/Format/IniFile.cpp b/Common/Data/Format/IniFile.cpp index 32fcf43b68..cd2a658e5c 100644 --- a/Common/Data/Format/IniFile.cpp +++ b/Common/Data/Format/IniFile.cpp @@ -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& newValues) } void Section::AddComment(const std::string &comment) { - lines.push_back("# " + comment); + lines.emplace_back("# " + comment); } bool Section::Get(const char* key, std::vector& values) diff --git a/Common/Data/Text/I18n.cpp b/Common/Data/Text/I18n.cpp index 0ede4c516d..00771fe119 100644 --- a/Common/Data/Text/I18n.cpp +++ b/Common/Data/Text/I18n.cpp @@ -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; } diff --git a/Common/File/DirListing.cpp b/Common/File/DirListing.cpp index abe5ff3af8..2f7178847b 100644 --- a/Common/File/DirListing.cpp +++ b/Common/File/DirListing.cpp @@ -148,7 +148,7 @@ std::vector ApplyFilter(std::vector 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 ApplyFilter(std::vector files, const filter++; } if (!tmp.empty()) - filters.insert("." + tmp); + filters.emplace("." + tmp); } auto pred = [&](const File::FileInfo &info) { diff --git a/Common/File/Path.cpp b/Common/File/Path.cpp index ba3372e78a..39a31e164e 100644 --- a/Common/File/Path.cpp +++ b/Common/File/Path.cpp @@ -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_); diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 2bce9ebaff..166a0ebe66 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -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)); diff --git a/Common/GPU/D3D9/D3D9ShaderCompiler.cpp b/Common/GPU/D3D9/D3D9ShaderCompiler.cpp index f41ea3911d..d3821066aa 100644 --- a/Common/GPU/D3D9/D3D9ShaderCompiler.cpp +++ b/Common/GPU/D3D9/D3D9ShaderCompiler.cpp @@ -47,7 +47,7 @@ LPD3DBLOB CompileShaderToByteCodeD3D9(const char *code, const char *target, std: pShaderCode = nullptr; } } else { - *errorMessage = ""; + errorMessage->clear(); } return pShaderCode; diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index 8554961119..377a0b4050 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -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; diff --git a/Common/GPU/DataFormat.h b/Common/GPU/DataFormat.h index b07d0a2f34..0c8a93e627 100644 --- a/Common/GPU/DataFormat.h +++ b/Common/GPU/DataFormat.h @@ -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, diff --git a/Common/GPU/OpenGL/DataFormatGL.cpp b/Common/GPU/OpenGL/DataFormatGL.cpp index 1b8ba63acc..48e439a76d 100644 --- a/Common/GPU/OpenGL/DataFormatGL.cpp +++ b/Common/GPU/OpenGL/DataFormatGL.cpp @@ -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; diff --git a/Common/GPU/OpenGL/DataFormatGL.h b/Common/GPU/OpenGL/DataFormatGL.h index 63fc0a22ec..163f155db3 100644 --- a/Common/GPU/OpenGL/DataFormatGL.h +++ b/Common/GPU/OpenGL/DataFormatGL.h @@ -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); } diff --git a/Common/GPU/OpenGL/GLFeatures.cpp b/Common/GPU/OpenGL/GLFeatures.cpp index bf9a639e67..aa79ac5941 100644 --- a/Common/GPU/OpenGL/GLFeatures.cpp +++ b/Common/GPU/OpenGL/GLFeatures.cpp @@ -51,7 +51,7 @@ static void ParseExtensionsString(const std::string& str, std::set 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 if (next == 0 && str.length() != 0) { output.insert(str); } else if (next < str.length()) { - output.insert(str.substr(next)); + output.emplace(str.substr(next)); } } diff --git a/Common/GPU/OpenGL/GLQueueRunner.cpp b/Common/GPU/OpenGL/GLQueueRunner.cpp index e19075f109..80222bbfbf 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.cpp +++ b/Common/GPU/OpenGL/GLQueueRunner.cpp @@ -252,8 +252,8 @@ void GLQueueRunner::RunInitSteps(const std::vector &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 &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 &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(); diff --git a/Common/GPU/OpenGL/GLRenderManager.cpp b/Common/GPU/OpenGL/GLRenderManager.cpp index ae5cb9dd17..01cccb8874 100644 --- a/Common/GPU/OpenGL/GLRenderManager.cpp +++ b/Common/GPU/OpenGL/GLRenderManager.cpp @@ -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; diff --git a/Common/GPU/OpenGL/GLRenderManager.h b/Common/GPU/OpenGL/GLRenderManager.h index 719603863e..51d7e1e9eb 100644 --- a/Common/GPU/OpenGL/GLRenderManager.h +++ b/Common/GPU/OpenGL/GLRenderManager.h @@ -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 semantics_; std::vector queries_; std::vector 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 shaders, std::vector semantics, std::vector queries, - std::vector initializers, bool supportDualSource, bool useClipDistance0) { + std::vector 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; diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index 2e93fc8791..95e737e63a 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -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 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: diff --git a/Common/GPU/ShaderTranslation.cpp b/Common/GPU/ShaderTranslation.cpp index 34de213451..9391d1b7da 100644 --- a/Common/GPU/ShaderTranslation.cpp +++ b/Common/GPU/ShaderTranslation.cpp @@ -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]{}; diff --git a/Common/GPU/ShaderWriter.cpp b/Common/GPU/ShaderWriter.cpp index b260d05fc9..7ca6deaeb0 100644 --- a/Common/GPU/ShaderWriter.cpp +++ b/Common/GPU/ShaderWriter.cpp @@ -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 inputs, Slice 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 uniforms, Slice var } } +void ShaderWriter::BeginGSMain(Slice varyings, Slice 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 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 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"); diff --git a/Common/GPU/ShaderWriter.h b/Common/GPU/ShaderWriter.h index f6a04c828b..50f3bb1f0d 100644 --- a/Common/GPU/ShaderWriter.h +++ b/Common/GPU/ShaderWriter.h @@ -85,10 +85,12 @@ public: // Simple shaders with no special tricks. void BeginVSMain(Slice inputs, Slice uniforms, Slice varyings); void BeginFSMain(Slice uniforms, Slice varyings, FSFlags flags); + void BeginGSMain(Slice varyings, Slice outVaryings); // For simple shaders that output a single color, we can deal with this generically. void EndVSMain(Slice varyings); void EndFSMain(const char *vec4_color_variable, FSFlags flags); + void EndGSMain(); const ShaderLanguageDesc &Lang() const { return lang_; diff --git a/Common/GPU/Vulkan/VulkanBarrier.cpp b/Common/GPU/Vulkan/VulkanBarrier.cpp index 125d51ea6d..e4f2d09089 100644 --- a/Common/GPU/Vulkan/VulkanBarrier.cpp +++ b/Common/GPU/Vulkan/VulkanBarrier.cpp @@ -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; diff --git a/Common/GPU/Vulkan/VulkanBarrier.h b/Common/GPU/Vulkan/VulkanBarrier.h index eb949dd2f0..19edc548ba 100644 --- a/Common/GPU/Vulkan/VulkanBarrier.h +++ b/Common/GPU/Vulkan/VulkanBarrier.h @@ -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 imageBarriers_; + VkDependencyFlags dependencyFlags_ = 0; }; diff --git a/Common/GPU/Vulkan/VulkanContext.cpp b/Common/GPU/Vulkan/VulkanContext.cpp index 55edc0f31f..c2140c5663 100644 --- a/Common/GPU/Vulkan/VulkanContext.cpp +++ b/Common/GPU/Vulkan/VulkanContext.cpp @@ -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; diff --git a/Common/GPU/Vulkan/VulkanDebug.cpp b/Common/GPU/Vulkan/VulkanDebug.cpp index d4052e970a..83b3d974c0 100644 --- a/Common/GPU/Vulkan/VulkanDebug.cpp +++ b/Common/GPU/Vulkan/VulkanDebug.cpp @@ -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). diff --git a/Common/GPU/Vulkan/VulkanFrameData.cpp b/Common/GPU/Vulkan/VulkanFrameData.cpp new file mode 100644 index 0000000000..ab74478396 --- /dev/null +++ b/Common/GPU/Vulkan/VulkanFrameData.cpp @@ -0,0 +1,224 @@ +#include + +#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 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); +} diff --git a/Common/GPU/Vulkan/VulkanFrameData.h b/Common/GPU/Vulkan/VulkanFrameData.h new file mode 100644 index 0000000000..88d4c185d2 --- /dev/null +++ b/Common/GPU/Vulkan/VulkanFrameData.h @@ -0,0 +1,93 @@ +#pragma once + +#include + +#include +#include + +#include "Common/GPU/Vulkan/VulkanContext.h" + +enum { + MAX_TIMESTAMP_QUERIES = 128, +}; + +enum class VKRRunType { + PRESENT, + SYNC, + EXIT, +}; + +struct QueueProfileContext { + VkQueryPool queryPool; + std::vector 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; +}; diff --git a/Common/GPU/Vulkan/VulkanLoader.h b/Common/GPU/Vulkan/VulkanLoader.h index 1f3d7d7704..b0c14570da 100644 --- a/Common/GPU/Vulkan/VulkanLoader.h +++ b/Common/GPU/Vulkan/VulkanLoader.h @@ -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. }; diff --git a/Common/GPU/Vulkan/VulkanProfiler.cpp b/Common/GPU/Vulkan/VulkanProfiler.cpp index 36305484da..fccea843b8 100644 --- a/Common/GPU/Vulkan/VulkanProfiler.cpp +++ b/Common/GPU/Vulkan/VulkanProfiler.cpp @@ -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(); diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index 10a88e623a..69d81b5391 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.cpp +++ b/Common/GPU/Vulkan/VulkanQueueRunner.cpp @@ -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 &steps) { // Optimizes renderpasses, then sequences them. // Planned optimizations: @@ -321,23 +544,54 @@ void VulkanQueueRunner::PreprocessSteps(std::vector &steps) { } } -void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &steps, QueueProfileContext *profile) { +void VulkanQueueRunner::RunSteps(std::vector &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 &st // Deleting all in one go should be easier on the instruction cache than deleting // them as we go - and easier to debug because we can look backwards in the frame. - for (size_t i = 0; i < steps.size(); i++) { - delete steps[i]; + for (auto step : steps) { + delete step; } + steps.clear(); + if (profile) profile->cpuEndTime = time_now_d(); } @@ -627,10 +883,13 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const { const char *renderCmd; switch (step.render.renderPassType) { case RP_TYPE_BACKBUFFER: renderCmd = "BACKBUF"; break; - case RP_TYPE_COLOR_DEPTH: renderCmd = "RENDER"; break; + case RP_TYPE_COLOR: renderCmd = "RENDER"; break; + case RP_TYPE_COLOR_DEPTH: renderCmd = "RENDER_DEPTH"; break; + case RP_TYPE_COLOR_INPUT: renderCmd = "RENDER_INPUT"; break; + case RP_TYPE_COLOR_DEPTH_INPUT: renderCmd = "RENDER_DEPTH_INPUT"; break; default: renderCmd = "N/A"; } - snprintf(buffer, sizeof(buffer), "%s %s (draws: %d, %dx%d/%dx%d, fb: %p, )", renderCmd, step.tag, step.render.numDraws, actual_w, actual_h, w, h, step.render.framebuffer); + snprintf(buffer, sizeof(buffer), "%s %s %s (draws: %d, %dx%d/%dx%d)", renderCmd, step.tag, step.render.framebuffer ? step.render.framebuffer->Tag() : "", step.render.numDraws, actual_w, actual_h, w, h); break; } case VKRStepType::COPY: @@ -640,7 +899,7 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const { snprintf(buffer, sizeof(buffer), "BLIT '%s' %s -> %s (%dx%d->%dx%d, %s)", step.tag, step.copy.src->Tag(), step.copy.dst->Tag(), step.blit.srcRect.extent.width, step.blit.srcRect.extent.height, step.blit.dstRect.extent.width, step.blit.dstRect.extent.height, AspectToString(step.blit.aspectMask)); break; case VKRStepType::READBACK: - snprintf(buffer, sizeof(buffer), "READBACK '%s' %s (%dx%d, %s)", step.tag, step.readback.src->Tag(), step.readback.srcRect.extent.width, step.readback.srcRect.extent.height, AspectToString(step.readback.aspectMask)); + snprintf(buffer, sizeof(buffer), "READBACK '%s' %s (%dx%d, %s)", step.tag, step.readback.src ? step.readback.src->Tag() : "(backbuffer)", step.readback.srcRect.extent.width, step.readback.srcRect.extent.height, AspectToString(step.readback.aspectMask)); break; case VKRStepType::READBACK_IMAGE: snprintf(buffer, sizeof(buffer), "READBACK_IMAGE '%s' (%dx%d)", step.tag, step.readback_image.srcRect.extent.width, step.readback_image.srcRect.extent.height); @@ -817,6 +1076,9 @@ void VulkanQueueRunner::LogRenderPass(const VKRStep &pass, bool verbose) { case VKRRenderCommand::REMOVED: INFO_LOG(G3D, " (Removed)"); break; + case VKRRenderCommand::SELF_DEPENDENCY_BARRIER: + INFO_LOG(G3D, " SelfBarrier()"); + break; case VKRRenderCommand::BIND_GRAPHICS_PIPELINE: INFO_LOG(G3D, " BindGraphicsPipeline(%x)", (int)(intptr_t)cmd.graphics_pipeline.pipeline); break; @@ -903,7 +1165,7 @@ void TransitionToOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayout srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; break; default: - _dbg_assert_msg_(false, "GetRenderPass: Unexpected color layout %d", (int)colorLayout); + _dbg_assert_msg_(false, "TransitionToOptimal: Unexpected color layout %d", (int)colorLayout); break; } recordBarrier->TransitionImage( @@ -939,7 +1201,7 @@ void TransitionToOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayout srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; break; default: - _dbg_assert_msg_(false, "GetRenderPass: Unexpected depth layout %d", (int)depthStencilLayout); + _dbg_assert_msg_(false, "TransitionToOptimal: Unexpected depth layout %d", (int)depthStencilLayout); break; } recordBarrier->TransitionImage( @@ -986,7 +1248,7 @@ void TransitionFromOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayou // Nothing to do. break; default: - _dbg_assert_msg_(false, "GetRenderPass: Unexpected final color layout %d", (int)colorLayout); + _dbg_assert_msg_(false, "TransitionFromOptimal: Unexpected final color layout %d", (int)colorLayout); break; } barrier[0].oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; @@ -1025,7 +1287,7 @@ void TransitionFromOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayou // Nothing to do. break; default: - _dbg_assert_msg_(false, "GetRenderPass: Unexpected final depth layout %d", (int)depthStencilLayout); + _dbg_assert_msg_(false, "TransitionFromOptimal: Unexpected final depth layout %d", (int)depthStencilLayout); break; } barrier[barrierCount].oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; @@ -1057,7 +1319,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c iter.targetLayout ); iter.fb->color.layout = iter.targetLayout; - } else if ((iter.aspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) && iter.fb->depth.layout != iter.targetLayout) { + } else if (iter.fb->depth.image != VK_NULL_HANDLE && (iter.aspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) && iter.fb->depth.layout != iter.targetLayout) { recordBarrier_.TransitionImageAuto( iter.fb->depth.image, 0, @@ -1070,7 +1332,6 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c } } - // Don't execute empty renderpasses that keep the contents. if (step.commands.empty() && step.render.colorLoad == VKRRenderPassLoadAction::KEEP && step.render.depthLoad == VKRRenderPassLoadAction::KEEP && step.render.stencilLoad == VKRRenderPassLoadAction::KEEP) { // Flush the pending barrier @@ -1087,7 +1348,6 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c int n = 0; int stage = 0; - VkImageMemoryBarrier barriers[2]{}; if (step.render.framebuffer->color.layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { recordBarrier_.TransitionImage( step.render.framebuffer->color.image, @@ -1102,7 +1362,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ); } - if (step.render.framebuffer->depth.layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + if (step.render.framebuffer->depth.image != VK_NULL_HANDLE && step.render.framebuffer->depth.layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { recordBarrier_.TransitionImage( step.render.framebuffer->depth.image, 0, @@ -1120,6 +1380,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c // This reads the layout of the color and depth images, and chooses a render pass using them that // will transition to the desired final layout. + // // NOTE: Flushes recordBarrier_. VKRRenderPass *renderPass = PerformBindFramebufferAsRenderTarget(step, cmd); @@ -1235,6 +1496,15 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c break; } + case VKRRenderCommand::SELF_DEPENDENCY_BARRIER: + { + _assert_(step.render.pipelineFlags & PipelineFlags::USES_INPUT_ATTACHMENT); + VulkanBarrier barrier; + SelfDependencyBarrier(step.render.framebuffer->color, VK_IMAGE_ASPECT_COLOR_BIT, &barrier); + barrier.Flush(cmd); + break; + } + case VKRRenderCommand::PUSH_CONSTANTS: vkCmdPushConstants(cmd, pipelineLayout, c.push.stages, c.push.offset, c.push.size, c.push.data); break; @@ -1327,6 +1597,9 @@ VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKR VkFramebuffer framebuf; int w; int h; + + bool hasDepth = RenderPassTypeHasDepth(step.render.renderPassType); + if (step.render.framebuffer) { _dbg_assert_(step.render.finalColorLayout != VK_IMAGE_LAYOUT_UNDEFINED); _dbg_assert_(step.render.finalDepthStencilLayout != VK_IMAGE_LAYOUT_UNDEFINED); @@ -1367,7 +1640,7 @@ VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKR Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor); numClearVals = 1; } - if (step.render.depthLoad == VKRRenderPassLoadAction::CLEAR || step.render.stencilLoad == VKRRenderPassLoadAction::CLEAR) { + if (hasDepth && (step.render.depthLoad == VKRRenderPassLoadAction::CLEAR || step.render.stencilLoad == VKRRenderPassLoadAction::CLEAR)) { clearVal[1].depthStencil.depth = step.render.clearDepth; clearVal[1].depthStencil.stencil = step.render.clearStencil; numClearVals = 2; @@ -1386,7 +1659,7 @@ VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKR h = vulkan_->GetBackbufferHeight(); Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor); - numClearVals = 2; // We don't bother with a depth buffer here. + numClearVals = hasDepth ? 2 : 1; // We might do depth-less backbuffer in the future, though doubtful of the value. clearVal[1].depthStencil.depth = 0.0f; clearVal[1].depthStencil.stencil = 0; } @@ -1457,6 +1730,10 @@ void VulkanQueueRunner::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) { if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { SetupTransitionToTransferDst(dst->depth, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, &recordBarrier_); _dbg_assert_(dst->depth.layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + } else { + // Kingdom Hearts: Subsequent copies to the same depth buffer without any other use. + // Not super sure how that happens, but we need a barrier to pass sync validation. + SetupTransferDstWriteAfterWrite(dst->depth, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, &recordBarrier_); } } @@ -1468,6 +1745,8 @@ void VulkanQueueRunner::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) { vkCmdCopyImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, ©); } if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + _dbg_assert_(src->depth.image != VK_NULL_HANDLE); + _dbg_assert_(dst->depth.image != VK_NULL_HANDLE); copy.srcSubresource.aspectMask = step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); copy.dstSubresource.aspectMask = step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); vkCmdCopyImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, ©); @@ -1478,9 +1757,6 @@ void VulkanQueueRunner::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) { // The barrier code doesn't handle this case. We'd need to transition to GENERAL to do an intra-image copy. _dbg_assert_(step.blit.src != step.blit.dst); - VkImageMemoryBarrier srcBarriers[2]{}; - VkImageMemoryBarrier dstBarriers[2]{}; - VKRFramebuffer *src = step.blit.src; VKRFramebuffer *dst = step.blit.dst; @@ -1522,6 +1798,9 @@ void VulkanQueueRunner::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) { // We can't copy only depth or only stencil unfortunately. if (step.blit.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + _dbg_assert_(src->depth.image != VK_NULL_HANDLE); + _dbg_assert_(dst->depth.image != VK_NULL_HANDLE); + if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { SetupTransitionToTransferSrc(src->depth, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, &recordBarrier_); } @@ -1652,6 +1931,33 @@ void VulkanQueueRunner::SetupTransitionToTransferDst(VKRImage &img, VkImageAspec img.layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; } +void VulkanQueueRunner::SetupTransferDstWriteAfterWrite(VKRImage &img, VkImageAspectFlags aspect, VulkanBarrier *recordBarrier) { + VkImageAspectFlags imageAspect = aspect; + VkAccessFlags srcAccessMask = 0; + VkPipelineStageFlags srcStageMask = 0; + if (img.format == VK_FORMAT_D16_UNORM_S8_UINT || img.format == VK_FORMAT_D24_UNORM_S8_UINT || img.format == VK_FORMAT_D32_SFLOAT_S8_UINT) { + // Barrier must specify both for combined depth/stencil buffers. + imageAspect = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + } else { + imageAspect = aspect; + } + _dbg_assert_(img.layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + recordBarrier->TransitionImage( + img.image, + 0, + 1, + aspect, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT + ); +} + void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd) { ResizeReadbackBuffer(sizeof(uint32_t) * step.readback.srcRect.extent.width * step.readback.srcRect.extent.height); diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.h b/Common/GPU/Vulkan/VulkanQueueRunner.h index adb9e7d96e..7036029149 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.h +++ b/Common/GPU/Vulkan/VulkanQueueRunner.h @@ -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 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 steps; + int frame; + VKRRunType runType; +}; + class VulkanQueueRunner { public: VulkanQueueRunner(VulkanContext *vulkan) : vulkan_(vulkan), renderPasses_(16) {} @@ -255,7 +278,7 @@ public: } void PreprocessSteps(std::vector &steps); - void RunSteps(VkCommandBuffer cmd, std::vector &steps, QueueProfileContext *profile); + void RunSteps(std::vector &steps, FrameData &frameData, FrameDataShared &frameDataShared); void LogSteps(const std::vector &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 framebuffers_; + std::vector 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_; }; diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 10cc890009..3343bd889f 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -14,7 +14,7 @@ #include "Common/Thread/ThreadUtil.h" #if 0 // def _DEBUG -#define VLOG(...) INFO_LOG(G3D, __VA_ARGS__) +#define VLOG(...) NOTICE_LOG(G3D, __VA_ARGS__) #else #define VLOG(...) #endif @@ -30,30 +30,38 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleR // Fill in the last part of the desc since now it's time to block. VkShaderModule vs = desc->vertexShader->BlockUntilReady(); VkShaderModule fs = desc->fragmentShader->BlockUntilReady(); + VkShaderModule gs = desc->geometryShader ? desc->geometryShader->BlockUntilReady() : VK_NULL_HANDLE; - if (!vs || !fs) { + if (!vs || !fs || (!gs && desc->geometryShader)) { ERROR_LOG(G3D, "Failed creating graphics pipeline - missing shader modules"); // We're kinda screwed here? return false; } - VkPipelineShaderStageCreateInfo ss[2]{}; + uint32_t stageCount = 2; + VkPipelineShaderStageCreateInfo ss[3]{}; ss[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; ss[0].stage = VK_SHADER_STAGE_VERTEX_BIT; ss[0].pSpecializationInfo = nullptr; ss[0].module = vs; ss[0].pName = "main"; - ss[0].flags = 0; ss[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; ss[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; ss[1].pSpecializationInfo = nullptr; ss[1].module = fs; ss[1].pName = "main"; - ss[1].flags = 0; + if (gs) { + stageCount++; + ss[2].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + ss[2].stage = VK_SHADER_STAGE_GEOMETRY_BIT; + ss[2].pSpecializationInfo = nullptr; + ss[2].module = gs; + ss[2].pName = "main"; + } VkGraphicsPipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; pipe.pStages = ss; - pipe.stageCount = 2; + pipe.stageCount = stageCount; pipe.renderPass = compatibleRenderPass; pipe.basePipelineIndex = 0; pipe.pColorBlendState = &desc->cbs; @@ -146,43 +154,71 @@ bool VKRComputePipeline::Create(VulkanContext *vulkan) { return success; } -VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, const char *tag) : vulkan_(vk), tag_(tag) { +VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, bool createDepthStencilBuffer, const char *tag) : vulkan_(vk), tag_(tag) { width = _width; height = _height; + _dbg_assert_(tag); + CreateImage(vulkan_, initCmd, color, width, height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, tag); - CreateImage(vulkan_, initCmd, depth, width, height, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, tag); + if (createDepthStencilBuffer) { + CreateImage(vulkan_, initCmd, depth, width, height, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, tag); + } + + UpdateTag(tag); // We create the actual framebuffer objects on demand, because some combinations might not make sense. + // Framebuffer objects are just pointers to a set of images, so no biggie. } -VkFramebuffer VKRFramebuffer::Get(VKRRenderPass *compatibleRenderPass, RenderPassType renderPassType) { - if (framebuf[(int)renderPassType]) { - return framebuf[(int)renderPassType]; +void VKRFramebuffer::UpdateTag(const char *newTag) { + char name[128]; + snprintf(name, sizeof(name), "fb_color_%s", tag_.c_str()); + vulkan_->SetDebugName(color.image, VK_OBJECT_TYPE_IMAGE, name); + vulkan_->SetDebugName(color.imageView, VK_OBJECT_TYPE_IMAGE_VIEW, name); + if (depth.image) { + snprintf(name, sizeof(name), "fb_depth_%s", tag_.c_str()); + vulkan_->SetDebugName(depth.image, VK_OBJECT_TYPE_IMAGE, name); + vulkan_->SetDebugName(depth.imageView, VK_OBJECT_TYPE_IMAGE_VIEW, name); + } + for (int rpType = 0; rpType < RP_TYPE_COUNT; rpType++) { + if (framebuf[rpType]) { + snprintf(name, sizeof(name), "fb_%s", tag_.c_str()); + vulkan_->SetDebugName(framebuf[(int)rpType], VK_OBJECT_TYPE_FRAMEBUFFER, name); + } + } +} + +VkFramebuffer VKRFramebuffer::Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType) { + if (framebuf[(int)rpType]) { + return framebuf[(int)rpType]; } VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; VkImageView views[2]{}; - fbci.renderPass = compatibleRenderPass->Get(vulkan_, renderPassType); - fbci.attachmentCount = 2; - fbci.pAttachments = views; + bool hasDepth = rpType == RP_TYPE_BACKBUFFER || rpType == RP_TYPE_COLOR_DEPTH || rpType == RP_TYPE_COLOR_DEPTH_INPUT; + views[0] = color.imageView; - views[1] = depth.imageView; + if (hasDepth) { + _dbg_assert_(depth.imageView != VK_NULL_HANDLE); + views[1] = depth.imageView; + } + fbci.renderPass = compatibleRenderPass->Get(vulkan_, rpType); + fbci.attachmentCount = hasDepth ? 2 : 1; + fbci.pAttachments = views; fbci.width = width; fbci.height = height; fbci.layers = 1; - VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf[(int)renderPassType]); + VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf[(int)rpType]); _assert_(res == VK_SUCCESS); if (!tag_.empty() && vulkan_->Extensions().EXT_debug_utils) { - vulkan_->SetDebugName(color.image, VK_OBJECT_TYPE_IMAGE, StringFromFormat("fb_color_%s", tag_.c_str()).c_str()); - vulkan_->SetDebugName(depth.image, VK_OBJECT_TYPE_IMAGE, StringFromFormat("fb_depth_%s", tag_.c_str()).c_str()); - vulkan_->SetDebugName(framebuf[(int)renderPassType], VK_OBJECT_TYPE_FRAMEBUFFER, StringFromFormat("fb_%s", tag_.c_str()).c_str()); + vulkan_->SetDebugName(framebuf[(int)rpType], VK_OBJECT_TYPE_FRAMEBUFFER, StringFromFormat("fb_%s", tag_.c_str()).c_str()); } - return framebuf[(int)renderPassType]; + return framebuf[(int)rpType]; } VKRFramebuffer::~VKRFramebuffer() { @@ -221,7 +257,7 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int // Strictly speaking we don't yet need VK_IMAGE_USAGE_SAMPLED_BIT for depth buffers since we do not yet sample depth buffers. ici.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; if (color) { - ici.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + ici.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; } else { ici.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; } @@ -286,53 +322,15 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int } VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan), queueRunner_(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); - inflightFramesAtStart_ = vulkan_->GetInflightFrames(); + + frameDataShared_.Init(vulkan); + for (int i = 0; i < inflightFramesAtStart_; i++) { - 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(vulkan_->GetDevice(), &cmd_pool_info, nullptr, &frameData_[i].cmdPoolInit); - _dbg_assert_(res == VK_SUCCESS); - res = vkCreateCommandPool(vulkan_->GetDevice(), &cmd_pool_info, nullptr, &frameData_[i].cmdPoolMain); - _dbg_assert_(res == VK_SUCCESS); - - VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; - cmd_alloc.commandPool = frameData_[i].cmdPoolInit; - cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - cmd_alloc.commandBufferCount = 1; - - res = vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd_alloc, &frameData_[i].initCmd); - _dbg_assert_(res == VK_SUCCESS); - cmd_alloc.commandPool = frameData_[i].cmdPoolMain; - res = vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd_alloc, &frameData_[i].mainCmd); - _dbg_assert_(res == VK_SUCCESS); - - // Creating the frame fence with true so they can be instantly waited on the first frame - frameData_[i].fence = vulkan_->CreateFence(true); - - // This fence one is used for synchronizing readbacks. Does not need preinitialization. - frameData_[i].readbackFence = vulkan_->CreateFence(false); - - 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(vulkan_->GetDevice(), &query_ci, nullptr, &frameData_[i].profile.queryPool); + frameData_[i].Init(vulkan, i); } queueRunner_.CreateDeviceObjects(); - - // AMD hack for issue #10097 (older drivers only.) - const auto &props = vulkan_->GetPhysicalDeviceProperties().properties; - if (props.vendorID == VULKAN_VENDOR_AMD && props.apiVersion < VK_API_VERSION_1_1) { - useThread_ = false; - } } bool VulkanRenderManager::CreateBackbuffers() { @@ -340,52 +338,14 @@ bool VulkanRenderManager::CreateBackbuffers() { ERROR_LOG(G3D, "No swapchain - can't create backbuffers"); return false; } - 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; - } VkCommandBuffer cmdInit = GetInitCmd(); - 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); + if (!queueRunner_.CreateSwapchain(cmdInit)) { + return false; } - delete[] swapchainImages; - // Must be before InitBackbufferRenderPass. - if (InitDepthStencilBuffer(cmdInit)) { - InitBackbufferFramebuffers(vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); - } curWidthRaw_ = -1; curHeightRaw_ = -1; @@ -402,11 +362,10 @@ bool VulkanRenderManager::CreateBackbuffers() { outOfDateFrames_ = 0; // Start the thread. - if (useThread_ && HasBackbuffers()) { - run_ = true; - // Won't necessarily be 0. - threadInitFrame_ = vulkan_->GetCurFrame(); - INFO_LOG(G3D, "Starting Vulkan submission thread (threadInitFrame_ = %d)", vulkan_->GetCurFrame()); + if (HasBackbuffers()) { + run_ = true; // For controlling the compiler thread's exit + + INFO_LOG(G3D, "Starting Vulkan submission thread"); thread_ = std::thread(&VulkanRenderManager::ThreadFunc, this); INFO_LOG(G3D, "Starting Vulkan compiler thread"); compileThread_ = std::thread(&VulkanRenderManager::CompileThreadFunc, this); @@ -414,58 +373,58 @@ bool VulkanRenderManager::CreateBackbuffers() { return true; } +// Called from main thread. void VulkanRenderManager::StopThread() { - if (useThread_ && run_) { - run_ = false; - // Stop the thread. - for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { - auto &frameData = frameData_[i]; - { - std::unique_lock lock(frameData.push_mutex); - frameData.push_condVar.notify_all(); - } - { - std::unique_lock lock(frameData.pull_mutex); - frameData.pull_condVar.notify_all(); - } - // Zero the queries so we don't try to pull them later. - frameData.profile.timestampDescriptions.clear(); + { + // Tell the render thread to quit when it's done. + VKRRenderThreadTask task; + task.frame = vulkan_->GetCurFrame(); + task.runType = VKRRunType::EXIT; + std::unique_lock lock(pushMutex_); + renderThreadQueue_.push(task); + pushCondVar_.notify_one(); + } + + // Compiler thread still relies on this. + run_ = false; + + // Stop the thread. + thread_.join(); + + for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { + auto &frameData = frameData_[i]; + // Zero the queries so we don't try to pull them later. + frameData.profile.timestampDescriptions.clear(); + } + + INFO_LOG(G3D, "Vulkan submission thread joined. Frame=%d", vulkan_->GetCurFrame()); + + compileCond_.notify_all(); + compileThread_.join(); + INFO_LOG(G3D, "Vulkan compiler thread joined."); + + // Eat whatever has been queued up for this frame if anything. + Wipe(); + + // Clean out any remaining queued data, which might refer to things that might not be valid + // when we restart the thread... + + // Not sure if this is still needed + for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { + auto &frameData = frameData_[i]; + if (frameData.hasInitCommands) { + // Clear 'em out. This can happen on restart sometimes. + vkEndCommandBuffer(frameData.initCmd); + frameData.hasInitCommands = false; } - thread_.join(); - INFO_LOG(G3D, "Vulkan submission thread joined. Frame=%d", vulkan_->GetCurFrame()); - compileCond_.notify_all(); - compileThread_.join(); - INFO_LOG(G3D, "Vulkan compiler thread joined."); - - // Eat whatever has been queued up for this frame if anything. - Wipe(); - - // Wait for any fences to finish and be resignaled, so we don't have sync issues. - // Also clean out any queued data, which might refer to things that might not be valid - // when we restart... - for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { - auto &frameData = frameData_[i]; - _assert_(!frameData.readyForRun); - _assert_(frameData.steps.empty()); - if (frameData.hasInitCommands) { - // Clear 'em out. This can happen on restart sometimes. - vkEndCommandBuffer(frameData.initCmd); - frameData.hasInitCommands = false; - } - frameData.readyForRun = false; - for (size_t i = 0; i < frameData.steps.size(); i++) { - delete frameData.steps[i]; - } - frameData.steps.clear(); - - std::unique_lock lock(frameData.push_mutex); - while (!frameData.readyForFence) { - VLOG("PUSH: Waiting for frame[%d].readyForFence = 1 (stop)", i); - frameData.push_condVar.wait(lock); - } + if (frameData.hasMainCommands) { + vkEndCommandBuffer(frameData.mainCmd); + frameData.hasMainCommands = false; + } + if (frameData.hasPresentCommands) { + vkEndCommandBuffer(frameData.presentCmd); + frameData.hasPresentCommands = false; } - } else { - INFO_LOG(G3D, "Vulkan submission thread was already stopped."); } } @@ -473,45 +432,21 @@ void VulkanRenderManager::DestroyBackbuffers() { StopThread(); vulkan_->WaitUntilQueueIdle(); - 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"); + queueRunner_.DestroyBackBuffers(); } VulkanRenderManager::~VulkanRenderManager() { INFO_LOG(G3D, "VulkanRenderManager destructor"); - StopThread(); + + _dbg_assert_(!run_); // StopThread should already have been called from DestroyBackbuffers. + vulkan_->WaitUntilQueueIdle(); DrainCompileQueue(); VkDevice device = vulkan_->GetDevice(); - vkDestroySemaphore(device, acquireSemaphore_, nullptr); - vkDestroySemaphore(device, renderingCompleteSemaphore_, nullptr); + frameDataShared_.Destroy(vulkan_); for (int i = 0; i < inflightFramesAtStart_; i++) { - vkFreeCommandBuffers(device, frameData_[i].cmdPoolInit, 1, &frameData_[i].initCmd); - vkFreeCommandBuffers(device, frameData_[i].cmdPoolMain, 1, &frameData_[i].mainCmd); - vkDestroyCommandPool(device, frameData_[i].cmdPoolInit, nullptr); - vkDestroyCommandPool(device, frameData_[i].cmdPoolMain, nullptr); - vkDestroyFence(device, frameData_[i].fence, nullptr); - vkDestroyFence(device, frameData_[i].readbackFence, nullptr); - vkDestroyQueryPool(device, frameData_[i].profile.queryPool, nullptr); + frameData_[i].Destroy(vulkan_); } queueRunner_.DestroyDeviceObjects(); } @@ -532,7 +467,9 @@ void VulkanRenderManager::CompileThreadFunc() { break; } - INFO_LOG(G3D, "Compilation thread has %d pipelines to create", (int)toCompile.size()); + if (!toCompile.empty()) { + INFO_LOG(G3D, "Compilation thread has %d pipelines to create", (int)toCompile.size()); + } // TODO: Here we can sort the pending pipelines by vertex and fragment shaders, // and split up further. @@ -561,45 +498,30 @@ void VulkanRenderManager::DrainCompileQueue() { void VulkanRenderManager::ThreadFunc() { SetCurrentThreadName("RenderMan"); - int threadFrame = threadInitFrame_; - bool nextFrame = false; - bool firstFrame = true; while (true) { + // Pop a task of the queue and execute it. + VKRRenderThreadTask task; { - if (nextFrame) { - threadFrame++; - if (threadFrame >= vulkan_->GetInflightFrames()) - threadFrame = 0; + std::unique_lock lock(pushMutex_); + while (renderThreadQueue_.empty()) { + pushCondVar_.wait(lock); } - FrameData &frameData = frameData_[threadFrame]; - std::unique_lock lock(frameData.pull_mutex); - while (!frameData.readyForRun && run_) { - VLOG("PULL: Waiting for frame[%d].readyForRun", threadFrame); - frameData.pull_condVar.wait(lock); - } - if (!frameData.readyForRun && !run_) { - // This means we're out of frames to render and run_ is false, so bail. - break; - } - VLOG("PULL: frame[%d].readyForRun = false", threadFrame); - frameData.readyForRun = false; - // Previously we had a quick exit here that avoided calling Run() if run_ was suddenly false, - // but that created a race condition where frames could end up not finished properly on resize etc. + task = renderThreadQueue_.front(); + renderThreadQueue_.pop(); + } - // Only increment next time if we're done. - nextFrame = frameData.type == VKRRunType::END; - _dbg_assert_(frameData.type == VKRRunType::END || frameData.type == VKRRunType::SYNC); + // Oh, we got a task! We can now have pushMutex_ unlocked, allowing the host to + // push more work when it feels like it, and just start working. + if (task.runType == VKRRunType::EXIT) { + // Oh, host wanted out. Let's leave. + break; } - VLOG("PULL: Running frame %d", threadFrame); - if (firstFrame) { - INFO_LOG(G3D, "Running first frame (%d)", threadFrame); - firstFrame = false; - } - Run(threadFrame); - VLOG("PULL: Finished frame %d", threadFrame); + + Run(task); } // Wait for the device to be done with everything, before tearing stuff down. + // TODO: Do we need this? vkDeviceWaitIdle(vulkan_->GetDevice()); VLOG("PULL: Quitting"); @@ -612,18 +534,20 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling, bool enableLogProfile int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; - // Make sure the very last command buffer from the frame before the previous has been fully executed. - if (useThread_) { - std::unique_lock lock(frameData.push_mutex); + VLOG("PUSH: Fencing %d", curFrame); + + // Makes sure the submission from the previous time around has happened. Otherwise + // we are not allowed to wait from another thread here.. + { + std::unique_lock lock(frameData.fenceMutex); while (!frameData.readyForFence) { - VLOG("PUSH: Waiting for frame[%d].readyForFence = 1", curFrame); - frameData.push_condVar.wait(lock); + frameData.fenceCondVar.wait(lock); } frameData.readyForFence = false; } - VLOG("PUSH: Fencing %d", curFrame); - + // This must be the very first Vulkan call we do in a new frame. + // Makes sure the very last command buffer from the frame before the previous has been fully executed. if (vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX) == VK_ERROR_DEVICE_LOST) { _assert_msg_(false, "Device lost in vkWaitForFences"); } @@ -631,7 +555,6 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling, bool enableLogProfile // Can't set this until after the fence. frameData.profilingEnabled_ = enableProfiling; - frameData.readbackFenceUsed = false; uint64_t queryResults[MAX_TIMESTAMP_QUERIES]; @@ -671,9 +594,6 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling, bool enableLogProfile // Must be after the fence - this performs deletes. VLOG("PUSH: BeginFrame %d", curFrame); - if (!run_) { - WARN_LOG(G3D, "BeginFrame while !run_!"); - } vulkan_->BeginFrame(enableLogProfiler ? GetInitCmd() : VK_NULL_HANDLE); @@ -689,31 +609,15 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling, bool enableLogProfile frameData.profile.timestampDescriptions.push_back("initCmd Begin"); frameData.profile.timestampDescriptions.push_back("initCmd"); VkCommandBuffer initCmd = GetInitCmd(); - vkCmdResetQueryPool(initCmd, frameData.profile.queryPool, 0, MAX_TIMESTAMP_QUERIES); - vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, frameData.profile.queryPool, 0); } } VkCommandBuffer VulkanRenderManager::GetInitCmd() { int curFrame = vulkan_->GetCurFrame(); - FrameData &frameData = frameData_[curFrame]; - if (!frameData.hasInitCommands) { - VkCommandBufferBeginInfo begin = { - VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - nullptr, - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT - }; - vkResetCommandPool(vulkan_->GetDevice(), frameData.cmdPoolInit, 0); - VkResult res = vkBeginCommandBuffer(frameData.initCmd, &begin); - if (res != VK_SUCCESS) { - return VK_NULL_HANDLE; - } - frameData.hasInitCommands = true; - } - return frameData_[curFrame].initCmd; + return frameData_[curFrame].GetInitCmd(vulkan_); } -VKRGraphicsPipeline *VulkanRenderManager::CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, uint32_t variantBitmask, const char *tag) { +VKRGraphicsPipeline *VulkanRenderManager::CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, PipelineFlags pipelineFlags, uint32_t variantBitmask, const char *tag) { VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline(); _dbg_assert_(desc->vertexShader); _dbg_assert_(desc->fragmentShader); @@ -739,6 +643,21 @@ VKRGraphicsPipeline *VulkanRenderManager::CreateGraphicsPipeline(VKRGraphicsPipe if (!(variantBitmask & (1 << i))) continue; RenderPassType rpType = (RenderPassType)i; + + // Sanity check - don't compile incompatible types (could be caused by corrupt caches, changes in data structures, etc). + if (pipelineFlags & PipelineFlags::USES_DEPTH_STENCIL) { + if (!RenderPassTypeHasDepth(rpType)) { + WARN_LOG(G3D, "Not compiling pipeline that requires depth, for non depth renderpass type"); + continue; + } + } + if (pipelineFlags & PipelineFlags::USES_INPUT_ATTACHMENT) { + if (!RenderPassTypeHasInput(rpType)) { + WARN_LOG(G3D, "Not compiling pipeline that requires input attachment, for non input renderpass type"); + continue; + } + } + pipeline->pipeline[rpType] = Promise::CreateEmpty(); compileQueue_.push_back(CompileQueueEntry(pipeline, compatibleRenderPass->Get(vulkan_, rpType), rpType)); needsCompile = true; @@ -768,17 +687,22 @@ void VulkanRenderManager::EndCurRenderStep() { curRenderStep_->render.colorLoad, curRenderStep_->render.depthLoad, curRenderStep_->render.stencilLoad, curRenderStep_->render.colorStore, curRenderStep_->render.depthStore, curRenderStep_->render.stencilStore, }; - RenderPassType rpType = RP_TYPE_COLOR_DEPTH; + // Save the accumulated pipeline flags so we can use that to configure the render pass. + // We'll often be able to avoid loading/saving the depth/stencil buffer. curRenderStep_->render.pipelineFlags = curPipelineFlags_; + bool depthStencil = (curPipelineFlags_ & PipelineFlags::USES_DEPTH_STENCIL) != 0; + RenderPassType rpType = depthStencil ? RP_TYPE_COLOR_DEPTH : RP_TYPE_COLOR; if (!curRenderStep_->render.framebuffer) { rpType = RP_TYPE_BACKBUFFER; + } else if (curPipelineFlags_ & PipelineFlags::USES_INPUT_ATTACHMENT) { + // Not allowed on backbuffers. + rpType = depthStencil ? RP_TYPE_COLOR_DEPTH_INPUT : RP_TYPE_COLOR_INPUT; } + // TODO: Also add render pass types for depth/stencil-less. VKRRenderPass *renderPass = queueRunner_.GetRenderPass(key); curRenderStep_->render.renderPassType = rpType; - // Save the accumulated pipeline flags so we can use that to configure the render pass. - // We'll often be able to avoid loading/saving the depth/stencil buffer. compileMutex_.lock(); bool needsCompile = false; for (VKRGraphicsPipeline *pipeline : pipelinesToCheck_) { @@ -804,7 +728,12 @@ void VulkanRenderManager::EndCurRenderStep() { // We no longer have a current render step. curRenderStep_ = nullptr; - curPipelineFlags_ = 0; + curPipelineFlags_ = (PipelineFlags)0; +} + +void VulkanRenderManager::BindCurrentFramebufferAsInputAttachment0(VkImageAspectFlags aspectBits) { + _dbg_assert_(curRenderStep_); + curRenderStep_->commands.push_back(VkRenderData{ VKRRenderCommand::SELF_DEPENDENCY_BARRIER }); } void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassLoadAction color, VKRRenderPassLoadAction depth, VKRRenderPassLoadAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag) { @@ -817,9 +746,11 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR } if (depth == VKRRenderPassLoadAction::CLEAR) { clearMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + curPipelineFlags_ |= PipelineFlags::USES_DEPTH_STENCIL; } if (stencil == VKRRenderPassLoadAction::CLEAR) { clearMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + curPipelineFlags_ |= PipelineFlags::USES_DEPTH_STENCIL; } // If we need a clear and the previous step has commands already, it's best to just add a clear and keep going. @@ -1026,98 +957,6 @@ void VulkanRenderManager::CopyImageToMemorySync(VkImage image, int mipLevel, int queueRunner_.CopyReadbackBuffer(w, h, destFormat, destFormat, pixelStride, pixels); } -bool VulkanRenderManager::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 = queueRunner_.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 VulkanRenderManager::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; -} - static void RemoveDrawCommands(std::vector *cmds) { // Here we remove any DRAW type commands when we hit a CLEAR. for (auto &c : *cmds) { @@ -1192,6 +1031,10 @@ void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearSten curRenderStep_->render.depthLoad = (clearMask & VK_IMAGE_ASPECT_DEPTH_BIT) ? VKRRenderPassLoadAction::CLEAR : VKRRenderPassLoadAction::KEEP; curRenderStep_->render.stencilLoad = (clearMask & VK_IMAGE_ASPECT_STENCIL_BIT) ? VKRRenderPassLoadAction::CLEAR : VKRRenderPassLoadAction::KEEP; + if (clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + curPipelineFlags_ |= PipelineFlags::USES_DEPTH_STENCIL; + } + // In case there were commands already. curRenderStep_->render.numDraws = 0; RemoveDrawCommands(&curRenderStep_->commands); @@ -1268,7 +1111,6 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, if (dstPos.x != 0 || dstPos.y != 0 || !fillsDst) step->dependencies.insert(dst); - std::unique_lock lock(mutex_); steps_.push_back(step); } @@ -1314,12 +1156,15 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, if (!fillsDst) step->dependencies.insert(dst); - std::unique_lock lock(mutex_); steps_.push_back(step); } VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBit, int attachment) { _dbg_assert_(curRenderStep_ != nullptr); + + // We don't support texturing from stencil, neither do we support texturing from depth|stencil together (nonsensical). + _dbg_assert_(aspectBit == VK_IMAGE_ASPECT_COLOR_BIT || aspectBit == VK_IMAGE_ASPECT_DEPTH_BIT); + // Mark the dependency, check for required transitions, and return the image. // Optimization: If possible, use final*Layout to put the texture into the correct layout "early". @@ -1346,17 +1191,15 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in // Track dependencies fully. curRenderStep_->dependencies.insert(fb); - if (!curRenderStep_->preTransitions.empty() && - curRenderStep_->preTransitions.back().fb == fb && - curRenderStep_->preTransitions.back().targetLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - // We're done. - return aspectBit == VK_IMAGE_ASPECT_COLOR_BIT ? fb->color.imageView : fb->depth.depthSampleView; - } else { - curRenderStep_->preTransitions.push_back({ fb, aspectBit, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }); - return aspectBit == VK_IMAGE_ASPECT_COLOR_BIT ? fb->color.imageView : fb->depth.depthSampleView; - } + // Add this pretransition unless we already have it. + TransitionRequest rq{ fb, aspectBit, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }; + curRenderStep_->preTransitions.insert(rq); // Note that insert avoids inserting duplicates. + return aspectBit == VK_IMAGE_ASPECT_COLOR_BIT ? fb->color.imageView : fb->depth.depthSampleView; } +// Called on main thread. +// Sends the collected commands to the render thread. Submit-latency should be +// measured from here, probably. void VulkanRenderManager::Finish() { EndCurRenderStep(); @@ -1369,22 +1212,20 @@ void VulkanRenderManager::Finish() { int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; - if (!useThread_) { - frameData.steps = std::move(steps_); - steps_.clear(); - frameData.type = VKRRunType::END; - Run(curFrame); - } else { - std::unique_lock lock(frameData.pull_mutex); - VLOG("PUSH: Frame[%d].readyForRun = true", curFrame); - frameData.steps = std::move(steps_); - steps_.clear(); - frameData.readyForRun = true; - frameData.type = VKRRunType::END; - frameData.pull_condVar.notify_all(); - } - vulkan_->EndFrame(); + { + VLOG("PUSH: Frame[%d]", curFrame); + VKRRenderThreadTask task; + task.frame = curFrame; + task.runType = VKRRunType::PRESENT; + std::unique_lock lock(pushMutex_); + renderThreadQueue_.push(task); + renderThreadQueue_.back().steps = std::move(steps_); + pushCondVar_.notify_one(); + } + + steps_.clear(); + vulkan_->EndFrame(); insideFrame_ = false; } @@ -1395,223 +1236,105 @@ void VulkanRenderManager::Wipe() { steps_.clear(); } -// Can be called multiple times with no bad side effects. This is so that we can either begin a frame the normal way, -// or stop it in the middle for a synchronous readback, then start over again mostly normally but without repeating -// the backbuffer image acquisition. -void VulkanRenderManager::BeginSubmitFrame(int frame) { - FrameData &frameData = frameData_[frame]; - if (!frameData.hasBegun) { - // 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, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, &frameData.curSwapchainImage); - if (res == VK_SUBOPTIMAL_KHR) { - // Hopefully the resize will happen shortly. Ignore - one frame might look bad or something. - WARN_LOG(G3D, "VK_SUBOPTIMAL_KHR returned - ignoring"); - } else if (res == VK_ERROR_OUT_OF_DATE_KHR) { - WARN_LOG(G3D, "VK_ERROR_OUT_OF_DATE_KHR returned - processing the frame, but not presenting"); - frameData.skipSwap = true; - } else { - _assert_msg_(res == VK_SUCCESS, "vkAcquireNextImageKHR failed! result=%s", VulkanResultToString(res)); - } +// Called on the render thread. +// +// Can be called again after a VKRRunType::SYNC on the same frame. +void VulkanRenderManager::Run(VKRRenderThreadTask &task) { + FrameData &frameData = frameData_[task.frame]; + _dbg_assert_(!frameData.hasPresentCommands); + frameData.SubmitPending(vulkan_, FrameSubmitType::Pending, frameDataShared_); + + if (!frameData.hasMainCommands) { + // Effectively resets both main and present command buffers, since they both live in this pool. + // We always record main commands first, so we don't need to reset the present command buffer separately. vkResetCommandPool(vulkan_->GetDevice(), frameData.cmdPoolMain, 0); + VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - res = vkBeginCommandBuffer(frameData.mainCmd, &begin); - + VkResult res = vkBeginCommandBuffer(frameData.mainCmd, &begin); + frameData.hasMainCommands = true; _assert_msg_(res == VK_SUCCESS, "vkBeginCommandBuffer failed! result=%s", VulkanResultToString(res)); - - queueRunner_.SetBackbuffer(framebuffers_[frameData.curSwapchainImage], swapchainImages_[frameData.curSwapchainImage].image); - - frameData.hasBegun = true; - } -} - -void VulkanRenderManager::Submit(int frame, bool triggerFrameFence) { - FrameData &frameData = frameData_[frame]; - if (frameData.hasInitCommands) { - if (frameData.profilingEnabled_ && triggerFrameFence) { - // Pre-allocated query ID 1. - vkCmdWriteTimestamp(frameData.initCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, frameData.profile.queryPool, 1); - } - VkResult res = vkEndCommandBuffer(frameData.initCmd); - _assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (init)! result=%s", VulkanResultToString(res)); } - VkResult res = vkEndCommandBuffer(frameData.mainCmd); - _assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (main)! result=%s", VulkanResultToString(res)); - - VkCommandBuffer cmdBufs[2]; - int numCmdBufs = 0; - if (frameData.hasInitCommands) { - cmdBufs[numCmdBufs++] = frameData.initCmd; - if (splitSubmit_) { - // Send the init commands off separately. Used this once to confirm that the cause of a device loss was in the init cmdbuf. - VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; - submit_info.commandBufferCount = (uint32_t)numCmdBufs; - submit_info.pCommandBuffers = cmdBufs; - res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, VK_NULL_HANDLE); - if (res == VK_ERROR_DEVICE_LOST) { - _assert_msg_(false, "Lost the Vulkan device in split submit! If this happens again, switch Graphics Backend away from Vulkan"); - } else { - _assert_msg_(res == VK_SUCCESS, "vkQueueSubmit failed (init)! result=%s", VulkanResultToString(res)); - } - numCmdBufs = 0; - } - } - cmdBufs[numCmdBufs++] = frameData.mainCmd; - - VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; - VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; - if (triggerFrameFence && !frameData.skipSwap) { - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &acquireSemaphore_; - submit_info.pWaitDstStageMask = waitStage; - } - submit_info.commandBufferCount = (uint32_t)numCmdBufs; - submit_info.pCommandBuffers = cmdBufs; - if (triggerFrameFence && !frameData.skipSwap) { - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &renderingCompleteSemaphore_; - } - res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, triggerFrameFence ? frameData.fence : frameData.readbackFence); - 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, split=%d)! result=%s", (int)splitSubmit_, VulkanResultToString(res)); - } - - // When !triggerFence, we notify after syncing with Vulkan. - if (useThread_ && triggerFrameFence) { - VLOG("PULL: Frame %d.readyForFence = true", frame); - std::unique_lock lock(frameData.push_mutex); - frameData.readyForFence = true; - frameData.push_condVar.notify_all(); - } - - frameData.hasInitCommands = false; -} - -void VulkanRenderManager::EndSubmitFrame(int frame) { - FrameData &frameData = frameData_[frame]; - frameData.hasBegun = false; - - Submit(frame, true); - - if (!frameData.skipSwap) { - VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); - VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; - present.swapchainCount = 1; - present.pSwapchains = &swapchain; - present.pImageIndices = &frameData.curSwapchainImage; - present.pWaitSemaphores = &renderingCompleteSemaphore_; - present.waitSemaphoreCount = 1; - - VkResult res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present); - if (res == VK_ERROR_OUT_OF_DATE_KHR) { - // We clearly didn't get this in vkAcquireNextImageKHR because of the skipSwap check above. - // Do the increment. - outOfDateFrames_++; - } else if (res == VK_SUBOPTIMAL_KHR) { - outOfDateFrames_++; - } else if (res != VK_SUCCESS) { - _assert_msg_(false, "vkQueuePresentKHR failed! result=%s", VulkanResultToString(res)); - } else { - // Success - outOfDateFrames_ = 0; - } - } else { - // We only get here if vkAcquireNextImage returned VK_ERROR_OUT_OF_DATE. - outOfDateFrames_++; - frameData.skipSwap = false; - } -} - -void VulkanRenderManager::Run(int frame) { - BeginSubmitFrame(frame); - - FrameData &frameData = frameData_[frame]; - auto &stepsOnThread = frameData_[frame].steps; - VkCommandBuffer cmd = frameData.mainCmd; - queueRunner_.PreprocessSteps(stepsOnThread); + queueRunner_.PreprocessSteps(task.steps); + // Likely during shutdown, happens in headless. + if (task.steps.empty() && !frameData.hasAcquired) + frameData.skipSwap = true; //queueRunner_.LogSteps(stepsOnThread, false); - queueRunner_.RunSteps(cmd, stepsOnThread, frameData.profilingEnabled_ ? &frameData.profile : nullptr); - stepsOnThread.clear(); + queueRunner_.RunSteps(task.steps, frameData, frameDataShared_); - switch (frameData.type) { - case VKRRunType::END: - EndSubmitFrame(frame); + switch (task.runType) { + case VKRRunType::PRESENT: + frameData.SubmitPending(vulkan_, FrameSubmitType::Present, frameDataShared_); + + if (!frameData.skipSwap) { + VkResult res = frameData.QueuePresent(vulkan_, frameDataShared_); + if (res == VK_ERROR_OUT_OF_DATE_KHR) { + // We clearly didn't get this in vkAcquireNextImageKHR because of the skipSwap check above. + // Do the increment. + outOfDateFrames_++; + } else if (res == VK_SUBOPTIMAL_KHR) { + outOfDateFrames_++; + } else if (res != VK_SUCCESS) { + _assert_msg_(false, "vkQueuePresentKHR failed! result=%s", VulkanResultToString(res)); + } else { + // Success + outOfDateFrames_ = 0; + } + } else { + // We only get here if vkAcquireNextImage returned VK_ERROR_OUT_OF_DATE. + outOfDateFrames_++; + frameData.skipSwap = false; + } break; case VKRRunType::SYNC: - EndSyncFrame(frame); + // The submit will trigger the readbackFence, and also do the wait for it. + frameData.SubmitPending(vulkan_, FrameSubmitType::Sync, frameDataShared_); + + { + std::unique_lock lock(syncMutex_); + syncCondVar_.notify_one(); + } + + // At this point the GPU is idle, and we can resume filling the command buffers for the + // current frame since and thus all previously enqueued command buffers have been + // processed. No need to switch to the next frame number, would just be confusing. break; default: _dbg_assert_(false); } - VLOG("PULL: Finished running frame %d", frame); -} - -void VulkanRenderManager::EndSyncFrame(int frame) { - FrameData &frameData = frameData_[frame]; - - frameData.readbackFenceUsed = true; - - // The submit will trigger the readbackFence. - Submit(frame, false); - - // Hard stall of the GPU, not ideal, but necessary so the CPU has the contents of the readback. - vkWaitForFences(vulkan_->GetDevice(), 1, &frameData.readbackFence, true, UINT64_MAX); - vkResetFences(vulkan_->GetDevice(), 1, &frameData.readbackFence); - - // At this point we can resume filling the command buffers for the current frame since - // we know the device is idle - and thus all previously enqueued command buffers have been processed. - // No need to switch to the next frame number. - VkCommandBufferBeginInfo begin{ - VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - nullptr, - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT - }; - vkResetCommandPool(vulkan_->GetDevice(), frameData.cmdPoolMain, 0); - VkResult res = vkBeginCommandBuffer(frameData.mainCmd, &begin); - _assert_(res == VK_SUCCESS); - - if (useThread_) { - std::unique_lock lock(frameData.push_mutex); - frameData.readyForFence = true; - frameData.push_condVar.notify_all(); - } + VLOG("PULL: Finished running frame %d", task.frame); } +// Called from main thread. void VulkanRenderManager::FlushSync() { renderStepOffset_ += (int)steps_.size(); int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; - if (!useThread_) { - frameData.steps = std::move(steps_); - steps_.clear(); - frameData.type = VKRRunType::SYNC; - Run(curFrame); - } else { - std::unique_lock lock(frameData.pull_mutex); - VLOG("PUSH: Frame[%d].readyForRun = true (sync)", curFrame); - frameData.steps = std::move(steps_); - steps_.clear(); - frameData.readyForRun = true; - _dbg_assert_(!frameData.readyForFence); - frameData.type = VKRRunType::SYNC; - frameData.pull_condVar.notify_all(); + + { + VLOG("PUSH: Frame[%d]", curFrame); + VKRRenderThreadTask task; + task.frame = curFrame; + task.runType = VKRRunType::SYNC; + std::unique_lock lock(pushMutex_); + renderThreadQueue_.push(task); + renderThreadQueue_.back().steps = std::move(steps_); + pushCondVar_.notify_one(); } - if (useThread_) { - std::unique_lock lock(frameData.push_mutex); + { + std::unique_lock lock(syncMutex_); // Wait for the flush to be hit, since we're syncing. - while (!frameData.readyForFence) { + while (!frameData.syncDone) { VLOG("PUSH: Waiting for frame[%d].readyForFence = 1 (sync)", curFrame); - frameData.push_condVar.wait(lock); + syncCondVar_.wait(lock); } - frameData.readyForFence = false; + frameData.syncDone = false; } } diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index 4cc7aafdfc..7fd45e2246 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -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 *vertexShader = nullptr; Promise *fragmentShader = nullptr; + Promise *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 *pipeline; + Promise *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 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 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 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 pipelinesToCheck_; - - // Swap chain management - struct SwapchainImageData { - VkImage image; - VkImageView view; - }; - std::vector framebuffers_; - std::vector 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; }; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index f673691224..cf2a28ea53 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -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 *Get() const { return module_; } ShaderStage GetStage() const override { return stage_; } private: VulkanContext *vulkan_; - VkShaderModule module_ = VK_NULL_HANDLE; + Promise *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 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::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 GetFeatureList() const override; std::vector 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 curFramebuffer_; + AutoRef 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::AlreadyDone(vkshader->Get()); + gDesc.vertexShader = vkshader->Get(); } else if (vkshader->GetStage() == ShaderStage::Fragment) { - gDesc.fragmentShader = Promise::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 &features, const char *name, VkBool32 available, VkBool32 enabled) { @@ -1418,7 +1425,7 @@ std::vector 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 diff --git a/Common/GPU/thin3d.cpp b/Common/GPU/thin3d.cpp index d712195c7b..a81c38ba10 100644 --- a/Common/GPU/thin3d.cpp +++ b/Common/GPU/thin3d.cpp @@ -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)"; } } diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index 6196f494f9..c04b7119cd 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -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 adapterNames); #endif -DrawContext *T3DCreateVulkanContext(VulkanContext *context, bool splitSubmit); +DrawContext *T3DCreateVulkanContext(VulkanContext *context); } // namespace Draw diff --git a/Common/LogReporting.cpp b/Common/LogReporting.cpp index 2189b1c7e2..0fcc78d0ad 100644 --- a/Common/LogReporting.cpp +++ b/Common/LogReporting.cpp @@ -35,7 +35,7 @@ bool ShouldLogNTimes(const char *identifier, int count) { std::lock_guard lock(logNTimesLock); auto iter = logNTimes.find(identifier); if (iter == logNTimes.end()) { - logNTimes.insert(std::pair(identifier, 1)); + logNTimes.emplace(identifier, 1); return true; } else { if (iter->second >= count) { diff --git a/Common/RiscVEmitter.cpp b/Common/RiscVEmitter.cpp index 2fc0966c1f..3f0d47499b 100644 --- a/Common/RiscVEmitter.cpp +++ b/Common/RiscVEmitter.cpp @@ -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 } diff --git a/Common/StringUtils.cpp b/Common/StringUtils.cpp index f88f6194ab..3a9e29c61a 100644 --- a/Common/StringUtils.cpp +++ b/Common/StringUtils.cpp @@ -273,7 +273,7 @@ void SplitString(const std::string& str, const char delim, std::vector& 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 diff --git a/Common/Thread/Promise.h b/Common/Thread/Promise.h index 6fae6d7a2b..4145b4b6c1 100644 --- a/Common/Thread/Promise.h +++ b/Common/Thread/Promise.h @@ -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 guard(readyMutex_); if (ready_) { return data_; @@ -96,7 +95,6 @@ public: } T BlockUntilReady() { - _dbg_assert_(this != nullptr); std::lock_guard guard(readyMutex_); if (ready_) { return data_; diff --git a/Common/UI/Context.cpp b/Common/UI/Context.cpp index e9fe8ed0c5..21c687bd2b 100644 --- a/Common/UI/Context.cpp +++ b/Common/UI/Context.cpp @@ -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); diff --git a/Common/UI/Context.h b/Common/UI/Context.h index ad02b4027c..ac26ab0202 100644 --- a/Common/UI/Context.h +++ b/Common/UI/Context.h @@ -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; }; diff --git a/Common/UI/Screen.h b/Common/UI/Screen.h index de24c74985..42f0830627 100644 --- a/Common/UI/Screen.h +++ b/Common/UI/Screen.h @@ -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; } diff --git a/Common/UI/UIScreen.cpp b/Common/UI/UIScreen.cpp index 8026de37bc..ecebae3a81 100644 --- a/Common/UI/UIScreen.cpp +++ b/Common/UI/UIScreen.cpp @@ -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_}); diff --git a/Common/UI/UIScreen.h b/Common/UI/UIScreen.h index 43ec130d9d..4b113d3001 100644 --- a/Common/UI/UIScreen.h +++ b/Common/UI/UIScreen.h @@ -136,7 +136,7 @@ public: void SetHiddenChoices(std::set 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: diff --git a/Common/VR/PPSSPPVR.cpp b/Common/VR/PPSSPPVR.cpp index a18bfa11c3..18b9f00e3c 100644 --- a/Common/VR/PPSSPPVR.cpp +++ b/Common/VR/PPSSPPVR.cpp @@ -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() { diff --git a/Common/VR/PPSSPPVR.h b/Common/VR/PPSSPPVR.h index e5191c1e46..6fbe2f00fb 100644 --- a/Common/VR/PPSSPPVR.h +++ b/Common/VR/PPSSPPVR.h @@ -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; } diff --git a/Common/VR/VRBase.cpp b/Common/VR/VRBase.cpp index e146967b42..abbad01d78 100644 --- a/Common/VR/VRBase.cpp +++ b/Common/VR/VRBase.cpp @@ -6,6 +6,36 @@ #include #include +#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 = {}; diff --git a/Common/VR/VRBase.h b/Common/VR/VRBase.h index 89bec2b3dd..c05e4739a9 100644 --- a/Common/VR/VRBase.h +++ b/Common/VR/VRBase.h @@ -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 }; diff --git a/Common/VR/VRFramebuffer.cpp b/Common/VR/VRFramebuffer.cpp index 4a71cb5cd8..1a62417c3e 100644 --- a/Common/VR/VRFramebuffer.cpp +++ b/Common/VR/VRFramebuffer.cpp @@ -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)); diff --git a/Common/VR/VRInput.cpp b/Common/VR/VRInput.cpp index 63fc97a030..2fc8816f61 100644 --- a/Common/VR/VRInput.cpp +++ b/Common/VR/VRInput.cpp @@ -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); } } } diff --git a/Common/VR/VRRenderer.cpp b/Common/VR/VRRenderer.cpp index f9a8e5ce53..4d41d09bcd 100644 --- a/Common/VR/VRRenderer.cpp +++ b/Common/VR/VRRenderer.cpp @@ -6,12 +6,14 @@ #include #include +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]; } diff --git a/Common/VR/VRRenderer.h b/Common/VR/VRRenderer.h index 9375370cad..e19a0e82a5 100644 --- a/Common/VR/VRRenderer.h +++ b/Common/VR/VRRenderer.h @@ -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 { diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index 75c0623ead..861e926429 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -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) { diff --git a/Core/Compatibility.h b/Core/Compatibility.h index 04f3ab22b7..8bbdc52ebd 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -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; }; diff --git a/Core/Config.cpp b/Core/Config.cpp index 27f97e89c5..4eeca428ab 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -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(); } } diff --git a/Core/Config.h b/Core/Config.h index 6ae172bfba..65eaf74099 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -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; diff --git a/Core/Core.cpp b/Core/Core.cpp index 52a1df8f4c..c1bb28cefd 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -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) { diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index bdfaea4361..1978f3bcac 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -139,7 +139,7 @@ Level3 ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib - _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) + _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) StreamingSIMDExtensions2 Precise true @@ -148,6 +148,7 @@ MultiThreadedDebug Common/DbgNew.h ProgramDatabase + stdcpp17 true @@ -157,12 +158,15 @@ ../git-version.cpp;%(Outputs) Updating git-version.cpp + + /ignore:4221 %(AdditionalOptions) + Level3 ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib;../ext/zstd/lib - _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) + _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) NotSet Precise false @@ -172,6 +176,7 @@ MultiThreadedDebug Common/DbgNew.h ProgramDatabase + stdcpp17 true @@ -181,12 +186,15 @@ ../git-version.cpp;%(Outputs) Updating git-version.cpp + + /ignore:4221 %(AdditionalOptions) + Level3 ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib - _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) + _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) NotSet Precise false @@ -196,6 +204,7 @@ MultiThreadedDebug Common/DbgNew.h ProgramDatabase + stdcpp17 true @@ -205,12 +214,15 @@ ../git-version.cpp;%(Outputs) Updating git-version.cpp + + /ignore:4221 %(AdditionalOptions) + Level3 ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib - _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) + _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) NotSet Precise false @@ -221,6 +233,7 @@ ProgramDatabase + stdcpp17 true @@ -230,6 +243,9 @@ ../git-version.cpp;%(Outputs) Updating git-version.cpp + + /ignore:4221 %(AdditionalOptions) + @@ -241,12 +257,13 @@ false StreamingSIMDExtensions2 Precise - 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) + 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) Speed MultiThreaded true false ProgramDatabase + stdcpp17 true @@ -255,6 +272,7 @@ %(AdditionalDependencies) + /ignore:4221 %(AdditionalOptions) ../Windows/git-version-gen.cmd @@ -276,11 +294,12 @@ Speed false true - 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) + 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) false MultiThreaded true ProgramDatabase + stdcpp17 true @@ -292,6 +311,9 @@ ../git-version.cpp;%(Outputs) Updating git-version.cpp + + /ignore:4221 %(AdditionalOptions) + @@ -307,11 +329,12 @@ Speed false true - USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_LIB;NDEBUG;_UNICODE;UNICODE;%(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) false MultiThreaded true ProgramDatabase + stdcpp17 true @@ -323,6 +346,9 @@ ../git-version.cpp;%(Outputs) Updating git-version.cpp + + /ignore:4221 %(AdditionalOptions) + @@ -338,11 +364,12 @@ Speed false true - USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_LIB;NDEBUG;_UNICODE;UNICODE;%(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) false MultiThreaded true ProgramDatabase + stdcpp17 true @@ -354,6 +381,9 @@ ../git-version.cpp;%(Outputs) Updating git-version.cpp + + /ignore:4221 %(AdditionalOptions) + @@ -568,7 +598,16 @@ - + + ..\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/ + ..\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/ + ..\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/ + ..\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/ + ..\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/ + ..\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/ + ..\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/ + ..\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/ + @@ -900,7 +939,16 @@ - + + ..\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/ + ..\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/ + ..\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/ + ..\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/ + ..\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/ + ..\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/ + ..\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/ + ..\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/ + diff --git a/Core/Debugger/DisassemblyManager.cpp b/Core/Debugger/DisassemblyManager.cpp index 511aeee82a..ab0e10aa9b 100644 --- a/Core/Debugger/DisassemblyManager.cpp +++ b/Core/Debugger/DisassemblyManager.cpp @@ -316,7 +316,7 @@ void DisassemblyManager::getLine(u32 address, bool insertSymbols, DisassemblyLin dest.params = "Disassembly failure"; } else { dest.name = "-"; - dest.params = ""; + dest.params.clear(); } } @@ -1003,7 +1003,7 @@ void DisassemblyData::createLines() lines[currentLineStart] = entry; lineAddresses.push_back(currentLineStart); - currentLine = ""; + currentLine.clear(); currentLineStart = pos-1; inString = false; } @@ -1028,7 +1028,7 @@ void DisassemblyData::createLines() lines[currentLineStart] = entry; lineAddresses.push_back(currentLineStart); - currentLine = ""; + currentLine.clear(); currentLineStart = pos-1; inString = false; } @@ -1099,7 +1099,7 @@ void DisassemblyData::createLines() lines[currentLineStart] = entry; lineAddresses.push_back(currentLineStart); - currentLine = ""; + currentLine.clear(); currentLineStart = currentPos; } diff --git a/Core/Debugger/MemBlockInfo.cpp b/Core/Debugger/MemBlockInfo.cpp index e38b6861b7..e361b63014 100644 --- a/Core/Debugger/MemBlockInfo.cpp +++ b/Core/Debugger/MemBlockInfo.cpp @@ -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 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 FindMemInfo(uint32_t start, uint32_t size) { } std::vector 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 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(); diff --git a/Core/Debugger/SymbolMap.cpp b/Core/Debugger/SymbolMap.cpp index e276e9be27..63e087d7a8 100644 --- a/Core/Debugger/SymbolMap.cpp +++ b/Core/Debugger/SymbolMap.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #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 &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); } } diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index b71db06877..23333d5a19 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -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: diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index 79a0fe70a8..5d766785a2 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -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(); diff --git a/Core/Debugger/WebSocket/GameBroadcaster.cpp b/Core/Debugger/WebSocket/GameBroadcaster.cpp index 3045e70fc4..c8b4039769 100644 --- a/Core/Debugger/WebSocket/GameBroadcaster.cpp +++ b/Core/Debugger/WebSocket/GameBroadcaster.cpp @@ -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; } diff --git a/Core/Debugger/WebSocket/GameSubscriber.cpp b/Core/Debugger/WebSocket/GameSubscriber.cpp index 8cb9fcfc69..c48f396bb7 100644 --- a/Core/Debugger/WebSocket/GameSubscriber.cpp +++ b/Core/Debugger/WebSocket/GameSubscriber.cpp @@ -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"); } diff --git a/Core/Debugger/WebSocket/HLESubscriber.cpp b/Core/Debugger/WebSocket/HLESubscriber.cpp index fe3cde43ef..2e4a7be55e 100644 --- a/Core/Debugger/WebSocket/HLESubscriber.cpp +++ b/Core/Debugger/WebSocket/HLESubscriber.cpp @@ -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. diff --git a/Core/Debugger/WebSocket/HLESubscriber.h b/Core/Debugger/WebSocket/HLESubscriber.h index 6d473c3563..e70b4c8ff0 100644 --- a/Core/Debugger/WebSocket/HLESubscriber.h +++ b/Core/Debugger/WebSocket/HLESubscriber.h @@ -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); diff --git a/Core/Debugger/WebSocket/WebSocketUtils.cpp b/Core/Debugger/WebSocket/WebSocketUtils.cpp index cc49e33336..3ba5984f4b 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.cpp +++ b/Core/Debugger/WebSocket/WebSocketUtils.cpp @@ -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) { diff --git a/Core/Dialog/PSPGamedataInstallDialog.cpp b/Core/Dialog/PSPGamedataInstallDialog.cpp index 9fe30db3c8..3bbe9442f1 100644 --- a/Core/Dialog/PSPGamedataInstallDialog.cpp +++ b/Core/Dialog/PSPGamedataInstallDialog.cpp @@ -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(); } diff --git a/Core/Dialog/PSPGamedataInstallDialog.h b/Core/Dialog/PSPGamedataInstallDialog.h index e669a31873..a74fdb5254 100644 --- a/Core/Dialog/PSPGamedataInstallDialog.h +++ b/Core/Dialog/PSPGamedataInstallDialog.h @@ -22,7 +22,7 @@ struct SceUtilityGamedataInstallParam { pspUtilityDialogCommon common; - u32_le unknown1; + s32_le mode; char gameName[13]; char ignore1[3]; char dataName[20]; diff --git a/Core/Dialog/PSPNetconfDialog.cpp b/Core/Dialog/PSPNetconfDialog.cpp index 1e0535f301..079ff28358 100644 --- a/Core/Dialog/PSPNetconfDialog.cpp +++ b/Core/Dialog/PSPNetconfDialog.cpp @@ -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(); diff --git a/Core/Dialog/PSPNpSigninDialog.cpp b/Core/Dialog/PSPNpSigninDialog.cpp index 3c015046db..48245c2008 100644 --- a/Core/Dialog/PSPNpSigninDialog.cpp +++ b/Core/Dialog/PSPNpSigninDialog.cpp @@ -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(); diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 5a04ebc882..67ef655ccf 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -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. diff --git a/Core/ELF/ElfReader.cpp b/Core/ELF/ElfReader.cpp index abfd881e87..980e427e83 100644 --- a/Core/ELF/ElfReader.cpp +++ b/Core/ELF/ElfReader.cpp @@ -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; } } diff --git a/Core/ELF/ParamSFO.cpp b/Core/ELF/ParamSFO.cpp index 71d07dcaca..a2c002f46e 100644 --- a/Core/ELF/ParamSFO.cpp +++ b/Core/ELF/ParamSFO.cpp @@ -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("/"); diff --git a/Core/FileSystems/ISOFileSystem.cpp b/Core/FileSystems/ISOFileSystem.cpp index a873f3f19b..0d57bf2848 100644 --- a/Core/FileSystems/ISOFileSystem.cpp +++ b/Core/FileSystems/ISOFileSystem.cpp @@ -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(); diff --git a/Core/FileSystems/VirtualDiscFileSystem.cpp b/Core/FileSystems/VirtualDiscFileSystem.cpp index dd41e95855..9a9e0f8c8b 100644 --- a/Core/FileSystems/VirtualDiscFileSystem.cpp +++ b/Core/FileSystems/VirtualDiscFileSystem.cpp @@ -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; diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index 896bc3cac8..bd0e9d1cac 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -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; diff --git a/Core/HLE/HLE.h b/Core/HLE/HLE.h index d2a74f9d43..0331fb7b9b 100644 --- a/Core/HLE/HLE.h +++ b/Core/HLE/HLE.h @@ -189,7 +189,7 @@ T hleDoLog(LogTypes::LOG_TYPE t, LogTypes::LOG_LEVELS level, T res, const char * template 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; } diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index dcacbcc5bd..dfcc535df0 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -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 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> InitPrivateIPRanges() { struct sockaddr_in saNet {}, saMask{}; std::vector> 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)); } diff --git a/Core/HLE/sceGe.cpp b/Core/HLE/sceGe.cpp index 81667a448e..c4feedd6c6 100644 --- a/Core/HLE/sceGe.cpp +++ b/Core/HLE/sceGe.cpp @@ -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", 'i', "p" }, {0X05DB22CE, &WrapI_U, "sceGeUnsetCallback", 'i', "x" }, {0X1F6752AD, &WrapU_V, "sceGeEdramGetSize", 'x', "" }, - {0XB77905EA, &WrapU_I, "sceGeEdramSetAddrTranslation", 'x', "i" }, + {0XB77905EA, &WrapU_U, "sceGeEdramSetAddrTranslation", 'x', "x" }, {0XDC93CFEF, &WrapU_I, "sceGeGetCmd", 'x', "i" }, - {0X57C8945B, &WrapI_IU, "sceGeGetMtx", 'i', "ix" }, + {0X57C8945B, &WrapI_IU, "sceGeGetMtx", 'i', "ip" }, {0X438A385A, &WrapU_U, "sceGeSaveContext", 'x', "x" }, {0X0BF608FB, &WrapU_U, "sceGeRestoreContext", 'x', "x" }, {0X5FB86AB0, &WrapI_U, "sceGeListDeQueue", 'i', "x" }, diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index cb858c0e90..b9d1028b48 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -820,7 +820,7 @@ const HLEFunction ThreadManForUser[] = {0X87D4DD36, &WrapI_IU, "sceKernelCancelReceiveMbx", 'i', "ix" }, {0XA8E8C846, &WrapI_IU, "sceKernelReferMbxStatus", 'i', "ip" }, - {0X7C0DC2A0, &WrapI_CIUUU, "sceKernelCreateMsgPipe", 'i', "sixxx" }, + {0X7C0DC2A0, &WrapI_CIUUU, "sceKernelCreateMsgPipe", 'i', "sixxp" }, {0XF0B7DA1C, &WrapI_I, "sceKernelDeleteMsgPipe", 'i', "i" }, {0X876DBFAD, &WrapI_IUUUUU, "sceKernelSendMsgPipe", 'i', "ixxxxx" }, {0X7C41F2C2, &WrapI_IUUUUU, "sceKernelSendMsgPipeCB", 'i', "ixxxxx" }, @@ -831,7 +831,7 @@ const HLEFunction ThreadManForUser[] = {0X349B864D, &WrapI_IUU, "sceKernelCancelMsgPipe", 'i', "ixx" }, {0X33BE4024, &WrapI_IU, "sceKernelReferMsgPipeStatus", 'i', "ip" }, - {0X56C039B5, &WrapI_CIUUU, "sceKernelCreateVpl", 'i', "sixxx" }, + {0X56C039B5, &WrapI_CIUUU, "sceKernelCreateVpl", 'i', "sixxp" }, {0X89B3D48C, &WrapI_I, "sceKernelDeleteVpl", 'i', "i" }, {0XBED27435, &WrapI_IUUU, "sceKernelAllocateVpl", 'i', "ixxx", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0XEC0A693F, &WrapI_IUUU, "sceKernelAllocateVplCB", 'i', "ixxx", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, @@ -840,7 +840,7 @@ const HLEFunction ThreadManForUser[] = {0X1D371B8A, &WrapI_IU, "sceKernelCancelVpl", 'i', "ix" }, {0X39810265, &WrapI_IU, "sceKernelReferVplStatus", 'i', "ip" }, - {0XC07BB470, &WrapI_CUUUUU, "sceKernelCreateFpl", 'i', "sxxxxx" }, + {0XC07BB470, &WrapI_CUUUUU, "sceKernelCreateFpl", 'i', "sixxxp" }, {0XED1410E0, &WrapI_I, "sceKernelDeleteFpl", 'i', "i" }, {0XD979E9BF, &WrapI_IUU, "sceKernelAllocateFpl", 'i', "ixx", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0XE7282CB6, &WrapI_IUU, "sceKernelAllocateFplCB", 'i', "ixx", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, @@ -864,7 +864,7 @@ const HLEFunction ThreadManForUser[] = {0XD8B299AE, &WrapU_IUUU, "sceKernelSetVTimerHandler", 'x', "ixxx" }, {0X53B00E9A, &WrapU_IU64UU, "sceKernelSetVTimerHandlerWide", 'x', "iXxx" }, - {0X8DAFF657, &WrapI_CUUUUU, "sceKernelCreateTlspl", 'i', "sxxxxx" }, + {0X8DAFF657, &WrapI_CUUUUU, "sceKernelCreateTlspl", 'i', "sixxxp" }, {0X32BF938E, &WrapI_I, "sceKernelDeleteTlspl", 'i', "i" }, {0X721067F3, &WrapI_IU, "sceKernelReferTlsplStatus", 'i', "xp" }, // Not completely certain about args. @@ -908,7 +908,7 @@ const HLEFunction ThreadManForKernel[] = {0x1fb15a32, &WrapU_IU, "sceKernelSetEventFlag", 'x', "ix", HLE_KERNEL_SYSCALL }, {0x812346e4, &WrapU_IU, "sceKernelClearEventFlag", 'x', "ix", HLE_KERNEL_SYSCALL }, {0x402fcf22, &WrapI_IUUUU, "sceKernelWaitEventFlag", 'i', "ixxpp", HLE_NOT_IN_INTERRUPT | HLE_KERNEL_SYSCALL}, - {0xc07bb470, &WrapI_CUUUUU, "sceKernelCreateFpl", 'i', "sxxxxx" ,HLE_KERNEL_SYSCALL }, + {0xc07bb470, &WrapI_CUUUUU, "sceKernelCreateFpl", 'i', "sixxxp" ,HLE_KERNEL_SYSCALL }, {0xed1410e0, &WrapI_I, "sceKernelDeleteFpl", 'i', "i" ,HLE_KERNEL_SYSCALL }, {0x623ae665, &WrapI_IU, "sceKernelTryAllocateFpl", 'i', "ix" ,HLE_KERNEL_SYSCALL }, {0x616403ba, &WrapI_I, "sceKernelTerminateThread", 'i', "i" ,HLE_KERNEL_SYSCALL }, @@ -932,7 +932,7 @@ const HLEFunction ThreadManForKernel[] = {0x0D81716A, &WrapI_IU, "sceKernelPollMbx", 'i', "ix", HLE_KERNEL_SYSCALL }, {0x87D4DD36, &WrapI_IU, "sceKernelCancelReceiveMbx", 'i', "ix", HLE_KERNEL_SYSCALL }, {0xA8E8C846, &WrapI_IU, "sceKernelReferMbxStatus", 'i', "ip", HLE_KERNEL_SYSCALL }, - {0x56C039B5, &WrapI_CIUUU, "sceKernelCreateVpl", 'i', "sixxx", HLE_KERNEL_SYSCALL }, + {0x56C039B5, &WrapI_CIUUU, "sceKernelCreateVpl", 'i', "sixxp", HLE_KERNEL_SYSCALL }, {0x89B3D48C, &WrapI_I, "sceKernelDeleteVpl", 'i', "i", HLE_KERNEL_SYSCALL }, {0xBED27435, &WrapI_IUUU, "sceKernelAllocateVpl", 'i', "ixxx", HLE_KERNEL_SYSCALL | HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0xEC0A693F, &WrapI_IUUU, "sceKernelAllocateVplCB", 'i', "ixxx", HLE_KERNEL_SYSCALL | HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index ac853d5bb3..55b7ff7044 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -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(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(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(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(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(&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. diff --git a/Core/HLE/sceKernelMemory.h b/Core/HLE/sceKernelMemory.h index 29d7356d00..b217974170 100644 --- a/Core/HLE/sceKernelMemory.h +++ b/Core/HLE/sceKernelMemory.h @@ -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); diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index a32604e928..4f3d586672 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -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); } } diff --git a/Core/HLE/sceKernelMsgPipe.cpp b/Core/HLE/sceKernelMsgPipe.cpp index b16e8060e2..0ff34c6290 100644 --- a/Core/HLE/sceKernelMsgPipe.cpp +++ b/Core/HLE/sceKernelMsgPipe.cpp @@ -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(); diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 2cba7b5ca5..6921ffdf26 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -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; diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index ea9caaba1e..f717cd5a3e 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -230,7 +230,7 @@ public: MipsCallManager() : idGen_(0) {} u32 add(MipsCall *call) { u32 id = genId(); - calls_.insert(std::pair(id, call)); + calls_.emplace(id, call); return id; } MipsCall *get(u32 id) { diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 8aa429ba1d..929e782d0c 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -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 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); diff --git a/Core/HLE/sceMpeg.h b/Core/HLE/sceMpeg.h index aded1238cc..e8bde2da34 100644 --- a/Core/HLE/sceMpeg.h +++ b/Core/HLE/sceMpeg.h @@ -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(); diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index c2a8987408..13b87f6b6b 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -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); } diff --git a/Core/HLE/scePsmf.cpp b/Core/HLE/scePsmf.cpp index b2aefa9a1a..06fc172c53 100644 --- a/Core/HLE/scePsmf.cpp +++ b/Core/HLE/scePsmf.cpp @@ -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 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. diff --git a/Core/HLE/scePsmf.h b/Core/HLE/scePsmf.h index 3248962470..bb0e53c411 100644 --- a/Core/HLE/scePsmf.h +++ b/Core/HLE/scePsmf.h @@ -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(); diff --git a/Core/HLE/sceUtility.cpp b/Core/HLE/sceUtility.cpp index e6a5124600..f1d7f73d0b 100644 --- a/Core/HLE/sceUtility.cpp +++ b/Core/HLE/sceUtility.cpp @@ -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"); } diff --git a/Core/HW/Display.cpp b/Core/HW/Display.cpp index ebabb647d0..1dcb51e18c 100644 --- a/Core/HW/Display.cpp +++ b/Core/HW/Display.cpp @@ -265,7 +265,7 @@ void __DisplayListenVblank(VblankCallback callback) { void __DisplayListenFlip(FlipCallback callback, void *userdata) { std::lock_guard guard(listenersLock); - flipListeners.push_back(std::make_pair(callback, userdata)); + flipListeners.emplace_back(callback, userdata); } void __DisplayForgetFlip(FlipCallback callback, void *userdata) { diff --git a/Core/Loaders.cpp b/Core/Loaders.cpp index 09ac48f47f..37722fe0cf 100644 --- a/Core/Loaders.cpp +++ b/Core/Loaders.cpp @@ -60,7 +60,7 @@ FileLoader *ConstructFileLoader(const Path &filename) { // TODO : improve, look in the file more IdentifiedFileType Identify_File(FileLoader *fileLoader, std::string *errorString) { - *errorString = ""; + errorString->clear(); if (fileLoader == nullptr) { *errorString = "Invalid fileLoader"; return IdentifiedFileType::ERROR_IDENTIFYING; diff --git a/Core/MIPS/JitCommon/JitBlockCache.cpp b/Core/MIPS/JitCommon/JitBlockCache.cpp index b85b336fcd..f5b11ca4a5 100644 --- a/Core/MIPS/JitCommon/JitBlockCache.cpp +++ b/Core/MIPS/JitCommon/JitBlockCache.cpp @@ -198,7 +198,7 @@ void JitBlockCache::ProxyBlock(u32 rootAddress, u32 startAddress, u32 size, cons // Make binary searches and stuff work ok b.normalEntry = codePtr; b.checkedEntry = codePtr; - proxyBlockMap_.insert(std::make_pair(startAddress, num_blocks_)); + proxyBlockMap_.emplace(startAddress, num_blocks_); AddBlockMap(num_blocks_); num_blocks_++; //commit the current block @@ -253,7 +253,7 @@ void JitBlockCache::FinalizeBlock(int block_num, bool block_link) { if (block_link) { for (int i = 0; i < MAX_JIT_BLOCK_EXITS; i++) { if (b.exitAddress[i] != INVALID_EXIT) { - links_to_.insert(std::make_pair(b.exitAddress[i], block_num)); + links_to_.emplace(b.exitAddress[i], block_num); } } diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index 2c4e11bea4..81f87da663 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -771,7 +771,7 @@ namespace MIPSAnalyst { for (auto iter = functions.begin(); iter != functions.end(); iter++) { AnalyzedFunction &f = *iter; if (f.hasHash && f.size > 16) { - hashToFunction.insert(std::make_pair(f.hash, &f)); + hashToFunction.emplace(f.hash, &f); } } } @@ -1420,6 +1420,7 @@ skip: std::vector GetOutputRegs(MIPSOpcode op) { std::vector vec; + vec.reserve(3); MIPSInfo info = MIPSGetInfo(op); if (info & OUT_RD) vec.push_back(MIPS_GET_RD(op)); if (info & OUT_RT) vec.push_back(MIPS_GET_RT(op)); diff --git a/Core/MIPS/MIPSAsm.cpp b/Core/MIPS/MIPSAsm.cpp index 97d34a4f18..1c45f14ac5 100644 --- a/Core/MIPS/MIPSAsm.cpp +++ b/Core/MIPS/MIPSAsm.cpp @@ -1,10 +1,12 @@ #include #include #include +#include #include #include "Common/CommonTypes.h" #include "ext/armips/Core/Assembler.h" +#include "ext/armips/Core/FileManager.h" #include "Common/Data/Encoding/Utf8.h" #include "Core/Debugger/SymbolMap.h" @@ -14,9 +16,9 @@ namespace MIPSAsm { - static std::wstring errorText; + static std::string errorText; -std::wstring GetAssembleError() +std::string GetAssembleError() { return errorText; } @@ -55,22 +57,21 @@ public: return true; } bool seekPhysical(int64_t physicalAddress) override { return seekVirtual(physicalAddress); } - const std::wstring &getFileName() override { return dummyWFilename_; } + const fs::path &getFileName() override { return dummyFilename_; } private: u64 address; - std::wstring dummyWFilename_; + fs::path dummyFilename_; }; -bool MipsAssembleOpcode(const char* line, DebugInterface* cpu, u32 address) -{ - StringList errors; +bool MipsAssembleOpcode(const char *line, DebugInterface *cpu, u32 address) { + std::vector errors; - wchar_t str[64]; - swprintf(str,64,L".psp\n.org 0x%08X\n",address); + char str[64]; + snprintf(str, 64, ".psp\n.org 0x%08X\n", address); ArmipsArguments args; args.mode = ArmipsMode::MEMORY; - args.content = str + ConvertUTF8ToWString(line); + args.content = str + std::string(line); args.silent = true; args.memoryFile.reset(new PspAssemblerFile()); args.errorsResult = &errors; @@ -79,14 +80,14 @@ bool MipsAssembleOpcode(const char* line, DebugInterface* cpu, u32 address) g_symbolMap->GetLabels(args.labels); } - errorText = L""; + errorText.clear(); if (!runArmips(args)) { for (size_t i = 0; i < errors.size(); i++) { errorText += errors[i]; - if (i != errors.size()-1) - errorText += L"\n"; + if (i != errors.size() - 1) + errorText += "\n"; } return false; diff --git a/Core/MIPS/MIPSAsm.h b/Core/MIPS/MIPSAsm.h index 5693ff6c74..27bf3eef08 100644 --- a/Core/MIPS/MIPSAsm.h +++ b/Core/MIPS/MIPSAsm.h @@ -4,5 +4,5 @@ namespace MIPSAsm { bool MipsAssembleOpcode(const char* line, DebugInterface* cpu, u32 address); - std::wstring GetAssembleError(); + std::string GetAssembleError(); } diff --git a/Core/Reporting.cpp b/Core/Reporting.cpp index f344e1bac9..4f15062e3c 100644 --- a/Core/Reporting.cpp +++ b/Core/Reporting.cpp @@ -434,7 +434,8 @@ namespace Reporting void AddGameplayInfo(UrlEncoder &postdata) { // Just to get an idea of how long they played. - postdata.Add("ticks", (const uint64_t)CoreTiming::GetTicks()); + if (PSP_IsInited()) + postdata.Add("ticks", (const uint64_t)CoreTiming::GetTicks()); float vps, fps; __DisplayGetAveragedFPS(&vps, &fps); diff --git a/Core/Screenshot.cpp b/Core/Screenshot.cpp index c049756798..00f7d0a305 100644 --- a/Core/Screenshot.cpp +++ b/Core/Screenshot.cpp @@ -406,8 +406,7 @@ bool Save888RGBScreenshot(const Path &filename, ScreenshotFormat fmt, const u8 * } bool Save8888RGBAScreenshot(const Path &filename, const u8 *buffer, int w, int h) { - png_image png; - memset(&png, 0, sizeof(png)); + png_image png{}; png.version = PNG_IMAGE_VERSION; png.format = PNG_FORMAT_RGBA; png.width = w; @@ -421,3 +420,30 @@ bool Save8888RGBAScreenshot(const Path &filename, const u8 *buffer, int w, int h } return success; } + +bool Save8888RGBAScreenshot(std::vector &bufferPNG, const u8 *bufferRGBA8888, int w, int h) { + png_image png{}; + png.version = PNG_IMAGE_VERSION; + png.format = PNG_FORMAT_RGBA; + png.width = w; + png.height = h; + + png_alloc_size_t allocSize = bufferPNG.size(); + int result = png_image_write_to_memory(&png, allocSize == 0 ? nullptr : bufferPNG.data(), &allocSize, 0, bufferRGBA8888, w * 4, nullptr); + bool success = result != 0 && png.warning_or_error <= 1; + if (!success && allocSize != bufferPNG.size()) { + bufferPNG.resize(allocSize); + png.warning_or_error = 0; + result = png_image_write_to_memory(&png, bufferPNG.data(), &allocSize, 0, bufferRGBA8888, w * 4, nullptr); + success = result != 0 && png.warning_or_error <= 1; + } + if (success) + bufferPNG.resize(allocSize); + png_image_free(&png); + + if (!success) { + ERROR_LOG(IO, "Buffering screenshot to PNG produced errors."); + bufferPNG.clear(); + } + return success; +} diff --git a/Core/Screenshot.h b/Core/Screenshot.h index 117cd5e625..ac124fef9a 100644 --- a/Core/Screenshot.h +++ b/Core/Screenshot.h @@ -39,5 +39,8 @@ const u8 *ConvertBufferToScreenshot(const GPUDebugBuffer &buf, bool alpha, u8 *& // Can only be used while in game. bool TakeGameScreenshot(const Path &filename, ScreenshotFormat fmt, ScreenshotType type, int *width = nullptr, int *height = nullptr, int maxRes = -1); + bool Save888RGBScreenshot(const Path &filename, ScreenshotFormat fmt, const u8 *bufferRGB888, int w, int h); bool Save8888RGBAScreenshot(const Path &filename, const u8 *bufferRGBA8888, int w, int h); +// Overallocate bufferPNG for better encoding speed. +bool Save8888RGBAScreenshot(std::vector &bufferPNG, const u8 *bufferRGBA8888, int w, int h); diff --git a/Core/System.cpp b/Core/System.cpp index 29b1765fb0..cb6fc34fa8 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -114,6 +114,7 @@ static std::string gpuBackendDevice; static volatile bool pspIsInited = false; static volatile bool pspIsIniting = false; static volatile bool pspIsQuitting = false; +static volatile bool pspIsRebooting = false; void ResetUIState() { globalUIState = UISTATE_MENU; @@ -413,11 +414,11 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) { } #if defined(_WIN32) && PPSSPP_ARCH(AMD64) - INFO_LOG(BOOT, "PPSSPP %s Windows 64 bit", PPSSPP_GIT_VERSION); + NOTICE_LOG(BOOT, "PPSSPP %s Windows 64 bit", PPSSPP_GIT_VERSION); #elif defined(_WIN32) && !PPSSPP_ARCH(AMD64) - INFO_LOG(BOOT, "PPSSPP %s Windows 32 bit", PPSSPP_GIT_VERSION); + NOTICE_LOG(BOOT, "PPSSPP %s Windows 32 bit", PPSSPP_GIT_VERSION); #else - INFO_LOG(BOOT, "PPSSPP %s", PPSSPP_GIT_VERSION); + NOTICE_LOG(BOOT, "PPSSPP %s", PPSSPP_GIT_VERSION); #endif Core_NotifyLifecycle(CoreLifecycle::STARTING); @@ -426,7 +427,7 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) { if (g_CoreParameter.graphicsContext == nullptr) { g_CoreParameter.graphicsContext = temp; } - g_CoreParameter.errorString = ""; + g_CoreParameter.errorString.clear(); pspIsIniting = true; PSP_SetLoading("Loading game..."); @@ -448,6 +449,7 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) { bool success = !g_CoreParameter.fileToStart.empty(); if (!success) { Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE); + pspIsRebooting = false; // In this case, we must call shutdown since the caller won't know to. // It must've partially started since CPU_Init returned true. PSP_Shutdown(); @@ -475,6 +477,7 @@ bool PSP_InitUpdate(std::string *error_string) { } } if (!success) { + pspIsRebooting = false; PSP_Shutdown(); return true; } @@ -483,6 +486,7 @@ bool PSP_InitUpdate(std::string *error_string) { pspIsIniting = !pspIsInited; if (pspIsInited) { Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE); + pspIsRebooting = false; } return pspIsInited; } @@ -501,7 +505,11 @@ bool PSP_IsIniting() { } bool PSP_IsInited() { - return pspIsInited && !pspIsQuitting; + return pspIsInited && !pspIsQuitting && !pspIsRebooting; +} + +bool PSP_IsRebooting() { + return pspIsRebooting; } bool PSP_IsQuitting() { @@ -515,7 +523,7 @@ void PSP_Shutdown() { } // Make sure things know right away that PSP memory, etc. is going away. - pspIsQuitting = true; + pspIsQuitting = !pspIsRebooting; if (coreState == CORE_RUNNING) Core_Stop(); @@ -540,6 +548,18 @@ void PSP_Shutdown() { Core_NotifyLifecycle(CoreLifecycle::STOPPED); } +bool PSP_Reboot(std::string *error_string) { + if (!pspIsInited || pspIsQuitting) + return false; + + pspIsRebooting = true; + Core_Stop(); + Core_WaitInactive(); + PSP_Shutdown(); + std::string resetError; + return PSP_Init(PSP_CoreParameter(), error_string); +} + void PSP_BeginHostFrame() { // Reapply the graphics state of the PSP if (gpu) { diff --git a/Core/System.h b/Core/System.h index 599836e259..9a801596ab 100644 --- a/Core/System.h +++ b/Core/System.h @@ -75,8 +75,10 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string); bool PSP_InitUpdate(std::string *error_string); bool PSP_IsIniting(); bool PSP_IsInited(); +bool PSP_IsRebooting(); bool PSP_IsQuitting(); void PSP_Shutdown(); +bool PSP_Reboot(std::string *error_string); void PSP_BeginHostFrame(); void PSP_EndHostFrame(); diff --git a/Core/Util/AudioFormat.h b/Core/Util/AudioFormat.h index 8774e83546..06d53676cd 100644 --- a/Core/Util/AudioFormat.h +++ b/Core/Util/AudioFormat.h @@ -49,7 +49,7 @@ static inline s16 clamp_s16(int i) { static inline s16 ApplySampleVolume(s16 sample, int vol) { #if PPSSPP_ARCH(ARM) && !defined(_MSC_VER) - register int r; + int r; asm volatile("smulwb %0, %1, %2\n\t" \ "ssat %0, #16, %0" \ : "=r"(r) : "r"(vol), "r"(sample)); diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index b6f4ccbd65..e808f07b6c 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -674,7 +674,7 @@ bool GameManager::InstallRawISO(const Path &file, const std::string &originalNam void GameManager::ResetInstallError() { if (!installInProgress_) { - installError_ = ""; + installError_.clear(); } } diff --git a/Core/WebServer.cpp b/Core/WebServer.cpp index 3d6d9b37cb..4aff212eba 100644 --- a/Core/WebServer.cpp +++ b/Core/WebServer.cpp @@ -297,7 +297,7 @@ static void ForwardDebuggerRequest(const http::Request &request) { // Check if this is a websocket request... std::string upgrade; if (!request.GetHeader("upgrade", &upgrade)) { - upgrade = ""; + upgrade.clear(); } // Yes - proceed with the socket. diff --git a/GPU/Common/DepalettizeShaderCommon.cpp b/GPU/Common/DepalettizeShaderCommon.cpp index ee04359135..a1e593e626 100644 --- a/GPU/Common/DepalettizeShaderCommon.cpp +++ b/GPU/Common/DepalettizeShaderCommon.cpp @@ -84,6 +84,9 @@ void GenerateDepalShader300(ShaderWriter &writer, const DepalConfig &config) { int shiftedMask = mask << shift; switch (config.bufferFormat) { + case GE_FORMAT_CLUT8: + writer.C(" int index = int(color.r * 255.99);\n"); + break; case GE_FORMAT_8888: if (shiftedMask & 0xFF) writer.C(" int r = int(color.r * 255.99);\n"); else writer.C(" int r = 0;\n"); if (shiftedMask & 0xFF00) writer.C(" int g = int(color.g * 255.99);\n"); else writer.C(" int g = 0;\n"); @@ -168,6 +171,16 @@ void GenerateDepalShaderFloat(ShaderWriter &writer, const DepalConfig &config) { // pixelformat is the format of the texture we are sampling. bool formatOK = true; switch (config.bufferFormat) { + case GE_FORMAT_CLUT8: + if (shift == 0 && mask == 0xFF) { + // Easy peasy. + sprintf(lookupMethod, "index.r"); + formatOK = true; + } else { + // Deal with this if we find it. + formatOK = false; + } + break; case GE_FORMAT_8888: if ((mask & (mask + 1)) == 0) { // If the value has all bits contiguous (bitmask check above), we can mod by it + 1. diff --git a/GPU/Common/Draw2D.cpp b/GPU/Common/Draw2D.cpp index ff7aab3169..05bead52e4 100644 --- a/GPU/Common/Draw2D.cpp +++ b/GPU/Common/Draw2D.cpp @@ -70,6 +70,23 @@ Draw2DPipelineInfo GenerateDraw2DCopyColorFs(ShaderWriter &writer) { }; } +Draw2DPipelineInfo GenerateDraw2DCopyColorRect2LinFs(ShaderWriter &writer) { + writer.DeclareSamplers(samplers); + writer.BeginFSMain(g_draw2Duniforms, varyings, FSFLAG_NONE); + writer.C(" vec2 tSize = texSize / scaleFactor;\n"); + writer.C(" vec2 pixels = v_texcoord * tSize;\n"); + writer.C(" float u = mod(pixels.x, tSize.x);\n"); + writer.C(" float v = floor(pixels.x / tSize.x);\n"); + writer.C(" vec4 outColor = ").SampleTexture2D("tex", "vec2(u, v) / tSize").C(";\n"); + writer.EndFSMain("outColor", FSFLAG_NONE); + + return Draw2DPipelineInfo{ + "draw2d_copy_color_rect2lin", + RASTER_COLOR, + RASTER_COLOR, + }; +} + Draw2DPipelineInfo GenerateDraw2DCopyDepthFs(ShaderWriter &writer) { writer.DeclareSamplers(samplers); writer.BeginFSMain(Slice::empty(), varyings, FSFLAG_WRITEDEPTH); @@ -161,9 +178,10 @@ void Draw2D::Ensure2DResources() { const ShaderLanguageDesc &shaderLanguageDesc = draw_->GetShaderLanguageDesc(); if (!draw2DVs_) { - char *vsCode = new char[4000]; + char *vsCode = new char[8192]; ShaderWriter writer(vsCode, shaderLanguageDesc, ShaderStage::Vertex); GenerateDraw2DVS(writer); + _assert_msg_(strlen(vsCode) < 8192, "Draw2D VS length error: %d", (int)strlen(vsCode)); draw2DVs_ = draw_->CreateShaderModule(ShaderStage::Vertex, shaderLanguageDesc.shaderLanguage, (const uint8_t *)vsCode, strlen(vsCode), "draw2d_vs"); _assert_(draw2DVs_); delete[] vsCode; @@ -198,9 +216,10 @@ Draw2DPipeline *Draw2D::Create2DPipeline(std::functionGetShaderLanguageDesc(); - char *fsCode = new char[4000]; + char *fsCode = new char[8192]; ShaderWriter writer(fsCode, shaderLanguageDesc, ShaderStage::Fragment); Draw2DPipelineInfo info = generate(writer); + _assert_msg_(strlen(fsCode) < 8192, "Draw2D FS length error: %d", (int)strlen(fsCode)); ShaderModule *fs = draw_->CreateShaderModule(ShaderStage::Fragment, shaderLanguageDesc.shaderLanguage, (const uint8_t *)fsCode, strlen(fsCode), info.tag); @@ -318,6 +337,13 @@ Draw2DPipeline *FramebufferManagerCommon::Get2DPipeline(Draw2DShader shader) { pipeline = draw2DPipelineColor_; break; + case DRAW2D_COPY_COLOR_RECT2LIN: + if (!draw2DPipelineColorRect2Lin_) { + draw2DPipelineColorRect2Lin_ = draw2D_.Create2DPipeline(&GenerateDraw2DCopyColorRect2LinFs); + } + pipeline = draw2DPipelineColorRect2Lin_; + break; + case DRAW2D_COPY_DEPTH: if (!draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported) { // Can't do it diff --git a/GPU/Common/Draw2D.h b/GPU/Common/Draw2D.h index d44f7ff613..bf156e6d39 100644 --- a/GPU/Common/Draw2D.h +++ b/GPU/Common/Draw2D.h @@ -16,6 +16,7 @@ enum Draw2DShader { DRAW2D_COPY_DEPTH, DRAW2D_565_TO_DEPTH, DRAW2D_565_TO_DEPTH_DESWIZZLE, + DRAW2D_COPY_COLOR_RECT2LIN, }; inline RasterChannel Draw2DSourceChannel(Draw2DShader shader) { diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index d361458111..f884486c65 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -19,6 +19,7 @@ #include "Common/Data/Convert/ColorConv.h" #include "Common/Profiler/Profiler.h" +#include "Common/LogReporting.h" #include "Core/Config.h" #include "GPU/Common/DrawEngineCommon.h" #include "GPU/Common/SplineCommon.h" @@ -188,6 +189,57 @@ u32 DrawEngineCommon::NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, return DrawEngineCommon::NormalizeVertices(outPtr, bufPtr, inPtr, dec, lowerBound, upperBound, vertType); } +void DrawEngineCommon::DispatchSubmitImm(GEPrimitiveType prim, TransformedVertex *buffer, int vertexCount, int cullMode, bool continuation) { + // Instead of plumbing through properly (we'd need to inject these pretransformed vertices in the middle + // of SoftwareTransform(), which would take a lot of refactoring), we'll cheat and just turn these into + // through vertices. + // Since the only known use is Thrillville and it only uses it to clear, we just use color and pos. + struct ImmVertex { + float uv[2]; + uint32_t color; + float xyz[3]; + }; + std::vector temp; + temp.resize(vertexCount); + uint32_t color1Used = 0; + for (int i = 0; i < vertexCount; i++) { + // Since we're sending through, scale back up to w/h. + temp[i].uv[0] = buffer[i].u * gstate.getTextureWidth(0); + temp[i].uv[1] = buffer[i].v * gstate.getTextureHeight(0); + temp[i].color = buffer[i].color0_32; + temp[i].xyz[0] = buffer[i].pos[0]; + temp[i].xyz[1] = buffer[i].pos[1]; + temp[i].xyz[2] = buffer[i].pos[2]; + color1Used |= buffer[i].color1_32; + } + int vtype = GE_VTYPE_TC_FLOAT | GE_VTYPE_POS_FLOAT | GE_VTYPE_COL_8888 | GE_VTYPE_THROUGH; + // TODO: Handle fog and secondary color somehow? + + if (gstate.isFogEnabled() && !gstate.isModeThrough()) { + WARN_LOG_REPORT_ONCE(geimmfog, G3D, "Imm vertex used fog"); + } + if (color1Used != 0 && gstate.isUsingSecondaryColor() && !gstate.isModeThrough()) { + WARN_LOG_REPORT_ONCE(geimmcolor1, G3D, "Imm vertex used secondary color"); + } + + bool prevThrough = gstate.isModeThrough(); + // Code checks this reg directly, not just the vtype ID. + if (!prevThrough) { + gstate.vertType |= GE_VTYPE_THROUGH; + gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_UVSCALEOFFSET | DIRTY_CULLRANGE); + } + + int bytesRead; + uint32_t vertTypeID = GetVertTypeID(vtype, 0); + SubmitPrim(&temp[0], nullptr, prim, vertexCount, vertTypeID, cullMode, &bytesRead); + DispatchFlush(); + + if (!prevThrough) { + gstate.vertType &= ~GE_VTYPE_THROUGH; + gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_UVSCALEOFFSET | DIRTY_CULLRANGE); + } +} + // This code has plenty of potential for optimization. // // It does the simplest and safest test possible: If all points of a bbox is outside a single of @@ -484,12 +536,12 @@ u32 DrawEngineCommon::NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, return GE_VTYPE_TC_FLOAT | GE_VTYPE_COL_8888 | GE_VTYPE_NRM_FLOAT | GE_VTYPE_POS_FLOAT | (vertType & (GE_VTYPE_IDX_MASK | GE_VTYPE_THROUGH)); } -void DrawEngineCommon::ApplyFramebufferRead(bool *fboTexNeedsBind) { +void DrawEngineCommon::ApplyFramebufferRead(FBOTexState *fboTexState) { if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) { - *fboTexNeedsBind = false; + *fboTexState = FBO_TEX_READ_FRAMEBUFFER; } else { gpuStats.numCopiesForShaderBlend++; - *fboTexNeedsBind = true; + *fboTexState = FBO_TEX_COPY_BIND_TEX; } gstate_c.Dirty(DIRTY_SHADERBLEND); @@ -665,6 +717,16 @@ uint64_t DrawEngineCommon::ComputeHash() { return fullhash; } +// Cheap bit scrambler from https://nullprogram.com/blog/2018/07/31/ +inline uint32_t lowbias32_r(uint32_t x) { + x ^= x >> 16; + x *= 0x43021123U; + x ^= x >> 15 ^ x >> 30; + x *= 0x1d69e2a5U; + x ^= x >> 16; + return x; +} + // vertTypeID is the vertex type but with the UVGen mode smashed into the top bits. void DrawEngineCommon::SubmitPrim(const void *verts, const void *inds, GEPrimitiveType prim, int vertexCount, u32 vertTypeID, int cullMode, int *bytesRead) { if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls_ + vertexCount > VERTEX_BUFFER_MAX) { @@ -693,10 +755,10 @@ void DrawEngineCommon::SubmitPrim(const void *verts, const void *inds, GEPrimiti if (g_Config.bVertexCache) { u32 dhash = dcid_; dhash = __rotl(dhash ^ (u32)(uintptr_t)verts, 13); - dhash = __rotl(dhash ^ (u32)(uintptr_t)inds, 13); - dhash = __rotl(dhash ^ (u32)vertTypeID, 13); - dhash = __rotl(dhash ^ (u32)vertexCount, 13); - dcid_ = dhash ^ (u32)prim; + dhash = __rotl(dhash ^ (u32)(uintptr_t)inds, 19); + dhash = __rotl(dhash ^ (u32)vertTypeID, 7); + dhash = __rotl(dhash ^ (u32)vertexCount, 11); + dcid_ = lowbias32_r(dhash ^ (u32)prim); } DeferredDrawCall &dc = drawCalls[numDrawCalls]; diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index a8997454d9..8fc0382a29 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -46,6 +46,12 @@ enum { TEX_SLOT_SPLINE_WEIGHTS_V = 6, }; +enum FBOTexState { + FBO_TEX_NONE, + FBO_TEX_COPY_BIND_TEX, + FBO_TEX_READ_FRAMEBUFFER, +}; + inline uint32_t GetVertTypeID(uint32_t vertType, int uvGenMode) { // As the decoder depends on the UVGenMode when we use UV prescale, we simply mash it // into the top of the verttype where there are unused bits. @@ -84,10 +90,7 @@ public: SubmitPrim(verts, inds, prim, vertexCount, vertTypeID, cullMode, bytesRead); } - virtual void DispatchSubmitImm(const void *verts, const void *inds, GEPrimitiveType prim, int vertexCount, u32 vertTypeID, int cullMode, int *bytesRead) { - SubmitPrim(verts, inds, prim, vertexCount, vertTypeID, cullMode, bytesRead); - DispatchFlush(); - } + virtual void DispatchSubmitImm(GEPrimitiveType prim, TransformedVertex *buffer, int vertexCount, int cullMode, bool continuation); bool TestBoundingBox(const void* control_points, int vertexCount, u32 vertType, int *bytesRead); @@ -130,7 +133,7 @@ protected: // Vertex decoding void DecodeVertsStep(u8 *dest, int &i, int &decodedVerts); - void ApplyFramebufferRead(bool *fboTexNeedsBind); + void ApplyFramebufferRead(FBOTexState *fboTexState); inline int IndexSize(u32 vtype) const { const u32 indexType = (vtype & GE_VTYPE_IDX_MASK); @@ -144,6 +147,8 @@ protected: bool useHWTransform_ = false; bool useHWTessellation_ = false; + // Used to prevent unnecessary flushing in softgpu. + bool flushOnParams_ = true; // Vertex collector buffers u8 *decoded = nullptr; diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index 9586d01b48..2f8f248dbd 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -134,10 +134,12 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu GELogicOp replaceLogicOpType = isModeClear ? GE_LOGIC_COPY : (GELogicOp)id.Bits(FS_BIT_REPLACE_LOGIC_OP, 4); bool replaceLogicOp = replaceLogicOpType != GE_LOGIC_COPY && compat.bitwiseOps; - bool readFramebuffer = replaceBlend == REPLACE_BLEND_READ_FRAMEBUFFER || colorWriteMask || replaceLogicOp; - bool readFramebufferTex = readFramebuffer && !gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH); + bool needFramebufferRead = replaceBlend == REPLACE_BLEND_READ_FRAMEBUFFER || colorWriteMask || replaceLogicOp; - bool needFragCoord = readFramebuffer || gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT); + bool fetchFramebuffer = needFramebufferRead && gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH); + bool readFramebufferTex = needFramebufferRead && !gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH); + + bool needFragCoord = readFramebufferTex || gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT); bool writeDepth = gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT); if (shaderDepalMode != ShaderDepalMode::OFF && !doTexture) { @@ -157,6 +159,11 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu if (readFramebufferTex) { WRITE(p, "layout (binding = 1) uniform sampler2D fbotex;\n"); + } else if (fetchFramebuffer) { + WRITE(p, "layout (input_attachment_index = 0, binding = 9) uniform subpassInput inputColor;\n"); + if (fragmentShaderFlags) { + *fragmentShaderFlags |= FragmentShaderFlags::INPUT_ATTACHMENT; + } } if (shaderDepalMode != ShaderDepalMode::OFF) { @@ -167,9 +174,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, "layout (location = 1) %s in lowp vec4 v_color0;\n", shading); if (lmode) WRITE(p, "layout (location = 2) %s in lowp vec3 v_color1;\n", shading); - if (enableFog) { - WRITE(p, "layout (location = 3) in highp float v_fogdepth;\n"); - } + WRITE(p, "layout (location = 3) in highp float v_fogdepth;\n"); if (doTexture) { WRITE(p, "layout (location = 0) in highp vec3 v_texcoord;\n"); } @@ -178,7 +183,8 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, "int roundAndScaleTo255i(in highp float x) { return int(floor(x * 255.0 + 0.5)); }\n"); } if (enableColorTest && !colorTestAgainstZero) { - WRITE(p, "ivec3 roundAndScaleTo255iv(in highp vec3 x) { return ivec3(floor(x * 255.0 + 0.5)); }\n"); + WRITE(p, "uint roundAndScaleTo8x4(in highp vec3 x) { uvec3 u = uvec3(floor(x * 255.0 + 0.5)); return u.r | (u.g << 8) | (u.b << 16); }\n"); + WRITE(p, "uint packFloatsTo8x4(in vec3 x) { uvec3 u = uvec3(x); return u.r | (u.g << 8) | (u.b << 16); }\n"); } WRITE(p, "layout (location = 0, index = 0) out vec4 fragColor0;\n"); @@ -257,7 +263,8 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu } if (enableColorTest) { if (compat.shaderLanguage == HLSL_D3D11) { - WRITE(p, "uvec3 roundAndScaleTo255iv(float3 x) { return (floor(x * 255.0f + 0.5f)); }\n"); + WRITE(p, "uint roundAndScaleTo8x4(float3 x) { uvec3 u = (floor(x * 255.0f + 0.5f)); return u.r | (u.g << 8) | (u.b << 16); }\n"); + WRITE(p, "uint packFloatsTo8x4(in vec3 x) { uvec3 u = uvec3(x); return u.r | (u.g << 8) | (u.b << 16); }\n"); } else { WRITE(p, "vec3 roundAndScaleTo255v(float3 x) { return floor(x * 255.0f + 0.5f); }\n"); } @@ -272,9 +279,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu if (lmode) { WRITE(p, " vec3 v_color1: COLOR1;\n"); } - if (enableFog) { - WRITE(p, " float v_fogdepth: TEXCOORD1;\n"); - } + WRITE(p, " float v_fogdepth: TEXCOORD1;\n"); if (needFragCoord) { if (compat.shaderLanguage == HLSL_D3D11) { WRITE(p, " vec4 pixelPos : SV_POSITION;\n"); @@ -351,7 +356,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, "uniform vec4 u_alphacolorref;\n"); if (compat.bitwiseOps && ((enableColorTest && !colorTestAgainstZero) || (enableAlphaTest && !alphaTestAgainstZero))) { *uniformMask |= DIRTY_ALPHACOLORMASK; - WRITE(p, "uniform ivec4 u_alphacolormask;\n"); + WRITE(p, "uniform uint u_alphacolormask;\n"); } } } @@ -387,8 +392,8 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu if (enableFog) { *uniformMask |= DIRTY_FOGCOLOR; WRITE(p, "uniform vec3 u_fogcolor;\n"); - WRITE(p, "%s %s float v_fogdepth;\n", compat.varying_fs, highpFog ? "highp" : "mediump"); } + WRITE(p, "%s %s float v_fogdepth;\n", compat.varying_fs, highpFog ? "highp" : "mediump"); if (doTexture) { WRITE(p, "%s %s vec3 v_texcoord;\n", compat.varying_fs, highpTexcoord ? "highp" : "mediump"); } @@ -405,7 +410,8 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu } if (enableColorTest && !colorTestAgainstZero) { if (compat.bitwiseOps) { - WRITE(p, "ivec3 roundAndScaleTo255iv(in vec3 x) { return ivec3(floor(x * 255.0 + 0.5)); }\n"); + WRITE(p, "uint roundAndScaleTo8x4(in vec3 x) { uvec3 u = uvec3(floor(x * 255.99)); return u.r | (u.g << 8) | (u.b << 16); }\n"); + WRITE(p, "uint packFloatsTo8x4(in vec3 x) { uvec3 u = uvec3(x); return u.r | (u.g << 8) | (u.b << 16); }\n"); } else if (gl_extensions.gpuVendor == GPU_VENDOR_IMGTEC) { WRITE(p, "vec3 roundTo255thv(in vec3 x) { vec3 y = x + (0.5/255.0); return y - fract(y * 255.0) * (1.0 / 255.0); }\n"); } else { @@ -416,7 +422,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu if (!strcmp(compat.fragColor0, "fragColor0")) { const char *qualifierColor0 = "out"; - if (readFramebuffer && compat.lastFragData && !strcmp(compat.lastFragData, compat.fragColor0)) { + if (fetchFramebuffer && compat.lastFragData && !strcmp(compat.lastFragData, compat.fragColor0)) { qualifierColor0 = "inout"; } // Output the output color definitions. @@ -455,6 +461,12 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, "}\n"); } + if (compat.bitwiseOps && enableColorTest) { + p.C("uvec3 unpackUVec3(highp uint x) {\n"); + p.C(" return uvec3(x & 0xFFU, (x >> 8) & 0xFFU, (x >> 16) & 0xFFU);\n"); + p.C("}\n"); + } + // PowerVR needs a custom modulo function. For some reason, this has far higher precision than the builtin one. if ((gl_extensions.bugs & BUG_PVR_SHADER_PRECISION_BAD) && needShaderTexClamp) { WRITE(p, "float mymod(float a, float b) { return a - b * floor(a / b); }\n"); @@ -492,20 +504,26 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu } // Two things read from the old framebuffer - shader replacement blending and bit-level masking. - if (readFramebuffer) { + if (readFramebufferTex) { if (compat.shaderLanguage == HLSL_D3D11) { WRITE(p, " vec4 destColor = fbotex.Load(int3((int)gl_FragCoord.x, (int)gl_FragCoord.y, 0));\n"); } else if (compat.shaderLanguage == HLSL_D3D9) { WRITE(p, " vec4 destColor = tex2D(fbotex, gl_FragCoord.xy * u_fbotexSize.xy);\n", compat.texture); - } else if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) { - // If we have EXT_shader_framebuffer_fetch / ARM_shader_framebuffer_fetch, we skip the blit. - // We can just read the prev value more directly. - WRITE(p, " lowp vec4 destColor = %s;\n", compat.lastFragData); } else if (!compat.texelFetch) { WRITE(p, " lowp vec4 destColor = %s(fbotex, gl_FragCoord.xy * u_fbotexSize.xy);\n", compat.texture); } else { WRITE(p, " lowp vec4 destColor = %s(fbotex, ivec2(gl_FragCoord.x, gl_FragCoord.y), 0);\n", compat.texelFetch); } + } else if (fetchFramebuffer) { + // If we have EXT_shader_framebuffer_fetch / ARM_shader_framebuffer_fetch, we skip the blit. + // We can just read the prev value more directly. + if (compat.shaderLanguage == GLSL_3xx) { + WRITE(p, " lowp vec4 destColor = %s;\n", compat.lastFragData); + } else if (compat.shaderLanguage == GLSL_VULKAN) { + WRITE(p, " lowp vec4 destColor = subpassLoad(inputColor);\n", compat.lastFragData); + } else { + _assert_msg_(false, "Need fetch destColor, but not a compatible language"); + } } if (isModeClear) { @@ -734,8 +752,27 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, " }\n"); break; case ShaderDepalMode::CLUT8_8888: - // Not yet implemented. - WRITE(p, " vec4 t = vec4(0.0, 0.0, 0.0, 0.0);\n"); + if (doTextureProjection) { + // We don't use textureProj because we need better control and it's probably not much of a savings anyway. + // However it is good for precision on older hardware like PowerVR. + p.F(" vec2 uv = %s.xy/%s.z;\n vec2 uv_round;\n", texcoord, texcoord); + } else { + p.F(" vec2 uv = %s.xy;\n vec2 uv_round;\n", texcoord); + } + p.C(" vec2 tsize = vec2(textureSize(tex, 0).xy);\n"); + p.C(" uv_round = floor(uv * tsize);\n"); + p.C(" int component = int(uv_round.x) & 3;\n"); + p.C(" uv_round.x *= 0.25;\n"); + p.C(" uv_round /= tsize;\n"); + p.C(" vec4 t = ").SampleTexture2D("tex", "uv_round").C(";\n"); + p.C(" int index;\n"); + p.C(" switch (component) {\n"); + p.C(" case 0: index = int(t.x * 254.99); break;\n"); // TODO: Not sure why 254.99 instead of 255.99, but it's currently needed. + p.C(" case 1: index = int(t.y * 254.99); break;\n"); + p.C(" case 2: index = int(t.z * 254.99); break;\n"); + p.C(" case 3: index = int(t.w * 254.99); break;\n"); + p.C(" }\n"); + p.C(" t = ").LoadTexture2D("pal", "ivec2(index, 0)", 0).C(";\n"); break; } @@ -845,7 +882,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu const char *alphaTestFuncs[] = { "#", "#", " != ", " == ", " >= ", " > ", " <= ", " < " }; if (alphaTestFuncs[alphaTestFunc][0] != '#') { if (compat.bitwiseOps) { - WRITE(p, " if ((roundAndScaleTo255i(v.a) & u_alphacolormask.a) %s int(u_alphacolorref.a)) %s\n", alphaTestFuncs[alphaTestFunc], discardStatement); + WRITE(p, " if ((roundAndScaleTo255i(v.a) & int(u_alphacolormask >> 24)) %s int(u_alphacolorref.a)) %s\n", alphaTestFuncs[alphaTestFunc], discardStatement); } else if (gl_extensions.gpuVendor == GPU_VENDOR_IMGTEC) { // Work around bad PVR driver problem where equality check + discard just doesn't work. if (alphaTestFunc != GE_COMP_NOTEQUAL) { @@ -899,34 +936,22 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu } } else { const char *colorTestFuncs[] = { "#", "#", " != ", " == " }; - if (colorTestFuncs[colorTestFunc][0] != '#') { + const char *test = colorTestFuncs[colorTestFunc]; + if (test[0] != '#') { // TODO: Unify these paths better. - if (compat.shaderLanguage == HLSL_D3D11) { - const char *test = colorTestFuncs[colorTestFunc]; - WRITE(p, " uvec3 v_scaled = roundAndScaleTo255iv(v.rgb);\n"); - WRITE(p, " uvec3 v_masked = v_scaled & u_alphacolormask.rgb;\n"); - WRITE(p, " uvec3 colorTestRef = u_alphacolorref.rgb & u_alphacolormask.rgb;\n"); - // We have to test the components separately, or we get incorrect results. See #10629. - WRITE(p, " if (v_masked.r %s colorTestRef.r && v_masked.g %s colorTestRef.g && v_masked.b %s colorTestRef.b) %s\n", test, test, test, discardStatement); - } else if (compat.shaderLanguage == HLSL_D3D9) { - const char *test = colorTestFuncs[colorTestFunc]; + if (compat.shaderLanguage == HLSL_D3D9) { // TODO: Use a texture to lookup bitwise ops instead? WRITE(p, " vec3 colortest = roundAndScaleTo255v(v.rgb);\n"); WRITE(p, " if ((colortest.r %s u_alphacolorref.r) && (colortest.g %s u_alphacolorref.g) && (colortest.b %s u_alphacolorref.b)) %s\n", test, test, test, discardStatement); } else if (compat.bitwiseOps) { - WRITE(p, " ivec3 v_scaled = roundAndScaleTo255iv(v.rgb);\n"); - if (compat.shaderLanguage == GLSL_VULKAN) { - // Apparently GLES3 does not support vector bitwise ops, but Vulkan does? - WRITE(p, " if ((v_scaled & u_alphacolormask.rgb) %s (u_alphacolorref.rgb & u_alphacolormask.rgb)) %s\n", colorTestFuncs[colorTestFunc], discardStatement); - } else { - const char *maskedFragColor = "ivec3(v_scaled.r & u_alphacolormask.r, v_scaled.g & u_alphacolormask.g, v_scaled.b & u_alphacolormask.b)"; - const char *maskedColorRef = "ivec3(int(u_alphacolorref.r) & u_alphacolormask.r, int(u_alphacolorref.g) & u_alphacolormask.g, int(u_alphacolorref.b) & u_alphacolormask.b)"; - WRITE(p, " if (%s %s %s) %s\n", maskedFragColor, colorTestFuncs[colorTestFunc], maskedColorRef, discardStatement); - } + WRITE(p, " uint v_uint = roundAndScaleTo8x4(v.rgb);\n"); + WRITE(p, " uint v_masked = v_uint & u_alphacolormask;\n"); + WRITE(p, " uint colorTestRef = packFloatsTo8x4(u_alphacolorref.rgb) & u_alphacolormask;\n"); + WRITE(p, " if (v_masked %s colorTestRef) %s\n", test, discardStatement); } else if (gl_extensions.gpuVendor == GPU_VENDOR_IMGTEC) { - WRITE(p, " if (roundTo255thv(v.rgb) %s u_alphacolorref.rgb) %s\n", colorTestFuncs[colorTestFunc], discardStatement); + WRITE(p, " if (roundTo255thv(v.rgb) %s u_alphacolorref.rgb) %s\n", test, discardStatement); } else { - WRITE(p, " if (roundAndScaleTo255v(v.rgb) %s u_alphacolorref.rgb) %s\n", colorTestFuncs[colorTestFunc], discardStatement); + WRITE(p, " if (roundAndScaleTo255v(v.rgb) %s u_alphacolorref.rgb) %s\n", test, discardStatement); } } else { WRITE(p, " %s\n", discardStatement); @@ -1126,7 +1151,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu case GE_LOGIC_COPY_INVERTED: p.C(" v32 = (~v32 & 0x00FFFFFFu) | (v32 & 0xFF000000u);\n"); break; case GE_LOGIC_OR_INVERTED: p.C(" v32 = ((~v32 | d32) & 0x00FFFFFFu) | (v32 & 0xFF000000u);\n"); break; case GE_LOGIC_NAND: p.C(" v32 = (~(v32 & d32) & 0x00FFFFFFu) | (v32 & 0xFF000000u);\n"); break; - case GE_LOGIC_SET: p.C(" v32 |= 0x00FFFFFF;\n"); break; + case GE_LOGIC_SET: p.C(" v32 |= 0x00FFFFFFu;\n"); break; } // Note that the mask has already been flipped to the PC way - 1 means write. diff --git a/GPU/Common/FragmentShaderGenerator.h b/GPU/Common/FragmentShaderGenerator.h index 85c651cf8b..8f358fa7ff 100644 --- a/GPU/Common/FragmentShaderGenerator.h +++ b/GPU/Common/FragmentShaderGenerator.h @@ -42,7 +42,7 @@ struct FShaderID; // Can technically be deduced from the fragment shader ID, but this is safer. enum class FragmentShaderFlags : u32 { - FS_FLAG_INPUT_ATTACHMENT = 1, + INPUT_ATTACHMENT = 1, }; ENUM_CLASS_BITOPS(FragmentShaderFlags); diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index bac8514562..df0342724a 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -49,6 +49,10 @@ #include "GPU/GPUInterface.h" #include "GPU/GPUState.h" +static size_t FormatFramebufferName(VirtualFramebuffer *vfb, char *tag, size_t len) { + return snprintf(tag, len, "FB_%08x_%08x_%dx%d_%s", vfb->fb_address, vfb->z_address, vfb->bufferWidth, vfb->bufferHeight, GeBufferFormatToString(vfb->fb_format)); +} + FramebufferManagerCommon::FramebufferManagerCommon(Draw::DrawContext *draw) : draw_(draw), draw2D_(draw_) { presentation_ = new PresentationCommon(draw); @@ -108,7 +112,9 @@ void FramebufferManagerCommon::BeginFrame() { } void FramebufferManagerCommon::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) { - displayFramebufPtr_ = framebuf; + displayFramebufPtr_ = framebuf & 0x3FFFFFFF; + if (Memory::IsVRAMAddress(displayFramebufPtr_)) + displayFramebufPtr_ = framebuf & 0x041FFFFF; displayStride_ = stride; displayFormat_ = format; GPUDebug::NotifyDisplay(framebuf, stride, format); @@ -117,6 +123,8 @@ void FramebufferManagerCommon::SetDisplayFramebuffer(u32 framebuf, u32 stride, G VirtualFramebuffer *FramebufferManagerCommon::GetVFBAt(u32 addr) const { addr &= 0x3FFFFFFF; + if (Memory::IsVRAMAddress(addr)) + addr &= 0x041FFFFF; VirtualFramebuffer *match = nullptr; for (auto vfb : vfbs_) { if (vfb->fb_address == addr) { @@ -130,6 +138,9 @@ VirtualFramebuffer *FramebufferManagerCommon::GetVFBAt(u32 addr) const { } VirtualFramebuffer *FramebufferManagerCommon::GetExactVFB(u32 addr, int stride, GEBufferFormat format) const { + addr &= 0x3FFFFFFF; + if (Memory::IsVRAMAddress(addr)) + addr &= 0x041FFFFF; VirtualFramebuffer *newest = nullptr; for (auto vfb : vfbs_) { if (vfb->fb_address == addr && vfb->fb_stride == stride && vfb->fb_format == format) { @@ -146,6 +157,9 @@ VirtualFramebuffer *FramebufferManagerCommon::GetExactVFB(u32 addr, int stride, } VirtualFramebuffer *FramebufferManagerCommon::ResolveVFB(u32 addr, int stride, GEBufferFormat format) { + addr &= 0x3FFFFFFF; + if (Memory::IsVRAMAddress(addr)) + addr &= 0x041FFFFF; // Find the newest one matching addr and stride. VirtualFramebuffer *newest = nullptr; for (auto vfb : vfbs_) { @@ -231,7 +245,7 @@ void FramebufferManagerCommon::EstimateDrawingSize(u32 fb_address, int fb_stride // The majority of the time, these are equal. If not, let's check what we know. u32 nearest_address = 0xFFFFFFFF; for (auto vfb : vfbs_) { - const u32 other_address = vfb->fb_address & 0x3FFFFFFF; + const u32 other_address = vfb->fb_address; if (other_address > fb_address && other_address < nearest_address) { nearest_address = other_address; } @@ -292,10 +306,11 @@ void FramebufferManagerCommon::EstimateDrawingSize(u32 fb_address, int fb_stride } void GetFramebufferHeuristicInputs(FramebufferHeuristicParams *params, const GPUgstate &gstate) { - params->fb_address = (gstate.getFrameBufRawAddress() & 0x3FFFFFFF) | 0x04000000; // GetFramebufferHeuristicInputs is only called from rendering, and thus, it's VRAM. + // GetFramebufferHeuristicInputs is only called from rendering, and thus, it's VRAM. + params->fb_address = gstate.getFrameBufRawAddress() | 0x04000000; params->fb_stride = gstate.FrameBufStride(); - params->z_address = (gstate.getDepthBufRawAddress() & 0x3FFFFFFF) | 0x04000000; + params->z_address = gstate.getDepthBufRawAddress() | 0x04000000; params->z_stride = gstate.DepthBufStride(); if (params->z_address == params->fb_address) { @@ -475,9 +490,9 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer vfb->fb_format = params.fb_format; vfb->usageFlags = FB_USAGE_RENDER_COLOR; - u32 byteSize = ColorBufferByteSize(vfb); - if (Memory::IsVRAMAddress(params.fb_address) && params.fb_address + byteSize > framebufRangeEnd_) { - framebufRangeEnd_ = params.fb_address + byteSize; + u32 colorByteSize = ColorBufferByteSize(vfb); + if (Memory::IsVRAMAddress(params.fb_address) && params.fb_address + colorByteSize > framebufRangeEnd_) { + framebufRangeEnd_ = params.fb_address + colorByteSize; } // This is where we actually create the framebuffer. The true is "force". @@ -499,9 +514,9 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer // Assume that if we're clearing right when switching to a new framebuffer, we don't need to upload. if (useBufferedRendering_ && params.isDrawing) { - gpu->PerformMemoryUpload(params.fb_address, byteSize); + gpu->PerformMemoryUpload(params.fb_address, colorByteSize); // Alpha was already done by PerformMemoryUpload. - PerformStencilUpload(params.fb_address, byteSize, StencilUpload::STENCIL_IS_ZERO | StencilUpload::IGNORE_ALPHA); + PerformStencilUpload(params.fb_address, colorByteSize, StencilUpload::STENCIL_IS_ZERO | StencilUpload::IGNORE_ALPHA); // TODO: Is it worth trying to upload the depth buffer (only if it wasn't copied above..?) } @@ -547,13 +562,42 @@ void FramebufferManagerCommon::SetDepthFrameBuffer(bool isClearingDepth) { return; } + // First time use of this framebuffer's depth buffer. + bool newlyUsingDepth = (currentRenderVfb_->usageFlags & FB_USAGE_RENDER_DEPTH) == 0; + currentRenderVfb_->usageFlags |= FB_USAGE_RENDER_DEPTH; + + uint32_t boundDepthBuffer = gstate.getDepthBufRawAddress() | 0x04000000; + if (currentRenderVfb_->z_address != boundDepthBuffer) { + WARN_LOG_N_TIMES(z_reassign, 5, G3D, "Framebuffer at %08x/%d has switched associated depth buffer from %08x to %08x, updating.", + currentRenderVfb_->fb_address, currentRenderVfb_->fb_stride, currentRenderVfb_->z_address, boundDepthBuffer); + + // Technically, here we should copy away the depth buffer to another framebuffer that uses that z_address, or maybe + // even write it back to RAM. However, this is rare. Silent Hill is one example, see #16126. + currentRenderVfb_->z_address = boundDepthBuffer; + + if (currentRenderVfb_->fbo) { + char tag[128]; + FormatFramebufferName(currentRenderVfb_, tag, sizeof(tag)); + currentRenderVfb_->fbo->UpdateTag(tag); + } + } + // If this first draw call is anything other than a clear, "resolve" the depth buffer, // by copying from any overlapping buffers with fresher content. - if (!isClearingDepth) { + if (!isClearingDepth && useBufferedRendering_) { CopyToDepthFromOverlappingFramebuffers(currentRenderVfb_); + + // Need to upload the first line of depth buffers, for Burnout Dominator lens flares. See issue #11100 and comments to #16081. + // Might make this more generic and upload the whole depth buffer if we find it's needed for something. + if (newlyUsingDepth && draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported) { + // Sanity check the depth buffer pointer. + if (Memory::IsValidRange(currentRenderVfb_->z_address, currentRenderVfb_->width * 2)) { + const u16 *src = (const u16 *)Memory::GetPointerUnchecked(currentRenderVfb_->z_address); + DrawPixels(currentRenderVfb_, 0, 0, (const u8 *)src, GE_FORMAT_DEPTH16, currentRenderVfb_->z_stride, currentRenderVfb_->width, currentRenderVfb_->height, RASTER_DEPTH, "Depth Upload"); + } + } } - currentRenderVfb_->usageFlags |= FB_USAGE_RENDER_DEPTH; currentRenderVfb_->depthBindSeq = GetBindSeqCount(); } @@ -633,7 +677,7 @@ void FramebufferManagerCommon::CopyToDepthFromOverlappingFramebuffers(VirtualFra } } - gstate_c.Dirty(DIRTY_TEXTURE_IMAGE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_BLEND_STATE); + gstate_c.Dirty(DIRTY_ALL_RENDER_STATE); } // Can't easily dynamically create these strings, we just pass along the pointer. @@ -811,6 +855,11 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra } Draw2DPipeline *FramebufferManagerCommon::GetReinterpretPipeline(GEBufferFormat from, GEBufferFormat to, float *scaleFactorX) { + if (from == to) { + *scaleFactorX = 1.0f; + return Get2DPipeline(DRAW2D_COPY_COLOR); + } + if (IsBufferFormat16Bit(from) && !IsBufferFormat16Bit(to)) { // We halve the X coordinates in the destination framebuffer. // The shader will collect two pixels worth of input data and merge into one. @@ -819,6 +868,8 @@ Draw2DPipeline *FramebufferManagerCommon::GetReinterpretPipeline(GEBufferFormat // We double the X coordinates in the destination framebuffer. // The shader will sample and depending on the X coordinate & 1, use the upper or lower bits. *scaleFactorX = 2.0f; + } else { + *scaleFactorX = 1.0f; } Draw2DPipeline *pipeline = reinterpretFromTo_[(int)from][(int)to]; @@ -894,7 +945,7 @@ void FramebufferManagerCommon::BlitFramebufferDepth(VirtualFramebuffer *src, Vir // Some GPUs can copy depth but only if stencil gets to come along for the ride. We only want to use this if there is no blit functionality. if (useCopy) { - draw_->CopyFramebufferImage(src->fbo, 0, 0, 0, 0, dst->fbo, 0, 0, 0, 0, w, h, 1, Draw::FB_DEPTH_BIT, "BlitFramebufferDepth"); + draw_->CopyFramebufferImage(src->fbo, 0, 0, 0, 0, dst->fbo, 0, 0, 0, 0, w, h, 1, Draw::FB_DEPTH_BIT, "CopyFramebufferDepth"); RebindFramebuffer("After BlitFramebufferDepth"); } else if (useBlit) { // We'll accept whether we get a separate depth blit or not... @@ -944,7 +995,7 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe if (useBufferedRendering_) { if (vfb->fbo) { shaderManager_->DirtyLastShader(); - draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "FramebufferSwitch"); + draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "FBSwitch"); } else { // This should only happen very briefly when toggling useBufferedRendering_. ResizeFramebufFBO(vfb, vfb->width, vfb->height, true); @@ -997,10 +1048,12 @@ void FramebufferManagerCommon::NotifyVideoUpload(u32 addr, int size, int stride, void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size) { // Take off the uncached flag from the address. Not to be confused with the start of VRAM. addr &= 0x3FFFFFFF; + if (Memory::IsVRAMAddress(addr)) + addr &= 0x041FFFFF; // TODO: Could go through all FBOs, but probably not important? // TODO: Could also check for inner changes, but video is most important. // TODO: This shouldn't care if it's a display framebuf or not, should work exactly the same. - bool isDisplayBuf = addr == DisplayFramebufAddr() || addr == PrevDisplayFramebufAddr(); + bool isDisplayBuf = addr == CurrentDisplayFramebufAddr() || addr == PrevDisplayFramebufAddr(); // TODO: Deleting the FBO is a heavy hammer solution, so let's only do it if it'd help. if (!Memory::IsValidAddress(displayFramebufPtr_)) return; @@ -1017,7 +1070,7 @@ void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size) { // TODO: This doesn't seem quite right anymore. fmt = displayFormat_; } - DrawPixels(vfb, 0, 0, Memory::GetPointer(addr), fmt, vfb->fb_stride, vfb->width, vfb->height); + DrawPixels(vfb, 0, 0, Memory::GetPointer(addr), fmt, vfb->fb_stride, vfb->width, vfb->height, RASTER_COLOR, "UpdateFromMemory_DrawPixels"); SetColorUpdated(vfb, gstate_c.skipDrawReason); } else { INFO_LOG(FRAMEBUF, "Invalidating FBO for %08x (%dx%d %s)", vfb->fb_address, vfb->width, vfb->height, GeBufferFormatToString(vfb->fb_format)); @@ -1033,20 +1086,24 @@ void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size) { gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); } -void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height) { +void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, RasterChannel channel, const char *tag) { textureCache_->ForgetLastTexture(); - shaderManager_->DirtyLastShader(); // On GL, important that this is BEFORE drawing + shaderManager_->DirtyLastShader(); float u0 = 0.0f, u1 = 1.0f; float v0 = 0.0f, v1 = 1.0f; DrawTextureFlags flags; if (useBufferedRendering_ && vfb && vfb->fbo) { - flags = DRAWTEX_LINEAR; - draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "DrawPixels"); - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE); + if (channel == RASTER_DEPTH || PSP_CoreParameter().compat.flags().NearestFilteringOnFramebufferCreate) { + flags = DRAWTEX_NEAREST; + } else { + flags = DRAWTEX_LINEAR; + } + draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, tag); SetViewport2D(0, 0, vfb->renderWidth, vfb->renderHeight); draw_->SetScissorRect(0, 0, vfb->renderWidth, vfb->renderHeight); } else { + _dbg_assert_(channel == RASTER_COLOR); // We are drawing directly to the back buffer so need to flip. // Should more of this be handled by the presentation engine? if (needBackBufferYSwap_) @@ -1060,15 +1117,23 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_); } + if (channel == RASTER_DEPTH) { + _dbg_assert_(srcPixelFormat == GE_FORMAT_DEPTH16); + flags = flags | DRAWTEX_DEPTH; + } + Draw::Texture *pixelsTex = MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height); if (pixelsTex) { draw_->BindTextures(0, 1, &pixelsTex); + + // TODO: Replace with draw2D_.Blit() directly. DrawActiveTexture(dstX, dstY, width, height, vfb->bufferWidth, vfb->bufferHeight, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags); + gpuStats.numUploads++; pixelsTex->Release(); draw_->InvalidateCachedState(); - gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); + gstate_c.Dirty(DIRTY_ALL_RENDER_STATE); } } @@ -1153,6 +1218,7 @@ Draw::Texture *FramebufferManagerCommon::MakePixelTexture(const u8 *srcPixels, G const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y; const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y; u32 *dst = (u32 *)(data + byteStride * y); + u16 *dst16 = (u16 *)(data + byteStride * y); switch (srcPixelFormat) { case GE_FORMAT_565: if (preferredPixelsFormat_ == Draw::DataFormat::B8G8R8A8_UNORM) @@ -1185,18 +1251,28 @@ Draw::Texture *FramebufferManagerCommon::MakePixelTexture(const u8 *srcPixels, G memcpy(dst, src32, width * 4); break; - case GE_FORMAT_INVALID: case GE_FORMAT_DEPTH16: - _dbg_assert_msg_(false, "Invalid pixelFormat passed to DrawPixels()."); + // TODO: Must take the depth range into account, unless it's already 0-1. + // TODO: Depending on the color buffer format used with this depth buffer, we need + // to do one of two different swizzle operations. However, for the only use of this so far, + // the Burnout lens flare trickery, swizzle doesn't matter since it's just a 0, 7fff, 0, 7fff pattern + // which comes out the same. + memcpy(dst16, src16, w * 2); + break; + + case GE_FORMAT_INVALID: + // Bad break; } } return true; }; + // Note: For depth, we create an R16_UNORM texture, that'll be just fine for uploading depth through a shader, + // and likely more efficient. Draw::TextureDesc desc{ Draw::TextureType::LINEAR2D, - preferredPixelsFormat_, + srcPixelFormat == GE_FORMAT_DEPTH16 ? Draw::DataFormat::R16_UNORM : preferredPixelsFormat_, width, height, 1, @@ -1206,11 +1282,12 @@ Draw::Texture *FramebufferManagerCommon::MakePixelTexture(const u8 *srcPixels, G { (uint8_t *)srcPixels }, generateTexture, }; + // Hot Shots Golf (#12355) does tons of these in a frame in some situations! So creating textures // better be fast. Draw::Texture *tex = draw_->CreateTexture(desc); if (!tex) - ERROR_LOG(G3D, "Failed to create drawpixels texture"); + ERROR_LOG(G3D, "Failed to create DrawPixels texture"); return tex; } @@ -1229,7 +1306,7 @@ void FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, int if (needBackBufferYSwap_) { flags |= OutputFlags::BACKBUFFER_FLIPPED; } - // DrawActiveTexture reverses these, probably to match "up". + // CopyToOutput reverses these, probably to match "up". if (GetGPUBackend() == GPUBackend::DIRECT3D9 || GetGPUBackend() == GPUBackend::DIRECT3D11) { flags |= OutputFlags::POSITION_FLIPPED; } @@ -1293,10 +1370,16 @@ void FramebufferManagerCommon::CopyDisplayToOutput(bool reallyDirty) { // Let's search for a framebuf within this range. Note that we also look for // "framebuffers" sitting in RAM (created from block transfer or similar) so we only take off the kernel // and uncached bits of the address when comparing. - const u32 addr = fbaddr & 0x3FFFFFFF; + const u32 addr = fbaddr; for (auto v : vfbs_) { - const u32 v_addr = v->fb_address & 0x3FFFFFFF; + const u32 v_addr = v->fb_address; const u32 v_size = ColorBufferByteSize(v); + + if (v->fb_format != displayFormat_ || v->fb_stride != displayStride_) { + // Displaying a buffer of the wrong format or stride is nonsense, ignore it. + continue; + } + if (addr >= v_addr && addr < v_addr + v_size) { const u32 dstBpp = BufferFormatBytesPerPixel(v->fb_format); const u32 v_offsetX = ((addr - v_addr) / dstBpp) % v->fb_stride; @@ -1442,10 +1525,6 @@ void FramebufferManagerCommon::DecimateFBOs() { } } -static size_t FormatFramebufferName(VirtualFramebuffer *vfb, char *tag, size_t len) { - return snprintf(tag, len, "FB_%08x_%08x_%dx%d_%s", vfb->fb_address, vfb->z_address, vfb->bufferWidth, vfb->bufferHeight, GeBufferFormatToString(vfb->fb_format)); -} - // Requires width/height to be set already. void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, int h, bool force, bool skipCopy) { _dbg_assert_(w > 0); @@ -1497,9 +1576,9 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, bool creating = old.bufferWidth == 0; if (creating) { - WARN_LOG(FRAMEBUF, "Creating %s FBO at %08x/%d %dx%d (force=%d)", GeBufferFormatToString(vfb->fb_format), vfb->fb_address, vfb->fb_stride, vfb->bufferWidth, vfb->bufferHeight, (int)force); + WARN_LOG(FRAMEBUF, "Creating %s FBO at %08x/%08x stride=%d %dx%d (force=%d)", GeBufferFormatToString(vfb->fb_format), vfb->fb_address, vfb->z_address, vfb->fb_stride, vfb->bufferWidth, vfb->bufferHeight, (int)force); } else { - WARN_LOG(FRAMEBUF, "Resizing %s FBO at %08x/%d from %dx%d to %dx%d (force=%d)", GeBufferFormatToString(vfb->fb_format), vfb->fb_address, vfb->fb_stride, old.bufferWidth, old.bufferHeight, vfb->bufferWidth, vfb->bufferHeight, (int)force); + WARN_LOG(FRAMEBUF, "Resizing %s FBO at %08x/%08x stride=%d from %dx%d to %dx%d (force=%d, skipCopy=%d)", GeBufferFormatToString(vfb->fb_format), vfb->fb_address, vfb->z_address, vfb->fb_stride, old.bufferWidth, old.bufferHeight, vfb->bufferWidth, vfb->bufferHeight, (int)force, (int)skipCopy); } // During hardware rendering, we always render at full color depth even if the game wouldn't on real hardware. @@ -1537,8 +1616,10 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, if (vfb->fbo) { draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "ResizeFramebufFBO"); if (!skipCopy) { - BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0, RASTER_COLOR, "Blit_ResizeFramebufFBO"); - // Depth copying is handled by deferred copies later. + BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0, RASTER_COLOR, "BlitColor_ResizeFramebufFBO"); + } + if (vfb->usageFlags & FB_USAGE_RENDER_DEPTH) { + BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0, RASTER_DEPTH, "BlitDepth_ResizeFramebufFBO"); } } fbosToDelete_.push_back(old.fbo); @@ -1556,18 +1637,27 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, // This is called from detected memcopies and framebuffer initialization from VRAM. Not block transfers. // MotoGP goes this path so we need to catch those copies here. -bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, bool isMemset, u32 skipDrawReason) { +bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, GPUCopyFlag flags, u32 skipDrawReason) { if (size == 0) { return false; } dst &= 0x3FFFFFFF; src &= 0x3FFFFFFF; + if (Memory::IsVRAMAddress(dst)) + dst &= 0x041FFFFF; + if (Memory::IsVRAMAddress(src)) + src &= 0x041FFFFF; - // TODO: Merge the below into FindTransferFramebuffer + // TODO: Merge the below into FindTransferFramebuffer. + // Or at least this should be like the other ones, gathering possible candidates + // with the ability to list them out for debugging. + + VirtualFramebuffer *dstBuffer = nullptr; + VirtualFramebuffer *srcBuffer = nullptr; + bool ignoreDstBuffer = flags & GPUCopyFlag::FORCE_DST_MEM; + bool ignoreSrcBuffer = flags & (GPUCopyFlag::FORCE_SRC_MEM | GPUCopyFlag::MEMSET); - VirtualFramebuffer *dstBuffer = 0; - VirtualFramebuffer *srcBuffer = 0; u32 dstY = (u32)-1; u32 dstH = 0; u32 srcY = (u32)-1; @@ -1578,13 +1668,21 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, } // We only remove the kernel and uncached bits when comparing. - const u32 vfb_address = vfb->fb_address & 0x3FFFFFFF; + const u32 vfb_address = vfb->fb_address; const u32 vfb_size = ColorBufferByteSize(vfb); const u32 vfb_bpp = BufferFormatBytesPerPixel(vfb->fb_format); const u32 vfb_byteStride = vfb->fb_stride * vfb_bpp; const int vfb_byteWidth = vfb->width * vfb_bpp; - if (dst >= vfb_address && (dst + size <= vfb_address + vfb_size || dst == vfb_address)) { + // Heuristic to try to prevent potential glitches with video playback. + if (!ignoreDstBuffer && vfb_address == dst && (size == 0x44000 && vfb_size == 0x88000 || size == 0x88000 && vfb_size == 0x44000)) { + // Not likely to be a correct color format copy for this buffer. Ignore it, there will either be RAM + // that can be displayed from, or another matching buffer with the right format if rendering is going on. + WARN_LOG_N_TIMES(notify_copy_2x, 5, G3D, "Framebuffer size %08x conspicuously not matching copy size %08x in NotifyFramebufferCopy. Ignoring.", size, vfb_size); + continue; + } + + if (!ignoreDstBuffer && dst >= vfb_address && (dst + size <= vfb_address + vfb_size || dst == vfb_address)) { const u32 offset = dst - vfb_address; const u32 yOffset = offset / vfb_byteStride; if ((offset % vfb_byteStride) == 0 && (size == vfb_byteWidth || (size % vfb_byteStride) == 0) && yOffset < dstY) { @@ -1594,7 +1692,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, } } - if (src >= vfb_address && (src + size <= vfb_address + vfb_size || src == vfb_address)) { + if (!ignoreSrcBuffer && src >= vfb_address && (src + size <= vfb_address + vfb_size || src == vfb_address)) { const u32 offset = src - vfb_address; const u32 yOffset = offset / vfb_byteStride; if ((offset % vfb_byteStride) == 0 && (size == vfb_byteWidth || (size % vfb_byteStride) == 0) && yOffset < srcY) { @@ -1636,7 +1734,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, dstBuffer->last_frame_used = gpuStats.numFlips; } - if (dstBuffer && srcBuffer && !isMemset) { + if (dstBuffer && srcBuffer) { if (srcBuffer == dstBuffer) { WARN_LOG_ONCE(dstsrccpy, G3D, "Intra-buffer memcpy (not supported) %08x -> %08x (size: %x)", src, dst, size); } else { @@ -1648,13 +1746,13 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, } return false; } else if (dstBuffer) { - if (isMemset) { + if (flags & GPUCopyFlag::MEMSET) { gpuStats.numClears++; } WARN_LOG_ONCE(btucpy, G3D, "Memcpy fbo upload %08x -> %08x (size: %x)", src, dst, size); FlushBeforeCopy(); const u8 *srcBase = Memory::GetPointerUnchecked(src); - DrawPixels(dstBuffer, 0, dstY, srcBase, dstBuffer->fb_format, dstBuffer->fb_stride, dstBuffer->width, dstH); + DrawPixels(dstBuffer, 0, dstY, srcBase, dstBuffer->fb_format, dstBuffer->fb_stride, dstBuffer->width, dstH, RASTER_COLOR, "MemcpyFboUpload_DrawPixels"); SetColorUpdated(dstBuffer, skipDrawReason); RebindFramebuffer("RebindFramebuffer - Memcpy fbo upload"); // This is a memcpy, let's still copy just in case. @@ -1683,6 +1781,8 @@ std::string BlockTransferRect::ToString() const { // for depth data yet. bool FramebufferManagerCommon::FindTransferFramebuffer(u32 basePtr, int stride_pixels, int x_pixels, int y, int w_pixels, int h, int bpp, bool destination, BlockTransferRect *rect) { basePtr &= 0x3FFFFFFF; + if (Memory::IsVRAMAddress(basePtr)) + basePtr &= 0x041FFFFF; rect->vfb = nullptr; if (!stride_pixels) { @@ -1700,8 +1800,17 @@ bool FramebufferManagerCommon::FindTransferFramebuffer(u32 basePtr, int stride_p // that of their buffers. Then after matching we try to map the copy to the simplest operation that does // what we need. + // We are only looking at color for now, have not found any block transfers of depth data (although it's plausible). + for (auto vfb : vfbs_) { - const u32 vfb_address = vfb->fb_address & 0x3FFFFFFF; + // Check for easily detected depth copies for logging purposes. + // Depth copies are not that useful though because you manually need to account for swizzle, so + // not sure if games will use them. + if (vfb->z_address == basePtr) { + WARN_LOG_N_TIMES(z_xfer, 5, G3D, "FindTransferFramebuffer: found matching depth buffer, %08x (dest=%d, bpp=%d)", basePtr, (int)destination, bpp); + } + + const u32 vfb_address = vfb->fb_address; const u32 vfb_size = ColorBufferByteSize(vfb); if (basePtr < vfb_address || basePtr >= vfb_address + vfb_size) { @@ -1773,7 +1882,14 @@ bool FramebufferManagerCommon::FindTransferFramebuffer(u32 basePtr, int stride_p // Sort candidates by just recency for now, we might add other. for (size_t i = 0; i < candidates.size(); i++) { const BlockTransferRect *candidate = &candidates[i]; - if (!best || candidate->vfb->colorBindSeq > best->vfb->colorBindSeq) { + + bool better = !best || candidate->vfb->colorBindSeq > best->vfb->colorBindSeq; + if ((candidate->vfb->usageFlags & FB_USAGE_CLUT) && candidate->x_bytes == 0 && candidate->y == 0 && destination) { + // Hack to prioritize copies to clut buffers. + best = candidate; + break; + } + if (better) { best = candidate; } } @@ -1807,7 +1923,8 @@ VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAd // create a new one each frame. VirtualFramebuffer *vfb = new VirtualFramebuffer{}; vfb->fbo = nullptr; - vfb->fb_address = fbAddress; // NOTE - not necessarily in VRAM! + uint32_t mask = Memory::IsVRAMAddress(fbAddress) ? 0x041FFFFF : 0x3FFFFFFF; + vfb->fb_address = fbAddress & mask; // NOTE - not necessarily in VRAM! vfb->fb_stride = stride; vfb->z_address = 0; // marks that if anyone tries to render to this framebuffer, it should be dropped and recreated. vfb->z_stride = 0; @@ -1883,6 +2000,7 @@ VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFram nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, channel == RASTER_DEPTH ? true : false, name }); if (!nvfb->fbo) { ERROR_LOG(FRAMEBUF, "Error creating FBO! %d x %d", nvfb->renderWidth, nvfb->renderHeight); + delete nvfb; return nullptr; } bvfbs_.push_back(nvfb); @@ -2125,7 +2243,7 @@ void FramebufferManagerCommon::NotifyBlockTransferAfter(u32 dstBasePtr, int dstS // We may still do a partial block draw below if this doesn't pass. if (!useBufferedRendering_ && dstStride >= 480 && width >= 480 && height == 272) { bool isPrevDisplayBuffer = PrevDisplayFramebufAddr() == dstBasePtr; - bool isDisplayBuffer = DisplayFramebufAddr() == dstBasePtr; + bool isDisplayBuffer = CurrentDisplayFramebufAddr() == dstBasePtr; if (isPrevDisplayBuffer || isDisplayBuffer) { FlushBeforeCopy(); DrawFramebufferToOutput(Memory::GetPointerUnchecked(dstBasePtr), dstStride, displayFormat_); @@ -2150,14 +2268,16 @@ void FramebufferManagerCommon::NotifyBlockTransferAfter(u32 dstBasePtr, int dstS } if (dstBuffer && !srcBuffer) { - WARN_LOG_ONCE(btu, G3D, "Block transfer upload %08x -> %08x", srcBasePtr, dstBasePtr); + WARN_LOG_ONCE(btu, G3D, "Block transfer upload %08x -> %08x (%dx%d %d,%d bpp=%d)", srcBasePtr, dstBasePtr, width, height, dstX, dstY, bpp); FlushBeforeCopy(); const u8 *srcBase = Memory::GetPointerUnchecked(srcBasePtr) + (srcX + srcY * srcStride) * bpp; + int dstBpp = BufferFormatBytesPerPixel(dstRect.vfb->fb_format); float dstXFactor = (float)bpp / dstBpp; if (dstRect.w_bytes / bpp > dstRect.vfb->width || dstRect.h > dstRect.vfb->height) { - // The buffer isn't big enough, and we have a clear hint of size. Resize. + // The buffer isn't big enough, and we have a clear hint of size. Resize. // This happens in Valkyrie Profile when uploading video at the ending. + // Also happens to the CLUT framebuffer in the Burnout Dominator lens flare effect. See #16075 ResizeFramebufFBO(dstRect.vfb, dstRect.w_bytes / bpp, dstRect.h, false, true); // Make sure we don't flop back and forth. dstRect.vfb->newWidth = std::max(dstRect.w_bytes / bpp, (int)dstRect.vfb->width); @@ -2166,7 +2286,7 @@ void FramebufferManagerCommon::NotifyBlockTransferAfter(u32 dstBasePtr, int dstS // Resizing may change the viewport/etc. gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE); } - DrawPixels(dstRect.vfb, static_cast(dstX * dstXFactor), dstY, srcBase, dstRect.vfb->fb_format, static_cast(srcStride * dstXFactor), static_cast(dstRect.w_bytes / bpp * dstXFactor), dstRect.h); + DrawPixels(dstRect.vfb, static_cast(dstX * dstXFactor), dstY, srcBase, dstRect.vfb->fb_format, static_cast(srcStride * dstXFactor), static_cast(dstRect.w_bytes / bpp * dstXFactor), dstRect.h, RASTER_COLOR, "BlockTransferCopy_DrawPixels"); SetColorUpdated(dstRect.vfb, skipDrawReason); RebindFramebuffer("RebindFramebuffer - NotifyBlockTransferAfter"); } @@ -2299,8 +2419,8 @@ void FramebufferManagerCommon::ShowScreenResolution() { // * Save state screenshots(could probably be async but need to manage the stall.) bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxScaleFactor) { VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); + if (!vfb || vfb->fb_address != fb_address) { + vfb = ResolveVFB(fb_address, fb_stride, format); } if (!vfb) { @@ -2450,7 +2570,7 @@ void FramebufferManagerCommon::PackFramebufferSync(VirtualFramebuffer *vfb, int return; } - const u32 fb_address = vfb->fb_address & 0x3FFFFFFF; + const u32 fb_address = vfb->fb_address; Draw::DataFormat destFormat = channel == RASTER_COLOR ? GEFormatToThin3D(vfb->fb_format) : GEFormatToThin3D(GE_FORMAT_DEPTH16); const int dstBpp = (int)DataFormatSizeInBytes(destFormat); @@ -2643,6 +2763,7 @@ void FramebufferManagerCommon::DeviceLost() { DoRelease(stencilUploadSampler_); DoRelease(stencilUploadPipeline_); DoRelease(draw2DPipelineColor_); + DoRelease(draw2DPipelineColorRect2Lin_); DoRelease(draw2DPipelineDepth_); DoRelease(draw2DPipeline565ToDepth_); DoRelease(draw2DPipeline565ToDepthDeswizzle_); @@ -2706,9 +2827,9 @@ void FramebufferManagerCommon::DrawActiveTexture(float x, float y, float w, floa // Rearrange to strip form. std::swap(coord[2], coord[3]); - draw2D_.DrawStrip2D(nullptr, coord, 4, (flags & DRAWTEX_LINEAR) != 0, Get2DPipeline(DRAW2D_COPY_COLOR)); + draw2D_.DrawStrip2D(nullptr, coord, 4, (flags & DRAWTEX_LINEAR) != 0, Get2DPipeline((flags & DRAWTEX_DEPTH) ? DRAW2D_COPY_DEPTH : DRAW2D_COPY_COLOR)); - gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); + gstate_c.Dirty(DIRTY_ALL_RENDER_STATE); } void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, RasterChannel channel, const char *tag) { @@ -2721,6 +2842,11 @@ void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX return; } + if (channel == RASTER_DEPTH && !draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported) { + // Can't do anything :( + return; + } + // Perform a little bit of clipping first. // Block transfer coords are unsigned so I don't think we need to clip on the left side.. Although there are // other uses for BlitFramebuffer. @@ -2812,7 +2938,7 @@ void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX draw_->InvalidateCachedState(); - gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); + gstate_c.Dirty(DIRTY_ALL_RENDER_STATE); } // The input is raw pixel coordinates, scale not taken into account. @@ -2848,7 +2974,7 @@ void FramebufferManagerCommon::BlitUsingRaster( draw2D_.Blit(pipeline, srcX1, srcY1, srcX2, srcY2, destX1, destY1, destX2, destY2, (float)srcW, (float)srcH, (float)destW, (float)destH, linearFilter, scaleFactor); - gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); + gstate_c.Dirty(DIRTY_ALL_RENDER_STATE); } VirtualFramebuffer *FramebufferManagerCommon::ResolveFramebufferColorToFormat(VirtualFramebuffer *src, GEBufferFormat newFormat) { @@ -2926,6 +3052,10 @@ static void ApplyKillzoneFramebufferSplit(FramebufferHeuristicParams *params, in margin = true; } } + + // TODO: Implement this for Burnout Dominator. It has to handle self-reads inside + // the margin framebuffer though, so framebuffer copies are still needed, just smaller. + // It uses 0x0080019f (through, float texcoords, ABGR 8888 colors, float positions). } if (margin) { diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 905996979b..b3bd21d7c9 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -192,6 +192,7 @@ enum DrawTextureFlags { DRAWTEX_NEAREST = 0, DRAWTEX_LINEAR = 1, DRAWTEX_TO_BACKBUFFER = 8, + DRAWTEX_DEPTH = 16, }; inline DrawTextureFlags operator | (const DrawTextureFlags &lhs, const DrawTextureFlags &rhs) { @@ -305,7 +306,7 @@ public: void CopyDisplayToOutput(bool reallyDirty); - bool NotifyFramebufferCopy(u32 src, u32 dest, int size, bool isMemset, u32 skipDrawReason); + bool NotifyFramebufferCopy(u32 src, u32 dest, int size, GPUCopyFlag flags, u32 skipDrawReason); void NotifyVideoUpload(u32 addr, int size, int width, GEBufferFormat fmt); void UpdateFromMemory(u32 addr, int size); void ApplyClearToMemory(int x1, int y1, int x2, int y2, u32 clearColor); @@ -327,22 +328,25 @@ public: void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes); void DrawFramebufferToOutput(const u8 *srcPixels, int srcStride, GEBufferFormat srcPixelFormat); - void DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height); + void DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, RasterChannel channel, const char *tag); size_t NumVFBs() const { return vfbs_.size(); } u32 PrevDisplayFramebufAddr() const { return prevDisplayFramebuf_ ? prevDisplayFramebuf_->fb_address : 0; } - u32 DisplayFramebufAddr() const { + u32 CurrentDisplayFramebufAddr() const { return displayFramebuf_ ? displayFramebuf_->fb_address : 0; } + u32 DisplayFramebufAddr() const { + return displayFramebufPtr_; + } u32 DisplayFramebufStride() const { - return displayFramebuf_ ? displayStride_ : 0; + return displayStride_; } GEBufferFormat DisplayFramebufFormat() const { - return displayFramebuf_ ? displayFormat_ : GE_FORMAT_INVALID; + return displayFormat_; } bool UseBufferedRendering() const { @@ -351,7 +355,9 @@ public: bool MayIntersectFramebuffer(u32 start) const { // Clear the cache/kernel bits. - start = start & 0x3FFFFFFF; + start &= 0x3FFFFFFF; + if (Memory::IsVRAMAddress(start)) + start &= 0x041FFFFF; // Most games only have two framebuffers at the start. if (start >= framebufRangeEnd_ || start < PSP_GetVidMemBase()) { return false; @@ -422,8 +428,18 @@ public: VirtualFramebuffer *ResolveFramebufferColorToFormat(VirtualFramebuffer *vfb, GEBufferFormat newFormat); Draw2DPipeline *Get2DPipeline(Draw2DShader shader); + + // If from==to, returns a copy pipeline. Draw2DPipeline *GetReinterpretPipeline(GEBufferFormat from, GEBufferFormat to, float *scaleFactorX); + // Public to be used from the texture cache's depal shenanigans. + void BlitUsingRaster( + Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2, + Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2, + bool linearFilter, + int scaleFactor, // usually unused, except for swizzle... + Draw2DPipeline *pipeline, const char *tag); + protected: virtual void PackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel); void SetViewport2D(int x, int y, int w, int h); @@ -441,13 +457,6 @@ protected: // Used by ReadFramebufferToMemory and later framebuffer block copies void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, RasterChannel channel, const char *tag); - void BlitUsingRaster( - Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2, - Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2, - bool linearFilter, - int scaleFactor, // usually unused, except for swizzle... - Draw2DPipeline *pipeline, const char *tag); - void CopyFramebufferForColorTexture(VirtualFramebuffer *dst, VirtualFramebuffer *src, int flags); void EstimateDrawingSize(u32 fb_address, int fb_stride, GEBufferFormat fb_format, int viewport_width, int viewport_height, int region_width, int region_height, int scissor_width, int scissor_height, int &drawing_width, int &drawing_height); @@ -562,6 +571,7 @@ protected: // Draw2D pipelines Draw2DPipeline *draw2DPipelineColor_ = nullptr; + Draw2DPipeline *draw2DPipelineColorRect2Lin_ = nullptr; Draw2DPipeline *draw2DPipelineDepth_ = nullptr; Draw2DPipeline *draw2DPipeline565ToDepth_ = nullptr; Draw2DPipeline *draw2DPipeline565ToDepthDeswizzle_ = nullptr; diff --git a/GPU/Common/GPUDebugInterface.h b/GPU/Common/GPUDebugInterface.h index 09254ddf53..f584dfd576 100644 --- a/GPU/Common/GPUDebugInterface.h +++ b/GPU/Common/GPUDebugInterface.h @@ -213,6 +213,9 @@ public: virtual void SetCmdValue(u32 op) = 0; virtual void DispatchFlush() = 0; + virtual uint32_t SetAddrTranslation(uint32_t value) = 0; + virtual uint32_t GetAddrTranslation() = 0; + virtual bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) { return false; } diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index fb90850cb9..2b8bb84dc1 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -231,6 +231,7 @@ StencilValueType ReplaceAlphaWithStencilType() { case GE_FORMAT_8888: case GE_FORMAT_INVALID: case GE_FORMAT_DEPTH16: + case GE_FORMAT_CLUT8: switch (gstate.getStencilOpZPass()) { case GE_STENCILOP_REPLACE: // TODO: Could detect zero here and force ZERO - less uniform updates? @@ -859,66 +860,63 @@ static inline bool blendColorSimilar(uint32_t a, uint32_t b, int margin = 25) { static bool SimulateLogicOpIfNeeded(BlendFactor &srcBlend, BlendFactor &dstBlend, BlendEq &blendEq) { // Note: our shader solution applies logic ops BEFORE blending, not correctly after. // This is however fine for the most common ones, like CLEAR/NOOP/SET, etc. - if (!gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) { - if (gstate.isLogicOpEnabled()) { - switch (gstate.getLogicOp()) { - case GE_LOGIC_CLEAR: - srcBlend = BlendFactor::ZERO; - dstBlend = BlendFactor::ZERO; - blendEq = BlendEq::ADD; - return true; - case GE_LOGIC_AND: - case GE_LOGIC_AND_REVERSE: - WARN_LOG_REPORT_ONCE(d3dLogicOpAnd, G3D, "Unsupported AND logic op: %x", gstate.getLogicOp()); - break; - case GE_LOGIC_COPY: - // This is the same as off. - break; - case GE_LOGIC_COPY_INVERTED: - // Handled in the shader. - break; - case GE_LOGIC_AND_INVERTED: - case GE_LOGIC_NOR: - case GE_LOGIC_NAND: - case GE_LOGIC_EQUIV: - // Handled in the shader. - WARN_LOG_REPORT_ONCE(d3dLogicOpAndInverted, G3D, "Attempted invert for logic op: %x", gstate.getLogicOp()); - break; - case GE_LOGIC_INVERTED: - srcBlend = BlendFactor::ONE; - dstBlend = BlendFactor::ONE; - blendEq = BlendEq::SUBTRACT; - WARN_LOG_REPORT_ONCE(d3dLogicOpInverted, G3D, "Attempted inverse for logic op: %x", gstate.getLogicOp()); - return true; - case GE_LOGIC_NOOP: - srcBlend = BlendFactor::ZERO; - dstBlend = BlendFactor::ONE; - blendEq = BlendEq::ADD; - return true; - case GE_LOGIC_XOR: - WARN_LOG_REPORT_ONCE(d3dLogicOpOrXor, G3D, "Unsupported XOR logic op: %x", gstate.getLogicOp()); - break; - case GE_LOGIC_OR: - case GE_LOGIC_OR_INVERTED: - // Inverted in shader. - srcBlend = BlendFactor::ONE; - dstBlend = BlendFactor::ONE; - blendEq = BlendEq::ADD; - WARN_LOG_REPORT_ONCE(d3dLogicOpOr, G3D, "Attempted or for logic op: %x", gstate.getLogicOp()); - return true; - case GE_LOGIC_OR_REVERSE: - WARN_LOG_REPORT_ONCE(d3dLogicOpOrReverse, G3D, "Unsupported OR REVERSE logic op: %x", gstate.getLogicOp()); - break; - case GE_LOGIC_SET: - srcBlend = BlendFactor::ONE; - dstBlend = BlendFactor::ONE; - blendEq = BlendEq::ADD; - WARN_LOG_REPORT_ONCE(d3dLogicOpSet, G3D, "Attempted set for logic op: %x", gstate.getLogicOp()); - return true; - } + if (!gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP) && gstate.isLogicOpEnabled()) { + switch (gstate.getLogicOp()) { + case GE_LOGIC_CLEAR: + srcBlend = BlendFactor::ZERO; + dstBlend = BlendFactor::ZERO; + blendEq = BlendEq::ADD; + return true; + case GE_LOGIC_AND: + case GE_LOGIC_AND_REVERSE: + WARN_LOG_REPORT_ONCE(d3dLogicOpAnd, G3D, "Unsupported AND logic op: %x", gstate.getLogicOp()); + break; + case GE_LOGIC_COPY: + // This is the same as off. + break; + case GE_LOGIC_COPY_INVERTED: + // Handled in the shader. + break; + case GE_LOGIC_AND_INVERTED: + case GE_LOGIC_NOR: + case GE_LOGIC_NAND: + case GE_LOGIC_EQUIV: + // Handled in the shader. + WARN_LOG_REPORT_ONCE(d3dLogicOpAndInverted, G3D, "Attempted invert for logic op: %x", gstate.getLogicOp()); + break; + case GE_LOGIC_INVERTED: + srcBlend = BlendFactor::ONE; + dstBlend = BlendFactor::ONE; + blendEq = BlendEq::SUBTRACT; + WARN_LOG_REPORT_ONCE(d3dLogicOpInverted, G3D, "Attempted inverse for logic op: %x", gstate.getLogicOp()); + return true; + case GE_LOGIC_NOOP: + srcBlend = BlendFactor::ZERO; + dstBlend = BlendFactor::ONE; + blendEq = BlendEq::ADD; + return true; + case GE_LOGIC_XOR: + WARN_LOG_REPORT_ONCE(d3dLogicOpOrXor, G3D, "Unsupported XOR logic op: %x", gstate.getLogicOp()); + break; + case GE_LOGIC_OR: + case GE_LOGIC_OR_INVERTED: + // Inverted in shader. + srcBlend = BlendFactor::ONE; + dstBlend = BlendFactor::ONE; + blendEq = BlendEq::ADD; + WARN_LOG_REPORT_ONCE(d3dLogicOpOr, G3D, "Attempted or for logic op: %x", gstate.getLogicOp()); + return true; + case GE_LOGIC_OR_REVERSE: + WARN_LOG_REPORT_ONCE(d3dLogicOpOrReverse, G3D, "Unsupported OR REVERSE logic op: %x", gstate.getLogicOp()); + break; + case GE_LOGIC_SET: + srcBlend = BlendFactor::ONE; + dstBlend = BlendFactor::ONE; + blendEq = BlendEq::ADD; + WARN_LOG_REPORT_ONCE(d3dLogicOpSet, G3D, "Attempted set for logic op: %x", gstate.getLogicOp()); + return true; } } - return false; } @@ -1080,6 +1078,12 @@ static void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBl case REPLACE_BLEND_NO: // We may still want to do something about stencil -> alpha. ApplyStencilReplaceAndLogicOpIgnoreBlend(replaceAlphaWithStencil, blendState); + + if (forceReplaceBlend) { + // If this is true, the logic and mask replacements will be applied, at least. In that case, + // we should not apply any logic op simulation. + blendState.simulateLogicOpType = LOGICOPTYPE_NORMAL; + } return; case REPLACE_BLEND_BLUE_TO_ALPHA: diff --git a/GPU/Common/GeometryShaderGenerator.cpp b/GPU/Common/GeometryShaderGenerator.cpp new file mode 100644 index 0000000000..a814a345da --- /dev/null +++ b/GPU/Common/GeometryShaderGenerator.cpp @@ -0,0 +1,310 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include +#include +#include + +#include "Common/StringUtils.h" +#include "Common/GPU/OpenGL/GLFeatures.h" +#include "Common/GPU/ShaderWriter.h" +#include "Common/GPU/thin3d.h" +#include "Core/Config.h" +#include "GPU/ge_constants.h" +#include "GPU/GPUState.h" +#include "GPU/Common/ShaderId.h" +#include "GPU/Common/ShaderUniforms.h" +#include "GPU/Common/GeometryShaderGenerator.h" + +#undef WRITE + +#define WRITE(p, ...) p.F(__VA_ARGS__) + +// TODO: Could support VK_NV_geometry_shader_passthrough, though the hardware that supports +// it is already pretty fast at geometry shaders.. + + +bool GenerateGeometryShader(const GShaderID &id, char *buffer, const ShaderLanguageDesc &compat, const Draw::Bugs bugs, std::string *errorString) { + std::vector gl_exts; + if (ShaderLanguageIsOpenGL(compat.shaderLanguage)) { + if (gl_extensions.EXT_gpu_shader4) { + gl_exts.push_back("#extension GL_EXT_gpu_shader4 : enable"); + } + } + bool vertexRangeCulling = !id.Bit(GS_BIT_CURVE); + bool clipClampedDepth = gstate_c.Supports(GPU_SUPPORTS_DEPTH_CLAMP); + + ShaderWriter p(buffer, compat, ShaderStage::Geometry, gl_exts.data(), gl_exts.size()); + p.C("layout(triangles) in;\n"); + if (clipClampedDepth && vertexRangeCulling && !gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) { + p.C("layout(triangle_strip, max_vertices = 12) out;\n"); + } else { + p.C("layout(triangle_strip, max_vertices = 6) out;\n"); + } + + if (compat.shaderLanguage == GLSL_VULKAN) { + WRITE(p, "\n"); + WRITE(p, "layout (std140, set = 0, binding = 3) uniform baseVars {\n%s};\n", ub_baseStr); + } else if (compat.shaderLanguage == HLSL_D3D11) { + WRITE(p, "cbuffer base : register(b0) {\n%s};\n", ub_baseStr); + } + + std::vector varyings, outVaryings; + + if (id.Bit(GS_BIT_DO_TEXTURE)) { + varyings.push_back(VaryingDef{ "vec3", "v_texcoord", Draw::SEM_TEXCOORD0, 0, "highp" }); + outVaryings.push_back(VaryingDef{ "vec3", "v_texcoordOut", Draw::SEM_TEXCOORD0, 0, "highp" }); + } + varyings.push_back(VaryingDef{ "vec4", "v_color0", Draw::SEM_COLOR0, 1, "lowp" }); + outVaryings.push_back(VaryingDef{ "vec4", "v_color0Out", Draw::SEM_COLOR0, 1, "lowp" }); + if (id.Bit(GS_BIT_LMODE)) { + varyings.push_back(VaryingDef{ "vec3", "v_color1", Draw::SEM_COLOR1, 2, "lowp" }); + outVaryings.push_back(VaryingDef{ "vec3", "v_color1Out", Draw::SEM_COLOR1, 2, "lowp" }); + } + varyings.push_back(VaryingDef{ "float", "v_fogdepth", Draw::SEM_TEXCOORD1, 3, "highp" }); + outVaryings.push_back(VaryingDef{ "float", "v_fogdepthOut", Draw::SEM_TEXCOORD1, 3, "highp" }); + + p.BeginGSMain(varyings, outVaryings); + + // Apply culling. + if (vertexRangeCulling) { + p.C(" bool anyInside = false;\n"); + } + // And apply manual clipping if necessary. + if (!gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) { + p.C(" float clip0[3];\n"); + if (clipClampedDepth) { + p.C(" float clip1[3];\n"); + } + } + + p.C(" for (int i = 0; i < 3; i++) {\n"); // TODO: 3 or gl_in.length()? which will be faster? + p.C(" vec4 outPos = gl_in[i].gl_Position;\n"); + p.C(" vec3 projPos = outPos.xyz / outPos.w;\n"); + + if (vertexRangeCulling) { + p.C(" float projZ = (projPos.z - u_depthRange.z) * u_depthRange.w;\n"); + // Vertex range culling doesn't happen when Z clips, note sign of w is important. + p.C(" if (u_cullRangeMin.w <= 0.0 || projZ * outPos.w > -outPos.w) {\n"); + const char *outMin = "projPos.x < u_cullRangeMin.x || projPos.y < u_cullRangeMin.y"; + const char *outMax = "projPos.x > u_cullRangeMax.x || projPos.y > u_cullRangeMax.y"; + p.F(" if ((%s) || (%s)) {\n", outMin, outMax); + p.C(" return;\n"); // Cull! + p.C(" }\n"); + p.C(" }\n"); + p.C(" if (u_cullRangeMin.w <= 0.0) {\n"); + p.C(" if (projPos.z < u_cullRangeMin.z || projPos.z > u_cullRangeMax.z) {\n"); + // When not clamping depth, cull the triangle of Z is outside the valid range (not based on clip Z.) + p.C(" return;\n"); + p.C(" }\n"); + p.C(" } else {\n"); + p.C(" if (projPos.z >= u_cullRangeMin.z) { anyInside = true; }\n"); + p.C(" if (projPos.z <= u_cullRangeMax.z) { anyInside = true; }\n"); + p.C(" }\n"); + } + + if (!gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) { + // This is basically the same value as gl_ClipDistance would take, z + w. + if (vertexRangeCulling) { + p.C(" clip0[i] = projZ * outPos.w + outPos.w;\n"); + } else { + // Let's not complicate the code overly for this case. We'll clipClampedDepth. + p.C(" clip0[i] = 0.0;\n"); + } + + // This one does happen for rectangles. + if (clipClampedDepth) { + if (ShaderLanguageIsOpenGL(compat.shaderLanguage)) { + // On OpenGL/GLES, these values account for the -1 -> 1 range. + p.C(" if (u_depthRange.y - u_depthRange.x >= 1.0) {\n"); + p.C(" clip1[i] = outPos.w + outPos.z;\n"); + } else { + // Everywhere else, it's 0 -> 1, simpler. + p.C(" if (u_depthRange.y >= 1.0) {\n"); + p.C(" clip1[i] = outPos.z;\n"); + } + // This is similar, but for maxz when it's below 65535.0. -1/0 don't matter here. + p.C(" } else if (u_depthRange.x + u_depthRange.y <= 65534.0) {\n"); + p.C(" clip1[i] = outPos.w - outPos.z;\n"); + p.C(" } else {\n"); + p.C(" clip1[i] = 0.0;\n"); + p.C(" }\n"); + } + } + + p.C(" } // for\n"); + + // Cull any triangle fully outside in the same direction when depth clamp enabled. + // Basically simulate cull distances. + if (vertexRangeCulling) { + p.C(" if (u_cullRangeMin.w > 0.0 && !anyInside) {\n"); + p.C(" return;\n"); + p.C(" }\n"); + } + + if (!gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) { + // Clipping against one half-space cuts a triangle (17/27), culls (7/27), or creates two triangles (3/27). + // We clip against two, so we can generate up to 4 triangles, a polygon with 6 points. + p.C(" int indices[6];\n"); + p.C(" float factors[6];\n"); + p.C(" int ind = 0;\n"); + + // Pass 1 - clip against first half-space. + p.C(" for (int i = 0; i < 3; i++) {\n"); + // First, use this vertex if it doesn't need clipping. + p.C(" if (clip0[i] >= 0.0) {\n"); + p.C(" indices[ind] = i;\n"); + p.C(" factors[ind] = 0.0;\n"); + p.C(" ind++;\n"); + p.C(" }\n"); + + // Next, we generate an interpolated vertex if signs differ. + p.C(" int inext = i == 2 ? 0 : i + 1;\n"); + p.C(" if (clip0[i] * clip0[inext] < 0.0) {\n"); + p.C(" float t = clip0[i] < 0.0 ? clip0[i] / (clip0[i] - clip0[inext]) : 1.0 - (clip0[inext] / (clip0[inext] - clip0[i]));\n"); + p.C(" indices[ind] = i;\n"); + p.C(" factors[ind] = t;\n"); + p.C(" ind++;\n"); + p.C(" }\n"); + + p.C(" }\n"); + + // Pass 2 - further clip against clamped Z. + if (clipClampedDepth) { + p.C(" int count0 = ind;\n"); + p.C(" int indices1[6];\n"); + p.C(" float factors1[6];\n"); + p.C(" ind = 0;\n"); + + // Let's start by interpolating the clip values. + p.C(" float clip1after[4];\n"); + p.C(" for (int i = 0; i < count0; i++) {\n"); + p.C(" int idx = indices[i];\n"); + p.C(" float factor = factors[i];\n"); + p.C(" int next = idx == 2 ? 0 : idx + 1;\n"); + p.C(" clip1after[i] = mix(clip1[idx], clip1[next], factor);\n"); + p.C(" }\n"); + + // Alright, now time to clip, again. + p.C(" for (int i = 0; i < count0; i++) {\n"); + // First, use this vertex if it doesn't need clipping. + p.C(" if (clip1after[i] >= 0.0) {\n"); + p.C(" indices1[ind] = i;\n"); + p.C(" factors1[ind] = 0.0;\n"); + p.C(" ind++;\n"); + p.C(" }\n"); + + // Next, we generate an interpolated vertex if signs differ. + p.C(" int inext = i == count0 - 1 ? 0 : i + 1;\n"); + p.C(" if (clip1after[i] * clip1after[inext] < 0.0) {\n"); + p.C(" float t = clip1after[i] < 0.0 ? clip1after[i] / (clip1after[i] - clip1after[inext]) : 1.0 - (clip1after[inext] / (clip1after[inext] - clip1after[i]));\n"); + p.C(" indices1[ind] = i;\n"); + p.C(" factors1[ind] = t;\n"); + p.C(" ind++;\n"); + p.C(" }\n"); + + p.C(" }\n"); + } + + p.C(" if (ind < 3) {\n"); + p.C(" return;\n"); + p.C(" }\n"); + + p.C(" int idx;\n"); + p.C(" int next;\n"); + p.C(" float factor;\n"); + + auto emitIndex = [&](const char *which) { + if (clipClampedDepth) { + // We have to interpolate between four vertices. + p.F(" idx = indices1[%s];\n", which); + p.F(" factor = factors1[%s];\n", which); + p.C(" next = idx == count0 - 1 ? 0 : idx + 1;\n"); + p.C(" gl_Position = mix(mix(gl_in[indices[idx]].gl_Position, gl_in[(indices[idx] + 1) % 3].gl_Position, factors[idx]), mix(gl_in[indices[next]].gl_Position, gl_in[(indices[next] + 1) % 3].gl_Position, factors[next]), factor);\n"); + for (size_t i = 0; i < varyings.size(); i++) { + const VaryingDef &in = varyings[i]; + const VaryingDef &out = outVaryings[i]; + p.F(" %s = mix(mix(%s[indices[idx]], %s[(indices[idx] + 1) % 3], factors[idx]), mix(%s[indices[next]], %s[(indices[next] + 1) % 3], factors[next]), factor);\n", out.name, in.name, in.name, in.name, in.name); + } + } else { + p.F(" idx = indices[%s];\n", which); + p.F(" factor = factors[%s];\n", which); + p.C(" next = idx == 2 ? 0 : idx + 1;\n"); + p.C(" gl_Position = mix(gl_in[idx].gl_Position, gl_in[next].gl_Position, factor);\n"); + for (size_t i = 0; i < varyings.size(); i++) { + const VaryingDef &in = varyings[i]; + const VaryingDef &out = outVaryings[i]; + p.F(" %s = mix(%s[idx], %s[next], factor);\n", out.name, in.name, in.name); + } + } + p.C(" EmitVertex();\n"); + }; + + // Alright, time to actually emit the first triangle. + p.C(" for (int i = 0; i < 3; i++) {\n"); + emitIndex("i"); + p.C(" }\n"); + + // Did we end up with additional triangles? We'll do three points each for the rest. + p.C(" for (int i = 3; i < ind; i++) {\n"); + p.C(" EndPrimitive();\n"); + + // Point one, always index zero. + emitIndex("0"); + + // After that, one less than i (basically a triangle fan.) + emitIndex("(i - 1)"); + + // And the new vertex itself. + emitIndex("i"); + + p.C(" }\n"); + } else { + const char *clipSuffix0 = compat.shaderLanguage == HLSL_D3D11 ? ".x" : "[0]"; + const char *clipSuffix1 = compat.shaderLanguage == HLSL_D3D11 ? ".y" : "[1]"; + + p.C(" for (int i = 0; i < 3; i++) {\n"); // TODO: 3 or gl_in.length()? which will be faster? + p.C(" vec4 outPos = gl_in[i].gl_Position;\n"); + p.C(" vec3 projPos = outPos.xyz / outPos.w;\n"); + p.C(" float projZ = (projPos.z - u_depthRange.z) * u_depthRange.w;\n"); + if (clipClampedDepth) { + // Copy the clip distance from the vertex shader. + p.F(" gl_ClipDistance%s = gl_in[i].gl_ClipDistance%s;\n", clipSuffix0, clipSuffix0); + p.F(" gl_ClipDistance%s = projZ * outPos.w + outPos.w;\n", clipSuffix1); + } else { + // We shouldn't need to worry about rectangles-as-triangles here, since we don't use geometry shaders for that. + p.F(" gl_ClipDistance%s = projZ * outPos.w + outPos.w;\n", clipSuffix0); + } + p.C(" gl_Position = outPos;\n"); + if (gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) { + } + + for (size_t i = 0; i < varyings.size(); i++) { + const VaryingDef &in = varyings[i]; + const VaryingDef &out = outVaryings[i]; + p.F(" %s = %s[i];\n", out.name, in.name); + } + // Debug - null the red channel + //p.C(" if (i == 0) v_color0Out.x = 0.0;\n"); + p.C(" EmitVertex();\n"); + p.C(" }\n"); + } + + p.EndGSMain(); + + return true; +} diff --git a/GPU/Common/GeometryShaderGenerator.h b/GPU/Common/GeometryShaderGenerator.h new file mode 100644 index 0000000000..9848ffde18 --- /dev/null +++ b/GPU/Common/GeometryShaderGenerator.h @@ -0,0 +1,5 @@ +#pragma once + +#include "GPU/Common/ShaderId.h" + +bool GenerateGeometryShader(const GShaderID &id, char *buffer, const ShaderLanguageDesc &compat, const Draw::Bugs bugs, std::string *errorString); diff --git a/GPU/Common/PostShader.cpp b/GPU/Common/PostShader.cpp index ed86397521..2d8529f356 100644 --- a/GPU/Common/PostShader.cpp +++ b/GPU/Common/PostShader.cpp @@ -55,7 +55,7 @@ void LoadPostShaderInfo(Draw::DrawContext *draw, const std::vector &direct off.name = "Off"; off.section = "Off"; for (size_t i = 0; i < ARRAY_SIZE(off.settings); ++i) { - off.settings[i].name = ""; + off.settings[i].name.clear(); off.settings[i].value = 0.0f; off.settings[i].minValue = -1.0f; off.settings[i].maxValue = 1.0f; @@ -174,7 +174,7 @@ void LoadPostShaderInfo(Draw::DrawContext *draw, const std::vector &direct section.Get("UsePreviousFrame", &info.usePreviousFrame, false); if (info.parent == "Off") - info.parent = ""; + info.parent.clear(); for (size_t i = 0; i < ARRAY_SIZE(info.settings); ++i) { auto &setting = info.settings[i]; @@ -187,7 +187,7 @@ void LoadPostShaderInfo(Draw::DrawContext *draw, const std::vector &direct // Populate the default setting value. std::string section = StringFromFormat("%sSettingValue%d", info.section.c_str(), i + 1); if (!setting.name.empty() && g_Config.mPostShaderSetting.find(section) == g_Config.mPostShaderSetting.end()) { - g_Config.mPostShaderSetting.insert(std::pair(section, setting.value)); + g_Config.mPostShaderSetting.emplace(section, setting.value); } } diff --git a/GPU/Common/ShaderCommon.h b/GPU/Common/ShaderCommon.h index 1a69e5c387..63e3116014 100644 --- a/GPU/Common/ShaderCommon.h +++ b/GPU/Common/ShaderCommon.h @@ -88,12 +88,13 @@ enum : uint64_t { DIRTY_COLORWRITEMASK = 1ULL << 36, DIRTY_MIPBIAS = 1ULL << 37, + DIRTY_LIGHT_CONTROL = 1ULL << 38, - // space for 4 more uniform dirty flags. Remember to update DIRTY_ALL_UNIFORMS. + // space for 1 more uniform dirty flags. Remember to update DIRTY_ALL_UNIFORMS. DIRTY_BONE_UNIFORMS = 0xFF000000ULL, - DIRTY_ALL_UNIFORMS = 0x3FFFFFFFFFULL, + DIRTY_ALL_UNIFORMS = 0x7FFFFFFFFFULL, DIRTY_ALL_LIGHTS = DIRTY_LIGHT0 | DIRTY_LIGHT1 | DIRTY_LIGHT2 | DIRTY_LIGHT3, // Other dirty elements that aren't uniforms! @@ -108,6 +109,13 @@ enum : uint64_t { DIRTY_VIEWPORTSCISSOR_STATE = 1ULL << 46, DIRTY_VERTEXSHADER_STATE = 1ULL << 47, DIRTY_FRAGMENTSHADER_STATE = 1ULL << 48, + DIRTY_GEOMETRYSHADER_STATE = 1ULL << 49, + + // Everything that's not uniforms. Use this after using thin3d. + // TODO: Should we also add DIRTY_FRAMEBUF here? It kinda generally takes care of itself. + DIRTY_ALL_RENDER_STATE = DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS, + + // Note that the top 8 bits (54-63) cannot be dirtied through the commonCommandTable due to packing of other flags. DIRTY_ALL = 0xFFFFFFFFFFFFFFFF }; diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index f873024c39..94ade2fd26 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -22,7 +22,6 @@ std::string VertexShaderDesc(const VShaderID &id) { if (id.Bit(VS_BIT_HAS_TEXCOORD)) desc << "T "; if (id.Bit(VS_BIT_HAS_NORMAL)) desc << "N "; if (id.Bit(VS_BIT_LMODE)) desc << "LM "; - if (id.Bit(VS_BIT_ENABLE_FOG)) desc << "Fog "; if (id.Bit(VS_BIT_NORM_REVERSE)) desc << "RevN "; if (id.Bit(VS_BIT_DO_TEXTURE)) desc << "Tex "; int uvgMode = id.Bits(VS_BIT_UVGEN_MODE, 2); @@ -41,6 +40,9 @@ std::string VertexShaderDesc(const VShaderID &id) { if (id.Bit(VS_BIT_LIGHTING_ENABLE)) { desc << "Light: "; } + if (id.Bit(VS_BIT_LIGHT_UBERSHADER)) { + desc << "LightUberShader "; + } for (int i = 0; i < 4; i++) { bool enabled = id.Bit(VS_BIT_LIGHT0_ENABLE + i) && id.Bit(VS_BIT_LIGHTING_ENABLE); if (enabled || (uvgMode == GE_TEXMAP_ENVIRONMENT_MAP && (ls0 == i || ls1 == i))) { @@ -80,14 +82,12 @@ void ComputeVertexShaderID(VShaderID *id_out, u32 vertType, bool useHWTransform, _assert_(hasNormal); } - bool enableFog = gstate.isFogEnabled() && !isModeThrough && !gstate.isModeClear(); bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled() && !isModeThrough && !gstate.isModeClear(); bool vertexRangeCulling = gstate_c.Supports(GPU_SUPPORTS_VS_RANGE_CULLING) && !isModeThrough && gstate_c.submitType == SubmitType::DRAW; // neither hw nor sw spline/bezier. See #11692 VShaderID id; id.SetBit(VS_BIT_LMODE, lmode); - id.SetBit(VS_BIT_ENABLE_FOG, enableFog); id.SetBit(VS_BIT_IS_THROUGH, isModeThrough); id.SetBit(VS_BIT_HAS_COLOR, hasColor); id.SetBit(VS_BIT_VERTEX_RANGE_CULLING, vertexRangeCulling); @@ -123,15 +123,19 @@ void ComputeVertexShaderID(VShaderID *id_out, u32 vertType, bool useHWTransform, if (gstate.isLightingEnabled()) { // doShadeMapping is stored as UVGenMode, and light type doesn't matter for shade mapping. - id.SetBits(VS_BIT_MATERIAL_UPDATE, 3, gstate.getMaterialUpdate()); id.SetBit(VS_BIT_LIGHTING_ENABLE); - // Light bits - for (int i = 0; i < 4; i++) { - bool chanEnabled = gstate.isLightChanEnabled(i) != 0; - id.SetBit(VS_BIT_LIGHT0_ENABLE + i, chanEnabled); - if (chanEnabled) { - id.SetBits(VS_BIT_LIGHT0_COMP + 4 * i, 2, gstate.getLightComputation(i)); - id.SetBits(VS_BIT_LIGHT0_TYPE + 4 * i, 2, gstate.getLightType(i)); + if (gstate_c.Supports(GPU_USE_LIGHT_UBERSHADER)) { + id.SetBit(VS_BIT_LIGHT_UBERSHADER); + } else { + id.SetBits(VS_BIT_MATERIAL_UPDATE, 3, gstate.getMaterialUpdate()); + // Light bits + for (int i = 0; i < 4; i++) { + bool chanEnabled = gstate.isLightChanEnabled(i) != 0; + id.SetBit(VS_BIT_LIGHT0_ENABLE + i, chanEnabled); + if (chanEnabled) { + id.SetBits(VS_BIT_LIGHT0_COMP + 4 * i, 2, gstate.getLightComputation(i)); + id.SetBits(VS_BIT_LIGHT0_TYPE + 4 * i, 2, gstate.getLightType(i)); + } } } } @@ -180,7 +184,7 @@ std::string FragmentShaderDesc(const FShaderID &id) { if (id.Bit(FS_BIT_COLOR_DOUBLE)) desc << "2x "; if (id.Bit(FS_BIT_FLATSHADE)) desc << "Flat "; if (id.Bit(FS_BIT_BGRA_TEXTURE)) desc << "BGRA "; - switch ((ShaderDepalMode)id.Bit(FS_BIT_SHADER_DEPAL_MODE)) { + switch ((ShaderDepalMode)id.Bits(FS_BIT_SHADER_DEPAL_MODE, 2)) { case ShaderDepalMode::OFF: break; case ShaderDepalMode::NORMAL: desc << "Depal "; break; case ShaderDepalMode::SMOOTHED: desc << "SmoothDepal "; break; @@ -223,7 +227,7 @@ std::string FragmentShaderDesc(const FShaderID &id) { case STENCIL_VALUE_INCR_4: desc << "StenIncr4 "; break; case STENCIL_VALUE_INCR_8: desc << "StenIncr8 "; break; case STENCIL_VALUE_DECR_4: desc << "StenDecr4 "; break; - case STENCIL_VALUE_DECR_8: desc << "StenDecr4 "; break; + case STENCIL_VALUE_DECR_8: desc << "StenDecr8 "; break; default: desc << "StenUnknown "; break; } } else if (id.Bit(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE)) { @@ -244,6 +248,9 @@ std::string FragmentShaderDesc(const FShaderID &id) { else if (id.Bit(FS_BIT_ALPHA_TEST)) desc << "AlphaTest " << alphaTestFuncs[id.Bits(FS_BIT_ALPHA_TEST_FUNC, 3)] << " "; if (id.Bit(FS_BIT_COLOR_AGAINST_ZERO)) desc << "ColorTest0 " << alphaTestFuncs[id.Bits(FS_BIT_COLOR_TEST_FUNC, 2)] << " "; // first 4 match; else if (id.Bit(FS_BIT_COLOR_TEST)) desc << "ColorTest " << alphaTestFuncs[id.Bits(FS_BIT_COLOR_TEST_FUNC, 2)] << " "; // first 4 match + if (id.Bit(FS_BIT_TEST_DISCARD_TO_ZERO)) desc << "TestDiscardToZero "; + if (id.Bit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL)) desc << "StencilDiscardWorkaround "; + if (id.Bits(FS_BIT_REPLACE_LOGIC_OP, 4) != GE_LOGIC_COPY) desc << "ReplaceLogic "; return desc.str(); } @@ -349,16 +356,57 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip id.SetBit(FS_BIT_COLOR_WRITEMASK, colorWriteMask); - if (g_Config.bVendorBugChecksEnabled) { - if (bugs.Has(Draw::Bugs::NO_DEPTH_CANNOT_DISCARD_STENCIL)) { - id.SetBit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL, !IsStencilTestOutputDisabled() && !gstate.isDepthWriteEnabled()); - } else if (bugs.Has(Draw::Bugs::MALI_STENCIL_DISCARD_BUG) && PSP_CoreParameter().compat.flags().MaliDepthStencilBugWorkaround) { - // Very similar driver bug to the Adreno one, with the same workaround (though might look into if there are cheaper ones!) - // Keeping the conditions separate since it can probably be made tighter. - id.SetBit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL, !IsStencilTestOutputDisabled() && (!gstate.isDepthTestEnabled() || !gstate.isDepthWriteEnabled())); + if (g_Config.bVendorBugChecksEnabled && bugs.Has(Draw::Bugs::NO_DEPTH_CANNOT_DISCARD_STENCIL)) { + bool stencilWithoutDepth = !IsStencilTestOutputDisabled() && (!gstate.isDepthTestEnabled() || !gstate.isDepthWriteEnabled()); + if (stencilWithoutDepth) { + id.SetBit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL, stencilWithoutDepth); } } } *id_out = id; } + +std::string GeometryShaderDesc(const GShaderID &id) { + std::stringstream desc; + desc << StringFromFormat("%08x:%08x ", id.d[1], id.d[0]); + if (id.Bit(GS_BIT_ENABLED)) desc << "ENABLED "; + if (id.Bit(GS_BIT_DO_TEXTURE)) desc << "TEX "; + if (id.Bit(GS_BIT_LMODE)) desc << "LMODE "; + return desc.str(); +} + +void ComputeGeometryShaderID(GShaderID *id_out, const Draw::Bugs &bugs, int prim) { + GShaderID id; + + bool isModeThrough = gstate.isModeThrough(); + bool isCurve = gstate_c.submitType != SubmitType::DRAW; + bool isTriangle = prim == GE_PRIM_TRIANGLES || prim == GE_PRIM_TRIANGLE_FAN || prim == GE_PRIM_TRIANGLE_STRIP; + + bool vertexRangeCulling = !isCurve; + bool clipClampedDepth = gstate_c.Supports(GPU_SUPPORTS_DEPTH_CLAMP) && !gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE); + + // If we're not using GS culling, return a zero ID. + // Also, only use this for triangle primitives. + if ((!vertexRangeCulling && !clipClampedDepth) || isModeThrough || !isTriangle || !gstate_c.Supports(GPU_SUPPORTS_GS_CULLING)) { + *id_out = id; + return; + } + + id.SetBit(GS_BIT_ENABLED, true); + // Vertex range culling doesn't seem tno happen for spline/bezier, see #11692. + id.SetBit(GS_BIT_CURVE, isCurve); + + if (gstate.isModeClear()) { + // No attribute bits. + } else { + bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled() && !isModeThrough; + + id.SetBit(GS_BIT_LMODE, lmode); + if (gstate.isTextureMapEnabled()) { + id.SetBit(GS_BIT_DO_TEXTURE); + } + } + + *id_out = id; +} diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index 999faf2e44..560b8a873a 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -12,7 +12,6 @@ enum VShaderBit : uint8_t { VS_BIT_LMODE = 0, VS_BIT_IS_THROUGH = 1, - VS_BIT_ENABLE_FOG = 2, VS_BIT_HAS_COLOR = 3, VS_BIT_DO_TEXTURE = 4, VS_BIT_VERTEX_RANGE_CULLING = 5, @@ -33,7 +32,11 @@ enum VShaderBit : uint8_t { VS_BIT_BONES = 22, // 3 should be enough, not 8 // 25 - 29 are free. VS_BIT_ENABLE_BONES = 30, - // 31 is free. + + // If this is set along with LIGHTING_ENABLE, all other lighting bits below + // are passed to the shader directly instead. + VS_BIT_LIGHT_UBERSHADER = 31, + VS_BIT_LIGHT0_COMP = 32, // 2 bits VS_BIT_LIGHT0_TYPE = 34, // 2 bits VS_BIT_LIGHT1_COMP = 36, // 2 bits @@ -101,6 +104,18 @@ static inline FShaderBit operator +(FShaderBit bit, int i) { return FShaderBit((int)bit + i); } +// Some of these bits are straight from FShaderBit, since they essentially enable attributes directly. +enum GShaderBit : uint8_t { + GS_BIT_ENABLED = 0, // If not set, we don't use a geo shader. + GS_BIT_DO_TEXTURE = 1, // presence of texcoords + GS_BIT_LMODE = 2, // presence of specular color (regular color always present) + GS_BIT_CURVE = 3, // curve, which means don't do range culling. +}; + +static inline GShaderBit operator +(GShaderBit bit, int i) { + return GShaderBit((int)bit + i); +} + struct ShaderID { ShaderID() { clear(); @@ -115,6 +130,13 @@ struct ShaderID { d[i] = 0xFFFFFFFF; } } + bool is_invalid() const { + for (size_t i = 0; i < ARRAY_SIZE(d); i++) { + if (d[i] != 0xFFFFFFFF) + return false; + } + return true; + } uint32_t d[2]; bool operator < (const ShaderID &other) const { @@ -222,6 +244,31 @@ struct FShaderID : ShaderID { } }; +struct GShaderID : ShaderID { + GShaderID() : ShaderID() { + } + + explicit GShaderID(ShaderID &src) { + memcpy(d, src.d, sizeof(d)); + } + + bool Bit(GShaderBit bit) const { + return ShaderID::Bit((int)bit); + } + + int Bits(GShaderBit bit, int count) const { + return ShaderID::Bits((int)bit, count); + } + + void SetBit(GShaderBit bit, bool value = true) { + ShaderID::SetBit((int)bit, value); + } + + void SetBits(GShaderBit bit, int count, int value) { + ShaderID::SetBits((int)bit, count, value); + } +}; + namespace Draw { class Bugs; } @@ -234,3 +281,6 @@ std::string VertexShaderDesc(const VShaderID &id); struct ComputedPipelineState; void ComputeFragmentShaderID(FShaderID *id, const ComputedPipelineState &pipelineState, const Draw::Bugs &bugs); std::string FragmentShaderDesc(const FShaderID &id); + +void ComputeGeometryShaderID(GShaderID *id, const Draw::Bugs &bugs, int prim); +std::string GeometryShaderDesc(const GShaderID &id); diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index c3480e6fef..b276f7f094 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -74,20 +74,20 @@ void CalcCullRange(float minValues[4], float maxValues[4], bool flipViewport, bo void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipViewport, bool useBufferedRendering) { if (dirtyUniforms & DIRTY_TEXENV) { - Uint8x3ToFloat4(ub->texEnvColor, gstate.texenvcolor); + Uint8x3ToFloat3(ub->texEnvColor, gstate.texenvcolor); } if (dirtyUniforms & DIRTY_ALPHACOLORREF) { Uint8x3ToInt4_Alpha(ub->alphaColorRef, gstate.getColorTestRef(), gstate.getAlphaTestRef() & gstate.getAlphaTestMask()); } if (dirtyUniforms & DIRTY_ALPHACOLORMASK) { - Uint8x3ToInt4_Alpha(ub->colorTestMask, gstate.getColorTestMask(), gstate.getAlphaTestMask()); + ub->colorTestMask = gstate.getColorTestMask() | (gstate.getAlphaTestMask() << 24); } if (dirtyUniforms & DIRTY_FOGCOLOR) { Uint8x3ToFloat4(ub->fogColor, gstate.fogcolor); } if (dirtyUniforms & DIRTY_SHADERBLEND) { - Uint8x3ToFloat4(ub->blendFixA, gstate.getFixA()); - Uint8x3ToFloat4(ub->blendFixB, gstate.getFixB()); + Uint8x3ToFloat3(ub->blendFixA, gstate.getFixA()); + Uint8x3ToFloat3(ub->blendFixB, gstate.getFixB()); } if (dirtyUniforms & DIRTY_TEXCLAMP) { const float invW = 1.0f / (float)gstate_c.curTextureWidth; @@ -267,6 +267,29 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView } } +uint32_t PackLightControlBits() { + // Bit organization + // Bottom 4 bits are enable bits for each light. + // Then, for each light, comes 2 bits for "comp" and 2 bits for "type". + // At the end, at bit 20, we put the three material update bits. + + uint32_t lightControl = 0; + for (int i = 0; i < 4; i++) { + if (gstate.isLightChanEnabled(i)) { + lightControl |= 1 << i; + } + + u32 computation = (u32)gstate.getLightComputation(i); // 2 bits + u32 type = (u32)gstate.getLightType(i); // 2 bits + lightControl |= computation << (4 + i * 4); + lightControl |= type << (4 + i * 4 + 2); + } + + lightControl |= gstate.getMaterialUpdate() << 20; + + return lightControl; +} + void LightUpdateUniforms(UB_VS_Lights *ub, uint64_t dirtyUniforms) { // Lighting if (dirtyUniforms & DIRTY_AMBIENT) { @@ -279,7 +302,11 @@ void LightUpdateUniforms(UB_VS_Lights *ub, uint64_t dirtyUniforms) { Uint8x3ToFloat4_Alpha(ub->materialSpecular, gstate.materialspecular, std::max(0.0f, getFloat24(gstate.materialspecularcoef))); } if (dirtyUniforms & DIRTY_MATEMISSIVE) { - Uint8x3ToFloat4(ub->materialEmissive, gstate.materialemissive); + // We're not touching the fourth f32 here, because we store an u32 of control bits in it. + Uint8x3ToFloat3(ub->materialEmissive, gstate.materialemissive); + } + if (dirtyUniforms & DIRTY_LIGHT_CONTROL) { + ub->lightControl = PackLightControlBits(); } for (int i = 0; i < 4; i++) { if (dirtyUniforms & (DIRTY_LIGHT0 << i)) { diff --git a/GPU/Common/ShaderUniforms.h b/GPU/Common/ShaderUniforms.h index 691835a262..04b6437dff 100644 --- a/GPU/Common/ShaderUniforms.h +++ b/GPU/Common/ShaderUniforms.h @@ -13,14 +13,13 @@ enum : uint64_t { DIRTY_ALPHACOLORMASK | DIRTY_SHADERBLEND | DIRTY_COLORWRITEMASK | DIRTY_UVSCALEOFFSET | DIRTY_TEXCLAMP | DIRTY_DEPTHRANGE | DIRTY_MATAMBIENTALPHA | DIRTY_BEZIERSPLINE | DIRTY_DEPAL, DIRTY_LIGHT_UNIFORMS = - DIRTY_LIGHT0 | DIRTY_LIGHT1 | DIRTY_LIGHT2 | DIRTY_LIGHT3 | + DIRTY_LIGHT_CONTROL | DIRTY_LIGHT0 | DIRTY_LIGHT1 | DIRTY_LIGHT2 | DIRTY_LIGHT3 | DIRTY_MATDIFFUSE | DIRTY_MATSPECULAR | DIRTY_MATEMISSIVE | DIRTY_AMBIENT, }; -// TODO: Split into two structs, one for software transform and one for hardware transform, to save space. -// Currently 512 bytes. Probably can't get to 256 (nVidia's UBO alignment). +// Currently 480 bytes. Probably can't get to 256 (nVidia's UBO alignment, also common in other vendors). // Every line here is a 4-float. -struct UB_VS_FS_Base { +struct alignas(16) UB_VS_FS_Base { float proj[16]; float proj_through[16]; float view[12]; @@ -29,24 +28,22 @@ struct UB_VS_FS_Base { float uvScaleOffset[4]; float depthRange[4]; // Rotation is used only for software transform. - float fogCoef[2]; float stencil; float rotation; float matAmbient[4]; float cullRangeMin[4]; float cullRangeMax[4]; uint32_t spline_counts; uint32_t depal_mask_shift_off_fmt; // 4 params packed into one. uint32_t colorWriteMask; float mipBias; // Fragment data - float fogColor[4]; - float texEnvColor[4]; + float fogColor[4]; // .w is unused + float texEnvColor[3]; uint32_t colorTestMask; int alphaColorRef[4]; - int colorTestMask[4]; - float blendFixA[4]; - float blendFixB[4]; + float blendFixA[3]; float stencil; + float blendFixB[3]; float rotation; float texClamp[4]; - float texClampOffset[4]; + float texClampOffset[2]; float fogCoef[2]; }; -static const char *ub_baseStr = +static const char * const ub_baseStr = R"( mat4 u_proj; mat4 u_proj_through; mat3x4 u_view; @@ -54,9 +51,6 @@ R"( mat4 u_proj; mat3x4 u_texmtx; vec4 u_uvscaleoffset; vec4 u_depthRange; - vec2 u_fogcoef; - float u_stencilReplaceValue; - float u_rotation; vec4 u_matambientalpha; vec4 u_cullRangeMin; vec4 u_cullRangeMax; @@ -66,21 +60,23 @@ R"( mat4 u_proj; float u_mipBias; vec3 u_fogcolor; vec3 u_texenv; + uint u_alphacolormask; ivec4 u_alphacolorref; - ivec4 u_alphacolormask; - vec3 u_blendFixA; - vec3 u_blendFixB; + vec3 u_blendFixA; float u_stencilReplaceValue; + vec3 u_blendFixB; float u_rotation; vec4 u_texclamp; vec2 u_texclampoff; + vec2 u_fogcoef; )"; // 512 bytes. Would like to shrink more. Some colors only have 8-bit precision and we expand // them to float unnecessarily, could just as well expand in the shader. -struct UB_VS_Lights { +struct alignas(16) UB_VS_Lights { float ambientColor[4]; float materialDiffuse[4]; float materialSpecular[4]; - float materialEmissive[4]; + float materialEmissive[3]; + uint32_t lightControl; float lpos[4][4]; float ldir[4][4]; float latt[4][4]; @@ -90,11 +86,12 @@ struct UB_VS_Lights { float lightSpecular[4][4]; }; -static const char *ub_vs_lightsStr = +static const char * const ub_vs_lightsStr = R"( vec4 u_ambient; vec3 u_matdiffuse; vec4 u_matspecular; vec3 u_matemissive; + uint u_lightControl; // light ubershader vec3 u_lightpos0; vec3 u_lightpos1; vec3 u_lightpos2; @@ -127,11 +124,11 @@ R"( vec4 u_ambient; // With some cleverness, we could get away with uploading just half this when only the four or five first // bones are being used. This is 384b. -struct UB_VS_Bones { +struct alignas(16) UB_VS_Bones { float bones[8][12]; }; -static const char *ub_vs_bonesStr = +static const char * const ub_vs_bonesStr = R"( mat3x4 u_bone0; mat3x4 u_bone1; mat3x4 u_bone2; mat3x4 u_bone3; mat3x4 u_bone4; mat3x4 u_bone5; mat3x4 u_bone6; mat3x4 u_bone7; mat3x4 u_bone8; )"; @@ -141,3 +138,4 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView void LightUpdateUniforms(UB_VS_Lights *ub, uint64_t dirtyUniforms); void BoneUpdateUniforms(UB_VS_Bones *ub, uint64_t dirtyUniforms); +uint32_t PackLightControlBits(); diff --git a/GPU/Common/SoftwareTransformCommon.cpp b/GPU/Common/SoftwareTransformCommon.cpp index 98b5adc079..5e3c0c5850 100644 --- a/GPU/Common/SoftwareTransformCommon.cpp +++ b/GPU/Common/SoftwareTransformCommon.cpp @@ -168,6 +168,29 @@ void SoftwareTransform::SetProjMatrix(float mtx[14], bool invertedX, bool invert projMatrix_.translateAndScale(trans, scale); } +static void ReadWeightedNormal(Vec3f &source, VertexReader &reader, u32 vertType, bool skinningEnabled) { + if (reader.hasNormal()) + reader.ReadNrm(source.AsArray()); + if (skinningEnabled) { + float weights[8]; + reader.ReadWeights(weights); + + // Have to recalculate this, unfortunately. Please use software skinning... + Vec3f nsum(0, 0, 0); + for (int i = 0; i < vertTypeGetNumBoneWeights(vertType); i++) { + if (weights[i] != 0.0f) { + Vec3f norm; + Norm3ByMatrix43(norm.AsArray(), source.AsArray(), gstate.boneMatrix + i * 12); + nsum += norm * weights[i]; + } + } + + source = nsum; + } + if (gstate.areNormalsReversed()) + source = -source; +} + void SoftwareTransform::Decode(int prim, u32 vertType, const DecVtxFormat &decVtxFormat, int maxIndex, SoftwareTransformResult *result) { u8 *decoded = params_.decoded; TransformedVertex *transformed = params_.transformed; @@ -284,7 +307,7 @@ void SoftwareTransform::Decode(int prim, u32 vertType, const DecVtxFormat &decVt } } else { float weights[8]; - // TODO: For flat, are weights from the provoking used for color/normal? + // For flat, we need the vertex weights. reader.Goto(index); reader.ReadWeights(weights); @@ -358,10 +381,8 @@ void SoftwareTransform::Decode(int prim, u32 vertType, const DecVtxFormat &decVt case GE_TEXMAP_TEXTURE_MATRIX: { - // TODO: What's the correct behavior with flat shading? Provoked normal or real normal? - // Projection mapping - Vec3f source; + Vec3f source(0.0f, 0.0f, 1.0f); switch (gstate.getUVProjMode()) { case GE_PROJMAP_POSITION: // Use model space XYZ as source source = pos; @@ -372,14 +393,28 @@ void SoftwareTransform::Decode(int prim, u32 vertType, const DecVtxFormat &decVt break; case GE_PROJMAP_NORMALIZED_NORMAL: // Use normalized normal as source - source = normal.NormalizedOr001(cpu_info.bSSE4_1); + // Flat uses the vertex normal, not provoking. + if (provokeIndOffset == 0) { + source = normal.Normalized(cpu_info.bSSE4_1); + } else { + reader.Goto(index); + ReadWeightedNormal(source, reader, vertType, skinningEnabled); + source.Normalize(); + } if (!reader.hasNormal()) { ERROR_LOG_REPORT(G3D, "Normal projection mapping without normal?"); } break; case GE_PROJMAP_NORMAL: // Use non-normalized normal as source! - source = normal; + // Flat uses the vertex normal, not provoking. + if (provokeIndOffset == 0) { + source = normal; + } else { + // Need to read the normal for this vertex and weight it again.. + reader.Goto(index); + ReadWeightedNormal(source, reader, vertType, skinningEnabled); + } if (!reader.hasNormal()) { ERROR_LOG_REPORT(G3D, "Normal projection mapping without normal?"); } @@ -790,13 +825,13 @@ void SoftwareTransform::ExpandLines(int vertexCount, int &maxIndex, u16 *&inds, float yoff = addWidth.y * dy; // bottom right - trans[0].CopyFromWithOffset(transVtx2, xoff, yoff); + trans[0].CopyFromWithOffset(transVtx2, xoff * transVtx2.pos_w, yoff * transVtx2.pos_w); // top right - trans[1].CopyFromWithOffset(transVtx1, xoff, yoff); + trans[1].CopyFromWithOffset(transVtx1, xoff * transVtx1.pos_w, yoff * transVtx1.pos_w); // top left - trans[2].CopyFromWithOffset(transVtx1, -xoff, -yoff); + trans[2].CopyFromWithOffset(transVtx1, -xoff * transVtx1.pos_w, -yoff * transVtx1.pos_w); // bottom left - trans[3].CopyFromWithOffset(transVtx2, -xoff, -yoff); + trans[3].CopyFromWithOffset(transVtx2, -xoff * transVtx2.pos_w, -yoff * transVtx2.pos_w); // Triangle: BR-TR-TL indsOut[0] = i * 2 + 0; @@ -835,17 +870,17 @@ void SoftwareTransform::ExpandLines(int vertexCount, int &maxIndex, u16 *&inds, // bottom right trans[0] = transVtxBL; - trans[0].x += addWidth.x * dx; - trans[0].y += addWidth.y * dy; - trans[0].u += addWidth.x * du; - trans[0].v += addWidth.y * dv; + trans[0].x += addWidth.x * dx * trans[0].pos_w; + trans[0].y += addWidth.y * dy * trans[0].pos_w; + trans[0].u += addWidth.x * du * trans[0].uv_w; + trans[0].v += addWidth.y * dv * trans[0].uv_w; // top right trans[1] = transVtxTL; - trans[1].x += addWidth.x * dx; - trans[1].y += addWidth.y * dy; - trans[1].u += addWidth.x * du; - trans[1].v += addWidth.y * dv; + trans[1].x += addWidth.x * dx * trans[1].pos_w; + trans[1].y += addWidth.y * dy * trans[1].pos_w; + trans[1].u += addWidth.x * du * trans[1].uv_w; + trans[1].v += addWidth.y * dv * trans[1].uv_w; // top left trans[2] = transVtxTL; diff --git a/GPU/Common/SplineCommon.cpp b/GPU/Common/SplineCommon.cpp index d3f864a4d4..b2358f401b 100644 --- a/GPU/Common/SplineCommon.cpp +++ b/GPU/Common/SplineCommon.cpp @@ -577,7 +577,8 @@ void DrawEngineCommon::SubmitCurve(const void *control_points, const void *indic if (output.count) DispatchSubmitPrim(output.vertices, output.indices, PatchPrimToPrim(surface.primType), output.count, vertTypeID, gstate.getCullMode(), &generatedBytesRead); - DispatchFlush(); + if (flushOnParams_) + DispatchFlush(); if (origVertType & GE_VTYPE_TC_MASK) { gstate_c.uv = prevUVScale; diff --git a/GPU/Common/StencilCommon.cpp b/GPU/Common/StencilCommon.cpp index 81749ef678..d30327df93 100644 --- a/GPU/Common/StencilCommon.cpp +++ b/GPU/Common/StencilCommon.cpp @@ -171,6 +171,7 @@ bool FramebufferManagerCommon::PerformStencilUpload(u32 addr, int size, StencilU break; case GE_FORMAT_INVALID: case GE_FORMAT_DEPTH16: + case GE_FORMAT_CLUT8: // Inconceivable. _assert_(false); break; @@ -185,13 +186,9 @@ bool FramebufferManagerCommon::PerformStencilUpload(u32 addr, int size, StencilU // Otherwise, we can skip alpha in many cases, in which case we don't even use a shader. if (flags & StencilUpload::IGNORE_ALPHA) { - shaderManager_->DirtyLastShader(); - if (dstBuffer->fbo) { draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }, "PerformStencilUpload_Clear"); } - - gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE); return true; } } @@ -202,11 +199,14 @@ bool FramebufferManagerCommon::PerformStencilUpload(u32 addr, int size, StencilU if (!stencilUploadPipeline_) { const ShaderLanguageDesc &shaderLanguageDesc = draw_->GetShaderLanguageDesc(); - char *fsCode = new char[4000]; - char *vsCode = new char[4000]; + char *fsCode = new char[8192]; + char *vsCode = new char[8192]; GenerateStencilFs(fsCode, shaderLanguageDesc, draw_->GetBugs()); GenerateStencilVs(vsCode, shaderLanguageDesc); + _assert_msg_(strlen(fsCode) < 8192, "StenFS length error: %d", (int)strlen(fsCode)); + _assert_msg_(strlen(vsCode) < 8192, "StenVS length error: %d", (int)strlen(vsCode)); + ShaderModule *stencilUploadFs = draw_->CreateShaderModule(ShaderStage::Fragment, shaderLanguageDesc.shaderLanguage, (const uint8_t *)fsCode, strlen(fsCode), "stencil_fs"); ShaderModule *stencilUploadVs = draw_->CreateShaderModule(ShaderStage::Vertex, shaderLanguageDesc.shaderLanguage, (const uint8_t *)vsCode, strlen(vsCode), "stencil_vs"); @@ -332,6 +332,6 @@ bool FramebufferManagerCommon::PerformStencilUpload(u32 addr, int size, StencilU tex->Release(); draw_->InvalidateCachedState(); - gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); + gstate_c.Dirty(DIRTY_ALL_RENDER_STATE); return true; } diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 77b47e8b9e..cf012d4010 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -37,6 +37,7 @@ #include "GPU/Common/ShaderId.h" #include "GPU/Common/GPUStateUtils.h" #include "GPU/Debugger/Debugger.h" +#include "GPU/Debugger/Record.h" #include "GPU/GPUCommon.h" #include "GPU/GPUInterface.h" #include "GPU/GPUState.h" @@ -151,7 +152,7 @@ static int TexLog2(float delta) { } SamplerCacheKey TextureCacheCommon::GetSamplingParams(int maxLevel, const TexCacheEntry *entry) { - SamplerCacheKey key; + SamplerCacheKey key{}; int minFilt = gstate.texfilter & 0x7; key.minFilt = minFilt & 1; @@ -292,11 +293,18 @@ SamplerCacheKey TextureCacheCommon::GetSamplingParams(int maxLevel, const TexCac SamplerCacheKey TextureCacheCommon::GetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight) { SamplerCacheKey key = GetSamplingParams(0, nullptr); + // In case auto max quality was on, restore min filt. Another fix for water in Outrun. + if (g_Config.iTexFiltering == TEX_FILTER_AUTO_MAX_QUALITY) { + int minFilt = gstate.texfilter & 0x7; + key.minFilt = minFilt & 1; + } + // Kill any mipmapping settings. key.mipEnable = false; key.mipFilt = false; key.aniso = 0.0; key.maxLevel = 0.0f; + key.lodBias = 0.0f; // Often the framebuffer will not match the texture size. We'll wrap/clamp in the shader in that case. int w = gstate.getTextureWidth(0); @@ -377,13 +385,22 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { } bool hasClut = gstate.isTextureFormatIndexed(); + bool hasClutGPU = false; u32 cluthash; if (hasClut) { - if (clutLastFormat_ != gstate.clutformat) { - // We update here because the clut format can be specified after the load. - UpdateCurrentClut(gstate.getClutPaletteFormat(), gstate.getClutIndexStartPos(), gstate.isClutIndexSimple()); + if (clutRenderAddress_ != 0xFFFFFFFF) { + gstate_c.curTextureXOffset = 0.0f; + gstate_c.curTextureYOffset = 0.0f; + hasClutGPU = true; + cluthash = 0; // Or should we use some other marker value? + } else { + if (clutLastFormat_ != gstate.clutformat) { + // We update here because the clut format can be specified after the load. + // TODO: Unify this as far as possible (I think only GLES backend really needs its own implementation due to different component order). + UpdateCurrentClut(gstate.getClutPaletteFormat(), gstate.getClutIndexStartPos(), gstate.isClutIndexSimple()); + } + cluthash = clutHash_ ^ gstate.clutformat; } - cluthash = clutHash_ ^ gstate.clutformat; } else { cluthash = 0; } @@ -401,10 +418,13 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { // Should probably revisit how this works.. gstate_c.SetNeedShaderTexclamp(false); gstate_c.skipDrawReason &= ~SKIPDRAW_BAD_FB_TEXTURE; - if (gstate_c.bgraTexture != isBgraBackend_) { + + bool isBgraTexture = isBgraBackend_ && !hasClutGPU; + + if (gstate_c.bgraTexture != isBgraTexture) { gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); } - gstate_c.bgraTexture = isBgraBackend_; + gstate_c.bgraTexture = isBgraTexture; if (entryIter != cache_.end()) { entry = entryIter->second.get(); @@ -412,6 +432,13 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { bool match = entry->Matches(dim, texFormat, maxLevel); const char *reason = "different params"; + // Check for dynamic CLUT status + if (((entry->status & TexCacheEntry::STATUS_CLUT_GPU) != 0) != hasClutGPU) { + // Need to recreate, suddenly a CLUT GPU texture was used without it, or vice versa. + // I think this can only happen on a clut hash collision with the marker value, so highly unlikely. + match = false; + } + // Check for FBO changes. if (entry->status & TexCacheEntry::STATUS_FRAMEBUFFER_OVERLAP) { // Fall through to the end where we'll delete the entry if there's a framebuffer. @@ -542,6 +569,10 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { nextTexture_ = nullptr; nextNeedsRebuild_ = false; + + if (hasClutGPU) { + WARN_LOG_ONCE(clut_fb, G3D, "Render-to-CLUT combined with framebuffer texture at %08x - Not yet supported", texaddr); + } SetTextureFramebuffer(bestCandidate); // sets curTexture3D return nullptr; } @@ -553,10 +584,6 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { entry = new TexCacheEntry{}; cache_[cachekey].reset(entry); - if (hasClut && clutRenderAddress_ != 0xFFFFFFFF) { - WARN_LOG_REPORT_ONCE(clutUseRender, G3D, "Using texture with rendered CLUT: texfmt=%d, clutfmt=%d", gstate.getTextureFormat(), gstate.getClutPaletteFormat()); - } - if (PPGeIsFontTextureAddress(texaddr)) { // It's the builtin font texture. entry->status = TexCacheEntry::STATUS_RELIABLE; @@ -566,6 +593,11 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { entry->status = TexCacheEntry::STATUS_UNRELIABLE; } + if (hasClutGPU) { + WARN_LOG_REPORT_ONCE(clutUseRender, G3D, "Using texture with dynamic CLUT: texfmt=%d, clutfmt=%d", gstate.getTextureFormat(), gstate.getClutPaletteFormat()); + entry->status |= TexCacheEntry::STATUS_CLUT_GPU; + } + if (hasClut && clutRenderAddress_ == 0xFFFFFFFF) { const u64 cachekeyMin = (u64)(texaddr & 0x3FFFFFFF) << 32; const u64 cachekeyMax = cachekeyMin + (1ULL << 32); @@ -656,7 +688,7 @@ bool TextureCacheCommon::GetBestFramebufferCandidate(const TextureDefinition &en // Should avoid problems when pingponging two nearby buffers, like in Wipeout Pure in #15927. if (candidate.channel == RASTER_COLOR && (candidate.match.yOffset != 0 || candidate.match.xOffset != 0) && - (candidate.fb->fb_address & 0x1FFFFF) == (gstate.getFrameBufAddress() & 0x1FFFFF)) { + candidate.fb->fb_address == (gstate.getFrameBufRawAddress() | 0x04000000)) { relevancy -= 2; } @@ -813,10 +845,8 @@ void TextureCacheCommon::HandleTextureChange(TexCacheEntry *const entry, const c } void TextureCacheCommon::NotifyFramebuffer(VirtualFramebuffer *framebuffer, FramebufferNotification msg) { - const u32 mirrorMask = 0x00600000; const u32 fb_addr = framebuffer->fb_address; - - const u32 z_addr = framebuffer->z_address & ~mirrorMask; // Probably unnecessary. + const u32 z_addr = framebuffer->z_address; const u32 fb_bpp = BufferFormatBytesPerPixel(framebuffer->fb_format); const u32 z_bpp = 2; // No other format exists. @@ -835,8 +865,6 @@ void TextureCacheCommon::NotifyFramebuffer(VirtualFramebuffer *framebuffer, Fram // Try to match the new framebuffer to existing textures. // Backwards from the "usual" texturing case so can't share a utility function. - std::vector candidates; - u64 cacheKey = (u64)fb_addr << 32; // If it has a clut, those are the low 32 bits, so it'll be inside this range. // Also, if it's a subsample of the buffer, it'll also be within the FBO. @@ -899,7 +927,7 @@ bool TextureCacheCommon::MatchFramebuffer( uint32_t fb_stride_in_bytes = fb_stride * BufferFormatBytesPerPixel(fb_format); uint32_t tex_stride_in_bytes = entry.bufw * textureBitsPerPixel[entry.format] / 8; // Note, we're looking up bits here so need to divide by 8. - u32 addr = fb_address & 0x3FFFFFFF; + u32 addr = fb_address; u32 texaddr = entry.addr + texaddrOffset; bool texInVRAM = Memory::IsVRAMAddress(texaddr); @@ -911,10 +939,10 @@ bool TextureCacheCommon::MatchFramebuffer( } if (texInVRAM) { - const u32 mirrorMask = 0x00600000; + const u32 mirrorMask = 0x041FFFFF; - addr &= ~mirrorMask; - texaddr &= ~mirrorMask; + addr &= mirrorMask; + texaddr &= mirrorMask; } const bool noOffset = texaddr == addr; @@ -947,12 +975,13 @@ bool TextureCacheCommon::MatchFramebuffer( return false; } - // Check works for D16 too (???) + // Check works for D16 too. const bool matchingClutFormat = (fb_format == GE_FORMAT_DEPTH16 && entry.format == GE_TFMT_CLUT16) || (fb_format == GE_FORMAT_DEPTH16 && entry.format == GE_TFMT_5650) || (fb_format == GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT32) || - (fb_format != GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT16); + (fb_format != GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT16) || + (fb_format == GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT8); const int texBitsPerPixel = std::max(1U, (u32)textureBitsPerPixel[entry.format]); const int byteOffset = texaddr - addr; @@ -989,8 +1018,13 @@ bool TextureCacheCommon::MatchFramebuffer( return false; } - if (fb_stride_in_bytes != tex_stride_in_bytes) { + // Note the check for texHeight - we really don't care about a stride mismatch if texHeight == 1. + if (fb_stride_in_bytes != tex_stride_in_bytes && texHeight > 1) { // Probably irrelevant. Although, as we shall see soon, there are exceptions. + // Burnout Dominator lens flare trick special case. + if (fb_format == GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT8 && texWidth == 4 && texHeight == 1) { + return true; + } return false; } @@ -1017,8 +1051,8 @@ bool TextureCacheCommon::MatchFramebuffer( return true; } } else { - WARN_LOG_ONCE(diffFormat2, G3D, "Ignoring possible texturing from framebuffer with incompatible format %s != %s at %08x", - GeTextureFormatToString(entry.format), GeBufferFormatToString(fb_format), fb_address); + WARN_LOG_ONCE(diffFormat2, G3D, "Ignoring possible texturing from framebuffer with incompatible format %s != %s at %08x (+%dx%d)", + GeTextureFormatToString(entry.format), GeBufferFormatToString(fb_format), fb_address, matchInfo->xOffset, matchInfo->yOffset); return false; } } @@ -1167,47 +1201,94 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { if (Memory::IsValidAddress(clutAddr)) { if (Memory::IsVRAMAddress(clutAddr)) { - // Clear the uncached bit, etc. to match framebuffers. - const u32 clutFramebufAddr = clutAddr & 0x3FFFFFFF; - const u32 clutFramebufEnd = clutFramebufAddr + loadBytes; + // Clear the uncached and mirror bits, etc. to match framebuffers. + const u32 clutLoadAddr = clutAddr & 0x041FFFFF; + const u32 clutLoadEnd = clutLoadAddr + loadBytes; static const u32 MAX_CLUT_OFFSET = 4096; clutRenderOffset_ = MAX_CLUT_OFFSET; const std::vector &framebuffers = framebufferManager_->Framebuffers(); - for (VirtualFramebuffer *framebuffer : framebuffers) { - const u32 fb_address = framebuffer->fb_address & 0x3FFFFFFF; - const u32 bpp = BufferFormatBytesPerPixel(framebuffer->fb_format); - u32 offset = clutFramebufAddr - fb_address; - // Is this inside the framebuffer at all? - bool matchRange = fb_address + framebuffer->fb_stride * bpp > clutFramebufAddr && fb_address < clutFramebufEnd; - // And is it inside the rendered area? Sometimes games pack data outside. - bool matchRegion = ((offset / bpp) % framebuffer->fb_stride) < framebuffer->width; - if (matchRange && matchRegion && offset < clutRenderOffset_) { - WARN_LOG_N_TIMES(clutfb, 5, G3D, "Detected LoadCLUT(%d bytes) from framebuffer %08x (%s), byte offset %d", loadBytes, fb_address, GeBufferFormatToString(framebuffer->fb_format), offset); - framebuffer->last_frame_clut = gpuStats.numFlips; - framebuffer->usageFlags |= FB_USAGE_CLUT; - clutRenderAddress_ = framebuffer->fb_address; - clutRenderOffset_ = offset; - if (offset == 0) { - break; + u32 bestClutAddress = 0xFFFFFFFF; + + VirtualFramebuffer *chosenFramebuffer = nullptr; + for (VirtualFramebuffer *framebuffer : framebuffers) { + const u32 fb_address = framebuffer->fb_address; + const u32 fb_bpp = BufferFormatBytesPerPixel(framebuffer->fb_format); + int offset = clutLoadAddr - fb_address; + + // Is this inside the framebuffer at all? Note that we only check the first line here, this should + // be changed. + bool matchRange = offset >= 0 && offset < (int)(framebuffer->fb_stride * fb_bpp); + if (matchRange) { + // And is it inside the rendered area? Sometimes games pack data in the margin between width and stride. + // If the framebuffer width was detected as 512, we're gonna assume it's really 480. + int fbMatchWidth = framebuffer->width; + if (fbMatchWidth == 512) { + fbMatchWidth = 480; + } + bool inMargin = ((offset / fb_bpp) % framebuffer->fb_stride) == fbMatchWidth; + + // The offset check here means, in the context of the loop, that we'll pick + // the framebuffer with the smallest offset. This is yet another framebuffer matching + // loop with its own rules, eventually we'll probably want to do something + // more systematic. + if (matchRange && !inMargin && offset < (int)clutRenderOffset_) { + WARN_LOG_N_TIMES(clutfb, 5, G3D, "Detected LoadCLUT(%d bytes) from framebuffer %08x (%s), byte offset %d", loadBytes, fb_address, GeBufferFormatToString(framebuffer->fb_format), offset); + framebuffer->last_frame_clut = gpuStats.numFlips; + framebuffer->usageFlags |= FB_USAGE_CLUT; + bestClutAddress = framebuffer->fb_address; + clutRenderOffset_ = (u32)offset; + chosenFramebuffer = framebuffer; + if (offset == 0) { + // Not gonna find a better match according to the smallest-offset rule, so we'll go with this one. + break; + } } } } + // To turn off dynamic CLUT (for demonstration or testing purposes), add "false &&" to this check. + if (chosenFramebuffer && chosenFramebuffer->fbo) { + clutRenderAddress_ = bestClutAddress; + + if (!dynamicClutTemp_) { + Draw::FramebufferDesc desc{}; + desc.width = 512; + desc.height = 1; + desc.depth = 1; + desc.z_stencil = false; + desc.numColorAttachments = 1; + desc.tag = "dynamic_clut"; + dynamicClutFbo_ = draw_->CreateFramebuffer(desc); + desc.tag = "dynamic_clut_temp"; + dynamicClutTemp_ = draw_->CreateFramebuffer(desc); + } + + // Copy the pixels to our temp clut, scaling down if needed and wrapping. + // TODO: Take the clutRenderOffset_ into account here. + framebufferManager_->BlitUsingRaster( + chosenFramebuffer->fbo, 0.0f, 0.0f, 512.0f * chosenFramebuffer->renderScaleFactor, 1.0f, + dynamicClutTemp_, 0.0f, 0.0f, 512.0f, 1.0f, + false, chosenFramebuffer->renderScaleFactor, framebufferManager_->Get2DPipeline(DRAW2D_COPY_COLOR_RECT2LIN), "copy_clut_to_temp"); + clutRenderFormat_ = chosenFramebuffer->fb_format; + } NotifyMemInfo(MemBlockFlags::ALLOC, clutAddr, loadBytes, "CLUT"); } // It's possible for a game to load CLUT outside valid memory without crashing, should result in zeroes. u32 bytes = Memory::ValidSize(clutAddr, loadBytes); - if (clutRenderAddress_ != 0xFFFFFFFF && PSP_CoreParameter().compat.flags().AllowDownloadCLUT) { + bool performDownload = PSP_CoreParameter().compat.flags().AllowDownloadCLUT; + if (GPURecord::IsActive()) + performDownload = true; + if (clutRenderAddress_ != 0xFFFFFFFF && performDownload) { framebufferManager_->DownloadFramebufferForClut(clutRenderAddress_, clutRenderOffset_ + bytes); Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, bytes); if (bytes < loadBytes) { memset((u8 *)clutBufRaw_ + bytes, 0x00, loadBytes - bytes); } } else { - // Here we could check for clutRenderAddres_ != 0xFFFFFFFF and zero the CLUT or something, + // Here we could check for clutRenderAddress_ != 0xFFFFFFFF and zero the CLUT or something, // but choosing not to for now. Though the results of loading the CLUT from RAM here is // almost certainly going to be bogus. #ifdef _M_SSE @@ -1471,12 +1552,28 @@ inline u32 TfmtRawToFullAlpha(GETextureFormat fmt) { } } +// Used for converting CLUT4 to CLUT8. +// Could SIMD or whatever, though will hardly be a bottleneck. +static void Expand4To8Bits(u8 *dest, const u8 *src, int srcWidth) { + for (int i = 0; i < (srcWidth + 1) / 2; i++) { + u8 lower = src[i] & 0xF; + u8 upper = src[i] >> 4; + dest[i * 2] = lower; + dest[i * 2 + 1] = upper; + } +} + CheckAlphaResult TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, GETextureFormat format, GEPaletteFormat clutformat, uint32_t texaddr, int level, int bufw, TexDecodeFlags flags) { u32 alphaSum = 0xFFFFFFFF; u32 fullAlphaMask = 0x0; bool expandTo32bit = (flags & TexDecodeFlags::EXPAND32) != 0; bool reverseColors = (flags & TexDecodeFlags::REVERSE_COLORS) != 0; + bool toClut8 = (flags & TexDecodeFlags::TO_CLUT8) != 0; + + if (toClut8 && format != GE_TFMT_CLUT8 && format != GE_TFMT_CLUT4) { + _dbg_assert_(false); + } bool swizzled = gstate.isTextureSwizzled(); if ((texaddr & 0x00600000) != 0 && Memory::IsVRAMAddress(texaddr)) { @@ -1511,6 +1608,15 @@ CheckAlphaResult TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, G texptr = (u8 *)tmpTexBuf32_.data(); } + if (toClut8) { + // We just need to expand from 4 to 8 bits. + for (int y = 0; y < h; ++y) { + Expand4To8Bits((u8 *)out + outPitch * y, texptr + (bufw * y) / 2, w); + } + // We can't know anything about alpha. + return CHECKALPHA_ANY; + } + switch (clutformat) { case GE_CMODE_16BIT_BGR5650: case GE_CMODE_16BIT_ABGR5551: @@ -1573,6 +1679,19 @@ CheckAlphaResult TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, G break; case GE_TFMT_CLUT8: + if (toClut8) { + if (gstate.isTextureSwizzled()) { + tmpTexBuf32_.resize(bufw * ((h + 7) & ~7)); + UnswizzleFromMem(tmpTexBuf32_.data(), bufw, texptr, bufw, h, 1); + texptr = (u8 *)tmpTexBuf32_.data(); + } + // After deswizzling, we are in the correct format and can just copy. + for (int y = 0; y < h; ++y) { + memcpy((u8 *)out + outPitch * y, texptr + (bufw * y), w); + } + // We can't know anything about alpha. + return CHECKALPHA_ANY; + } return ReadIndexedTex(out, outPitch, level, texptr, 1, bufw, reverseColors, expandTo32bit); case GE_TFMT_CLUT16: @@ -1858,10 +1977,18 @@ void TextureCacheCommon::ApplyTexture() { InvalidateLastTexture(); } - entry->lastFrame = gpuStats.numFlips; - BindTexture(entry); - gstate_c.SetTextureFullAlpha(entry->GetAlphaStatus() == TexCacheEntry::STATUS_ALPHA_FULL); - gstate_c.SetTextureIs3D((entry->status & TexCacheEntry::STATUS_3D) != 0); + if (entry->status & TexCacheEntry::STATUS_CLUT_GPU) { + // Special process. + ApplyTextureDepal(entry); + entry->lastFrame = gpuStats.numFlips; + gstate_c.SetTextureFullAlpha(false); + gstate_c.SetTextureIs3D(false); + } else { + entry->lastFrame = gpuStats.numFlips; + BindTexture(entry); + gstate_c.SetTextureFullAlpha(entry->GetAlphaStatus() == TexCacheEntry::STATUS_ALPHA_FULL); + gstate_c.SetTextureIs3D((entry->status & TexCacheEntry::STATUS_3D) != 0); + } } static bool CanDepalettize(GETextureFormat texFormat, GEBufferFormat bufferFormat) { @@ -1876,10 +2003,13 @@ static bool CanDepalettize(GETextureFormat texFormat, GEBufferFormat bufferForma } break; case GE_FORMAT_8888: - if (texFormat == GE_TFMT_CLUT32) { + if (texFormat == GE_TFMT_CLUT32 || texFormat == GE_TFMT_CLUT8) { // clut8 takes a special depal mode. return true; } break; + case GE_FORMAT_CLUT8: + // Shouldn't happen here. + return false; } WARN_LOG(G3D, "Invalid CLUT/framebuffer combination: %s vs %s", GeTextureFormatToString(texFormat), GeBufferFormatToString(bufferFormat)); return false; @@ -1918,7 +2048,6 @@ static bool CanUseSmoothDepal(const GPUgstate &gstate, GEBufferFormat framebuffe return false; } - void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer, GETextureFormat texFormat, RasterChannel channel) { Draw2DPipeline *textureShader = nullptr; uint32_t clutMode = gstate.clutformat & 0xFFFFFF; @@ -1964,9 +2093,16 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer samplerKey.mipEnable = false; ApplySamplingParams(samplerKey); - // Since we started/ended render passes, might need these. + ShaderDepalMode mode = ShaderDepalMode::NORMAL; + if (texFormat == GE_TFMT_CLUT8 && framebuffer->fb_format == GE_FORMAT_8888) { + mode = ShaderDepalMode::CLUT8_8888; + smoothedDepal = false; // just in case + } else if (smoothedDepal) { + mode = ShaderDepalMode::SMOOTHED; + } + gstate_c.Dirty(DIRTY_DEPAL); - gstate_c.SetUseShaderDepal(smoothedDepal ? ShaderDepalMode::SMOOTHED : ShaderDepalMode::NORMAL); + gstate_c.SetUseShaderDepal(mode); gstate_c.depalFramebufferFormat = framebuffer->fb_format; const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16); @@ -2062,8 +2198,100 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer SamplerCacheKey samplerKey = GetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight); ApplySamplingParams(samplerKey); - // Since we started/ended render passes, might need these. - gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); + // Since we've drawn using thin3d, might need these. + gstate_c.Dirty(DIRTY_ALL_RENDER_STATE); +} + +// Applies depal to a normal (non-framebuffer) texture, pre-decoded to CLUT8 format. +void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { + uint32_t clutMode = gstate.clutformat & 0xFFFFFF; + + switch (entry->format) { + case GE_TFMT_CLUT4: + case GE_TFMT_CLUT8: + break; // These are OK + default: + _dbg_assert_(false); + return; + } + + const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); + u32 depthUpperBits = 0; + + // The CLUT texture is dynamic, it's the framebuffer pointed to by clutRenderAddress. + // Instead of texturing directly from that, we copy to a temporary CLUT texture. + GEBufferFormat expectedCLUTBufferFormat = (GEBufferFormat)clutFormat; // All entries from clutFormat correspond directly to buffer formats. + + // OK, figure out what format we want our framebuffer in, so it can be reinterpreted if needed. + // If no reinterpretation is needed, we'll automatically just get a copy shader. + float scaleFactorX = 1.0f; + Draw2DPipeline *reinterpret = framebufferManager_->GetReinterpretPipeline(clutRenderFormat_, expectedCLUTBufferFormat, &scaleFactorX); + framebufferManager_->BlitUsingRaster( + dynamicClutTemp_, 0.0f, 0.0f, 512.0f, 1.0f, dynamicClutFbo_, 0.0f, 0.0f, scaleFactorX * 512.0f, 1.0f, false, 1.0f, reinterpret, "reinterpret_clut"); + + Draw2DPipeline *textureShader = textureShaderCache_->GetDepalettizeShader(clutMode, GE_TFMT_CLUT8, GE_FORMAT_CLUT8, false, 0); + gstate_c.SetUseShaderDepal(ShaderDepalMode::OFF); + + int texWidth = gstate.getTextureWidth(0); + int texHeight = gstate.getTextureHeight(0); + + // If min is not < max, then we don't have values (wasn't set during decode.) + const KnownVertexBounds &bounds = gstate_c.vertBounds; + float u1 = 0.0f; + float v1 = 0.0f; + float u2 = texWidth; + float v2 = texHeight; + if (bounds.minV < bounds.maxV) { + u1 = (bounds.minU + gstate_c.curTextureXOffset) * texWidth; + v1 = (bounds.minV + gstate_c.curTextureYOffset) * texHeight; + u2 = (bounds.maxU + gstate_c.curTextureXOffset) * texWidth; + v2 = (bounds.maxV + gstate_c.curTextureYOffset) * texHeight; + // We need to reapply the texture next time since we cropped UV. + gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); + } + + Draw::Framebuffer *depalFBO = framebufferManager_->GetTempFBO(TempFBO::DEPAL, texWidth, texHeight); + draw_->BindTexture(0, nullptr); + draw_->BindTexture(1, nullptr); + draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Depal"); + draw_->InvalidateFramebuffer(Draw::FB_INVALIDATION_STORE, Draw::FB_DEPTH_BIT | Draw::FB_STENCIL_BIT); + draw_->SetScissorRect(u1, v1, u2 - u1, v2 - v1); + Draw::Viewport vp{ 0.0f, 0.0f, (float)texWidth, (float)texHeight, 0.0f, 1.0f }; + draw_->SetViewports(1, &vp); + + draw_->BindNativeTexture(0, GetNativeTextureView(entry)); + draw_->BindFramebufferAsTexture(dynamicClutFbo_, 1, Draw::FB_COLOR_BIT, 0); + Draw::SamplerState *nearest = textureShaderCache_->GetSampler(false); + Draw::SamplerState *clutSampler = textureShaderCache_->GetSampler(false); + draw_->BindSamplerStates(0, 1, &nearest); + draw_->BindSamplerStates(1, 1, &clutSampler); + + draw2D_->Blit(textureShader, u1, v1, u2, v2, u1, v1, u2, v2, texWidth, texHeight, texWidth, texHeight, false, 1); + + gpuStats.numDepal++; + + gstate_c.curTextureWidth = texWidth; + + draw_->BindTexture(0, nullptr); + framebufferManager_->RebindFramebuffer("ApplyTextureFramebuffer"); + + draw_->BindFramebufferAsTexture(depalFBO, 0, Draw::FB_COLOR_BIT, 0); + BoundFramebufferTexture(); + + const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16); + const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor; + + // We don't know about alpha at all. + gstate_c.SetTextureFullAlpha(false); + + draw_->InvalidateCachedState(); + shaderManager_->DirtyLastShader(); + + SamplerCacheKey samplerKey = GetFramebufferSamplingParams(texWidth, texHeight); + ApplySamplingParams(samplerKey); + + // Since we've drawn using thin3d, might need these. + gstate_c.Dirty(DIRTY_ALL_RENDER_STATE); } void TextureCacheCommon::Clear(bool delete_them) { @@ -2085,6 +2313,15 @@ void TextureCacheCommon::Clear(bool delete_them) { secondCacheSizeEstimate_ = 0; } videos_.clear(); + + if (dynamicClutFbo_) { + dynamicClutFbo_->Release(); + dynamicClutFbo_ = nullptr; + } + if (dynamicClutTemp_) { + dynamicClutTemp_->Release(); + dynamicClutTemp_ = nullptr; + } } void TextureCacheCommon::DeleteTexture(TexCache::iterator it) { @@ -2402,14 +2639,32 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt } } - if (isPPGETexture) { - plan.replaced = &replacer_.FindNone(); - plan.replaceValid = false; + bool canReplace = !isPPGETexture; + if (entry->status & TexCacheEntry::TexStatus::STATUS_CLUT_GPU) { + _dbg_assert_(entry->format == GE_TFMT_CLUT4 || entry->format == GE_TFMT_CLUT8); + plan.decodeToClut8 = true; + // We only support 1 mip level when doing CLUT on GPU for now. + // Supporting more would be possible, just not very interesting until we need it. + plan.levelsToCreate = 1; + plan.levelsToLoad = 1; + plan.maxPossibleLevels = 1; + plan.scaleFactor = 1; + plan.saveTexture = false; // Can't yet save these properly. + canReplace = false; } else { + plan.decodeToClut8 = false; + } + + if (canReplace) { plan.replaced = &FindReplacement(entry, plan.w, plan.h, plan.depth); plan.replaceValid = plan.replaced->Valid(); + } else { + plan.replaced = &replacer_.FindNone(); + plan.replaceValid = false; } + // NOTE! Last chance to change scale factor here! + plan.saveTexture = false; if (plan.replaceValid) { // We're replacing, so we won't scale. @@ -2420,7 +2675,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt // But, we still need to create the texture at a larger size. plan.replaced->GetSize(0, plan.createW, plan.createH); } else { - if (replacer_.Enabled() && !plan.replaceValid && plan.depth == 1) { + if (replacer_.Enabled() && !plan.replaceValid && plan.depth == 1 && canReplace) { ReplacedTextureDecodeInfo replacedInfo; // TODO: Do we handle the race where a replacement becomes valid AFTER this but before we save? replacedInfo.cachekey = entry->CacheKey(); @@ -2445,7 +2700,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt plan.levelsToLoad = 1; } - if (plan.isVideo || plan.depth != 1) { + if (plan.isVideo || plan.depth != 1 || plan.decodeToClut8) { plan.maxPossibleLevels = 1; } else { plan.maxPossibleLevels = log2i(std::min(plan.createW, plan.createH)) + 1; @@ -2492,6 +2747,9 @@ void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, i if (!gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || dstFmt == Draw::DataFormat::R8G8B8A8_UNORM) { texDecFlags |= TexDecodeFlags::EXPAND32; } + if (entry.status & TexCacheEntry::STATUS_CLUT_GPU) { + texDecFlags |= TexDecodeFlags::TO_CLUT8; + } CheckAlphaResult alphaResult = DecodeTextureLevel((u8 *)pixelData, decPitch, tfmt, clutformat, texaddr, srcLevel, bufw, texDecFlags); entry.SetAlphaStatus(alphaResult, srcLevel); @@ -2540,7 +2798,7 @@ CheckAlphaResult TextureCacheCommon::CheckCLUTAlpha(const uint8_t *pixelData, GE // Never has any alpha. return CHECKALPHA_FULL; default: - return CheckAlpha32((const u32 *)pixelData, w, 0xFF000000); // note, the normal order here, unlike the 16-bit formats + return CheckAlpha32((const u32 *)pixelData, w, 0xFF000000); } } diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 78ad53df1f..9922e9070a 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -53,6 +53,7 @@ class ShaderManagerCommon; enum class TexDecodeFlags { EXPAND32 = 1, REVERSE_COLORS = 2, + TO_CLUT8 = 4, }; ENUM_CLASS_BITOPS(TexDecodeFlags); @@ -108,6 +109,8 @@ struct TextureDefinition { // NOTE: These only handle textures loaded directly from PSP memory contents. // Framebuffer textures do not have entries, we bind the framebuffers directly. +// At one point we might merge the concepts of framebuffers and textures, but that +// moment is far away. struct TexCacheEntry { ~TexCacheEntry() { if (texturePtr || textureName || vkTex) @@ -144,6 +147,8 @@ struct TexCacheEntry { STATUS_FORCE_REBUILD = 0x2000, STATUS_3D = 0x4000, + + STATUS_CLUT_GPU = 0x8000, }; // Status, but int so we can zero initialize. @@ -281,6 +286,9 @@ struct BuildTexturePlan { bool replaceValid; bool saveTexture; + // TODO: Expand32 should probably also be decided in PrepareBuildTexture. + bool decodeToClut8; + void GetMipSize(int level, int *w, int *h) const { if (replaceValid) { replaced->GetSize(level, *w, *h); @@ -343,6 +351,7 @@ public: virtual bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) { return false; } protected: + virtual void *GetNativeTextureView(const TexCacheEntry *entry) = 0; bool PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEntry *entry); virtual void BindTexture(TexCacheEntry *entry) = 0; @@ -352,6 +361,7 @@ protected: void Decimate(bool forcePressure = false); void ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer, GETextureFormat texFormat, RasterChannel channel); + void ApplyTextureDepal(TexCacheEntry *entry); void HandleTextureChange(TexCacheEntry *const entry, const char *reason, bool initialMatch, bool doDelete); virtual void BuildTexture(TexCacheEntry *const entry) = 0; @@ -476,10 +486,16 @@ protected: u32 clutMaxBytes_ = 0; u32 clutRenderAddress_ = 0xFFFFFFFF; u32 clutRenderOffset_; + GEBufferFormat clutRenderFormat_; + // True if the clut is just alpha values in the same order (RGBA4444-bit only.) bool clutAlphaLinear_ = false; u16 clutAlphaLinearColor_; + // Facilities for GPU depal of static textures. + Draw::Framebuffer *dynamicClutTemp_ = nullptr; + Draw::Framebuffer *dynamicClutFbo_ = nullptr; + int standardScaleFactor_; int shaderScaleFactor_ = 0; diff --git a/GPU/Common/TextureShaderCommon.cpp b/GPU/Common/TextureShaderCommon.cpp index f364016e72..2860389fc2 100644 --- a/GPU/Common/TextureShaderCommon.cpp +++ b/GPU/Common/TextureShaderCommon.cpp @@ -102,7 +102,7 @@ ClutTexture TextureShaderCache::GetClutTexture(GEPaletteFormat clutFormat, const int lastA = 0; int rampLength = 0; - // Quick check for how many continouosly growing entries we have at the start. + // Quick check for how many continuously growing entries we have at the start. // Bilinearly filtering CLUTs only really makes sense for this kind of ramp. for (int i = 0; i < maxClutEntries; i++) { rampLength = i; @@ -209,7 +209,6 @@ Draw2DPipeline *TextureShaderCache::GetDepalettizeShader(uint32_t clutMode, GETe config.smoothedDepal = smoothedDepal; config.depthUpperBits = depthUpperBits; - char *buffer = new char[4096]; Draw2DPipeline *ts = draw2D_->Create2DPipeline([=](ShaderWriter &writer) -> Draw2DPipelineInfo { GenerateDepalFs(writer, config); return Draw2DPipelineInfo{ @@ -219,7 +218,6 @@ Draw2DPipeline *TextureShaderCache::GetDepalettizeShader(uint32_t clutMode, GETe samplers }; }); - delete[] buffer; depalCache_[id] = ts; diff --git a/GPU/Common/VertexDecoderArm.cpp b/GPU/Common/VertexDecoderArm.cpp index e3d5b11f6b..2b93b563f8 100644 --- a/GPU/Common/VertexDecoderArm.cpp +++ b/GPU/Common/VertexDecoderArm.cpp @@ -872,22 +872,14 @@ void VertexDecoderJitCache::Jit_NormalFloat() { STMIA(scratchReg, false, 3, tempReg1, tempReg2, tempReg3); } -// Through expands into floats, always. Might want to look at changing this. void VertexDecoderJitCache::Jit_PosS8Through() { - DEBUG_LOG_REPORT_ONCE(vertexS8Through, G3D, "Using S8 positions in throughmode"); _dbg_assert_msg_(fpScratchReg + 1 == fpScratchReg2, "VertexDecoder fpScratchRegs must be in order."); _dbg_assert_msg_(fpScratchReg2 + 1 == fpScratchReg3, "VertexDecoder fpScratchRegs must be in order."); - // TODO: SIMD - LDRSB(tempReg1, srcReg, dec_->posoff); - LDRSB(tempReg2, srcReg, dec_->posoff + 1); - LDRB(tempReg3, srcReg, dec_->posoff + 2); - static const ARMReg tr[3] = { tempReg1, tempReg2, tempReg3 }; - static const ARMReg fr[3] = { fpScratchReg, fpScratchReg2, fpScratchReg3 }; + // 8-bit positions in throughmode always decode to 0, depth included. + VEOR(neonScratchReg, neonScratchReg, neonScratchReg); + VEOR(neonScratchReg2, neonScratchReg, neonScratchReg); ADD(scratchReg, dstReg, dec_->decFmt.posoff); - VMOV(neonScratchReg, tempReg1, tempReg2); - VMOV(neonScratchReg2, tempReg3, tempReg3); - VCVT(F_32 | I_SIGNED, neonScratchRegQ, neonScratchRegQ); VST1(F_32, neonScratchReg, scratchReg, 2, ALIGN_NONE); } diff --git a/GPU/Common/VertexDecoderArm64.cpp b/GPU/Common/VertexDecoderArm64.cpp index 5ff1d605f3..0ad04dbe5c 100644 --- a/GPU/Common/VertexDecoderArm64.cpp +++ b/GPU/Common/VertexDecoderArm64.cpp @@ -668,15 +668,11 @@ void VertexDecoderJitCache::Jit_PosFloat() { } void VertexDecoderJitCache::Jit_PosS8Through() { - LDRSB(INDEX_UNSIGNED, tempReg1, srcReg, dec_->posoff); - LDRSB(INDEX_UNSIGNED, tempReg2, srcReg, dec_->posoff + 1); - LDRB(INDEX_UNSIGNED, tempReg3, srcReg, dec_->posoff + 2); - fp.SCVTF(fpScratchReg, tempReg1); - fp.SCVTF(fpScratchReg2, tempReg2); - fp.SCVTF(fpScratchReg3, tempReg3); + // 8-bit positions in throughmode always decode to 0, depth included. + fp.EOR(fpScratchReg, fpScratchReg, fpScratchReg); STR(INDEX_UNSIGNED, fpScratchReg, dstReg, dec_->decFmt.posoff); - STR(INDEX_UNSIGNED, fpScratchReg2, dstReg, dec_->decFmt.posoff + 4); - STR(INDEX_UNSIGNED, fpScratchReg3, dstReg, dec_->decFmt.posoff + 8); + STR(INDEX_UNSIGNED, fpScratchReg, dstReg, dec_->decFmt.posoff + 4); + STR(INDEX_UNSIGNED, fpScratchReg, dstReg, dec_->decFmt.posoff + 8); } void VertexDecoderJitCache::Jit_PosS16Through() { diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index a9b4a1039a..62eb20ef84 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -773,14 +773,20 @@ void VertexDecoder::Step_PosFloatSkin() const Vec3ByMatrix43(pos, fn, skinMatrix); } -void VertexDecoder::Step_PosS8Through() const -{ +void VertexDecoder::Step_PosInvalid() const { + // Invalid positions are just culled. Simulate by forcing invalid values. float *v = (float *)(decoded_ + decFmt.posoff); - const s8 *sv = (const s8 *)(ptr_ + posoff); - const u8 *uv = (const u8 *)(ptr_ + posoff); - v[0] = sv[0]; - v[1] = sv[1]; - v[2] = uv[2]; + v[0] = std::numeric_limits::infinity(); + v[1] = std::numeric_limits::infinity(); + v[2] = std::numeric_limits::infinity(); +} + +void VertexDecoder::Step_PosS8Through() const { + // 8-bit positions in throughmode always decode to 0, depth included. + float *v = (float *)(decoded_ + decFmt.posoff); + v[0] = 0; + v[1] = 0; + v[2] = 0; } void VertexDecoder::Step_PosS16Through() const @@ -1023,35 +1029,35 @@ static const StepFunction nrmstep_morphskin[4] = { }; static const StepFunction posstep[4] = { - &VertexDecoder::Step_PosS8, + &VertexDecoder::Step_PosInvalid, &VertexDecoder::Step_PosS8, &VertexDecoder::Step_PosS16, &VertexDecoder::Step_PosFloat, }; static const StepFunction posstep_skin[4] = { - &VertexDecoder::Step_PosS8Skin, + &VertexDecoder::Step_PosInvalid, &VertexDecoder::Step_PosS8Skin, &VertexDecoder::Step_PosS16Skin, &VertexDecoder::Step_PosFloatSkin, }; static const StepFunction posstep_morph[4] = { - &VertexDecoder::Step_PosS8Morph, + &VertexDecoder::Step_PosInvalid, &VertexDecoder::Step_PosS8Morph, &VertexDecoder::Step_PosS16Morph, &VertexDecoder::Step_PosFloatMorph, }; static const StepFunction posstep_morph_skin[4] = { - &VertexDecoder::Step_PosS8MorphSkin, + &VertexDecoder::Step_PosInvalid, &VertexDecoder::Step_PosS8MorphSkin, &VertexDecoder::Step_PosS16MorphSkin, &VertexDecoder::Step_PosFloatMorphSkin, }; static const StepFunction posstep_through[4] = { - &VertexDecoder::Step_PosS8Through, + &VertexDecoder::Step_PosInvalid, &VertexDecoder::Step_PosS8Through, &VertexDecoder::Step_PosS16Through, &VertexDecoder::Step_PosFloatThrough, @@ -1224,9 +1230,8 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options, bool reportNoPos = false; if (!pos) { reportNoPos = true; - pos = 1; } - if (pos) { // there's always a position + if (pos >= 0) { // there's always a position size = align(size, posalign[pos]); posoff = size; size += possize[pos]; diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h index 983f76f9b2..6a06093902 100644 --- a/GPU/Common/VertexDecoderCommon.h +++ b/GPU/Common/VertexDecoderCommon.h @@ -433,6 +433,7 @@ public: void Step_PosS16MorphSkin() const; void Step_PosFloatMorphSkin() const; + void Step_PosInvalid() const; void Step_PosS8Through() const; void Step_PosS16Through() const; void Step_PosFloatThrough() const; diff --git a/GPU/Common/VertexDecoderX86.cpp b/GPU/Common/VertexDecoderX86.cpp index d684078989..3618071c76 100644 --- a/GPU/Common/VertexDecoderX86.cpp +++ b/GPU/Common/VertexDecoderX86.cpp @@ -1345,14 +1345,9 @@ void VertexDecoderJitCache::Jit_NormalFloatSkin() { // Through expands into floats, always. Might want to look at changing this. void VertexDecoderJitCache::Jit_PosS8Through() { - DEBUG_LOG_REPORT_ONCE(vertexS8Through, G3D, "Using S8 positions in throughmode"); // SIMD doesn't really matter since this isn't useful on hardware. + XORPS(fpScratchReg, R(fpScratchReg)); for (int i = 0; i < 3; i++) { - if (i == 2) - MOVZX(32, 8, tempReg1, MDisp(srcReg, dec_->posoff + i)); - else - MOVSX(32, 8, tempReg1, MDisp(srcReg, dec_->posoff + i)); - CVTSI2SS(fpScratchReg, R(tempReg1)); MOVSS(MDisp(dstReg, dec_->decFmt.posoff + i * 4), fpScratchReg); } } @@ -1396,7 +1391,13 @@ void VertexDecoderJitCache::Jit_PosFloatThrough() { CVTTPS2DQ(fpScratchReg, R(fpScratchReg)); // Use pack to saturate to 0,65535. - PACKUSDW(fpScratchReg, R(fpScratchReg)); + if (cpu_info.bSSE4_1) { + PACKUSDW(fpScratchReg, R(fpScratchReg)); + } else { + PSLLD(fpScratchReg, 16); + PSRAD(fpScratchReg, 16); + PACKSSDW(fpScratchReg, R(fpScratchReg)); + } PUNPCKLWD(fpScratchReg, R(fpScratchReg2)); CVTDQ2PS(fpScratchReg, R(fpScratchReg)); diff --git a/GPU/Common/VertexShaderGenerator.cpp b/GPU/Common/VertexShaderGenerator.cpp index ccda9af86b..24f118603f 100644 --- a/GPU/Common/VertexShaderGenerator.cpp +++ b/GPU/Common/VertexShaderGenerator.cpp @@ -127,9 +127,6 @@ static const char * const boneWeightDecl[9] = { "layout(location = 3) in vec4 w1;\nlayout(location = 4) in vec4 w2;\n", }; -extern const char *vulkan_glsl_preamble_vs; -extern const char *hlsl_preamble_vs; - bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguageDesc &compat, Draw::Bugs bugs, uint32_t *attrMask, uint64_t *uniformMask, std::string *errorString) { *attrMask = 0; *uniformMask = 0; @@ -142,10 +139,11 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag if (gl_extensions.EXT_gpu_shader4) { gl_exts.push_back("#extension GL_EXT_gpu_shader4 : enable"); } - if (gl_extensions.EXT_clip_cull_distance && id.Bit(VS_BIT_VERTEX_RANGE_CULLING)) { + bool useClamp = gstate_c.Supports(GPU_SUPPORTS_DEPTH_CLAMP) && !id.Bit(VS_BIT_IS_THROUGH); + if (gl_extensions.EXT_clip_cull_distance && (id.Bit(VS_BIT_VERTEX_RANGE_CULLING) || useClamp)) { gl_exts.push_back("#extension GL_EXT_clip_cull_distance : enable"); } - if (gl_extensions.APPLE_clip_distance && id.Bit(VS_BIT_VERTEX_RANGE_CULLING)) { + if (gl_extensions.APPLE_clip_distance && (id.Bit(VS_BIT_VERTEX_RANGE_CULLING) || useClamp)) { gl_exts.push_back("#extension GL_APPLE_clip_distance : enable"); } if (gl_extensions.ARB_cull_distance && id.Bit(VS_BIT_VERTEX_RANGE_CULLING)) { @@ -176,7 +174,6 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag bool hasColor = id.Bit(VS_BIT_HAS_COLOR) || !useHWTransform; bool hasNormal = id.Bit(VS_BIT_HAS_NORMAL) && useHWTransform; bool hasTexcoord = id.Bit(VS_BIT_HAS_TEXCOORD) || !useHWTransform; - bool enableFog = id.Bit(VS_BIT_ENABLE_FOG); bool flipNormal = id.Bit(VS_BIT_NORM_REVERSE); int ls0 = id.Bits(VS_BIT_LS0, 2); int ls1 = id.Bits(VS_BIT_LS1, 2); @@ -184,6 +181,12 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag bool enableLighting = id.Bit(VS_BIT_LIGHTING_ENABLE); int matUpdate = id.Bits(VS_BIT_MATERIAL_UPDATE, 3); + bool lightUberShader = id.Bit(VS_BIT_LIGHT_UBERSHADER) && enableLighting; // checking lighting here for the shader test's benefit, in reality if ubershader is set, lighting is set. + if (lightUberShader && !compat.bitwiseOps) { + *errorString = "Light ubershader requires bitwise ops in shader language"; + return false; + } + // Apparently we don't support bezier/spline together with bones. bool doBezier = id.Bit(VS_BIT_BEZIER) && !enableBones && useHWTransform; bool doSpline = id.Bit(VS_BIT_SPLINE) && !enableBones && useHWTransform; @@ -227,6 +230,9 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag bool texCoordInVec3 = false; bool vertexRangeCulling = id.Bit(VS_BIT_VERTEX_RANGE_CULLING) && !isModeThrough; + bool clipClampedDepth = !isModeThrough && gstate_c.Supports(GPU_SUPPORTS_DEPTH_CLAMP) && gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE); + const char *clipClampedDepthSuffix = "[0]"; + const char *vertexRangeClipSuffix = clipClampedDepth ? "[1]" : "[0]"; if (compat.shaderLanguage == GLSL_VULKAN) { WRITE(p, "\n"); @@ -247,7 +253,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag if (useHWTransform && hasNormal) WRITE(p, "layout (location = %d) in vec3 normal;\n", (int)PspAttributeLocation::NORMAL); - if (!useHWTransform && enableFog) + if (!useHWTransform) WRITE(p, "layout (location = %d) in float fog;\n", (int)PspAttributeLocation::NORMAL); if (doTexture && hasTexcoord) { @@ -272,11 +278,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, "layout (location = 0) out highp vec3 v_texcoord;\n"); } - if (enableFog) { - // See the fragment shader generator - WRITE(p, "layout (location = 3) out highp float v_fogdepth;\n"); - } - + WRITE(p, "layout (location = 3) out highp float v_fogdepth;\n"); WRITE(p, "invariant gl_Position;\n"); } else if (compat.shaderLanguage == HLSL_D3D11 || compat.shaderLanguage == HLSL_D3D9) { @@ -295,9 +297,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, "float u_rotation : register(c%i);\n", CONST_VS_ROTATION); } - if (enableFog) { - WRITE(p, "vec2 u_fogcoef : register(c%i);\n", CONST_VS_FOGCOEF); - } + WRITE(p, "vec2 u_fogcoef : register(c%i);\n", CONST_VS_FOGCOEF); if (useHWTransform || !hasColor) WRITE(p, "vec4 u_matambientalpha : register(c%i);\n", CONST_VS_MATAMBIENTALPHA); // matambient + matalpha @@ -319,6 +319,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag if (doTexture) { WRITE(p, "vec4 u_uvscaleoffset : register(c%i);\n", CONST_VS_UVSCALEOFFSET); } + // No need for light ubershader support here, D3D9 doesn't do it. for (int i = 0; i < 4; i++) { if (doLight[i] != LIGHT_OFF) { // This is needed for shade mapping @@ -397,9 +398,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag if (lmode) { WRITE(p, " vec3 color1 : COLOR1;\n"); } - if (enableFog) { - WRITE(p, " float fog : NORMAL;\n"); - } + WRITE(p, " float fog : NORMAL;\n"); WRITE(p, "};\n"); } @@ -412,15 +411,20 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag if (lmode) WRITE(p, " vec3 v_color1 : COLOR1;\n"); - if (enableFog) { - WRITE(p, " float v_fogdepth: TEXCOORD1;\n"); - } + WRITE(p, " float v_fogdepth : TEXCOORD1;\n"); if (compat.shaderLanguage == HLSL_D3D9) { WRITE(p, " vec4 gl_Position : POSITION;\n"); } else { WRITE(p, " vec4 gl_Position : SV_Position;\n"); - if (vertexRangeCulling && gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) { - WRITE(p, " float gl_ClipDistance : SV_ClipDistance0;\n"); + bool clipRange = vertexRangeCulling && gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE); + if (clipClampedDepth && clipRange) { + WRITE(p, " float2 gl_ClipDistance : SV_ClipDistance;\n"); + clipClampedDepthSuffix = ".x"; + vertexRangeClipSuffix = ".y"; + } else if (clipClampedDepth || clipRange) { + WRITE(p, " float gl_ClipDistance : SV_ClipDistance;\n"); + clipClampedDepthSuffix = ""; + vertexRangeClipSuffix = ""; } if (vertexRangeCulling && gstate_c.Supports(GPU_SUPPORTS_CULL_DISTANCE)) { WRITE(p, " float2 gl_CullDistance : SV_CullDistance0;\n"); @@ -449,7 +453,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, "%s mediump vec3 normal;\n", compat.attribute); *attrMask |= 1 << ATTR_NORMAL; } - if (!useHWTransform && enableFog) { + if (!useHWTransform) { WRITE(p, "%s highp float fog;\n", compat.attribute); *attrMask |= 1 << ATTR_NORMAL; } @@ -511,28 +515,32 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, "uniform vec4 u_uvscaleoffset;\n"); *uniformMask |= DIRTY_UVSCALEOFFSET; } + if (lightUberShader) { + p.C("uniform uint u_lightControl;\n"); + *uniformMask |= DIRTY_LIGHT_CONTROL; + } for (int i = 0; i < 4; i++) { - if (doLight[i] != LIGHT_OFF) { + if (lightUberShader || doLight[i] != LIGHT_OFF) { // This is needed for shade mapping WRITE(p, "uniform vec3 u_lightpos%i;\n", i); *uniformMask |= DIRTY_LIGHT0 << i; } - if (doLight[i] == LIGHT_FULL) { + if (lightUberShader || doLight[i] == LIGHT_FULL) { *uniformMask |= DIRTY_LIGHT0 << i; GELightType type = static_cast(id.Bits(VS_BIT_LIGHT0_TYPE + 4 * i, 2)); GELightComputation comp = static_cast(id.Bits(VS_BIT_LIGHT0_COMP + 4 * i, 2)); - if (type != GE_LIGHTTYPE_DIRECTIONAL) + if (lightUberShader || type != GE_LIGHTTYPE_DIRECTIONAL) WRITE(p, "uniform mediump vec3 u_lightatt%i;\n", i); - if (type == GE_LIGHTTYPE_SPOT || type == GE_LIGHTTYPE_UNKNOWN) { + if (lightUberShader || type == GE_LIGHTTYPE_SPOT || type == GE_LIGHTTYPE_UNKNOWN) { WRITE(p, "uniform mediump vec3 u_lightdir%i;\n", i); WRITE(p, "uniform mediump vec2 u_lightangle_spotCoef%i;\n", i); } WRITE(p, "uniform lowp vec3 u_lightambient%i;\n", i); WRITE(p, "uniform lowp vec3 u_lightdiffuse%i;\n", i); - if (comp == GE_LIGHTCOMP_BOTH) { + if (lightUberShader || comp == GE_LIGHTCOMP_BOTH) { WRITE(p, "uniform lowp vec3 u_lightspecular%i;\n", i); } } @@ -540,7 +548,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag if (enableLighting) { WRITE(p, "uniform lowp vec4 u_ambient;\n"); *uniformMask |= DIRTY_AMBIENT; - if ((matUpdate & 2) == 0 || !hasColor) { + if (lightUberShader || (matUpdate & 2) == 0 || !hasColor) { WRITE(p, "uniform lowp vec3 u_matdiffuse;\n"); *uniformMask |= DIRTY_MATDIFFUSE; } @@ -561,10 +569,8 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, "uniform lowp vec4 u_matambientalpha;\n"); // matambient + matalpha *uniformMask |= DIRTY_MATAMBIENTALPHA; } - if (enableFog) { - WRITE(p, "uniform highp vec2 u_fogcoef;\n"); - *uniformMask |= DIRTY_FOGCOEF; - } + WRITE(p, "uniform highp vec2 u_fogcoef;\n"); + *uniformMask |= DIRTY_FOGCOEF; if (!isModeThrough) { WRITE(p, "uniform highp vec4 u_depthRange;\n"); @@ -582,13 +588,11 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, "%s %s vec3 v_texcoord;\n", compat.varying_vs, highpTexcoord ? "highp" : "mediump"); } - if (enableFog) { - // See the fragment shader generator - if (highpFog) { - WRITE(p, "%s highp float v_fogdepth;\n", compat.varying_vs); - } else { - WRITE(p, "%s mediump float v_fogdepth;\n", compat.varying_vs); - } + // See the fragment shader generator + if (highpFog) { + WRITE(p, "%s highp float v_fogdepth;\n", compat.varying_vs); + } else { + WRITE(p, "%s mediump float v_fogdepth;\n", compat.varying_vs); } } @@ -716,7 +720,6 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, " vec4 basis_u = tess_weights_u[weight_idx.x].basis;\n"); WRITE(p, " vec4 basis_v = tess_weights_v[weight_idx.y].basis;\n"); WRITE(p, " mat4 basis = outerProduct(basis_u, basis_v);\n"); - } else { WRITE(p, " int index_u, index_v;\n"); for (int i = 0; i < 4; i++) { @@ -799,7 +802,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag } else { WRITE(p, " vec4 position = In.position;\n"); } - if (!useHWTransform && enableFog) { + if (!useHWTransform) { WRITE(p, " float fog = In.fog;\n"); } if (enableBones) { @@ -825,9 +828,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag if (lmode) WRITE(p, " %sv_color1 = splat3(0.0);\n", compat.vsOutPrefix); } - if (enableFog) { - WRITE(p, " %sv_fogdepth = fog;\n", compat.vsOutPrefix); - } + WRITE(p, " %sv_fogdepth = fog;\n", compat.vsOutPrefix); if (isModeThrough) { // The proj_through matrix already has the rotation, if needed. WRITE(p, " vec4 outPos = mul(u_proj_through, vec4(position.xyz, 1.0));\n"); @@ -944,14 +945,25 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag // TODO: Declare variables for dots for shade mapping if needed. - const char *ambientStr = (matUpdate & 1) && hasColor ? "color0" : "u_matambientalpha"; - const char *diffuseStr = (matUpdate & 2) && hasColor ? "color0.rgb" : "u_matdiffuse"; - const char *specularStr = (matUpdate & 4) && hasColor ? "color0.rgb" : "u_matspecular.rgb"; + const char *srcCol = "color0"; if (doBezier || doSpline) { // TODO: Probably, should use hasColorTess but FF4 has a problem with drawing the background. - ambientStr = (matUpdate & 1) && hasColor ? "tess.col" : "u_matambientalpha"; - diffuseStr = (matUpdate & 2) && hasColor ? "tess.col.rgb" : "u_matdiffuse"; - specularStr = (matUpdate & 4) && hasColor ? "tess.col.rgb" : "u_matspecular.rgb"; + srcCol = "tess.col"; + } + + if (lightUberShader && hasColor) { + p.F(" vec4 ambientColor = ((u_lightControl & (1u << 20u)) != 0u) ? %s : u_matambientalpha;\n", srcCol); + if (enableLighting) { + p.F(" vec3 diffuseColor = ((u_lightControl & (1u << 21u)) != 0u) ? %s.rgb : u_matdiffuse;\n", srcCol); + p.F(" vec3 specularColor = ((u_lightControl & (1u << 22u)) != 0u) ? %s.rgb : u_matspecular.rgb;\n", srcCol); + } + } else { + // This path also takes care of the lightUberShader && !hasColor path, because all comparisons fail. + p.F(" vec4 ambientColor = %s;\n", (matUpdate & 1) && hasColor ? srcCol : "u_matambientalpha"); + if (enableLighting) { + p.F(" vec3 diffuseColor = %s.rgb;\n", (matUpdate & 2) && hasColor ? srcCol : "u_matdiffuse"); + p.F(" vec3 specularColor = %s.rgb;\n", (matUpdate & 4) && hasColor ? srcCol : "u_matspecular"); + } } bool diffuseIsZero = true; @@ -959,7 +971,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag bool distanceNeeded = false; bool anySpots = false; if (enableLighting) { - WRITE(p, " lowp vec4 lightSum0 = u_ambient * %s + vec4(u_matemissive, 0.0);\n", ambientStr); + p.C(" lowp vec4 lightSum0 = u_ambient * ambientColor + vec4(u_matemissive, 0.0);\n"); for (int i = 0; i < 4; i++) { GELightType type = static_cast(id.Bits(VS_BIT_LIGHT0_TYPE + 4*i, 2)); @@ -975,6 +987,13 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag anySpots = true; } + if (lightUberShader) { + anySpots = true; + diffuseIsZero = false; + specularIsZero = false; + distanceNeeded = true; + } + if (!specularIsZero) { WRITE(p, " lowp vec3 lightSum1 = splat3(0.0);\n"); } @@ -992,76 +1011,133 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag } } - // Calculate lights if needed. If shade mapping is enabled, lights may need to be - // at least partially calculated. - for (int i = 0; i < 4; i++) { - if (doLight[i] != LIGHT_FULL) - continue; - - GELightType type = static_cast(id.Bits(VS_BIT_LIGHT0_TYPE + 4*i, 2)); - GELightComputation comp = static_cast(id.Bits(VS_BIT_LIGHT0_COMP + 4*i, 2)); - - if (type == GE_LIGHTTYPE_DIRECTIONAL) { - // We prenormalize light positions for directional lights. - WRITE(p, " toLight = u_lightpos%i;\n", i); - } else { - WRITE(p, " toLight = u_lightpos%i - worldpos;\n", i); - WRITE(p, " distance = length(toLight);\n"); - WRITE(p, " toLight /= distance;\n"); + if (lightUberShader) { + // TODO: Actually loop in the shader. For now, we write it all out. + // Will need to change how the data is stored to loop efficiently. + // u_lightControl is computed in PackLightControlBits(). + for (int i = 0; i < 4; i++) { + p.F(" if ((u_lightControl & %du) != 0u) { \n", 1 << i); + p.F(" uint comp = (u_lightControl >> %d) & 3u;\n", 4 + 4 * i); + p.F(" uint type = (u_lightControl >> %d) & 3u;\n", 4 + 4 * i + 2); + p.C(" if (type == 0u) {\n"); // GE_LIGHTTYPE_DIRECTIONAL + p.F(" toLight = u_lightpos%d;\n", i); + p.C(" } else {\n"); + p.F(" toLight = u_lightpos%d - worldpos;\n", i); + p.F(" distance = length(toLight);\n", i); + p.F(" toLight /= distance;\n", i); + p.C(" }\n"); + p.C(" ldot = dot(toLight, worldnormal);\n"); + p.C(" if (comp == 2u) {\n"); // GE_LIGHTCOMP_ONLYPOWDIFFUSE + p.C(" if (u_matspecular.a <= 0.0) {\n"); + p.C(" ldot = 1.0;\n"); + p.C(" } else {\n"); + p.C(" ldot = pow(max(ldot, 0.0), u_matspecular.a);\n"); + p.C(" }\n"); + p.C(" }\n"); + p.C(" switch (type) {\n"); // Attenuation + p.C(" case 1u:\n"); // GE_LIGHTTYPE_POINT + p.F(" lightScale = clamp(1.0 / dot(u_lightatt%i, vec3(1.0, distance, distance*distance)), 0.0, 1.0);\n", i); + p.C(" break;\n"); + p.C(" case 2u:\n"); // GE_LIGHTTYPE_SPOT + p.F(" angle = length(u_lightdir%i) == 0.0 ? 0.0 : dot(normalize(u_lightdir%i), toLight);\n", i, i); + p.F(" if (angle >= u_lightangle_spotCoef%i.x) {\n", i); + p.F(" lightScale = clamp(1.0 / dot(u_lightatt%i, vec3(1.0, distance, distance*distance)), 0.0, 1.0) * (u_lightangle_spotCoef%i.y <= 0.0 ? 1.0 : pow(angle, u_lightangle_spotCoef%i.y));\n", i, i, i); + p.C(" } else {\n"); + p.C(" lightScale = 0.0;\n"); + p.C(" }\n"); + p.C(" break;\n"); + p.C(" default:\n"); // GE_LIGHTTYPE_DIRECTIONAL + p.C(" lightScale = 1.0;\n"); + p.C(" break;\n"); + p.C(" }\n"); + p.F(" diffuse = (u_lightdiffuse%i * diffuseColor) * max(ldot, 0.0);\n", i); + p.C(" if (comp == 1u) {\n"); // do specular + p.C(" if (ldot >= 0.0) {\n"); + p.C(" ldot = dot(normalize(toLight + vec3(0.0, 0.0, 1.0)), worldnormal);\n"); + p.C(" if (u_matspecular.a <= 0.0) {\n"); + p.C(" ldot = 1.0;\n"); + p.C(" } else {\n"); + p.C(" ldot = pow(max(ldot, 0.0), u_matspecular.a);\n"); + p.C(" }\n"); + p.C(" if (ldot > 0.0)\n"); + p.F(" lightSum1 += u_lightspecular%i * specularColor * ldot * lightScale;\n", i); + p.C(" }\n"); + p.C(" }\n"); + p.F(" lightSum0.rgb += (u_lightambient%i * ambientColor.rgb + diffuse) * lightScale;\n", i); + p.C(" }\n"); } + } else { + // Calculate lights if needed. If shade mapping is enabled, lights may need to be + // at least partially calculated. + for (int i = 0; i < 4; i++) { + if (doLight[i] != LIGHT_FULL) + continue; - bool doSpecular = comp == GE_LIGHTCOMP_BOTH; - bool poweredDiffuse = comp == GE_LIGHTCOMP_ONLYPOWDIFFUSE; + GELightType type = static_cast(id.Bits(VS_BIT_LIGHT0_TYPE + 4 * i, 2)); + GELightComputation comp = static_cast(id.Bits(VS_BIT_LIGHT0_COMP + 4 * i, 2)); - WRITE(p, " ldot = dot(toLight, worldnormal);\n"); - if (poweredDiffuse) { - // pow(0.0, 0.0) may be undefined, but the PSP seems to treat it as 1.0. - // Seen in Tales of the World: Radiant Mythology (#2424.) - WRITE(p, " if (u_matspecular.a <= 0.0) {\n"); - WRITE(p, " ldot = 1.0;\n"); - WRITE(p, " } else {\n"); - WRITE(p, " ldot = pow(max(ldot, 0.0), u_matspecular.a);\n"); - WRITE(p, " }\n"); + if (type == GE_LIGHTTYPE_DIRECTIONAL) { + // We prenormalize light positions for directional lights. + p.F(" toLight = u_lightpos%i;\n", i); + } else { + p.F(" toLight = u_lightpos%i - worldpos;\n", i); + p.C(" distance = length(toLight);\n"); + p.C(" toLight /= distance;\n"); + } + + bool doSpecular = comp == GE_LIGHTCOMP_BOTH; + bool poweredDiffuse = comp == GE_LIGHTCOMP_ONLYPOWDIFFUSE; + + p.C(" ldot = dot(toLight, worldnormal);\n"); + if (poweredDiffuse) { + // pow(0.0, 0.0) may be undefined, but the PSP seems to treat it as 1.0. + // Seen in Tales of the World: Radiant Mythology (#2424.) + p.C(" if (u_matspecular.a <= 0.0) {\n"); + p.C(" ldot = 1.0;\n"); + p.C(" } else {\n"); + p.C(" ldot = pow(max(ldot, 0.0), u_matspecular.a);\n"); + p.C(" }\n"); + } + + const char *timesLightScale = " * lightScale"; + + // Attenuation + switch (type) { + case GE_LIGHTTYPE_DIRECTIONAL: + timesLightScale = ""; + break; + case GE_LIGHTTYPE_POINT: + p.F(" lightScale = clamp(1.0 / dot(u_lightatt%i, vec3(1.0, distance, distance*distance)), 0.0, 1.0);\n", i); + break; + case GE_LIGHTTYPE_SPOT: + case GE_LIGHTTYPE_UNKNOWN: + p.F(" angle = length(u_lightdir%i) == 0.0 ? 0.0 : dot(normalize(u_lightdir%i), toLight);\n", i, i); + p.F(" if (angle >= u_lightangle_spotCoef%i.x) {\n", i); + p.F(" lightScale = clamp(1.0 / dot(u_lightatt%i, vec3(1.0, distance, distance*distance)), 0.0, 1.0) * (u_lightangle_spotCoef%i.y <= 0.0 ? 1.0 : pow(angle, u_lightangle_spotCoef%i.y));\n", i, i, i); + p.C(" } else {\n"); + p.C(" lightScale = 0.0;\n"); + p.C(" }\n"); + break; + default: + // ILLEGAL + break; + } + + p.F(" diffuse = (u_lightdiffuse%i * diffuseColor) * max(ldot, 0.0);\n", i); + if (doSpecular) { + p.C(" if (ldot >= 0.0) {\n"); + p.C(" ldot = dot(normalize(toLight + vec3(0.0, 0.0, 1.0)), worldnormal);\n"); + p.C(" if (u_matspecular.a <= 0.0) {\n"); + p.C(" ldot = 1.0;\n"); + p.C(" } else {\n"); + p.C(" ldot = pow(max(ldot, 0.0), u_matspecular.a);\n"); + p.C(" }\n"); + p.C(" if (ldot > 0.0)\n"); + p.F(" lightSum1 += u_lightspecular%i * specularColor * ldot %s;\n", i, timesLightScale); + p.C(" }\n"); + } + p.F(" lightSum0.rgb += (u_lightambient%i * ambientColor.rgb + diffuse)%s;\n", i, timesLightScale); } - - const char *timesLightScale = " * lightScale"; - - // Attenuation - switch (type) { - case GE_LIGHTTYPE_DIRECTIONAL: - timesLightScale = ""; - break; - case GE_LIGHTTYPE_POINT: - WRITE(p, " lightScale = clamp(1.0 / dot(u_lightatt%i, vec3(1.0, distance, distance*distance)), 0.0, 1.0);\n", i); - break; - case GE_LIGHTTYPE_SPOT: - case GE_LIGHTTYPE_UNKNOWN: - WRITE(p, " angle = length(u_lightdir%i) == 0.0 ? 0.0 : dot(normalize(u_lightdir%i), toLight);\n", i, i); - WRITE(p, " if (angle >= u_lightangle_spotCoef%i.x) {\n", i); - WRITE(p, " lightScale = clamp(1.0 / dot(u_lightatt%i, vec3(1.0, distance, distance*distance)), 0.0, 1.0) * (u_lightangle_spotCoef%i.y <= 0.0 ? 1.0 : pow(angle, u_lightangle_spotCoef%i.y));\n", i, i, i); - WRITE(p, " } else {\n"); - WRITE(p, " lightScale = 0.0;\n"); - WRITE(p, " }\n"); - break; - default: - // ILLEGAL - break; - } - - WRITE(p, " diffuse = (u_lightdiffuse%i * %s) * max(ldot, 0.0);\n", i, diffuseStr); - if (doSpecular) { - WRITE(p, " if (ldot >= 0.0) {\n"); - WRITE(p, " ldot = dot(normalize(toLight + vec3(0.0, 0.0, 1.0)), worldnormal);\n"); - WRITE(p, " if (u_matspecular.a <= 0.0) {\n"); - WRITE(p, " ldot = 1.0;\n"); - WRITE(p, " } else {\n"); - WRITE(p, " ldot = pow(max(ldot, 0.0), u_matspecular.a);\n"); - WRITE(p, " }\n"); - WRITE(p, " if (ldot > 0.0)\n"); - WRITE(p, " lightSum1 += u_lightspecular%i * %s * ldot %s;\n", i, specularStr, timesLightScale); - WRITE(p, " }\n"); - } - WRITE(p, " lightSum0.rgb += (u_lightambient%i * %s.rgb + diffuse)%s;\n", i, ambientStr, timesLightScale); } if (enableLighting) { @@ -1116,7 +1192,10 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag } } else { if (hasTexcoord) { - WRITE(p, " %sv_texcoord = vec3(texcoord.xy * u_uvscaleoffset.xy + u_uvscaleoffset.zw, 0.0);\n", compat.vsOutPrefix); + if (doBezier || doSpline) + WRITE(p, " %sv_texcoord = vec3(tess.tex.xy * u_uvscaleoffset.xy + u_uvscaleoffset.zw, 0.0);\n", compat.vsOutPrefix); + else + WRITE(p, " %sv_texcoord = vec3(texcoord.xy * u_uvscaleoffset.xy + u_uvscaleoffset.zw, 0.0);\n", compat.vsOutPrefix); } else { WRITE(p, " %sv_texcoord = vec3(u_uvscaleoffset.zw, 0.0);\n", compat.vsOutPrefix); } @@ -1128,26 +1207,36 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag std::string temp_tc; switch (uvProjMode) { case GE_PROJMAP_POSITION: // Use model space XYZ as source - temp_tc = "vec4(position, 1.0)"; + if (doBezier || doSpline) + temp_tc = "vec4(tess.pos, 1.0)"; + else + temp_tc = "vec4(position, 1.0)"; break; case GE_PROJMAP_UV: // Use unscaled UV as source { // prescale is false here. if (hasTexcoord) { - temp_tc = "vec4(texcoord.xy, 0.0, 1.0)"; + if (doBezier || doSpline) + temp_tc = "vec4(tess.tex.xy, 0.0, 1.0)"; + else + temp_tc = "vec4(texcoord.xy, 0.0, 1.0)"; } else { temp_tc = "vec4(0.0, 0.0, 0.0, 1.0)"; } } break; case GE_PROJMAP_NORMALIZED_NORMAL: // Use normalized transformed normal as source - if (hasNormal) - temp_tc = StringFromFormat("length(normal) == 0.0 ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(normalize(%snormal), 1.0)", flipNormal ? "-" : ""); + if ((doBezier || doSpline) && hasNormalTess) + temp_tc = StringFromFormat("length(tess.nrm) == 0.0 ? vec4(0.0, 0.0, 0.0, 1.0) : vec4(normalize(%stess.nrm), 1.0)", flipNormalTess ? "-" : ""); + else if (hasNormal) + temp_tc = StringFromFormat("length(normal) == 0.0 ? vec4(0.0, 0.0, 0.0, 1.0) : vec4(normalize(%snormal), 1.0)", flipNormal ? "-" : ""); else temp_tc = "vec4(0.0, 0.0, 1.0, 1.0)"; break; case GE_PROJMAP_NORMAL: // Use non-normalized transformed normal as source - if (hasNormal) + if ((doBezier || doSpline) && hasNormalTess) + temp_tc = flipNormalTess ? "vec4(-tess.nrm, 1.0)" : "vec4(tess.nrm, 1.0)"; + else if (hasNormal) temp_tc = flipNormal ? "vec4(-normal, 1.0)" : "vec4(normal, 1.0)"; else temp_tc = "vec4(0.0, 0.0, 1.0, 1.0)"; @@ -1173,33 +1262,53 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag } // Compute fogdepth - if (enableFog) - WRITE(p, " %sv_fogdepth = (viewPos.z + u_fogcoef.x) * u_fogcoef.y;\n", compat.vsOutPrefix); + WRITE(p, " %sv_fogdepth = (viewPos.z + u_fogcoef.x) * u_fogcoef.y;\n", compat.vsOutPrefix); + } + + if (clipClampedDepth) { + // This should clip against minz, but only when it's above zero. + if (ShaderLanguageIsOpenGL(compat.shaderLanguage)) { + // On OpenGL/GLES, these values account for the -1 -> 1 range. + WRITE(p, " if (u_depthRange.y - u_depthRange.x >= 1.0) {\n"); + WRITE(p, " %sgl_ClipDistance%s = outPos.w + outPos.z;\n", compat.vsOutPrefix, clipClampedDepthSuffix); + } else { + // Everywhere else, it's 0 -> 1, simpler. + WRITE(p, " if (u_depthRange.y >= 1.0) {\n"); + WRITE(p, " %sgl_ClipDistance%s = outPos.z;\n", compat.vsOutPrefix, clipClampedDepthSuffix); + } + // This is similar, but for maxz when it's below 65535.0. -1/0 don't matter here. + WRITE(p, " } else if (u_depthRange.x + u_depthRange.y <= 65534.0) {\n"); + WRITE(p, " %sgl_ClipDistance%s = outPos.w - outPos.z;\n", compat.vsOutPrefix, clipClampedDepthSuffix); + WRITE(p, " } else {\n"); + WRITE(p, " %sgl_ClipDistance%s = 0.0;\n", compat.vsOutPrefix, clipClampedDepthSuffix); + WRITE(p, " }\n"); } if (vertexRangeCulling && !IsVRBuild()) { WRITE(p, " vec3 projPos = outPos.xyz / outPos.w;\n"); WRITE(p, " float projZ = (projPos.z - u_depthRange.z) * u_depthRange.w;\n"); - // Vertex range culling doesn't happen when Z clips, note sign of w is important. - WRITE(p, " if (u_cullRangeMin.w <= 0.0 || projZ * outPos.w > -outPos.w) {\n"); - const char *outMin = "projPos.x < u_cullRangeMin.x || projPos.y < u_cullRangeMin.y"; - const char *outMax = "projPos.x > u_cullRangeMax.x || projPos.y > u_cullRangeMax.y"; - WRITE(p, " if (%s || %s) {\n", outMin, outMax); - WRITE(p, " outPos.xyzw = u_cullRangeMax.wwww;\n"); - WRITE(p, " }\n"); - WRITE(p, " }\n"); - WRITE(p, " if (u_cullRangeMin.w <= 0.0) {\n"); - WRITE(p, " if (projPos.z < u_cullRangeMin.z || projPos.z > u_cullRangeMax.z) {\n"); - WRITE(p, " outPos.xyzw = u_cullRangeMax.wwww;\n"); - WRITE(p, " }\n"); - WRITE(p, " }\n"); - const char *clip0 = compat.shaderLanguage == HLSL_D3D11 ? "" : "[0]"; + if (!bugs.Has(Draw::Bugs::BROKEN_NAN_IN_CONDITIONAL)) { + // Vertex range culling doesn't happen when Z clips, note sign of w is important. + WRITE(p, " if (u_cullRangeMin.w <= 0.0 || projZ * outPos.w > -outPos.w) {\n"); + const char *outMin = "projPos.x < u_cullRangeMin.x || projPos.y < u_cullRangeMin.y"; + const char *outMax = "projPos.x > u_cullRangeMax.x || projPos.y > u_cullRangeMax.y"; + WRITE(p, " if ((%s) || (%s)) {\n", outMin, outMax); + WRITE(p, " outPos.xyzw = u_cullRangeMax.wwww;\n"); + WRITE(p, " }\n"); + WRITE(p, " }\n"); + WRITE(p, " if (u_cullRangeMin.w <= 0.0) {\n"); + WRITE(p, " if (projPos.z < u_cullRangeMin.z || projPos.z > u_cullRangeMax.z) {\n"); + WRITE(p, " outPos.xyzw = u_cullRangeMax.wwww;\n"); + WRITE(p, " }\n"); + WRITE(p, " }\n"); + } + const char *cull0 = compat.shaderLanguage == HLSL_D3D11 ? ".x" : "[0]"; const char *cull1 = compat.shaderLanguage == HLSL_D3D11 ? ".y" : "[1]"; if (gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) { - // TODO: Not rectangles... - WRITE(p, " %sgl_ClipDistance%s = projZ * outPos.w + outPos.w;\n", compat.vsOutPrefix, clip0); + // TODO: Ignore triangles from GE_PRIM_RECTANGLES in transform mode, which should not clip to neg z. + WRITE(p, " %sgl_ClipDistance%s = projZ * outPos.w + outPos.w;\n", compat.vsOutPrefix, vertexRangeClipSuffix); } if (gstate_c.Supports(GPU_SUPPORTS_CULL_DISTANCE)) { // Cull any triangle fully outside in the same direction when depth clamp enabled. diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index bc3c67da1d..eff16b543b 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -340,7 +340,7 @@ void DrawEngineD3D11::DoFlush() { textureCache_->SetTexture(); gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); textureNeedsApply = true; - } else if (gstate.getTextureAddress(0) == ((gstate.getFrameBufRawAddress() | 0x04000000) & 0x3FFFFFFF)) { + } else if (gstate.getTextureAddress(0) == (gstate.getFrameBufRawAddress() | 0x04000000)) { // This catches the case of clearing a texture. (#10957) gstate_c.Dirty(DIRTY_TEXTURE_IMAGE); } diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp index 3cfe0c4b37..bfbd12f190 100644 --- a/GPU/D3D11/GPU_D3D11.cpp +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -82,7 +82,7 @@ GPU_D3D11::GPU_D3D11(GraphicsContext *gfxCtx, Draw::DrawContext *draw) // No need to flush before the tex scale/offset commands if we are baking // the tex scale/offset into the vertices anyway. UpdateCmdInfo(); - CheckGPUFeatures(); + gstate_c.featureFlags = CheckGPUFeatures(); BuildReportingInfo(); @@ -100,40 +100,16 @@ GPU_D3D11::~GPU_D3D11() { stockD3D11.Destroy(); } -void GPU_D3D11::CheckGPUFeatures() { - u32 features = 0; - - features |= GPU_SUPPORTS_BLEND_MINMAX; +u32 GPU_D3D11::CheckGPUFeatures() const { + u32 features = GPUCommon::CheckGPUFeatures(); // Accurate depth is required because the Direct3D API does not support inverse Z. // So we cannot incorrectly use the viewport transform as the depth range on Direct3D. // TODO: Breaks text in PaRappa for some reason? features |= GPU_SUPPORTS_ACCURATE_DEPTH; -#ifndef _M_ARM - // TODO: Do proper feature detection - features |= GPU_SUPPORTS_ANISOTROPY; -#endif - - features |= GPU_SUPPORTS_DEPTH_TEXTURE; - features |= GPU_SUPPORTS_TEXTURE_NPOT; - if (draw_->GetDeviceCaps().dualSourceBlend) - features |= GPU_SUPPORTS_DUALSOURCE_BLEND; if (draw_->GetDeviceCaps().depthClampSupported) features |= GPU_SUPPORTS_DEPTH_CLAMP; - if (draw_->GetDeviceCaps().clipDistanceSupported) - features |= GPU_SUPPORTS_CLIP_DISTANCE; - if (draw_->GetDeviceCaps().cullDistanceSupported) - features |= GPU_SUPPORTS_CULL_DISTANCE; - if (!draw_->GetBugs().Has(Draw::Bugs::BROKEN_NAN_IN_CONDITIONAL)) { - // Ignore the compat setting if clip and cull are both enabled. - // When supported, we can do the depth side of range culling more correctly. - const bool supported = draw_->GetDeviceCaps().clipDistanceSupported && draw_->GetDeviceCaps().cullDistanceSupported; - const bool disabled = PSP_CoreParameter().compat.flags().DisableRangeCulling; - if (supported || !disabled) { - features |= GPU_SUPPORTS_VS_RANGE_CULLING; - } - } features |= GPU_SUPPORTS_TEXTURE_FLOAT; features |= GPU_SUPPORTS_INSTANCE_RENDERING; @@ -146,10 +122,6 @@ void GPU_D3D11::CheckGPUFeatures() { features |= GPU_SUPPORTS_16BIT_FORMATS; } - if (draw_->GetDeviceCaps().logicOpSupported) { - features |= GPU_SUPPORTS_LOGIC_OP; - } - if (!g_Config.bHighQualityDepth && (features & GPU_SUPPORTS_ACCURATE_DEPTH) != 0) { features |= GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT; } else if (PSP_CoreParameter().compat.flags().PixelDepthRounding) { @@ -164,11 +136,7 @@ void GPU_D3D11::CheckGPUFeatures() { features |= GPU_USE_DEPTH_RANGE_HACK; } - if (PSP_CoreParameter().compat.flags().ClearToRAM) { - features |= GPU_USE_CLEAR_RAM_HACK; - } - - gstate_c.featureFlags = features; + return features; } // Needs to be called on GPU thread, not reporting thread. @@ -206,7 +174,7 @@ void GPU_D3D11::BeginHostFrame() { GPUCommon::BeginHostFrame(); UpdateCmdInfo(); if (resized_) { - CheckGPUFeatures(); + gstate_c.featureFlags = CheckGPUFeatures(); framebufferManager_->Resized(); drawEngine_.Resized(); textureCache_->NotifyConfigChanged(); diff --git a/GPU/D3D11/GPU_D3D11.h b/GPU/D3D11/GPU_D3D11.h index d1e5287782..e45c1ab79d 100644 --- a/GPU/D3D11/GPU_D3D11.h +++ b/GPU/D3D11/GPU_D3D11.h @@ -36,7 +36,7 @@ public: GPU_D3D11(GraphicsContext *gfxCtx, Draw::DrawContext *draw); ~GPU_D3D11(); - void CheckGPUFeatures() override; + u32 CheckGPUFeatures() const override; void PreExecuteOp(u32 op, u32 diff) override; void ExecuteOp(u32 op, u32 diff) override; diff --git a/GPU/D3D11/ShaderManagerD3D11.cpp b/GPU/D3D11/ShaderManagerD3D11.cpp index a8537c8545..0560b52941 100644 --- a/GPU/D3D11/ShaderManagerD3D11.cpp +++ b/GPU/D3D11/ShaderManagerD3D11.cpp @@ -88,9 +88,11 @@ std::string D3D11VertexShader::GetShaderString(DebugShaderStringType type) const } } +static constexpr size_t CODE_BUFFER_SIZE = 32768; + ShaderManagerD3D11::ShaderManagerD3D11(Draw::DrawContext *draw, ID3D11Device *device, ID3D11DeviceContext *context, D3D_FEATURE_LEVEL featureLevel) : ShaderManagerCommon(draw), device_(device), context_(context), featureLevel_(featureLevel) { - codeBuffer_ = new char[16384]; + codeBuffer_ = new char[CODE_BUFFER_SIZE]; memset(&ub_base, 0, sizeof(ub_base)); memset(&ub_lights, 0, sizeof(ub_lights)); memset(&ub_bones, 0, sizeof(ub_bones)); @@ -211,6 +213,7 @@ void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader ** uint32_t attrMask; uint64_t uniformMask; GenerateVertexShader(VSID, codeBuffer_, draw_->GetShaderLanguageDesc(), draw_->GetBugs(), &attrMask, &uniformMask, &genErrorString); + _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "VS length error: %d", (int)strlen(codeBuffer_)); vs = new D3D11VertexShader(device_, featureLevel_, VSID, codeBuffer_, vertType, useHWTransform); vsCache_[VSID] = vs; } else { @@ -225,6 +228,7 @@ void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader ** std::string genErrorString; uint64_t uniformMask; GenerateFragmentShader(FSID, codeBuffer_, draw_->GetShaderLanguageDesc(), draw_->GetBugs(), &uniformMask, nullptr, &genErrorString); + _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "FS length error: %d", (int)strlen(codeBuffer_)); fs = new D3D11FragmentShader(device_, featureLevel_, FSID, codeBuffer_, useHWTransform); fsCache_[FSID] = fs; } else { diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp index 0cee52a155..2f86aa9914 100644 --- a/GPU/D3D11/StateMappingD3D11.cpp +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -153,15 +153,16 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { // We ignore the logicState on D3D since there's no support, the emulation of it is blend-and-shader only. if (pipelineState_.FramebufferRead()) { - bool fboTexNeedsBind = false; - ApplyFramebufferRead(&fboTexNeedsBind); + FBOTexState fboTexBindState = FBO_TEX_NONE; + ApplyFramebufferRead(&fboTexBindState); // The shader takes over the responsibility for blending, so recompute. ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState); - if (fboTexNeedsBind) { + if (fboTexBindState == FBO_TEX_COPY_BIND_TEX) { framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); // No sampler required, we do a plain Load in the pixel shader. fboTexBound_ = true; + fboTexBindState = FBO_TEX_NONE; framebufferManager_->RebindFramebuffer("RebindFramebuffer - ApplyDrawState"); // Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects. @@ -432,7 +433,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) { textureCache_->SetTexture(); gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); - } else if (gstate.getTextureAddress(0) == ((gstate.getFrameBufRawAddress() | 0x04000000) & 0x3FFFFFFF)) { + } else if (gstate.getTextureAddress(0) == (gstate.getFrameBufRawAddress() | 0x04000000)) { // This catches the case of clearing a texture. gstate_c.Dirty(DIRTY_TEXTURE_IMAGE); } diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index fb6cfba8b7..8d051c915c 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -65,6 +65,8 @@ Draw::DataFormat FromD3D11Format(u32 fmt) { return Draw::DataFormat::A1R5G5B5_UNORM_PACK16; case DXGI_FORMAT_B5G6R5_UNORM: return Draw::DataFormat::R5G6B5_UNORM_PACK16; + case DXGI_FORMAT_R8_UNORM: + return Draw::DataFormat::R8_UNORM; case DXGI_FORMAT_B8G8R8A8_UNORM: default: return Draw::DataFormat::R8G8B8A8_UNORM; @@ -290,6 +292,8 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { dstFmt = ToDXGIFormat(plan.replaced->Format(plan.baseLevelSrc)); } else if (plan.scaleFactor > 1 || plan.saveTexture) { dstFmt = DXGI_FORMAT_B8G8R8A8_UNORM; + } else if (plan.decodeToClut8) { + dstFmt = DXGI_FORMAT_R8_UNORM; } int levels; @@ -449,20 +453,6 @@ DXGI_FORMAT TextureCacheD3D11::GetDestFormat(GETextureFormat format, GEPaletteFo } } -CheckAlphaResult TextureCacheD3D11::CheckAlpha(const u32 *pixelData, u32 dstFmt, int w) { - switch (dstFmt) { - case DXGI_FORMAT_B4G4R4A4_UNORM: - return CheckAlpha16((const u16 *)pixelData, w, 0xF000); - case DXGI_FORMAT_B5G5R5A1_UNORM: - return CheckAlpha16((const u16 *)pixelData, w, 0x8000); - case DXGI_FORMAT_B5G6R5_UNORM: - // Never has any alpha. - return CHECKALPHA_FULL; - default: - return CheckAlpha32((const u32 *)pixelData, w, 0xFF000000); - } -} - bool TextureCacheD3D11::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) { SetTexture(); if (!nextTexture_) { @@ -525,3 +515,8 @@ bool TextureCacheD3D11::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level stagingCopy->Release(); return true; } + +void *TextureCacheD3D11::GetNativeTextureView(const TexCacheEntry *entry) { + ID3D11ShaderResourceView *textureView = DxView(entry); + return (void *)textureView; +} diff --git a/GPU/D3D11/TextureCacheD3D11.h b/GPU/D3D11/TextureCacheD3D11.h index 03ec04a481..8b9d7caa78 100644 --- a/GPU/D3D11/TextureCacheD3D11.h +++ b/GPU/D3D11/TextureCacheD3D11.h @@ -61,10 +61,10 @@ protected: void ReleaseTexture(TexCacheEntry *entry, bool delete_them) override; void BindAsClutTexture(Draw::Texture *tex, bool smooth) override; void ApplySamplingParams(const SamplerCacheKey &key) override; + void *GetNativeTextureView(const TexCacheEntry *entry) override; private: DXGI_FORMAT GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const; - static CheckAlphaResult CheckAlpha(const u32 *pixelData, u32 dstFmt, int w); void UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBase, bool clutIndexIsSimple) override; void BuildTexture(TexCacheEntry *const entry) override; @@ -72,10 +72,10 @@ private: ID3D11Device *device_; ID3D11DeviceContext *context_; - ID3D11Resource *&DxTex(TexCacheEntry *entry) { + ID3D11Resource *&DxTex(const TexCacheEntry *entry) { return (ID3D11Resource *&)entry->texturePtr; } - ID3D11ShaderResourceView *DxView(TexCacheEntry *entry) { + ID3D11ShaderResourceView *DxView(const TexCacheEntry *entry) { return (ID3D11ShaderResourceView *)entry->textureView; } diff --git a/GPU/Debugger/Breakpoints.cpp b/GPU/Debugger/Breakpoints.cpp index 97fbca57ee..d1577d2b60 100644 --- a/GPU/Debugger/Breakpoints.cpp +++ b/GPU/Debugger/Breakpoints.cpp @@ -122,7 +122,7 @@ u32 GetAdjustedRenderTargetAddress(u32 op) { switch (cmd) { case GE_CMD_FRAMEBUFPTR: case GE_CMD_ZBUFPTR: - return op & 0x003FFFF0; + return op & 0x001FFFF0; } return (u32)-1; @@ -278,7 +278,7 @@ bool IsRenderTargetBreakpoint(u32 addr, bool &temp) { return false; } - addr &= 0x003FFFF0; + addr &= 0x001FFFF0; std::lock_guard guard(breaksLock); temp = breakRenderTargetsTemp.find(addr) != breakRenderTargetsTemp.end(); @@ -290,7 +290,7 @@ bool IsRenderTargetBreakpoint(u32 addr) { return false; } - addr &= 0x003FFFF0; + addr &= 0x001FFFF0; std::lock_guard guard(breaksLock); return breakRenderTargets.find(addr) != breakRenderTargets.end(); @@ -339,7 +339,7 @@ void AddAddressBreakpoint(u32 addr, bool temp) { } else { // Remove the temporary marking. breakPCsTemp.erase(addr); - breakPCs.insert(std::make_pair(addr, BreakpointInfo{})); + breakPCs.emplace(addr, BreakpointInfo{}); } breakPCsCount = breakPCs.size(); @@ -385,7 +385,7 @@ void AddTextureBreakpoint(u32 addr, bool temp) { void AddRenderTargetBreakpoint(u32 addr, bool temp) { std::lock_guard guard(breaksLock); - addr &= 0x003FFFF0; + addr &= 0x001FFFF0; if (temp) { if (breakRenderTargets.find(addr) == breakRenderTargets.end()) { @@ -444,7 +444,7 @@ void RemoveTextureBreakpoint(u32 addr) { void RemoveRenderTargetBreakpoint(u32 addr) { std::lock_guard guard(breaksLock); - addr &= 0x003FFFF0; + addr &= 0x001FFFF0; breakRenderTargetsTemp.erase(addr); breakRenderTargets.erase(addr); diff --git a/GPU/Debugger/Debugger.cpp b/GPU/Debugger/Debugger.cpp index fb88fdca2f..fa55ec6b8e 100644 --- a/GPU/Debugger/Debugger.cpp +++ b/GPU/Debugger/Debugger.cpp @@ -18,6 +18,7 @@ #include #include "Common/Log.h" #include "Common/StringUtils.h" +#include "Common/TimeUtil.h" #include "GPU/GPU.h" #include "GPU/Debugger/Breakpoints.h" #include "GPU/Debugger/Debugger.h" @@ -35,6 +36,8 @@ static int primsLastFrame = 0; static int primsThisFrame = 0; static int thisFlipNum = 0; +static double lastStepTime = -1.0; + static std::vector> restrictPrimRanges; static std::string restrictPrimRule; @@ -56,6 +59,7 @@ void SetActive(bool flag) { breakNext = BreakNext::NONE; breakAtCount = -1; GPUStepping::ResumeFromStepping(); + lastStepTime = -1.0; } } @@ -79,6 +83,7 @@ void SetBreakNext(BreakNext next) { GPUBreakpoints::AddCmdBreakpoint(GE_CMD_SPLINE, true); } GPUStepping::ResumeFromStepping(); + lastStepTime = next == BreakNext::NONE ? -1.0 : time_now_d(); } void SetBreakCount(int c, bool relative) { @@ -130,7 +135,12 @@ bool NotifyCommand(u32 pc) { GPUBreakpoints::ClearTempBreakpoints(); auto info = gpuDebug->DissassembleOp(pc); - NOTICE_LOG(G3D, "Waiting at %08x, %s", pc, info.desc.c_str()); + if (lastStepTime >= 0.0) { + NOTICE_LOG(G3D, "Waiting at %08x, %s (%fms)", pc, info.desc.c_str(), (time_now_d() - lastStepTime) * 1000.0); + lastStepTime = -1.0; + } else { + NOTICE_LOG(G3D, "Waiting at %08x, %s", pc, info.desc.c_str()); + } GPUStepping::EnterStepping(); } @@ -141,7 +151,12 @@ void NotifyDraw() { if (!active) return; if (breakNext == BreakNext::DRAW && !GPUStepping::IsStepping()) { - NOTICE_LOG(G3D, "Waiting at a draw"); + if (lastStepTime >= 0.0) { + NOTICE_LOG(G3D, "Waiting at a draw (%fms)", (time_now_d() - lastStepTime) * 1000.0); + lastStepTime = -1.0; + } else { + NOTICE_LOG(G3D, "Waiting at a draw"); + } GPUStepping::EnterStepping(); } } @@ -204,9 +219,9 @@ bool SetRestrictPrims(const char *rule) { // If there's nothing yet, add everything else. if (updated.empty()) { if (range.first > 0) - updated.push_back(std::make_pair(0, range.first - 1)); + updated.emplace_back(0, range.first - 1); if (range.second < MAX_PRIMS) - updated.push_back(std::make_pair(range.second + 1, MAX_PRIMS)); + updated.emplace_back(range.second + 1, MAX_PRIMS); continue; } @@ -225,7 +240,7 @@ bool SetRestrictPrims(const char *rule) { // We're slicing a hole in this subrange. int next = sub.second; sub.second = range.first - 1; - updated.push_back(std::make_pair(range.second + 1, next)); + updated.emplace_back(range.second + 1, next); continue; } diff --git a/GPU/Debugger/Playback.cpp b/GPU/Debugger/Playback.cpp index aa34ce3ccf..0410a61c76 100644 --- a/GPU/Debugger/Playback.cpp +++ b/GPU/Debugger/Playback.cpp @@ -28,6 +28,7 @@ #include "Core/Config.h" #include "Core/Core.h" #include "Core/CoreTiming.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/ELF/ParamSFO.h" #include "Core/FileSystems/MetaFileSystem.h" #include "Core/HLE/sceDisplay.h" @@ -298,6 +299,7 @@ private: void Registers(u32 ptr, u32 sz); void Vertices(u32 ptr, u32 sz); void Indices(u32 ptr, u32 sz); + void ClutAddr(u32 ptr, u32 sz); void Clut(u32 ptr, u32 sz); void TransferSrc(u32 ptr, u32 sz); void Memset(u32 ptr, u32 sz); @@ -306,8 +308,11 @@ private: void Texture(int level, u32 ptr, u32 sz); void Framebuf(int level, u32 ptr, u32 sz); void Display(u32 ptr, u32 sz); + void EdramTrans(u32 ptr, u32 sz); u32 execMemcpyDest = 0; + u32 execClutAddr = 0; + u32 execClutFlags = 0; u32 execListBuf = 0; u32 execListPos = 0; u32 execListID = 0; @@ -324,6 +329,10 @@ private: }; void DumpExecute::SyncStall() { + if (execListBuf == 0) { + return; + } + gpu->UpdateStall(execListID, execListPos); s64 listTicks = gpu->GetListTicks(execListID); if (listTicks != -1) { @@ -472,15 +481,39 @@ void DumpExecute::Indices(u32 ptr, u32 sz) { execListQueue.push_back((GE_CMD_IADDR << 24) | (psp & 0x00FFFFFF)); } -void DumpExecute::Clut(u32 ptr, u32 sz) { - u32 psp = mapping_.Map(ptr, sz, std::bind(&DumpExecute::SyncStall, this)); - if (psp == 0) { - ERROR_LOG(SYSTEM, "Unable to allocate for clut"); - return; - } +void DumpExecute::ClutAddr(u32 ptr, u32 sz) { + struct ClutAddrData { + u32 addr; + u32 flags; + }; + const ClutAddrData *data = (const ClutAddrData *)(pushbuf_.data() + ptr); + execClutAddr = data->addr; + execClutFlags = data->flags; +} - execListQueue.push_back((GE_CMD_CLUTADDRUPPER << 24) | ((psp >> 8) & 0x00FF0000)); - execListQueue.push_back((GE_CMD_CLUTADDR << 24) | (psp & 0x00FFFFFF)); +void DumpExecute::Clut(u32 ptr, u32 sz) { + // This is always run when we have the actual address set. + if (execClutAddr != 0) { + const bool isTarget = (execClutFlags & 1) != 0; + + // Could potentially always skip if !isTarget, but playing it safe for offset texture behavior. + if (Memory::IsValidRange(execClutAddr, sz) && (!isTarget || !g_Config.bSoftwareRendering)) { + // Intentionally don't trigger an upload here. + Memory::MemcpyUnchecked(execClutAddr, pushbuf_.data() + ptr, sz); + NotifyMemInfo(MemBlockFlags::WRITE, execClutAddr, sz, "ReplayClut"); + } + + execClutAddr = 0; + } else { + u32 psp = mapping_.Map(ptr, sz, std::bind(&DumpExecute::SyncStall, this)); + if (psp == 0) { + ERROR_LOG(SYSTEM, "Unable to allocate for clut"); + return; + } + + execListQueue.push_back((GE_CMD_CLUTADDRUPPER << 24) | ((psp >> 8) & 0x00FF0000)); + execListQueue.push_back((GE_CMD_CLUTADDR << 24) | (psp & 0x00FFFFFF)); + } } void DumpExecute::TransferSrc(u32 ptr, u32 sz) { @@ -522,6 +555,7 @@ void DumpExecute::Memcpy(u32 ptr, u32 sz) { if (Memory::IsVRAMAddress(execMemcpyDest)) { SyncStall(); Memory::MemcpyUnchecked(execMemcpyDest, pushbuf_.data() + ptr, sz); + NotifyMemInfo(MemBlockFlags::WRITE, execMemcpyDest, sz, "ReplayMemcpy"); gpu->PerformMemoryUpload(execMemcpyDest, sz); } } @@ -572,6 +606,7 @@ void DumpExecute::Framebuf(int level, u32 ptr, u32 sz) { if (Memory::IsValidRange(framebuf->addr, pspSize) && !unchangedVRAM && (!isTarget || !g_Config.bSoftwareRendering)) { // Intentionally don't trigger an upload here. Memory::MemcpyUnchecked(framebuf->addr, pushbuf_.data() + ptr + headerSize, pspSize); + NotifyMemInfo(MemBlockFlags::WRITE, framebuf->addr, pspSize, "ReplayTex"); } } @@ -590,6 +625,17 @@ void DumpExecute::Display(u32 ptr, u32 sz) { __DisplaySetFramebuf(disp->topaddr.ptr, disp->linesize, disp->pixelFormat, 0); } +void DumpExecute::EdramTrans(u32 ptr, u32 sz) { + uint32_t value; + memcpy(&value, pushbuf_.data() + ptr, 4); + + // Sync up drawing. + SyncStall(); + + if (gpu) + gpu->SetAddrTranslation(value); +} + DumpExecute::~DumpExecute() { execMemcpyDest = 0; if (execListBuf) { @@ -601,6 +647,10 @@ DumpExecute::~DumpExecute() { } bool DumpExecute::Run() { + // Start with the default value. + if (gpu) + gpu->SetAddrTranslation(0x400); + for (const Command &cmd : commands_) { switch (cmd.type) { case CommandType::INIT: @@ -619,6 +669,10 @@ bool DumpExecute::Run() { Indices(cmd.ptr, cmd.sz); break; + case CommandType::CLUTADDR: + ClutAddr(cmd.ptr, cmd.sz); + break; + case CommandType::CLUT: Clut(cmd.ptr, cmd.sz); break; @@ -639,6 +693,10 @@ bool DumpExecute::Run() { Memcpy(cmd.ptr, cmd.sz); break; + case CommandType::EDRAMTRANS: + EdramTrans(cmd.ptr, cmd.sz); + break; + case CommandType::TEXTURE0: case CommandType::TEXTURE1: case CommandType::TEXTURE2: diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index cc63dccf72..6058546b0f 100644 --- a/GPU/Debugger/Record.cpp +++ b/GPU/Debugger/Record.cpp @@ -37,6 +37,7 @@ #include "Core/MemMap.h" #include "Core/System.h" #include "Core/ThreadPools.h" +#include "GPU/Common/GPUDebugInterface.h" #include "GPU/GPUInterface.h" #include "GPU/GPUState.h" #include "GPU/ge_constants.h" @@ -51,6 +52,7 @@ static bool active = false; static bool nextFrame = false; static int flipLastAction = -1; static int flipFinishAt = -1; +static uint32_t lastEdramTrans = 0x400; static std::function writeCallback; static std::vector pushbuf; @@ -152,8 +154,19 @@ static void BeginRecording() { u32 sz = 512 * 4; pushbuf.resize(pushbuf.size() + sz); gstate.Save((u32_le *)(pushbuf.data() + ptr)); - commands.push_back({CommandType::INIT, sz, ptr}); + + // Also save the initial CLUT. + GPUDebugBuffer clut; + if (gpuDebug->GetCurrentClut(clut)) { + sz = clut.GetStride() * clut.PixelSize(); + _assert_msg_(sz == 1024, "CLUT should be 1024 bytes"); + ptr = (u32)pushbuf.size(); + pushbuf.resize(pushbuf.size() + sz); + memcpy(pushbuf.data() + ptr, clut.GetData(), sz); + commands.push_back({ CommandType::CLUT, sz, ptr }); + } + DirtyAllVRAM(DirtyVRAMFlag::DIRTY); } @@ -308,6 +321,40 @@ static Command EmitCommandWithRAM(CommandType t, const void *p, u32 sz, u32 alig return cmd; } +static u32 GetTargetFlags(u32 addr, u32 sizeInRAM) { + addr &= 0x041FFFFF; + const bool isTarget = lastRenderTargets.find(addr) != lastRenderTargets.end(); + + bool isDirtyVRAM = false; + bool isDrawnVRAM = false; + uint32_t start = (addr >> DIRTY_VRAM_SHIFT) & DIRTY_VRAM_MASK; + uint32_t blocks = (sizeInRAM + DIRTY_VRAM_ROUND) >> DIRTY_VRAM_SHIFT; + bool startEven = (addr & DIRTY_VRAM_ROUND) == 0; + bool endEven = ((addr + sizeInRAM) & DIRTY_VRAM_ROUND) == 0; + for (uint32_t i = 0; i < blocks; ++i) { + DirtyVRAMFlag flag = dirtyVRAM[start + i]; + isDirtyVRAM = isDirtyVRAM || flag != DirtyVRAMFlag::CLEAN; + isDrawnVRAM = isDrawnVRAM || flag == DirtyVRAMFlag::DRAWN; + + // Mark the VRAM clean now that it's been copied to VRAM. + if (flag == DirtyVRAMFlag::DIRTY) { + if ((i > 0 || startEven) && (i < blocks || endEven)) + dirtyVRAM[start + i] = DirtyVRAMFlag::CLEAN; + } + } + + // The isTarget flag is mostly used for replay of dumps on a PSP. + u32 flags = isTarget ? 1 : 0; + // The unchangedVRAM flag tells us we can skip recopying. + if (!isDirtyVRAM) + flags |= 2; + // And the drawn flag tells us this data was potentially drawn to. + if (isDrawnVRAM) + flags |= 4; + + return flags; +} + static void EmitTextureData(int level, u32 texaddr) { GETextureFormat format = gstate.getTextureFormat(); int w = gstate.getTextureWidth(level); @@ -315,7 +362,6 @@ static void EmitTextureData(int level, u32 texaddr) { int bufw = GetTextureBufw(level, texaddr, format); int extraw = w > bufw ? w - bufw : 0; u32 sizeInRAM = (textureBitsPerPixel[format] * (bufw * h + extraw)) / 8; - const bool isTarget = lastRenderTargets.find(texaddr) != lastRenderTargets.end(); CommandType type = CommandType((int)CommandType::TEXTURE0 + level); const u8 *p = Memory::GetPointerUnchecked(texaddr); @@ -330,27 +376,7 @@ static void EmitTextureData(int level, u32 texaddr) { u32 pad; }; - bool isDirtyVRAM = false; - bool isDrawnVRAM = false; - uint32_t start = (texaddr >> DIRTY_VRAM_SHIFT) & DIRTY_VRAM_MASK; - for (uint32_t i = 0; i < (sizeInRAM + DIRTY_VRAM_ROUND) >> DIRTY_VRAM_SHIFT; ++i) { - DirtyVRAMFlag flag = dirtyVRAM[start + i]; - isDirtyVRAM = isDirtyVRAM || flag != DirtyVRAMFlag::CLEAN; - isDrawnVRAM = isDrawnVRAM || flag == DirtyVRAMFlag::DRAWN; - - // Mark the VRAM clean now that it's been copied to VRAM. - if (flag == DirtyVRAMFlag::DIRTY) - dirtyVRAM[start + i] = DirtyVRAMFlag::CLEAN; - } - - // The isTarget flag is mostly used for replay of dumps on a PSP. - u32 flags = isTarget ? 1 : 0; - // The unchangedVRAM flag tells us we can skip recopying. - if (!isDirtyVRAM) - flags |= 2; - // And the drawn flag tells us this data was potentially drawn to. - if (isDrawnVRAM) - flags |= 4; + u32 flags = GetTargetFlags(texaddr, sizeInRAM); FramebufData framebuf{ texaddr, bufw, flags }; framebufData.resize(sizeof(framebuf) + bytes); memcpy(&framebufData[0], &framebuf, sizeof(framebuf)); @@ -456,12 +482,33 @@ static void EmitTransfer(u32 op) { static void EmitClut(u32 op) { u32 addr = gstate.getClutAddress(); + + // Hardware rendering may be using a framebuffer as CLUT. + // To get at this, we first run the command (normally we're called right before it has run.) + if (Memory::IsVRAMAddress(addr)) + gpuDebug->SetCmdValue(op); + // Actually should only be 0x3F, but we allow enhanced CLUTs. See #15727. u32 blocks = (op & 0x7F) == 0x40 ? 0x40 : (op & 0x3F); u32 bytes = blocks * 32; bytes = Memory::ValidSize(addr, bytes); if (bytes != 0) { + // Send the original address so VRAM can be reasoned about. + if (Memory::IsVRAMAddress(addr)) { + struct ClutAddrData { + u32 addr; + u32 flags; + }; + u32 flags = GetTargetFlags(addr, bytes); + ClutAddrData data{ addr, flags }; + + FlushRegisters(); + Command cmd{CommandType::CLUTADDR, sizeof(data), (u32)pushbuf.size()}; + pushbuf.resize(pushbuf.size() + sizeof(data)); + memcpy(pushbuf.data() + cmd.ptr, &data, sizeof(data)); + commands.push_back(cmd); + } EmitCommandWithRAM(CommandType::CLUT, Memory::GetPointerUnchecked(addr), bytes, 16); } @@ -516,17 +563,35 @@ static void FinishRecording() { active = false; flipLastAction = gpuStats.numFlips; flipFinishAt = -1; + lastEdramTrans = 0x400; if (writeCallback) writeCallback(filename); writeCallback = nullptr; } +static void CheckEdramTrans() { + if (!gpuDebug) + return; + + uint32_t value = gpuDebug->GetAddrTranslation(); + if (value == lastEdramTrans) + return; + lastEdramTrans = value; + + FlushRegisters(); + Command cmd{CommandType::EDRAMTRANS, sizeof(value), (u32)pushbuf.size()}; + pushbuf.resize(pushbuf.size() + sizeof(value)); + memcpy(pushbuf.data() + cmd.ptr, &value, sizeof(value)); + commands.push_back(cmd); +} + void NotifyCommand(u32 pc) { if (!active) { return; } + CheckEdramTrans(); const u32 op = Memory::Read_U32(pc); const GECommand cmd = GECommand(op >> 24); @@ -579,11 +644,14 @@ void NotifyMemcpy(u32 dest, u32 src, u32 sz) { if (!active) { return; } + + CheckEdramTrans(); if (Memory::IsVRAMAddress(dest)) { FlushRegisters(); Command cmd{CommandType::MEMCPYDEST, sizeof(dest), (u32)pushbuf.size()}; pushbuf.resize(pushbuf.size() + sizeof(dest)); memcpy(pushbuf.data() + cmd.ptr, &dest, sizeof(dest)); + commands.push_back(cmd); sz = Memory::ValidSize(dest, sz); if (sz != 0) { @@ -597,6 +665,8 @@ void NotifyMemset(u32 dest, int v, u32 sz) { if (!active) { return; } + + CheckEdramTrans(); struct MemsetCommand { u32 dest; int value; @@ -611,6 +681,7 @@ void NotifyMemset(u32 dest, int v, u32 sz) { Command cmd{CommandType::MEMSET, sizeof(data), (u32)pushbuf.size()}; pushbuf.resize(pushbuf.size() + sizeof(data)); memcpy(pushbuf.data() + cmd.ptr, &data, sizeof(data)); + commands.push_back(cmd); DirtyVRAM(dest, sz, DirtyVRAMFlag::DIRTY); } } @@ -619,9 +690,12 @@ void NotifyUpload(u32 dest, u32 sz) { if (!active) { return; } - NotifyMemcpy(dest, dest, sz); - if (Memory::IsVRAMAddress(dest)) + + if (Memory::IsVRAMAddress(dest)) { + // This also checks the edram translation value. + NotifyMemcpy(dest, dest, sz); DirtyVRAM(dest, sz, DirtyVRAMFlag::DIRTY); + } } static bool HasDrawCommands() { @@ -656,6 +730,7 @@ void NotifyDisplay(u32 framebuf, int stride, int fmt) { return; } + CheckEdramTrans(); struct DisplayBufData { PSPPointer topaddr; int linesize, pixelFormat; @@ -683,6 +758,7 @@ void NotifyBeginFrame() { if (active && HasDrawCommands() && (noDisplayAction || gpuStats.numFlips == flipFinishAt)) { NOTICE_LOG(SYSTEM, "Recording complete on frame"); + CheckEdramTrans(); struct DisplayBufData { PSPPointer topaddr; u32 linesize, pixelFormat; @@ -710,6 +786,10 @@ void NotifyBeginFrame() { } void NotifyCPU() { + if (!active) { + return; + } + DirtyAllVRAM(DirtyVRAMFlag::DIRTY); } diff --git a/GPU/Debugger/RecordFormat.h b/GPU/Debugger/RecordFormat.h index 8079136d0c..295273eb0f 100644 --- a/GPU/Debugger/RecordFormat.h +++ b/GPU/Debugger/RecordFormat.h @@ -49,6 +49,8 @@ enum class CommandType : u8 { MEMCPYDEST = 7, MEMCPYDATA = 8, DISPLAY = 9, + CLUTADDR = 10, + EDRAMTRANS = 11, TEXTURE0 = 0x10, TEXTURE1 = 0x11, diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index e05df6c98c..b8d4bfa0fb 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -327,7 +327,7 @@ void DrawEngineDX9::DoFlush() { textureCache_->SetTexture(); gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); textureNeedsApply = true; - } else if (gstate.getTextureAddress(0) == ((gstate.getFrameBufRawAddress() | 0x04000000) & 0x3FFFFFFF)) { + } else if (gstate.getTextureAddress(0) == (gstate.getFrameBufRawAddress() | 0x04000000)) { // This catches the case of clearing a texture. (#10957) gstate_c.Dirty(DIRTY_TEXTURE_IMAGE); } diff --git a/GPU/Directx9/DrawEngineDX9.h b/GPU/Directx9/DrawEngineDX9.h index a0ee23e60d..9ef5b37c65 100644 --- a/GPU/Directx9/DrawEngineDX9.h +++ b/GPU/Directx9/DrawEngineDX9.h @@ -170,6 +170,8 @@ private: // Hardware tessellation TessellationDataTransferDX9 *tessDataTransferDX9; + FBOTexState fboTexBindState_ = FBO_TEX_NONE; + int lastRenderStepId_ = -1; bool fboTexNeedsBind_ = false; diff --git a/GPU/Directx9/FramebufferManagerDX9.cpp b/GPU/Directx9/FramebufferManagerDX9.cpp index f828336edd..f0281c400e 100644 --- a/GPU/Directx9/FramebufferManagerDX9.cpp +++ b/GPU/Directx9/FramebufferManagerDX9.cpp @@ -140,7 +140,7 @@ return; } - const u32 fb_address = vfb->fb_address & 0x3FFFFFFF; + const u32 fb_address = vfb->fb_address; const int dstBpp = vfb->fb_format == GE_FORMAT_8888 ? 4 : 2; // We always need to convert from the framebuffer native format. @@ -365,8 +365,11 @@ } if (!vfb) { + if (!Memory::IsValidAddress(fb_address)) + return false; // If there's no vfb and we're drawing there, must be memory? - buffer = GPUDebugBuffer(Memory::GetPointerWrite(vfb->z_address), vfb->z_stride, 512, GPU_DBG_FORMAT_16BIT); + // TODO: Actually get the stencil. + buffer = GPUDebugBuffer(Memory::GetPointerWrite(fb_address), fb_stride, 512, GPU_DBG_FORMAT_8888); return true; } diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 3e5d0bac57..075d89e460 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -81,7 +81,7 @@ GPU_DX9::GPU_DX9(GraphicsContext *gfxCtx, Draw::DrawContext *draw) // No need to flush before the tex scale/offset commands if we are baking // the tex scale/offset into the vertices anyway. UpdateCmdInfo(); - CheckGPUFeatures(); + gstate_c.featureFlags = CheckGPUFeatures(); BuildReportingInfo(); @@ -98,68 +98,9 @@ GPU_DX9::GPU_DX9(GraphicsContext *gfxCtx, Draw::DrawContext *draw) } } -// 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; -} - -void GPU_DX9::CheckGPUFeatures() { - u32 features = 0; +u32 GPU_DX9::CheckGPUFeatures() const { + u32 features = GPUCommon::CheckGPUFeatures(); features |= GPU_SUPPORTS_16BIT_FORMATS; - features |= GPU_SUPPORTS_BLEND_MINMAX; - features |= GPU_SUPPORTS_DEPTH_TEXTURE; features |= GPU_SUPPORTS_TEXTURE_LOD_CONTROL; // Accurate depth is required because the Direct3D API does not support inverse Z. @@ -168,41 +109,6 @@ void GPU_DX9::CheckGPUFeatures() { features |= GPU_SUPPORTS_ACCURATE_DEPTH; auto vendor = draw_->GetDeviceCaps().vendor; - if (!PSP_CoreParameter().compat.flags().DisableRangeCulling) { - // VS range culling (killing triangles in the vertex shader using NaN) causes problems on Intel. - // Also causes problems on old NVIDIA. - switch (vendor) { - case Draw::GPUVendor::VENDOR_INTEL: - 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(draw_->GetDeviceCaps().deviceID) >= NV_KEPLER) { - features |= GPU_SUPPORTS_VS_RANGE_CULLING; - } - break; - default: - features |= GPU_SUPPORTS_VS_RANGE_CULLING; - break; - } - } - - D3DCAPS9 caps; - ZeroMemory(&caps, sizeof(caps)); - HRESULT result = 0; - if (deviceEx_) { - result = deviceEx_->GetDeviceCaps(&caps); - } else { - result = device_->GetDeviceCaps(&caps); - } - if (FAILED(result)) { - WARN_LOG_REPORT(G3D, "Direct3D9: Failed to get the device caps!"); - } else { - if ((caps.RasterCaps & D3DPRASTERCAPS_ANISOTROPY) != 0 && caps.MaxAnisotropy > 1) - features |= GPU_SUPPORTS_ANISOTROPY; - if ((caps.TextureCaps & (D3DPTEXTURECAPS_NONPOW2CONDITIONAL | D3DPTEXTURECAPS_POW2)) == 0) - features |= GPU_SUPPORTS_TEXTURE_NPOT; - } if (!g_Config.bHighQualityDepth) { features |= GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT; @@ -213,11 +119,7 @@ void GPU_DX9::CheckGPUFeatures() { features |= GPU_ROUND_DEPTH_TO_16BIT; } - if (PSP_CoreParameter().compat.flags().ClearToRAM) { - features |= GPU_USE_CLEAR_RAM_HACK; - } - - gstate_c.featureFlags = features; + return features; } GPU_DX9::~GPU_DX9() { @@ -261,7 +163,7 @@ void GPU_DX9::BeginHostFrame() { GPUCommon::BeginHostFrame(); UpdateCmdInfo(); if (resized_) { - CheckGPUFeatures(); + gstate_c.featureFlags = CheckGPUFeatures(); framebufferManager_->Resized(); drawEngine_.Resized(); shaderManagerDX9_->DirtyShader(); diff --git a/GPU/Directx9/GPU_DX9.h b/GPU/Directx9/GPU_DX9.h index 0e313da397..7c6a88d180 100644 --- a/GPU/Directx9/GPU_DX9.h +++ b/GPU/Directx9/GPU_DX9.h @@ -35,7 +35,7 @@ public: GPU_DX9(GraphicsContext *gfxCtx, Draw::DrawContext *draw); ~GPU_DX9(); - void CheckGPUFeatures() override; + u32 CheckGPUFeatures() const override; void PreExecuteOp(u32 op, u32 diff) override; void ExecuteOp(u32 op, u32 diff) override; diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index 2ebadb3974..0dfa352f00 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -99,14 +99,14 @@ void DrawEngineDX9::ApplyDrawState(int prim) { if (!gstate.isModeClear()) { textureCache_->ApplyTexture(); - if (fboTexNeedsBind_) { + if (fboTexBindState_ = FBO_TEX_COPY_BIND_TEX) { // Note that this is positions, not UVs, that we need the copy from. framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); // If we are rendering at a higher resolution, linear is probably best for the dest color. device_->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); device_->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); fboTexBound_ = true; - fboTexNeedsBind_ = false; + fboTexBindState_ = FBO_TEX_NONE; } // TODO: Test texture? @@ -133,20 +133,23 @@ void DrawEngineDX9::ApplyDrawState(int prim) { // We ignore the logicState on D3D since there's no support, the emulation of it is blend-and-shader only. if (pipelineState_.FramebufferRead()) { - bool fboTexNeedsBind = false; - ApplyFramebufferRead(&fboTexNeedsBind); + ApplyFramebufferRead(&fboTexBindState_); // The shader takes over the responsibility for blending, so recompute. ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState); - if (fboTexNeedsBind) { + if (fboTexBindState_ == FBO_TEX_COPY_BIND_TEX) { // Note that this is positions, not UVs, that we need the copy from. framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); // If we are rendering at a higher resolution, linear is probably best for the dest color. device_->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); device_->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); fboTexBound_ = true; + fboTexBindState_ = FBO_TEX_NONE; dirtyRequiresRecheck_ |= DIRTY_BLEND_STATE; gstate_c.Dirty(DIRTY_BLEND_STATE); + } else if (fboTexBindState_ == FBO_TEX_READ_FRAMEBUFFER) { + // Not supported. + fboTexBindState_ = FBO_TEX_NONE; } dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index bfb859bb40..9edc451a52 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -358,20 +358,6 @@ D3DFORMAT TextureCacheDX9::GetDestFormat(GETextureFormat format, GEPaletteFormat } } -CheckAlphaResult TextureCacheDX9::CheckAlpha(const u32 *pixelData, u32 dstFmt, int w) { - switch (dstFmt) { - case D3DFMT_A4R4G4B4: - return CheckAlpha16((const u16 *)pixelData, w, 0xF000); - case D3DFMT_A1R5G5B5: - return CheckAlpha16((const u16 *)pixelData, w, 0x8000); - case D3DFMT_R5G6B5: - // Never has any alpha. - return CHECKALPHA_FULL; - default: - return CheckAlpha32(pixelData, w, 0xFF000000); - } -} - bool TextureCacheDX9::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) { SetTexture(); ApplyTexture(); @@ -454,3 +440,8 @@ bool TextureCacheDX9::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) return success; } + +void *TextureCacheDX9::GetNativeTextureView(const TexCacheEntry *entry) { + LPDIRECT3DBASETEXTURE9 tex = DxTex(entry); + return (void *)tex; +} diff --git a/GPU/Directx9/TextureCacheDX9.h b/GPU/Directx9/TextureCacheDX9.h index e8f4d9e58f..6a52eb2007 100644 --- a/GPU/Directx9/TextureCacheDX9.h +++ b/GPU/Directx9/TextureCacheDX9.h @@ -50,17 +50,17 @@ protected: void Unbind() override; void ReleaseTexture(TexCacheEntry *entry, bool delete_them) override; void BindAsClutTexture(Draw::Texture *tex, bool smooth) override; + void *GetNativeTextureView(const TexCacheEntry *entry) override; private: void ApplySamplingParams(const SamplerCacheKey &key) override; D3DFORMAT GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const; - static CheckAlphaResult CheckAlpha(const u32 *pixelData, u32 dstFmt, int w); void UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBase, bool clutIndexIsSimple) override; void BuildTexture(TexCacheEntry *const entry) override; - LPDIRECT3DBASETEXTURE9 &DxTex(TexCacheEntry *entry) { + LPDIRECT3DBASETEXTURE9 &DxTex(const TexCacheEntry *entry) const { return *(LPDIRECT3DBASETEXTURE9 *)&entry->texturePtr; } diff --git a/GPU/GLES/DepthBufferGLES.cpp b/GPU/GLES/DepthBufferGLES.cpp index e5bcc20bf9..342ebdad60 100644 --- a/GPU/GLES/DepthBufferGLES.cpp +++ b/GPU/GLES/DepthBufferGLES.cpp @@ -118,7 +118,8 @@ void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int queries.push_back({ &u_depthDownloadTo8, "u_depthTo8" }); std::vector inits; inits.push_back({ &u_depthDownloadTex, 0, TEX_SLOT_PSP_TEXTURE }); - depthDownloadProgram_ = render->CreateProgram(shaders, semantics, queries, inits, false, false); + GLRProgramFlags flags{}; + depthDownloadProgram_ = render->CreateProgram(shaders, semantics, queries, inits, flags); for (auto iter : shaders) { render->DeleteShader(iter); } diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 4eb23cd4d4..2102e46095 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -259,7 +259,7 @@ void DrawEngineGLES::DoFlush() { textureCache_->SetTexture(); gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); textureNeedsApply = true; - } else if (gstate.getTextureAddress(0) == ((gstate.getFrameBufRawAddress() | 0x04000000) & 0x3FFFFFFF)) { + } else if (gstate.getTextureAddress(0) == (gstate.getFrameBufRawAddress() | 0x04000000)) { // This catches the case of clearing a texture. (#10957) gstate_c.Dirty(DIRTY_TEXTURE_IMAGE); } @@ -500,7 +500,8 @@ void TessellationDataTransferGLES::SendDataToShader(const SimpleVertex *const *p prevSizeU = size_u; prevSizeV = size_v; if (!data_tex[0]) - data_tex[0] = renderManager_->CreateTexture(GL_TEXTURE_2D, size_u * 3, size_v, 1, 1); + renderManager_->DeleteTexture(data_tex[0]); + data_tex[0] = renderManager_->CreateTexture(GL_TEXTURE_2D, size_u * 3, size_v, 1, 1); renderManager_->TextureImage(data_tex[0], 0, size_u * 3, size_v, 1, Draw::DataFormat::R32G32B32A32_FLOAT, nullptr, GLRAllocType::NONE, false); renderManager_->FinalizeTexture(data_tex[0], 0, false); } @@ -518,7 +519,8 @@ void TessellationDataTransferGLES::SendDataToShader(const SimpleVertex *const *p if (prevSizeWU < weights.size_u) { prevSizeWU = weights.size_u; if (!data_tex[1]) - data_tex[1] = renderManager_->CreateTexture(GL_TEXTURE_2D, weights.size_u * 2, 1, 1, 1); + renderManager_->DeleteTexture(data_tex[1]); + data_tex[1] = renderManager_->CreateTexture(GL_TEXTURE_2D, weights.size_u * 2, 1, 1, 1); renderManager_->TextureImage(data_tex[1], 0, weights.size_u * 2, 1, 1, Draw::DataFormat::R32G32B32A32_FLOAT, nullptr, GLRAllocType::NONE, false); renderManager_->FinalizeTexture(data_tex[1], 0, false); } @@ -529,7 +531,8 @@ void TessellationDataTransferGLES::SendDataToShader(const SimpleVertex *const *p if (prevSizeWV < weights.size_v) { prevSizeWV = weights.size_v; if (!data_tex[2]) - data_tex[2] = renderManager_->CreateTexture(GL_TEXTURE_2D, weights.size_v * 2, 1, 1, 1); + renderManager_->DeleteTexture(data_tex[2]); + data_tex[2] = renderManager_->CreateTexture(GL_TEXTURE_2D, weights.size_v * 2, 1, 1, 1); renderManager_->TextureImage(data_tex[2], 0, weights.size_v * 2, 1, 1, Draw::DataFormat::R32G32B32A32_FLOAT, nullptr, GLRAllocType::NONE, false); renderManager_->FinalizeTexture(data_tex[2], 0, false); } diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 75fd0080b5..abae5134f9 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -54,7 +54,7 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw) : GPUCommon(gfxCtx, draw), drawEngine_(draw), fragmentTestCache_(draw) { UpdateVsyncInterval(true); - CheckGPUFeatures(); + gstate_c.featureFlags = CheckGPUFeatures(); shaderManagerGL_ = new ShaderManagerGLES(draw); framebufferManagerGL_ = new FramebufferManagerGLES(draw); @@ -148,42 +148,17 @@ GPU_GLES::~GPU_GLES() { // Take the raw GL extension and versioning data and turn into feature flags. // TODO: This should use DrawContext::GetDeviceCaps() more and more, and eventually // this can be shared between all the backends. -void GPU_GLES::CheckGPUFeatures() { - u32 features = 0; +u32 GPU_GLES::CheckGPUFeatures() const { + u32 features = GPUCommon::CheckGPUFeatures(); features |= GPU_SUPPORTS_16BIT_FORMATS; - if (draw_->GetDeviceCaps().dualSourceBlend) { - if (!g_Config.bVendorBugChecksEnabled || !draw_->GetBugs().Has(Draw::Bugs::DUAL_SOURCE_BLENDING_BROKEN)) { - features |= GPU_SUPPORTS_DUALSOURCE_BLEND; - } - } - - if (gl_extensions.EXT_shader_framebuffer_fetch || gl_extensions.ARM_shader_framebuffer_fetch) { - // This has caused problems in the past. Let's only enable on GLES3. - if (gl_extensions.GLES3) { - features |= GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH; - } - } - if ((gl_extensions.gpuVendor == GPU_VENDOR_NVIDIA) || (gl_extensions.gpuVendor == GPU_VENDOR_AMD)) features |= GPU_PREFER_REVERSE_COLOR_ORDER; - if (draw_->GetDeviceCaps().textureNPOTFullySupported) - features |= GPU_SUPPORTS_TEXTURE_NPOT; - - if (gl_extensions.EXT_blend_minmax) - features |= GPU_SUPPORTS_BLEND_MINMAX; - - if (draw_->GetDeviceCaps().logicOpSupported) - features |= GPU_SUPPORTS_LOGIC_OP; - if (gl_extensions.GLES3 || !gl_extensions.IsGLES) features |= GPU_SUPPORTS_TEXTURE_LOD_CONTROL; - if (draw_->GetDeviceCaps().anisoSupported) - features |= GPU_SUPPORTS_ANISOTROPY; - bool canUseInstanceID = gl_extensions.EXT_draw_instanced || gl_extensions.ARB_draw_instanced; bool canDefInstanceID = gl_extensions.IsGLES || gl_extensions.EXT_gpu_shader4 || gl_extensions.VersionGEThan(3, 1); bool instanceRendering = gl_extensions.GLES3 || (canUseInstanceID && canDefInstanceID); @@ -202,21 +177,6 @@ void GPU_GLES::CheckGPUFeatures() { // Our implementation of depth texturing needs simple Z range, so can't // use the extension hacks (yet). } - if (draw_->GetDeviceCaps().textureDepthSupported) - features |= GPU_SUPPORTS_DEPTH_TEXTURE; - if (draw_->GetDeviceCaps().clipDistanceSupported) - features |= GPU_SUPPORTS_CLIP_DISTANCE; - if (draw_->GetDeviceCaps().cullDistanceSupported) - features |= GPU_SUPPORTS_CULL_DISTANCE; - if (!draw_->GetBugs().Has(Draw::Bugs::BROKEN_NAN_IN_CONDITIONAL)) { - // Ignore the compat setting if clip and cull are both enabled. - // When supported, we can do the depth side of range culling more correctly. - const bool supported = draw_->GetDeviceCaps().clipDistanceSupported && draw_->GetDeviceCaps().cullDistanceSupported; - const bool disabled = PSP_CoreParameter().compat.flags().DisableRangeCulling; - if (supported || !disabled) { - features |= GPU_SUPPORTS_VS_RANGE_CULLING; - } - } // If we already have a 16-bit depth buffer, we don't need to round. bool prefer24 = draw_->GetDeviceCaps().preferredDepthBufferFormat == Draw::DataFormat::D24_S8; @@ -245,11 +205,7 @@ void GPU_GLES::CheckGPUFeatures() { features |= GPU_USE_DEPTH_RANGE_HACK; } - if (PSP_CoreParameter().compat.flags().ClearToRAM) { - features |= GPU_USE_CLEAR_RAM_HACK; - } - - gstate_c.featureFlags = features; + return features; } bool GPU_GLES::IsReady() { @@ -321,7 +277,7 @@ void GPU_GLES::BeginHostFrame() { GPUCommon::BeginHostFrame(); UpdateCmdInfo(); if (resized_) { - CheckGPUFeatures(); + gstate_c.featureFlags = CheckGPUFeatures(); framebufferManager_->Resized(); drawEngine_.Resized(); shaderManagerGL_->DirtyShader(); diff --git a/GPU/GLES/GPU_GLES.h b/GPU/GLES/GPU_GLES.h index cbe6bc00b0..98bb1d9362 100644 --- a/GPU/GLES/GPU_GLES.h +++ b/GPU/GLES/GPU_GLES.h @@ -38,7 +38,7 @@ public: ~GPU_GLES(); // This gets called on startup and when we get back from settings. - void CheckGPUFeatures() override; + u32 CheckGPUFeatures() const override; bool IsReady() override; void CancelReady() override; diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 4d37ae2ba6..a1f37a12f4 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -84,6 +84,7 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, std::vector semantics; + semantics.reserve(7); semantics.push_back({ ATTR_POSITION, "position" }); semantics.push_back({ ATTR_TEXCOORD, "texcoord" }); if (useHWTransform_) @@ -152,6 +153,7 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, queries.push_back({ &u_uvscaleoffset, "u_uvscaleoffset" }); queries.push_back({ &u_texclamp, "u_texclamp" }); queries.push_back({ &u_texclampoff, "u_texclampoff" }); + queries.push_back({ &u_lightControl, "u_lightControl" }); for (int i = 0; i < 4; i++) { static const char * const lightPosNames[4] = { "u_lightpos0", "u_lightpos1", "u_lightpos2", "u_lightpos3", }; @@ -184,6 +186,7 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, availableUniforms = vs->GetUniformMask() | fs->GetUniformMask(); std::vector initialize; + initialize.reserve(7); initialize.push_back({ &u_tex, 0, TEX_SLOT_PSP_TEXTURE }); initialize.push_back({ &u_fbotex, 0, TEX_SLOT_SHADERBLEND_SRC }); initialize.push_back({ &u_testtex, 0, TEX_SLOT_ALPHATEST }); @@ -192,9 +195,17 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, initialize.push_back({ &u_tess_weights_u, 0, TEX_SLOT_SPLINE_WEIGHTS_U }); initialize.push_back({ &u_tess_weights_v, 0, TEX_SLOT_SPLINE_WEIGHTS_V }); - bool useDualSource = (gstate_c.featureFlags & GPU_SUPPORTS_DUALSOURCE_BLEND) != 0; - bool useClip0 = VSID.Bit(VS_BIT_VERTEX_RANGE_CULLING) && gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE); - program = render->CreateProgram(shaders, semantics, queries, initialize, useDualSource, useClip0); + GLRProgramFlags flags{}; + flags.supportDualSource = (gstate_c.featureFlags & GPU_SUPPORTS_DUALSOURCE_BLEND) != 0; + if (!VSID.Bit(VS_BIT_IS_THROUGH) && gstate_c.Supports(GPU_SUPPORTS_DEPTH_CLAMP)) { + flags.useClipDistance0 = true; + if (VSID.Bit(VS_BIT_VERTEX_RANGE_CULLING) && gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) + flags.useClipDistance1 = true; + } else if (VSID.Bit(VS_BIT_VERTEX_RANGE_CULLING) && gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) { + flags.useClipDistance0 = true; + } + + program = render->CreateProgram(shaders, semantics, queries, initialize, flags); // The rest, use the "dirty" mechanism. dirtyUniforms = DIRTY_ALL_UNIFORMS; @@ -354,6 +365,7 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu if (IsVRBuild()) { dirty |= DIRTY_VIEWMATRIX; + SetVRCompat(VR_COMPAT_FOG_COLOR, gstate.fogcolor); } if (!dirty) return; @@ -437,7 +449,7 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu SetColorUniform3Alpha255(render_, &u_alphacolorref, gstate.getColorTestRef(), gstate.getAlphaTestRef() & gstate.getAlphaTestMask()); } if (dirty & DIRTY_ALPHACOLORMASK) { - SetColorUniform3iAlpha(render_, &u_alphacolormask, gstate.colortestmask, gstate.getAlphaTestMask()); + render_->SetUniformUI1(&u_alphacolormask, gstate.getColorTestMask() | (gstate.getAlphaTestMask() << 24)); } if (dirty & DIRTY_COLORWRITEMASK) { render_->SetUniformUI1(&u_colorWriteMask, ~((gstate.pmska << 24) | (gstate.pmskc & 0xFFFFFF))); @@ -461,7 +473,6 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu } render_->SetUniformF(&u_fogcoef, 2, fogcoef); } - if (dirty & DIRTY_UVSCALEOFFSET) { const float invW = 1.0f / (float)gstate_c.curTextureWidth; const float invH = 1.0f / (float)gstate_c.curTextureHeight; @@ -595,6 +606,9 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu } // Lighting + if (dirty & DIRTY_LIGHT_CONTROL) { + render_->SetUniformUI1(&u_lightControl, PackLightControlBits()); + } if (dirty & DIRTY_AMBIENT) { SetColorUniform3Alpha(render_, &u_ambient, gstate.ambientcolor, gstate.getAmbientA()); } @@ -647,10 +661,12 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu } } +static constexpr size_t CODE_BUFFER_SIZE = 32768; + ShaderManagerGLES::ShaderManagerGLES(Draw::DrawContext *draw) : ShaderManagerCommon(draw), fsCache_(16), vsCache_(16) { render_ = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - codeBuffer_ = new char[16384]; + codeBuffer_ = new char[CODE_BUFFER_SIZE]; lastFSID_.set_invalid(); lastVSID_.set_invalid(); } @@ -713,6 +729,7 @@ Shader *ShaderManagerGLES::CompileFragmentShader(FShaderID FSID) { ERROR_LOG(G3D, "Shader gen error: %s", errorString.c_str()); return nullptr; } + _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "FS length error: %d", (int)strlen(codeBuffer_)); std::string desc = FragmentShaderDesc(FSID); ShaderDescGLES params{ GL_FRAGMENT_SHADER, 0, uniformMask }; return new Shader(render_, codeBuffer_, desc, params); @@ -727,6 +744,7 @@ Shader *ShaderManagerGLES::CompileVertexShader(VShaderID VSID) { ERROR_LOG(G3D, "Shader gen error: %s", errorString.c_str()); return nullptr; } + _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "VS length error: %d", (int)strlen(codeBuffer_)); std::string desc = VertexShaderDesc(VSID); ShaderDescGLES params{ GL_VERTEX_SHADER, attrMask, uniformMask }; params.useHWTransform = useHWTransform; @@ -828,7 +846,6 @@ LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs, if (ls == nullptr) { _dbg_assert_(FSID.Bit(FS_BIT_LMODE) == VSID.Bit(VS_BIT_LMODE)); _dbg_assert_(FSID.Bit(FS_BIT_DO_TEXTURE) == VSID.Bit(VS_BIT_DO_TEXTURE)); - _dbg_assert_(FSID.Bit(FS_BIT_ENABLE_FOG) == VSID.Bit(VS_BIT_ENABLE_FOG)); _dbg_assert_(FSID.Bit(FS_BIT_FLATSHADE) == VSID.Bit(VS_BIT_FLATSHADE)); // Check if we can link these. @@ -981,7 +998,7 @@ void ShaderManagerGLES::Load(const Path &filename) { if (!f.ReadArray(&fsid, 1)) { return; } - diskCachePending_.link.push_back(std::make_pair(vsid, fsid)); + diskCachePending_.link.emplace_back(vsid, fsid); } // Actual compilation happens in ContinuePrecompile(), called by GPU_GLES's IsReady. diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index 101326ee9b..2dd715cc0b 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -97,6 +97,7 @@ public: int u_texclampoff; // Lighting + int u_lightControl; int u_ambient; int u_matambientalpha; int u_matdiffuse; diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index dbfc115a91..cdcc9e5069 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -149,13 +149,14 @@ void DrawEngineGLES::ApplyDrawState(int prim) { GenericLogicState &logicState = pipelineState_.logicState; if (pipelineState_.FramebufferRead()) { - bool fboTexNeedsBind = false; - ApplyFramebufferRead(&fboTexNeedsBind); + FBOTexState fboTexBindState = FBO_TEX_NONE; + ApplyFramebufferRead(&fboTexBindState); // The shader takes over the responsibility for blending, so recompute. ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState); // We copy the framebuffer here, as doing so will wipe any blend state if we do it later. - if (fboTexNeedsBind) { + // fboTexNeedsBind_ won't be set if we can read directly from the target. + if (fboTexBindState == FBO_TEX_COPY_BIND_TEX) { // Note that this is positions, not UVs, that we need the copy from. framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); // If we are rendering at a higher resolution, linear is probably best for the dest color. @@ -166,6 +167,9 @@ void DrawEngineGLES::ApplyDrawState(int prim) { // Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects. dirtyRequiresRecheck_ |= DIRTY_BLEND_STATE; gstate_c.Dirty(DIRTY_BLEND_STATE); + } else if (fboTexBindState == FBO_TEX_READ_FRAMEBUFFER) { + // No action needed here. + fboTexBindState = FBO_TEX_NONE; } dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index c28fc6d1e5..83309208b8 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -266,6 +266,8 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { dstFmt = plan.replaced->Format(plan.baseLevelSrc); } else if (plan.scaleFactor > 1 || plan.saveTexture) { dstFmt = Draw::DataFormat::R8G8B8A8_UNORM; + } else if (plan.decodeToClut8) { + dstFmt = Draw::DataFormat::R8_UNORM; } if (plan.depth == 1) { @@ -313,7 +315,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { if (plan.scaleFactor > 1) { bpp = 4; } else { - bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2; + bpp = (int)Draw::DataFormatSizeInBytes(dstFmt); } } @@ -335,7 +337,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { render_->FinalizeTexture(entry->textureName, plan.levelsToLoad, genMips); } else { - int bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2; + int bpp = (int)Draw::DataFormatSizeInBytes(dstFmt); int stride = bpp * (plan.w * plan.scaleFactor); int levelStride = stride * (plan.h * plan.scaleFactor); @@ -443,3 +445,8 @@ void TextureCacheGLES::DeviceRestore(Draw::DrawContext *draw) { render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); textureShaderCache_->DeviceRestore(draw); } + +void *TextureCacheGLES::GetNativeTextureView(const TexCacheEntry *entry) { + GLRTexture *tex = entry->textureName; + return (void *)tex; +} diff --git a/GPU/GLES/TextureCacheGLES.h b/GPU/GLES/TextureCacheGLES.h index 1eae540b87..2a4d04574d 100644 --- a/GPU/GLES/TextureCacheGLES.h +++ b/GPU/GLES/TextureCacheGLES.h @@ -66,6 +66,7 @@ protected: void ReleaseTexture(TexCacheEntry *entry, bool delete_them) override; void BindAsClutTexture(Draw::Texture *tex, bool smooth) override; + void *GetNativeTextureView(const TexCacheEntry *entry) override; private: void ApplySamplingParams(const SamplerCacheKey &key) override; diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 0480b490d7..808ce53635 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -340,6 +340,7 @@ + @@ -455,6 +456,7 @@ + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index bd28a45e97..e110c20b8b 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -261,6 +261,9 @@ Debugger + + Common + @@ -512,6 +515,9 @@ Debugger + + Common + diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 107398b7c8..9830984739 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -87,9 +87,9 @@ const CommonCommandTableEntry commonCommandTable[] = { { GE_CMD_FOG2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FOGCOEF }, // These affect the fragment shader so need flushing. - { GE_CMD_CLEARMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE }, - { GE_CMD_TEXTUREMAPENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE }, - { GE_CMD_FOGENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE}, + { GE_CMD_CLEARMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE }, + { GE_CMD_TEXTUREMAPENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE }, + { GE_CMD_FOGENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAGMENTSHADER_STATE }, { GE_CMD_TEXMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS | DIRTY_FRAGMENTSHADER_STATE }, { GE_CMD_TEXSHADELS, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE }, // Raster state for Direct3D 9, uncommon. @@ -102,19 +102,19 @@ const CommonCommandTableEntry commonCommandTable[] = { // These change the vertex shader so need flushing. { GE_CMD_REVERSENORMAL, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE }, - { GE_CMD_LIGHTINGENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE }, - { GE_CMD_LIGHTENABLE0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE }, - { GE_CMD_LIGHTENABLE1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE }, - { GE_CMD_LIGHTENABLE2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE }, - { GE_CMD_LIGHTENABLE3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE }, + { GE_CMD_LIGHTINGENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE }, + { GE_CMD_LIGHTENABLE0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE }, + { GE_CMD_LIGHTENABLE1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE }, + { GE_CMD_LIGHTENABLE2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE }, + { GE_CMD_LIGHTENABLE3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE }, { GE_CMD_LIGHTTYPE0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_LIGHT0 }, { GE_CMD_LIGHTTYPE1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_LIGHT1 }, { GE_CMD_LIGHTTYPE2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_LIGHT2 }, { GE_CMD_LIGHTTYPE3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_LIGHT3 }, - { GE_CMD_MATERIALUPDATE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE }, // TODO: This should not need to dirty the fragment shader? + { GE_CMD_MATERIALUPDATE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE }, // These change both shaders so need flushing. - { GE_CMD_LIGHTMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE }, + { GE_CMD_LIGHTMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE }, { GE_CMD_TEXFILTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, { GE_CMD_TEXWRAP, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS | DIRTY_FRAGMENTSHADER_STATE }, @@ -425,6 +425,7 @@ GPUCommon::GPUCommon(GraphicsContext *gfxCtx, Draw::DrawContext *draw) : UpdateCmdInfo(); UpdateVsyncInterval(true); + ResetMatrices(); PPGeSetDrawContext(draw); } @@ -450,6 +451,29 @@ void GPUCommon::UpdateCmdInfo() { cmdInfo_[GE_CMD_JUMP].func = &GPUCommon::Execute_Jump; cmdInfo_[GE_CMD_CALL].func = &GPUCommon::Execute_Call; } + + // Reconfigure for light ubershader or not. + for (int i = 0; i < 4; i++) { + if (gstate_c.Supports(GPU_USE_LIGHT_UBERSHADER)) { + cmdInfo_[GE_CMD_LIGHTENABLE0 + i].RemoveDirty(DIRTY_VERTEXSHADER_STATE); + cmdInfo_[GE_CMD_LIGHTENABLE0 + i].AddDirty(DIRTY_LIGHT_CONTROL); + cmdInfo_[GE_CMD_LIGHTTYPE0 + i].RemoveDirty(DIRTY_VERTEXSHADER_STATE); + cmdInfo_[GE_CMD_LIGHTTYPE0 + i].AddDirty(DIRTY_LIGHT_CONTROL); + } else { + cmdInfo_[GE_CMD_LIGHTENABLE0 + i].RemoveDirty(DIRTY_LIGHT_CONTROL); + cmdInfo_[GE_CMD_LIGHTENABLE0 + i].AddDirty(DIRTY_VERTEXSHADER_STATE); + cmdInfo_[GE_CMD_LIGHTTYPE0 + i].RemoveDirty(DIRTY_LIGHT_CONTROL); + cmdInfo_[GE_CMD_LIGHTTYPE0 + i].AddDirty(DIRTY_VERTEXSHADER_STATE); + } + } + + if (gstate_c.Supports(GPU_USE_LIGHT_UBERSHADER)) { + cmdInfo_[GE_CMD_MATERIALUPDATE].RemoveDirty(DIRTY_VERTEXSHADER_STATE); + cmdInfo_[GE_CMD_MATERIALUPDATE].AddDirty(DIRTY_LIGHT_CONTROL); + } else { + cmdInfo_[GE_CMD_MATERIALUPDATE].RemoveDirty(DIRTY_LIGHT_CONTROL); + cmdInfo_[GE_CMD_MATERIALUPDATE].AddDirty(DIRTY_VERTEXSHADER_STATE); + } } void GPUCommon::BeginHostFrame() { @@ -708,6 +732,59 @@ int GPUCommon::GetStack(int index, u32 stackPtr) { return currentList->stackptr; } +static void CopyMatrix24(u32_le *result, const float *mtx, u32 count, u32 cmdbits) { + for (u32 i = 0; i < count; ++i) { + result[i] = toFloat24(mtx[i]) | cmdbits; + } +} + +bool GPUCommon::GetMatrix24(GEMatrixType type, u32_le *result, u32 cmdbits) { + 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: + CopyMatrix24(result, gstate.boneMatrix + (type - GE_MTX_BONE0) * 12, 12, cmdbits); + break; + case GE_MTX_TEXGEN: + CopyMatrix24(result, gstate.tgenMatrix, 12, cmdbits); + break; + case GE_MTX_WORLD: + CopyMatrix24(result, gstate.worldMatrix, 12, cmdbits); + break; + case GE_MTX_VIEW: + CopyMatrix24(result, gstate.viewMatrix, 12, cmdbits); + break; + case GE_MTX_PROJECTION: + CopyMatrix24(result, gstate.projMatrix, 16, cmdbits); + break; + default: + return false; + } + return true; +} + +void GPUCommon::ResetMatrices() { + // This means we restored a context, so update the visible matrix data. + for (size_t i = 0; i < ARRAY_SIZE(gstate.boneMatrix); ++i) + matrixVisible.bone[i] = toFloat24(gstate.boneMatrix[i]); + for (size_t i = 0; i < ARRAY_SIZE(gstate.worldMatrix); ++i) + matrixVisible.world[i] = toFloat24(gstate.worldMatrix[i]); + for (size_t i = 0; i < ARRAY_SIZE(gstate.viewMatrix); ++i) + matrixVisible.view[i] = toFloat24(gstate.viewMatrix[i]); + for (size_t i = 0; i < ARRAY_SIZE(gstate.projMatrix); ++i) + matrixVisible.proj[i] = toFloat24(gstate.projMatrix[i]); + for (size_t i = 0; i < ARRAY_SIZE(gstate.tgenMatrix); ++i) + matrixVisible.tgen[i] = toFloat24(gstate.tgenMatrix[i]); + + // Assume all the matrices changed, so dirty things related to them. + gstate_c.Dirty(DIRTY_WORLDMATRIX | DIRTY_VIEWMATRIX | DIRTY_PROJMATRIX | DIRTY_TEXMATRIX | DIRTY_FRAGMENTSHADER_STATE | DIRTY_BONE_UNIFORMS); +} + u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, PSPPointer args, bool head) { // TODO Check the stack values in missing arg and ajust the stack depth @@ -1203,6 +1280,15 @@ void GPUCommon::ReapplyGfxState() { // Let's just skip the transfer size stuff, it's just values. } +uint32_t GPUCommon::SetAddrTranslation(uint32_t value) { + std::swap(edramTranslation_, value); + return value; +} + +uint32_t GPUCommon::GetAddrTranslation() { + return edramTranslation_; +} + inline void GPUCommon::UpdateState(GPURunState state) { gpuState = state; if (state != GPUSTATE_RUNNING) @@ -1267,7 +1353,8 @@ void GPUCommon::Execute_Iaddr(u32 op, u32 diff) { } void GPUCommon::Execute_Origin(u32 op, u32 diff) { - gstate_c.offsetAddr = currentList->pc; + if (currentList) + gstate_c.offsetAddr = currentList->pc; } void GPUCommon::Execute_Jump(u32 op, u32 diff) { @@ -1330,7 +1417,7 @@ void GPUCommon::DoExecuteCall(u32 target) { // Check for the end if ((Memory::ReadUnchecked_U32(target + 11 * 4) >> 24) == GE_CMD_BONEMATRIXDATA && (Memory::ReadUnchecked_U32(target + 12 * 4) >> 24) == GE_CMD_RET && - (gstate.boneMatrixNumber & 0x7F) <= 96 - 12) { + (gstate.boneMatrixNumber & 0x00FFFFFF) <= 96 - 12) { // Yep, pretty sure this is a bone matrix call. Double check stall first. if (target > currentList->stall || target + 12 * 4 < currentList->stall) { FastLoadBoneMatrix(target); @@ -1596,7 +1683,7 @@ void GPUCommon::Execute_VertexType(u32 op, u32 diff) { if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) { gstate_c.Dirty(DIRTY_UVSCALEOFFSET); if (diff & GE_VTYPE_THROUGH_MASK) - gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_CULLRANGE); + gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE | DIRTY_CULLRANGE); } } @@ -1623,7 +1710,7 @@ void GPUCommon::Execute_VertexTypeSkinning(u32 op, u32 diff) { gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); } if (diff & GE_VTYPE_THROUGH_MASK) - gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_CULLRANGE); + gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE | DIRTY_CULLRANGE); } void GPUCommon::CheckDepthUsage(VirtualFramebuffer *vfb) { @@ -1864,7 +1951,7 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) { (Memory::ReadUnchecked_U32(target + 11 * 4) >> 24) == GE_CMD_BONEMATRIXDATA && (Memory::ReadUnchecked_U32(target + 12 * 4) >> 24) == GE_CMD_RET && (target > currentList->stall || target + 12 * 4 < currentList->stall) && - (gstate.boneMatrixNumber & 0x7F) <= 96 - 12) { + (gstate.boneMatrixNumber & 0x00FFFFFF) <= 96 - 12) { FastLoadBoneMatrix(target); } else { goto bail; @@ -1942,7 +2029,8 @@ void GPUCommon::Execute_Bezier(u32 op, u32 diff) { } // Can't flush after setting gstate_c.submitType below since it'll be a mess - it must be done already. - drawEngineCommon_->DispatchFlush(); + if (flushOnParams_) + drawEngineCommon_->DispatchFlush(); Spline::BezierSurface surface; surface.tess_u = gstate.getPatchDivisionU(); @@ -1956,8 +2044,8 @@ void GPUCommon::Execute_Bezier(u32 op, u32 diff) { SetDrawType(DRAW_BEZIER, PatchPrimToPrim(surface.primType)); + gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); if (drawEngineCommon_->CanUseHardwareTessellation(surface.primType)) { - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); gstate_c.submitType = SubmitType::HW_BEZIER; if (gstate_c.spline_num_points_u != surface.num_points_u) { gstate_c.Dirty(DIRTY_BEZIERSPLINE); @@ -1971,7 +2059,7 @@ void GPUCommon::Execute_Bezier(u32 op, u32 diff) { UpdateUVScaleOffset(); drawEngineCommon_->SubmitCurve(control_points, indices, surface, gstate.vertType, &bytesRead, "bezier"); - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); + gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); gstate_c.submitType = SubmitType::DRAW; // After drawing, we advance pointers - see SubmitPrim which does the same. @@ -2014,7 +2102,8 @@ void GPUCommon::Execute_Spline(u32 op, u32 diff) { } // Can't flush after setting gstate_c.submitType below since it'll be a mess - it must be done already. - drawEngineCommon_->DispatchFlush(); + if (flushOnParams_) + drawEngineCommon_->DispatchFlush(); Spline::SplineSurface surface; surface.tess_u = gstate.getPatchDivisionU(); @@ -2030,8 +2119,8 @@ void GPUCommon::Execute_Spline(u32 op, u32 diff) { SetDrawType(DRAW_SPLINE, PatchPrimToPrim(surface.primType)); + gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); if (drawEngineCommon_->CanUseHardwareTessellation(surface.primType)) { - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); gstate_c.submitType = SubmitType::HW_SPLINE; if (gstate_c.spline_num_points_u != surface.num_points_u) { gstate_c.Dirty(DIRTY_BEZIERSPLINE); @@ -2045,7 +2134,7 @@ void GPUCommon::Execute_Spline(u32 op, u32 diff) { UpdateUVScaleOffset(); drawEngineCommon_->SubmitCurve(control_points, indices, surface, gstate.vertType, &bytesRead, "spline"); - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); + gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); gstate_c.submitType = SubmitType::DRAW; // After drawing, we advance pointers - see SubmitPrim which does the same. @@ -2099,6 +2188,11 @@ void GPUCommon::Execute_BlockTransferStart(u32 op, u32 diff) { } void GPUCommon::Execute_WorldMtxNum(u32 op, u32 diff) { + if (!currentList) { + gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | (op & 0xF); + return; + } + // This is almost always followed by GE_CMD_WORLDMATRIXDATA. const u32_le *src = (const u32_le *)Memory::GetPointerUnchecked(currentList->pc + 4); u32 *dst = (u32 *)(gstate.worldMatrix + (op & 0xF)); @@ -2106,7 +2200,7 @@ void GPUCommon::Execute_WorldMtxNum(u32 op, u32 diff) { int i = 0; // We must record the individual data commands while debugRecording_. - bool fastLoad = !debugRecording_; + bool fastLoad = !debugRecording_ && end > 0; // Stalling in the middle of a matrix would be stupid, I doubt this check is necessary. if (currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) { fastLoad = false; @@ -2127,7 +2221,7 @@ void GPUCommon::Execute_WorldMtxNum(u32 op, u32 diff) { } const int count = i; - gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | ((op + count) & 0xF); + gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | ((op & 0xF) + count); // Skip over the loaded data, it's done now. UpdatePC(currentList->pc, currentList->pc + count * 4); @@ -2136,7 +2230,7 @@ void GPUCommon::Execute_WorldMtxNum(u32 op, u32 diff) { void GPUCommon::Execute_WorldMtxData(u32 op, u32 diff) { // Note: it's uncommon to get here now, see above. - int num = gstate.worldmtxnum & 0xF; + int num = gstate.worldmtxnum & 0x00FFFFFF; u32 newVal = op << 8; if (num < 12 && newVal != ((const u32 *)gstate.worldMatrix)[num]) { Flush(); @@ -2144,18 +2238,23 @@ void GPUCommon::Execute_WorldMtxData(u32 op, u32 diff) { gstate_c.Dirty(DIRTY_WORLDMATRIX); } num++; - gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | (num & 0xF); + gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | (num & 0x00FFFFFF); gstate.worldmtxdata = GE_CMD_WORLDMATRIXDATA << 24; } void GPUCommon::Execute_ViewMtxNum(u32 op, u32 diff) { + if (!currentList) { + gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | (op & 0xF); + return; + } + // This is almost always followed by GE_CMD_VIEWMATRIXDATA. const u32_le *src = (const u32_le *)Memory::GetPointerUnchecked(currentList->pc + 4); u32 *dst = (u32 *)(gstate.viewMatrix + (op & 0xF)); const int end = 12 - (op & 0xF); int i = 0; - bool fastLoad = !debugRecording_; + bool fastLoad = !debugRecording_ && end > 0; if (currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) { fastLoad = false; } @@ -2175,7 +2274,7 @@ void GPUCommon::Execute_ViewMtxNum(u32 op, u32 diff) { } const int count = i; - gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | ((op + count) & 0xF); + gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | ((op & 0xF) + count); // Skip over the loaded data, it's done now. UpdatePC(currentList->pc, currentList->pc + count * 4); @@ -2184,7 +2283,7 @@ void GPUCommon::Execute_ViewMtxNum(u32 op, u32 diff) { void GPUCommon::Execute_ViewMtxData(u32 op, u32 diff) { // Note: it's uncommon to get here now, see above. - int num = gstate.viewmtxnum & 0xF; + int num = gstate.viewmtxnum & 0x00FFFFFF; u32 newVal = op << 8; if (num < 12 && newVal != ((const u32 *)gstate.viewMatrix)[num]) { Flush(); @@ -2192,11 +2291,16 @@ void GPUCommon::Execute_ViewMtxData(u32 op, u32 diff) { gstate_c.Dirty(DIRTY_VIEWMATRIX); } num++; - gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | (num & 0xF); + gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | (num & 0x00FFFFFF); gstate.viewmtxdata = GE_CMD_VIEWMATRIXDATA << 24; } void GPUCommon::Execute_ProjMtxNum(u32 op, u32 diff) { + if (!currentList) { + gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | (op & 0xF); + return; + } + // This is almost always followed by GE_CMD_PROJMATRIXDATA. const u32_le *src = (const u32_le *)Memory::GetPointerUnchecked(currentList->pc + 4); u32 *dst = (u32 *)(gstate.projMatrix + (op & 0xF)); @@ -2223,7 +2327,7 @@ void GPUCommon::Execute_ProjMtxNum(u32 op, u32 diff) { } const int count = i; - gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | ((op + count) & 0x1F); + gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | ((op & 0xF) + count); // Skip over the loaded data, it's done now. UpdatePC(currentList->pc, currentList->pc + count * 4); @@ -2232,27 +2336,32 @@ void GPUCommon::Execute_ProjMtxNum(u32 op, u32 diff) { void GPUCommon::Execute_ProjMtxData(u32 op, u32 diff) { // Note: it's uncommon to get here now, see above. - int num = gstate.projmtxnum & 0x1F; // NOTE: Changed from 0xF to catch overflows + int num = gstate.projmtxnum & 0x00FFFFFF; u32 newVal = op << 8; - if (num < 0x10 && newVal != ((const u32 *)gstate.projMatrix)[num]) { + if (num < 16 && newVal != ((const u32 *)gstate.projMatrix)[num]) { Flush(); ((u32 *)gstate.projMatrix)[num] = newVal; gstate_c.Dirty(DIRTY_PROJMATRIX); } num++; if (num <= 16) - gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | (num & 0xF); + gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | (num & 0x00FFFFFF); gstate.projmtxdata = GE_CMD_PROJMATRIXDATA << 24; } void GPUCommon::Execute_TgenMtxNum(u32 op, u32 diff) { + if (!currentList) { + gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | (op & 0xF); + return; + } + // This is almost always followed by GE_CMD_TGENMATRIXDATA. const u32_le *src = (const u32_le *)Memory::GetPointerUnchecked(currentList->pc + 4); u32 *dst = (u32 *)(gstate.tgenMatrix + (op & 0xF)); const int end = 12 - (op & 0xF); int i = 0; - bool fastLoad = !debugRecording_; + bool fastLoad = !debugRecording_ && end > 0; if (currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) { fastLoad = false; } @@ -2263,7 +2372,8 @@ void GPUCommon::Execute_TgenMtxNum(u32 op, u32 diff) { if (dst[i] != newVal) { Flush(); dst[i] = newVal; - gstate_c.Dirty(DIRTY_TEXMATRIX); + // We check the matrix to see if we need projection. + gstate_c.Dirty(DIRTY_TEXMATRIX | DIRTY_FRAGMENTSHADER_STATE); } if (++i >= end) { break; @@ -2272,7 +2382,7 @@ void GPUCommon::Execute_TgenMtxNum(u32 op, u32 diff) { } const int count = i; - gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | ((op + count) & 0xF); + gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | ((op & 0xF) + count); // Skip over the loaded data, it's done now. UpdatePC(currentList->pc, currentList->pc + count * 4); @@ -2281,7 +2391,7 @@ void GPUCommon::Execute_TgenMtxNum(u32 op, u32 diff) { void GPUCommon::Execute_TgenMtxData(u32 op, u32 diff) { // Note: it's uncommon to get here now, see above. - int num = gstate.texmtxnum & 0xF; + int num = gstate.texmtxnum & 0x00FFFFFF; u32 newVal = op << 8; if (num < 12 && newVal != ((const u32 *)gstate.tgenMatrix)[num]) { Flush(); @@ -2289,11 +2399,16 @@ void GPUCommon::Execute_TgenMtxData(u32 op, u32 diff) { gstate_c.Dirty(DIRTY_TEXMATRIX | DIRTY_FRAGMENTSHADER_STATE); // We check the matrix to see if we need projection } num++; - gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | (num & 0xF); + gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | (num & 0x00FFFFFF); gstate.texmtxdata = GE_CMD_TGENMATRIXDATA << 24; } void GPUCommon::Execute_BoneMtxNum(u32 op, u32 diff) { + if (!currentList) { + gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (op & 0x7F); + return; + } + // This is almost always followed by GE_CMD_BONEMATRIXDATA. const u32_le *src = (const u32_le *)Memory::GetPointerUnchecked(currentList->pc + 4); u32 *dst = (u32 *)(gstate.boneMatrix + (op & 0x7F)); @@ -2339,7 +2454,7 @@ void GPUCommon::Execute_BoneMtxNum(u32 op, u32 diff) { } const int count = i; - gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | ((op + count) & 0x7F); + gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | ((op & 0x7F) + count); // Skip over the loaded data, it's done now. UpdatePC(currentList->pc, currentList->pc + count * 4); @@ -2348,7 +2463,7 @@ void GPUCommon::Execute_BoneMtxNum(u32 op, u32 diff) { void GPUCommon::Execute_BoneMtxData(u32 op, u32 diff) { // Note: it's uncommon to get here now, see above. - int num = gstate.boneMatrixNumber & 0x7F; + int num = gstate.boneMatrixNumber & 0x00FFFFFF; u32 newVal = op << 8; if (num < 96 && newVal != ((const u32 *)gstate.boneMatrix)[num]) { // Bone matrices should NOT flush when software skinning is enabled! @@ -2361,7 +2476,7 @@ void GPUCommon::Execute_BoneMtxData(u32 op, u32 diff) { ((u32 *)gstate.boneMatrix)[num] = newVal; } num++; - gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (num & 0x7F); + gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (num & 0x00FFFFFF); gstate.boneMatrixData = GE_CMD_BONEMATRIXDATA << 24; } @@ -2414,10 +2529,10 @@ void GPUCommon::Execute_ImmVertexAlphaPrim(u32 op, u32 diff) { immPrim_ = (GEPrimitiveType)prim; // Flags seem to only be respected from the first prim. immFlags_ = op & 0x00FFF800; + immFirstSent_ = false; } else if (prim == GE_PRIM_KEEP_PREVIOUS && immPrim_ != GE_PRIM_INVALID) { static constexpr int flushPrimCount[] = { 1, 2, 0, 3, 0, 0, 2, 0 }; - // Instead of finding a proper point to flush, we just emit a full rectangle every time one - // is finished. + // Instead of finding a proper point to flush, we just emit prims when we can. if (immCount_ == flushPrimCount[immPrim_ & 7]) FlushImm(); } else { @@ -2439,31 +2554,6 @@ void GPUCommon::FlushImm() { } UpdateUVScaleOffset(); - // Instead of plumbing through properly (we'd need to inject these pretransformed vertices in the middle - // of SoftwareTransform(), which would take a lot of refactoring), we'll cheat and just turn these into - // through vertices. - // Since the only known use is Thrillville and it only uses it to clear, we just use color and pos. - struct ImmVertex { - float uv[2]; - uint32_t color; - float xyz[3]; - }; - ImmVertex temp[MAX_IMMBUFFER_SIZE]; - uint32_t color1Used = 0; - for (int i = 0; i < immCount_; i++) { - // Since we're sending through, scale back up to w/h. - temp[i].uv[0] = immBuffer_[i].u * gstate.getTextureWidth(0); - temp[i].uv[1] = immBuffer_[i].v * gstate.getTextureHeight(0); - temp[i].color = immBuffer_[i].color0_32; - temp[i].xyz[0] = immBuffer_[i].pos[0]; - temp[i].xyz[1] = immBuffer_[i].pos[1]; - temp[i].xyz[2] = immBuffer_[i].pos[2]; - color1Used |= immBuffer_[i].color1_32; - } - int vtype = GE_VTYPE_TC_FLOAT | GE_VTYPE_POS_FLOAT | GE_VTYPE_COL_8888 | GE_VTYPE_THROUGH; - - // TODO: Handle fog and secondary color somehow? - bool antialias = (immFlags_ & GE_IMM_ANTIALIAS) != 0; bool prevAntialias = gstate.isAntiAliasEnabled(); bool shading = (immFlags_ & GE_IMM_SHADING) != 0; @@ -2473,40 +2563,42 @@ void GPUCommon::FlushImm() { int cullMode = (immFlags_ & GE_IMM_CULLFACE) != 0 ? 1 : 0; bool texturing = (immFlags_ & GE_IMM_TEXTURE) != 0; bool prevTexturing = gstate.isTextureMapEnabled(); + bool fog = (immFlags_ & GE_IMM_FOG) != 0; + bool prevFog = gstate.isFogEnabled(); bool dither = (immFlags_ & GE_IMM_DITHER) != 0; bool prevDither = gstate.isDitherEnabled(); if ((immFlags_ & GE_IMM_CLIPMASK) != 0) { WARN_LOG_REPORT_ONCE(geimmclipvalue, G3D, "Imm vertex used clip value, flags=%06x", immFlags_); - } else if ((immFlags_ & GE_IMM_FOG) != 0) { - WARN_LOG_REPORT_ONCE(geimmfog, G3D, "Imm vertex used fog, flags=%06x", immFlags_); - } else if (color1Used != 0 && gstate.isUsingSecondaryColor()) { - WARN_LOG_REPORT_ONCE(geimmcolor1, G3D, "Imm vertex used secondary color, flags=%06x", immFlags_); } - if (texturing != prevTexturing || cullEnable != prevCullEnable || dither != prevDither || prevShading != shading) { + bool changed = texturing != prevTexturing || cullEnable != prevCullEnable || dither != prevDither; + changed = changed || prevShading != shading || prevFog != fog; + if (changed) { DispatchFlush(); gstate.antiAliasEnable = (GE_CMD_ANTIALIASENABLE << 24) | (int)antialias; gstate.shademodel = (GE_CMD_SHADEMODE << 24) | (int)shading; gstate.cullfaceEnable = (GE_CMD_CULLFACEENABLE << 24) | (int)cullEnable; gstate.textureMapEnable = (GE_CMD_TEXTUREMAPENABLE << 24) | (int)texturing; + gstate.fogEnable = (GE_CMD_FOGENABLE << 24) | (int)fog; gstate.ditherEnable = (GE_CMD_DITHERENABLE << 24) | (int)dither; - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE); + gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_UVSCALEOFFSET | DIRTY_CULLRANGE); } - int bytesRead; - uint32_t vertTypeID = GetVertTypeID(vtype, 0); - drawEngineCommon_->DispatchSubmitImm(temp, nullptr, immPrim_, immCount_, vertTypeID, cullMode, &bytesRead); - // TODO: In the future, make a special path for these. - // drawEngineCommon_->DispatchSubmitImm(immBuffer_, immCount_); + drawEngineCommon_->DispatchSubmitImm(immPrim_, immBuffer_, immCount_, cullMode, immFirstSent_); immCount_ = 0; + immFirstSent_ = true; - gstate.antiAliasEnable = (GE_CMD_ANTIALIASENABLE << 24) | (int)prevAntialias; - gstate.shademodel = (GE_CMD_SHADEMODE << 24) | (int)prevShading; - gstate.cullfaceEnable = (GE_CMD_CULLFACEENABLE << 24) | (int)prevCullEnable; - gstate.textureMapEnable = (GE_CMD_TEXTUREMAPENABLE << 24) | (int)prevTexturing; - gstate.ditherEnable = (GE_CMD_DITHERENABLE << 24) | (int)prevDither; - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE); + if (changed) { + DispatchFlush(); + gstate.antiAliasEnable = (GE_CMD_ANTIALIASENABLE << 24) | (int)prevAntialias; + gstate.shademodel = (GE_CMD_SHADEMODE << 24) | (int)prevShading; + gstate.cullfaceEnable = (GE_CMD_CULLFACEENABLE << 24) | (int)prevCullEnable; + gstate.textureMapEnable = (GE_CMD_TEXTUREMAPENABLE << 24) | (int)prevTexturing; + gstate.fogEnable = (GE_CMD_FOGENABLE << 24) | (int)prevFog; + gstate.ditherEnable = (GE_CMD_DITHERENABLE << 24) | (int)prevDither; + gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_UVSCALEOFFSET | DIRTY_CULLRANGE); + } } void GPUCommon::ExecuteOp(u32 op, u32 diff) { @@ -2623,7 +2715,7 @@ struct DisplayList_v2 { }; void GPUCommon::DoState(PointerWrap &p) { - auto s = p.Section("GPUCommon", 1, 4); + auto s = p.Section("GPUCommon", 1, 6); if (!s) return; @@ -2695,6 +2787,13 @@ void GPUCommon::DoState(PointerWrap &p) { Do(p, isbreak); Do(p, drawCompleteTicks); Do(p, busyTicks); + + if (s >= 5) { + Do(p, matrixVisible.all); + } + if (s >= 6) { + Do(p, edramTranslation_); + } } void GPUCommon::InterruptStart(int listid) { @@ -2947,10 +3046,10 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) { cyclesExecuted += ((height * width * bpp) * 16) / 10; } -bool GPUCommon::PerformMemoryCopy(u32 dest, u32 src, int size) { +bool GPUCommon::PerformMemoryCopy(u32 dest, u32 src, int size, GPUCopyFlag flags) { // Track stray copies of a framebuffer in RAM. MotoGP does this. if (framebufferManager_->MayIntersectFramebuffer(src) || framebufferManager_->MayIntersectFramebuffer(dest)) { - if (!framebufferManager_->NotifyFramebufferCopy(src, dest, size, false, gstate_c.skipDrawReason)) { + if (!framebufferManager_->NotifyFramebufferCopy(src, dest, size, flags, gstate_c.skipDrawReason)) { // We use a little hack for PerformMemoryDownload/PerformMemoryUpload using a VRAM mirror. // Since they're identical we don't need to copy. if (!Memory::IsVRAMAddress(dest) || (dest ^ 0x00400000) != src) { @@ -2972,7 +3071,8 @@ bool GPUCommon::PerformMemoryCopy(u32 dest, u32 src, int size) { NotifyMemInfo(MemBlockFlags::WRITE, dest, size, tag.c_str(), tag.size()); } InvalidateCache(dest, size, GPU_INVALIDATE_HINT); - GPURecord::NotifyMemcpy(dest, src, size); + if (!(flags & GPUCopyFlag::DEBUG_NOTIFIED)) + GPURecord::NotifyMemcpy(dest, src, size); return false; } @@ -2980,7 +3080,7 @@ bool GPUCommon::PerformMemorySet(u32 dest, u8 v, int size) { // This may indicate a memset, usually to 0, of a framebuffer. if (framebufferManager_->MayIntersectFramebuffer(dest)) { Memory::Memset(dest, v, size, "GPUMemset"); - if (!framebufferManager_->NotifyFramebufferCopy(dest, dest, size, true, gstate_c.skipDrawReason)) { + if (!framebufferManager_->NotifyFramebufferCopy(dest, dest, size, GPUCopyFlag::MEMSET, gstate_c.skipDrawReason)) { InvalidateCache(dest, size, GPU_INVALIDATE_HINT); } return true; @@ -2994,20 +3094,16 @@ bool GPUCommon::PerformMemorySet(u32 dest, u8 v, int size) { } bool GPUCommon::PerformMemoryDownload(u32 dest, int size) { - // Cheat a bit to force a download of the framebuffer. - // VRAM + 0x00400000 is simply a VRAM mirror. if (Memory::IsVRAMAddress(dest)) { - return PerformMemoryCopy(dest ^ 0x00400000, dest, size); + return PerformMemoryCopy(dest, dest, size, GPUCopyFlag::FORCE_DST_MEM); } return false; } bool GPUCommon::PerformMemoryUpload(u32 dest, int size) { - // Cheat a bit to force an upload of the framebuffer. - // VRAM + 0x00400000 is simply a VRAM mirror. if (Memory::IsVRAMAddress(dest)) { GPURecord::NotifyUpload(dest, size); - return PerformMemoryCopy(dest, dest ^ 0x00400000, size); + return PerformMemoryCopy(dest, dest, size, GPUCopyFlag::FORCE_SRC_MEM | GPUCopyFlag::DEBUG_NOTIFIED); } return false; } @@ -3176,3 +3272,57 @@ size_t GPUCommon::FormatGPUStatsCommon(char *buffer, size_t size) { vertexAverageCycles ); } + +u32 GPUCommon::CheckGPUFeatures() const { + u32 features = 0; + if (draw_->GetDeviceCaps().logicOpSupported) { + features |= GPU_SUPPORTS_LOGIC_OP; + } + if (draw_->GetDeviceCaps().anisoSupported) { + features |= GPU_SUPPORTS_ANISOTROPY; + } + if (draw_->GetDeviceCaps().textureNPOTFullySupported) { + features |= GPU_SUPPORTS_TEXTURE_NPOT; + } + if (draw_->GetDeviceCaps().dualSourceBlend) { + if (!g_Config.bVendorBugChecksEnabled || !draw_->GetBugs().Has(Draw::Bugs::DUAL_SOURCE_BLENDING_BROKEN)) { + features |= GPU_SUPPORTS_DUALSOURCE_BLEND; + } + } + if (draw_->GetDeviceCaps().blendMinMaxSupported) { + features |= GPU_SUPPORTS_BLEND_MINMAX; + } + + if (draw_->GetDeviceCaps().clipDistanceSupported) { + features |= GPU_SUPPORTS_CLIP_DISTANCE; + } + + if (draw_->GetDeviceCaps().cullDistanceSupported) { + features |= GPU_SUPPORTS_CULL_DISTANCE; + } + + if (draw_->GetDeviceCaps().textureDepthSupported) { + features |= GPU_SUPPORTS_DEPTH_TEXTURE; + } + + bool canClipOrCull = draw_->GetDeviceCaps().clipDistanceSupported || draw_->GetDeviceCaps().cullDistanceSupported; + bool canDiscardVertex = draw_->GetBugs().Has(Draw::Bugs::BROKEN_NAN_IN_CONDITIONAL); + if (canClipOrCull || canDiscardVertex) { + // We'll dynamically use the parts that are supported, to reduce artifacts as much as possible. + features |= GPU_SUPPORTS_VS_RANGE_CULLING; + } + + if (draw_->GetDeviceCaps().framebufferFetchSupported) { + features |= GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH; + } + + if (draw_->GetDeviceCaps().fragmentShaderInt32Supported) { + features |= GPU_USE_LIGHT_UBERSHADER; + } + + if (PSP_CoreParameter().compat.flags().ClearToRAM) { + features |= GPU_USE_CLEAR_RAM_HACK; + } + + return features; +} diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index 3a5674127f..3c478e2cec 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -76,7 +76,7 @@ public: Draw::DrawContext *GetDrawContext() override { return draw_; } - virtual void CheckGPUFeatures() = 0; + virtual u32 CheckGPUFeatures() const; void UpdateCmdInfo(); @@ -103,7 +103,7 @@ public: void ExecuteOp(u32 op, u32 diff) override; void PreExecuteOp(u32 op, u32 diff) override; - bool InterpretList(DisplayList &list) override; + bool InterpretList(DisplayList &list); void ProcessDLQueue(); u32 UpdateStall(int listid, u32 newstall) override; u32 EnqueueList(u32 listpc, u32 stall, int subIntrBase, PSPPointer args, bool head) override; @@ -111,16 +111,20 @@ public: int ListSync(int listid, int mode) override; u32 DrawSync(int mode) override; int GetStack(int index, u32 stackPtr) override; + bool GetMatrix24(GEMatrixType type, u32_le *result, u32 cmdbits) override; + void ResetMatrices() override; void DoState(PointerWrap &p) override; bool BusyDrawing() override; u32 Continue() override; u32 Break(int mode) override; void ReapplyGfxState() override; + uint32_t SetAddrTranslation(uint32_t value) override; + uint32_t GetAddrTranslation() override; void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) override; void CopyDisplayToOutput(bool reallyDirty) override = 0; void InitClear() override = 0; - bool PerformMemoryCopy(u32 dest, u32 src, int size) override; + bool PerformMemoryCopy(u32 dest, u32 src, int size, GPUCopyFlag flags = GPUCopyFlag::NONE) override; bool PerformMemorySet(u32 dest, u8 v, int size) override; bool PerformMemoryDownload(u32 dest, int size) override; bool PerformMemoryUpload(u32 dest, int size) override; @@ -265,14 +269,14 @@ protected: void SetDrawType(DrawType type, GEPrimitiveType prim) { if (type != lastDraw_) { // We always flush when drawing splines/beziers so no need to do so here - gstate_c.Dirty(DIRTY_UVSCALEOFFSET | DIRTY_VERTEXSHADER_STATE); + gstate_c.Dirty(DIRTY_UVSCALEOFFSET | DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); lastDraw_ = type; } // Prim == RECTANGLES can cause CanUseHardwareTransform to flip, so we need to dirty. // Also, culling may be affected so dirty the raster state. if (IsTrianglePrim(prim) != IsTrianglePrim(lastPrim_)) { Flush(); - gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VERTEXSHADER_STATE); + gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); lastPrim_ = prim; } } @@ -314,6 +318,14 @@ protected: struct CommandInfo { uint64_t flags; GPUCommon::CmdFunc func; + + // Dirty flags are mashed into the regular flags by a left shift of 8. + void AddDirty(u64 dirty) { + flags |= dirty << 8; + } + void RemoveDirty(u64 dirty) { + flags &= ~(dirty << 8); + } }; static CommandInfo cmdInfo_[256]; @@ -355,6 +367,24 @@ protected: int immCount_ = 0; GEPrimitiveType immPrim_ = GE_PRIM_INVALID; uint32_t immFlags_ = 0; + bool immFirstSent_ = false; + + uint32_t edramTranslation_ = 0x400; + + // Whe matrix data overflows, the CPU visible values wrap and bleed between matrices. + // But this doesn't actually change the values used by rendering. + // The CPU visible values affect the GPU when list contexts are restored. + // Note: not maintained by all backends, here for save stating. + union { + struct { + u32 bone[12 * 8]; + u32 world[12]; + u32 view[12]; + u32 proj[16]; + u32 tgen[12]; + }; + u32 all[12 * 8 + 12 + 12 + 16 + 12]; + } matrixVisible; std::string reportingPrimaryInfo_; std::string reportingFullInfo_; diff --git a/GPU/GPUInterface.h b/GPU/GPUInterface.h index 832897f598..e7cf2f9af6 100644 --- a/GPU/GPUInterface.h +++ b/GPU/GPUInterface.h @@ -114,6 +114,16 @@ enum class StencilUpload { }; ENUM_CLASS_BITOPS(StencilUpload); +enum class GPUCopyFlag { + NONE = 0, + FORCE_SRC_MEM = 1, + FORCE_DST_MEM = 2, + // Note: implies src == dst and FORCE_SRC_MEM. + MEMSET = 4, + DEBUG_NOTIFIED = 8, +}; +ENUM_CLASS_BITOPS(GPUCopyFlag); + // Used for debug struct FramebufferInfo { u32 fb_address; @@ -198,6 +208,9 @@ public: virtual u32 Continue() = 0; virtual u32 Break(int mode) = 0; virtual int GetStack(int index, u32 stackPtr) = 0; + virtual bool GetMatrix24(GEMatrixType type, u32_le *result, u32 cmdbits) = 0; + virtual void ResetMatrices() = 0; + virtual uint32_t SetAddrTranslation(uint32_t value) = 0; virtual void InterruptStart(int listid) = 0; virtual void InterruptEnd(int listid) = 0; @@ -205,7 +218,6 @@ public: virtual void PreExecuteOp(u32 op, u32 diff) = 0; virtual void ExecuteOp(u32 op, u32 diff) = 0; - virtual bool InterpretList(DisplayList& list) = 0; // Framebuffer management virtual void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) = 0; @@ -220,7 +232,7 @@ public: virtual void InvalidateCache(u32 addr, int size, GPUInvalidationType type) = 0; virtual void NotifyVideoUpload(u32 addr, int size, int width, int format) = 0; // Update either RAM from VRAM, or VRAM from RAM... or even VRAM from VRAM. - virtual bool PerformMemoryCopy(u32 dest, u32 src, int size) = 0; + virtual bool PerformMemoryCopy(u32 dest, u32 src, int size, GPUCopyFlag flags = GPUCopyFlag::NONE) = 0; virtual bool PerformMemorySet(u32 dest, u8 v, int size) = 0; virtual bool PerformMemoryDownload(u32 dest, int size) = 0; virtual bool PerformMemoryUpload(u32 dest, int size) = 0; diff --git a/GPU/GPUState.cpp b/GPU/GPUState.cpp index cec0f0c5d1..374816fa76 100644 --- a/GPU/GPUState.cpp +++ b/GPU/GPUState.cpp @@ -24,6 +24,7 @@ #include "Core/System.h" #include "Core/MemMap.h" #include "GPU/ge_constants.h" +#include "GPU/GPUInterface.h" #include "GPU/GPUState.h" #ifdef _M_SSE @@ -85,11 +86,20 @@ static const CmdRange contextCmdRanges[] = { // Skip: {0xFA, 0xFF}, }; -static u32_le *SaveMatrix(u32_le *cmds, const float *mtx, int sz, int numcmd, int datacmd) { +static u32_le *SaveMatrix(u32_le *cmds, GEMatrixType type, int sz, int numcmd, int datacmd) { + if (!gpu) + return cmds; + *cmds++ = numcmd << 24; - for (int i = 0; i < sz; ++i) { - *cmds++ = (datacmd << 24) | toFloat24(mtx[i]); + // This saves the CPU-visible values, not the actual used ones, which may differ. + // Note that Restore overwrites both values. + if (type == GE_MTX_BONE0) { + for (int i = 0; i < 8; ++i) + gpu->GetMatrix24(GEMatrixType(GE_MTX_BONE0 + i), cmds + i * 12, datacmd << 24); + } else { + gpu->GetMatrix24(type, cmds, datacmd << 24); } + cmds += sz; return cmds; } @@ -130,6 +140,7 @@ void GPUgstate::Save(u32_le *ptr) { u32_le *cmds = ptr + 17; for (size_t i = 0; i < ARRAY_SIZE(contextCmdRanges); ++i) { for (int n = contextCmdRanges[i].start; n <= contextCmdRanges[i].end; ++n) { + // We'll run ReapplyGfxState after this to process dirtying. *cmds++ = cmdmem[n]; } } @@ -152,17 +163,17 @@ void GPUgstate::Save(u32_le *ptr) { memcpy(matrices, projMatrix, sizeof(projMatrix)); matrices += sizeof(projMatrix); memcpy(matrices, tgenMatrix, sizeof(tgenMatrix)); matrices += sizeof(tgenMatrix); } else { - cmds = SaveMatrix(cmds, boneMatrix, ARRAY_SIZE(boneMatrix), GE_CMD_BONEMATRIXNUMBER, GE_CMD_BONEMATRIXDATA); - cmds = SaveMatrix(cmds, worldMatrix, ARRAY_SIZE(worldMatrix), GE_CMD_WORLDMATRIXNUMBER, GE_CMD_WORLDMATRIXDATA); - cmds = SaveMatrix(cmds, viewMatrix, ARRAY_SIZE(viewMatrix), GE_CMD_VIEWMATRIXNUMBER, GE_CMD_VIEWMATRIXDATA); - cmds = SaveMatrix(cmds, projMatrix, ARRAY_SIZE(projMatrix), GE_CMD_PROJMATRIXNUMBER, GE_CMD_PROJMATRIXDATA); - cmds = SaveMatrix(cmds, tgenMatrix, ARRAY_SIZE(tgenMatrix), GE_CMD_TGENMATRIXNUMBER, GE_CMD_TGENMATRIXDATA); + cmds = SaveMatrix(cmds, GE_MTX_BONE0, ARRAY_SIZE(boneMatrix), GE_CMD_BONEMATRIXNUMBER, GE_CMD_BONEMATRIXDATA); + cmds = SaveMatrix(cmds, GE_MTX_WORLD, ARRAY_SIZE(worldMatrix), GE_CMD_WORLDMATRIXNUMBER, GE_CMD_WORLDMATRIXDATA); + cmds = SaveMatrix(cmds, GE_MTX_VIEW, ARRAY_SIZE(viewMatrix), GE_CMD_VIEWMATRIXNUMBER, GE_CMD_VIEWMATRIXDATA); + cmds = SaveMatrix(cmds, GE_MTX_PROJECTION, ARRAY_SIZE(projMatrix), GE_CMD_PROJMATRIXNUMBER, GE_CMD_PROJMATRIXDATA); + cmds = SaveMatrix(cmds, GE_MTX_TEXGEN, ARRAY_SIZE(tgenMatrix), GE_CMD_TGENMATRIXNUMBER, GE_CMD_TGENMATRIXDATA); - *cmds++ = boneMatrixNumber; - *cmds++ = worldmtxnum; - *cmds++ = viewmtxnum; - *cmds++ = projmtxnum; - *cmds++ = texmtxnum; + *cmds++ = boneMatrixNumber & 0xFF00007F; + *cmds++ = worldmtxnum & 0xFF00000F; + *cmds++ = viewmtxnum & 0xFF00000F; + *cmds++ = projmtxnum & 0xFF00000F; + *cmds++ = texmtxnum & 0xFF00000F; *cmds++ = GE_CMD_END << 24; } } @@ -199,7 +210,7 @@ void GPUgstate::FastLoadBoneMatrix(u32 addr) { #endif num += 12; - gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (num & 0x7F); + gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (num & 0x00FFFFFF); } void GPUgstate::Restore(u32_le *ptr) { @@ -238,12 +249,15 @@ void GPUgstate::Restore(u32_le *ptr) { cmds = LoadMatrix(cmds, projMatrix, ARRAY_SIZE(projMatrix)); cmds = LoadMatrix(cmds, tgenMatrix, ARRAY_SIZE(tgenMatrix)); - boneMatrixNumber = *cmds++; - worldmtxnum = *cmds++; - viewmtxnum = *cmds++; - projmtxnum = *cmds++; - texmtxnum = *cmds++; + boneMatrixNumber = (*cmds++) & 0xFF00007F; + worldmtxnum = (*cmds++) & 0xFF00000F; + viewmtxnum = (*cmds++) & 0xFF00000F; + projmtxnum = (*cmds++) & 0xFF00000F; + texmtxnum = (*cmds++) & 0xFF00000F; } + + if (gpu) + gpu->ResetMatrices(); } bool vertTypeIsSkinningEnabled(u32 vertType) { diff --git a/GPU/GPUState.h b/GPU/GPUState.h index cbfb58395d..fa9d4b575c 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -206,13 +206,14 @@ struct GPUgstate { float boneMatrix[12 * 8]; // Eight 4x3 bone matrices. // We ignore the high bits of the framebuffer in fbwidth - even 0x08000000 renders to vRAM. - u32 getFrameBufRawAddress() const { return (fbptr & 0xFFFFFF); } + // The top bits of mirroring are also not respected, so we mask them away. + u32 getFrameBufRawAddress() const { return fbptr & 0x1FFFF0; } // 0x44000000 is uncached VRAM. u32 getFrameBufAddress() const { return 0x44000000 | getFrameBufRawAddress(); } GEBufferFormat FrameBufFormat() const { return static_cast(framebufpixformat & 3); } int FrameBufStride() const { return fbwidth&0x7FC; } - u32 getDepthBufRawAddress() const { return (zbptr & 0xFFFFFF); } - u32 getDepthBufAddress() const { return 0x44000000 | getDepthBufRawAddress(); } + u32 getDepthBufRawAddress() const { return zbptr & 0x1FFFF0; } + u32 getDepthBufAddress() const { return 0x44600000 | getDepthBufRawAddress(); } int DepthBufStride() const { return zbwidth&0x7FC; } // Pixel Pipeline @@ -469,12 +470,13 @@ struct UVScale { // Might want to move this mechanism into the backend later. enum { GPU_SUPPORTS_DUALSOURCE_BLEND = FLAG_BIT(0), - // Free bit: 1 - GPU_SUPPORTS_GLSL_330 = FLAG_BIT(2), + GPU_USE_LIGHT_UBERSHADER = FLAG_BIT(1), + // Free bit: 2 GPU_SUPPORTS_VS_RANGE_CULLING = FLAG_BIT(3), GPU_SUPPORTS_BLEND_MINMAX = FLAG_BIT(4), GPU_SUPPORTS_LOGIC_OP = FLAG_BIT(5), GPU_USE_DEPTH_RANGE_HACK = FLAG_BIT(6), + // Free bit: 7 GPU_SUPPORTS_ANISOTROPY = FLAG_BIT(8), GPU_USE_CLEAR_RAM_HACK = FLAG_BIT(9), GPU_SUPPORTS_INSTANCE_RENDERING = FLAG_BIT(10), @@ -485,7 +487,8 @@ enum { // Free bit: 15 GPU_SUPPORTS_DEPTH_TEXTURE = FLAG_BIT(16), GPU_SUPPORTS_ACCURATE_DEPTH = FLAG_BIT(17), - // Free bits: 18-19 + GPU_SUPPORTS_GS_CULLING = FLAG_BIT(18), // Geometry shader + // Free bit: 19 GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH = FLAG_BIT(20), GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT = FLAG_BIT(21), GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT = FLAG_BIT(22), diff --git a/GPU/Math3D.cpp b/GPU/Math3D.cpp index 7da413829f..ecb959ec2c 100644 --- a/GPU/Math3D.cpp +++ b/GPU/Math3D.cpp @@ -121,7 +121,8 @@ __m128 SSENormalizeMultiplierSSE2(__m128 v) #endif __m128 SSENormalizeMultiplierSSE4(__m128 v) { - return _mm_rsqrt_ps(_mm_dp_ps(v, v, 0xFF)); + // This is only used for Vec3f, so ignore the 4th component, might be garbage. + return _mm_rsqrt_ps(_mm_dp_ps(v, v, 0x77)); } __m128 SSENormalizeMultiplier(bool useSSE4, __m128 v) diff --git a/GPU/Software/BinManager.cpp b/GPU/Software/BinManager.cpp index 3438cb222d..9a008e6658 100644 --- a/GPU/Software/BinManager.cpp +++ b/GPU/Software/BinManager.cpp @@ -161,13 +161,13 @@ BinManager::~BinManager() { } } -void BinManager::UpdateState(bool throughMode) { +void BinManager::UpdateState() { PROFILE_THIS_SCOPE("bin_state"); if (HasDirty(SoftDirty::PIXEL_ALL | SoftDirty::SAMPLER_ALL | SoftDirty::RAST_ALL)) { if (states_.Full()) Flush("states"); stateIndex_ = (uint16_t)states_.Push(RasterizerState()); - ComputeRasterizerState(&states_[stateIndex_], throughMode); + ComputeRasterizerState(&states_[stateIndex_]); states_[stateIndex_].samplerID.cached.clut = cluts_[clutIndex_].readable; ClearDirty(SoftDirty::PIXEL_ALL | SoftDirty::SAMPLER_ALL | SoftDirty::RAST_ALL); @@ -197,16 +197,16 @@ void BinManager::UpdateState(bool throughMode) { Flush("tex"); // Okay, now update what's pending. - constexpr uint32_t mirrorMask = 0x0FFFFFFF & ~0x00600000; - const uint32_t bpp = state.pixelID.FBFormat() == GE_FORMAT_8888 ? 4 : 2; - pendingWrites_[0].Expand(gstate.getFrameBufAddress() & mirrorMask, bpp, gstate.FrameBufStride(), scissorTL, scissorBR); - if (state.pixelID.depthWrite) - pendingWrites_[1].Expand(gstate.getDepthBufAddress() & mirrorMask, 2, gstate.DepthBufStride(), scissorTL, scissorBR); + MarkPendingWrites(state); ClearDirty(SoftDirty::BINNER_RANGE); } else if (pendingOverlap_) { - if (HasTextureWrite(state)) + if (HasTextureWrite(state)) { Flush("tex"); + + // We need the pending writes set, which flushing cleared. Set them again. + MarkPendingWrites(state); + } } if (HasDirty(SoftDirty::BINNER_OVERLAP)) { @@ -282,6 +282,17 @@ void BinManager::MarkPendingReads(const Rasterizer::RasterizerState &state) { } } +void BinManager::MarkPendingWrites(const Rasterizer::RasterizerState &state) { + DrawingCoords scissorTL(gstate.getScissorX1(), gstate.getScissorY1()); + DrawingCoords scissorBR(std::min(gstate.getScissorX2(), gstate.getRegionX2()), std::min(gstate.getScissorY2(), gstate.getRegionY2())); + + constexpr uint32_t mirrorMask = 0x041FFFFF; + const uint32_t bpp = state.pixelID.FBFormat() == GE_FORMAT_8888 ? 4 : 2; + pendingWrites_[0].Expand(gstate.getFrameBufAddress() & mirrorMask, bpp, gstate.FrameBufStride(), scissorTL, scissorBR); + if (state.pixelID.depthWrite) + pendingWrites_[1].Expand(gstate.getDepthBufAddress() & mirrorMask, 2, gstate.DepthBufStride(), scissorTL, scissorBR); +} + inline void BinDirtyRange::Expand(uint32_t newBase, uint32_t bpp, uint32_t stride, DrawingCoords &tl, DrawingCoords &br) { const uint32_t w = br.x - tl.x + 1; const uint32_t h = br.y - tl.y + 1; @@ -326,7 +337,7 @@ void BinManager::AddTriangle(const VertexData &v0, const VertexData &v1, const V if (d01.x * d02.y - d01.y * d02.x < 0) return; // If all points have identical coords, we'll have 0 weights and not skip properly, so skip here. - if (d01.x == 0 && d01.y == 0 && d02.x == 0 && d02.y == 0) + if ((d01.x == 0 && d02.x == 0) || (d01.y == 0 && d02.y == 0)) return; // Was it fully outside the scissor? @@ -395,7 +406,7 @@ void BinManager::AddPoint(const VertexData &v0) { Expand(range); } -void BinManager::Drain() { +void BinManager::Drain(bool flushing) { PROFILE_THIS_SCOPE("bin_drain"); // If the waitable has fully drained, we can update our binning decisions. @@ -435,6 +446,7 @@ void BinManager::Drain() { queue_.SkipNext(); } } else { + int max = flushing ? QUEUED_PRIMS : QUEUED_PRIMS / 2; while (!queue_.Empty()) { const BinItem &item = queue_.PeekNext(); for (int i = 0; i < (int)taskRanges_.size(); ++i) { @@ -442,17 +454,24 @@ void BinManager::Drain() { if (range.Invalid()) continue; - // This shouldn't often happen, but if it does, wait for space. - if (taskQueues_[i].Full()) - waitable_->Wait(); + if (taskQueues_[i].NearFull()) { + // This shouldn't often happen, but if it does, wait for space. + if (taskQueues_[i].Full()) + waitable_->Wait(); + // If we're not flushing and not near full, let's just continue later. + // Near full means we'd drain on next prim, so better to finish it now. + else if (!flushing && !queue_.NearFull()) + max = 0; + } BinItem &taskItem = taskQueues_[i].PeekPush(); taskItem = item; taskItem.range = range; taskQueues_[i].PushPeeked(); - } queue_.SkipNext(); + if (--max <= 0) + break; } int threads = 0; @@ -474,10 +493,13 @@ void BinManager::Drain() { } void BinManager::Flush(const char *reason) { + if (queueRange_.x1 == 0x7FFFFFFF) + return; + double st; if (coreCollectDebugStats) st = time_now_d(); - Drain(); + Drain(true); waitable_->Wait(); taskRanges_.clear(); tasksSplit_ = false; @@ -516,7 +538,7 @@ bool BinManager::HasPendingWrite(uint32_t start, uint32_t stride, uint32_t w, ui if (!Memory::IsVRAMAddress(start)) return false; // Ignore mirrors for overlap detection. - start &= 0x0FFFFFFF & ~0x00600000; + start &= 0x041FFFFF; uint32_t size = stride * (h - 1) + w; for (const auto &range : pendingWrites_) { @@ -547,7 +569,7 @@ bool BinManager::HasPendingWrite(uint32_t start, uint32_t stride, uint32_t w, ui bool BinManager::HasPendingRead(uint32_t start, uint32_t stride, uint32_t w, uint32_t h) { if (Memory::IsVRAMAddress(start)) { // Ignore VRAM mirrors. - start &= 0x0FFFFFFF & ~0x00600000; + start &= 0x041FFFFF; } else { // Ignore only regular RAM mirrors. start &= 0x3FFFFFFF; diff --git a/GPU/Software/BinManager.h b/GPU/Software/BinManager.h index 79146eae1d..d874f0d3c8 100644 --- a/GPU/Software/BinManager.h +++ b/GPU/Software/BinManager.h @@ -132,6 +132,10 @@ struct BinQueue { return size_ == N - 1; } + bool NearFull() const { + return size_ >= N - 2; + } + bool Empty() const { return size_ == 0; } @@ -181,7 +185,7 @@ public: BinManager(); ~BinManager(); - void UpdateState(bool throughMode); + void UpdateState(); void UpdateClut(const void *src); const Rasterizer::RasterizerState &State() { @@ -195,7 +199,7 @@ public: void AddLine(const VertexData &v0, const VertexData &v1); void AddPoint(const VertexData &v0); - void Drain(); + void Drain(bool flushing = false); void Flush(const char *reason); bool HasPendingWrite(uint32_t start, uint32_t stride, uint32_t w, uint32_t h); // Assumes you've also checked for a write (writes are partial so are automatically reads.) @@ -228,8 +232,8 @@ protected: static constexpr int QUEUED_STATES = 4096; // These are 1KB each, so half an MB. static constexpr int QUEUED_CLUTS = 512; - // About 320 KB, but we have usually 16 or less of them, so 5 MB - 20 MB. - static constexpr int QUEUED_PRIMS = 1024; + // About 360 KB, but we have usually 16 or less of them, so 5 MB - 22 MB. + static constexpr int QUEUED_PRIMS = 2048; typedef BinQueue BinStateQueue; typedef BinQueue BinClutQueue; @@ -267,6 +271,7 @@ private: int mostThreads_ = 0; void MarkPendingReads(const Rasterizer::RasterizerState &state); + void MarkPendingWrites(const Rasterizer::RasterizerState &state); bool HasTextureWrite(const Rasterizer::RasterizerState &state); BinCoords Scissor(BinCoords range); BinCoords Range(const VertexData &v0, const VertexData &v1, const VertexData &v2); diff --git a/GPU/Software/Clipper.cpp b/GPU/Software/Clipper.cpp index 166947ef14..2581cdf4c2 100644 --- a/GPU/Software/Clipper.cpp +++ b/GPU/Software/Clipper.cpp @@ -45,7 +45,7 @@ inline bool different_signs(float x, float y) { return ((x <= 0 && y > 0) || (x > 0 && y <= 0)); } -inline float clip_dotprod(const VertexData &vert, float A, float B, float C, float D) { +inline float clip_dotprod(const ClipVertexData &vert, float A, float B, float C, float D) { return (vert.clippos.x * A + vert.clippos.y * B + vert.clippos.z * C + vert.clippos.w * D); } @@ -131,8 +131,66 @@ static inline bool CheckOutsideZ(ClipCoords p, int &pos, int &neg) { return false; } -void ProcessRect(const VertexData &v0, const VertexData &v1, BinManager &binner) { +static void RotateUV(const VertexData &tl, const VertexData &br, VertexData &tr, VertexData &bl) { + const int x1 = tl.screenpos.x; + const int x2 = br.screenpos.x; + const int y1 = tl.screenpos.y; + const int y2 = br.screenpos.y; + + if ((x1 < x2 && y1 > y2) || (x1 > x2 && y1 < y2)) { + std::swap(bl.texturecoords, tr.texturecoords); + } +} + +// This is used for rectangle texture projection, which is very uncommon. +// To avoid complicating the common rectangle path, this just uses triangles. +static void AddTriangleRect(const VertexData &v0, const VertexData &v1, BinManager &binner) { + VertexData buf[4]; + buf[0] = v1; + buf[0].screenpos = ScreenCoords(v0.screenpos.x, v0.screenpos.y, v1.screenpos.z); + buf[0].texturecoords = v0.texturecoords; + + buf[1] = v1; + buf[1].screenpos = ScreenCoords(v0.screenpos.x, v1.screenpos.y, v1.screenpos.z); + buf[1].texturecoords = Vec3Packed(v0.texturecoords.x, v1.texturecoords.y, v0.texturecoords.z); + + buf[2] = v1; + buf[2].screenpos = ScreenCoords(v1.screenpos.x, v0.screenpos.y, v1.screenpos.z); + buf[2].texturecoords = Vec3Packed(v1.texturecoords.x, v0.texturecoords.y, v1.texturecoords.z); + + buf[3] = v1; + + VertexData *topleft = &buf[0]; + VertexData *topright = &buf[1]; + VertexData *bottomleft = &buf[2]; + VertexData *bottomright = &buf[3]; + + // DrawTriangle always culls, so sort out the drawing order. + for (int i = 0; i < 4; ++i) { + if (buf[i].screenpos.x < topleft->screenpos.x && buf[i].screenpos.y < topleft->screenpos.y) + topleft = &buf[i]; + if (buf[i].screenpos.x > topright->screenpos.x && buf[i].screenpos.y < topright->screenpos.y) + topright = &buf[i]; + if (buf[i].screenpos.x < bottomleft->screenpos.x && buf[i].screenpos.y > bottomleft->screenpos.y) + bottomleft = &buf[i]; + if (buf[i].screenpos.x > bottomright->screenpos.x && buf[i].screenpos.y > bottomright->screenpos.y) + bottomright = &buf[i]; + } + + RotateUV(v0, v1, *topright, *bottomleft); + + binner.AddTriangle(*topleft, *topright, *bottomleft); + binner.AddTriangle(*bottomleft, *topright, *topleft); + binner.AddTriangle(*topright, *bottomright, *bottomleft); + binner.AddTriangle(*bottomleft, *bottomright, *topright); +} + +void ProcessRect(const ClipVertexData &v0, const ClipVertexData &v1, BinManager &binner) { if (!binner.State().throughMode) { + // If any verts were outside range, throw the entire prim away. + if (v0.OutsideRange() || v1.OutsideRange()) + return; + // We may discard the entire rect based on depth values. int outsidePos = 0, outsideNeg = 0; CheckOutsideZ(v0.clippos, outsidePos, outsideNeg); @@ -145,48 +203,65 @@ void ProcessRect(const VertexData &v0, const VertexData &v1, BinManager &binner) else if (outsidePos >= 2 || outsideNeg >= 2) return; - if (v0.fogdepth != v1.fogdepth) { + if (v0.v.fogdepth != v1.v.fogdepth) { // Rectangles seem to always use nearest along X for fog depth, but reversed. // TODO: Check exactness of middle. - VertexData vhalf0 = v1; - vhalf0.screenpos.x = v0.screenpos.x + (v1.screenpos.x - v0.screenpos.x) / 2; + VertexData vhalf0 = v1.v; + vhalf0.screenpos.x = v0.v.screenpos.x + (v1.v.screenpos.x - v0.v.screenpos.x) / 2; - VertexData vhalf1 = v1; - vhalf1.screenpos.x = v0.screenpos.x + (v1.screenpos.x - v0.screenpos.x) / 2; - vhalf1.screenpos.y = v0.screenpos.y; + VertexData vhalf1 = v1.v; + vhalf1.screenpos.x = v0.v.screenpos.x + (v1.v.screenpos.x - v0.v.screenpos.x) / 2; + vhalf1.screenpos.y = v0.v.screenpos.y; - VertexData vrev1 = v1; - vrev1.fogdepth = v0.fogdepth; + VertexData vrev1 = v1.v; + vrev1.fogdepth = v0.v.fogdepth; - binner.AddRect(v0, vhalf0); - binner.AddRect(vhalf1, vrev1); + if (binner.State().textureProj) { + AddTriangleRect(v0.v, vhalf0, binner); + AddTriangleRect(vhalf1, vrev1, binner); + } else { + binner.AddRect(v0.v, vhalf0); + binner.AddRect(vhalf1, vrev1); + } + } else if (binner.State().textureProj) { + AddTriangleRect(v0.v, v1.v, binner); } else { - binner.AddRect(v0, v1); + binner.AddRect(v0.v, v1.v); } } else { // through mode handling - if (Rasterizer::RectangleFastPath(v0, v1, binner)) { + if (Rasterizer::RectangleFastPath(v0.v, v1.v, binner)) { return; } else if (gstate.isModeClear() && !gstate.isDitherEnabled()) { - binner.AddClearRect(v0, v1); + binner.AddClearRect(v0.v, v1.v); } else { - binner.AddRect(v0, v1); + binner.AddRect(v0.v, v1.v); } } } -void ProcessPoint(const VertexData &v0, BinManager &binner) { +void ProcessPoint(const ClipVertexData &v0, BinManager &binner) { + // If any verts were outside range, throw the entire prim away. + if (!binner.State().throughMode) { + if (v0.OutsideRange()) + return; + } + // Points need no clipping. Will be bounds checked in the rasterizer (which seems backwards?) - binner.AddPoint(v0); + binner.AddPoint(v0.v); } -void ProcessLine(const VertexData &v0, const VertexData &v1, BinManager &binner) { +void ProcessLine(const ClipVertexData &v0, const ClipVertexData &v1, BinManager &binner) { if (binner.State().throughMode) { // Actually, should clip this one too so we don't need to do bounds checks in the rasterizer. - binner.AddLine(v0, v1); + binner.AddLine(v0.v, v1.v); return; } + // If any verts were outside range, throw the entire prim away. + if (v0.OutsideRange() || v1.OutsideRange()) + return; + int outsidePos = 0, outsideNeg = 0; CheckOutsideZ(v0.clippos, outsidePos, outsideNeg); CheckOutsideZ(v1.clippos, outsidePos, outsideNeg); @@ -202,26 +277,32 @@ void ProcessLine(const VertexData &v0, const VertexData &v1, BinManager &binner) int mask1 = CalcClipMask(v1.clippos); int mask = mask0 | mask1; if ((mask & CLIP_NEG_Z_BIT) == 0) { - binner.AddLine(v0, v1); + binner.AddLine(v0.v, v1.v); return; } - VertexData ClippedVertices[2] = { v0, v1 }; - VertexData *Vertices[2] = { &ClippedVertices[0], &ClippedVertices[1] }; + ClipVertexData ClippedVertices[2] = { v0, v1 }; + ClipVertexData *Vertices[2] = { &ClippedVertices[0], &ClippedVertices[1] }; bool clipped = false; CLIP_LINE(CLIP_NEG_Z_BIT, 0, 0, 1, 1); - VertexData data[2] = { *Vertices[0], *Vertices[1] }; + ClipVertexData data[2] = { *Vertices[0], *Vertices[1] }; if (clipped) { - data[0].screenpos = TransformUnit::ClipToScreen(data[0].clippos); - data[1].screenpos = TransformUnit::ClipToScreen(data[1].clippos); + data[0].v.screenpos = TransformUnit::ClipToScreen(data[0].clippos); + data[1].v.screenpos = TransformUnit::ClipToScreen(data[1].clippos); + data[0].v.clipw = data[0].clippos.w; + data[1].v.clipw = data[1].clippos.w; } - binner.AddLine(data[0], data[1]); + binner.AddLine(data[0].v, data[1].v); } -void ProcessTriangle(const VertexData &v0, const VertexData &v1, const VertexData &v2, const VertexData &provoking, BinManager &binner) { +void ProcessTriangle(const ClipVertexData &v0, const ClipVertexData &v1, const ClipVertexData &v2, const ClipVertexData &provoking, BinManager &binner) { int mask = 0; if (!binner.State().throughMode) { + // If any verts were outside range, throw the entire prim away. + if (v0.OutsideRange() || v1.OutsideRange() || v2.OutsideRange()) + return; + mask |= CalcClipMask(v0.clippos); mask |= CalcClipMask(v1.clippos); mask |= CalcClipMask(v2.clippos); @@ -244,20 +325,20 @@ void ProcessTriangle(const VertexData &v0, const VertexData &v1, const VertexDat if ((mask & CLIP_NEG_Z_BIT) == 0) { if (gstate.getShadeMode() == GE_SHADE_FLAT) { // So that the order of clipping doesn't matter... - VertexData corrected2 = v2; - corrected2.color0 = provoking.color0; - corrected2.color1 = provoking.color1; - binner.AddTriangle(v0, v1, corrected2); + VertexData corrected2 = v2.v; + corrected2.color0 = provoking.v.color0; + corrected2.color1 = provoking.v.color1; + binner.AddTriangle(v0.v, v1.v, corrected2); } else { - binner.AddTriangle(v0, v1, v2); + binner.AddTriangle(v0.v, v1.v, v2.v); } return; } enum { NUM_CLIPPED_VERTICES = 3, NUM_INDICES = NUM_CLIPPED_VERTICES + 3 }; - VertexData* Vertices[NUM_INDICES]; - VertexData ClippedVertices[NUM_INDICES]; + ClipVertexData* Vertices[NUM_INDICES]; + ClipVertexData ClippedVertices[NUM_INDICES]; for (int i = 0; i < NUM_INDICES; ++i) Vertices[i] = &ClippedVertices[i]; @@ -301,22 +382,25 @@ void ProcessTriangle(const VertexData &v0, const VertexData &v1, const VertexDat for (int i = 0; i + 3 <= numIndices; i += 3) { if (indices[i] != SKIP_FLAG) { - VertexData &subv0 = *Vertices[indices[i + 0]]; - VertexData &subv1 = *Vertices[indices[i + 1]]; - VertexData &subv2 = *Vertices[indices[i + 2]]; + ClipVertexData &subv0 = *Vertices[indices[i + 0]]; + ClipVertexData &subv1 = *Vertices[indices[i + 1]]; + ClipVertexData &subv2 = *Vertices[indices[i + 2]]; if (clipped) { - subv0.screenpos = TransformUnit::ClipToScreen(subv0.clippos); - subv1.screenpos = TransformUnit::ClipToScreen(subv1.clippos); - subv2.screenpos = TransformUnit::ClipToScreen(subv2.clippos); + subv0.v.screenpos = TransformUnit::ClipToScreen(subv0.clippos); + subv1.v.screenpos = TransformUnit::ClipToScreen(subv1.clippos); + subv2.v.screenpos = TransformUnit::ClipToScreen(subv2.clippos); + subv0.v.clipw = subv0.clippos.w; + subv1.v.clipw = subv1.clippos.w; + subv2.v.clipw = subv2.clippos.w; } if (gstate.getShadeMode() == GE_SHADE_FLAT) { // So that the order of clipping doesn't matter... - subv2.color0 = provoking.color0; - subv2.color1 = provoking.color1; + subv2.v.color0 = provoking.v.color0; + subv2.v.color1 = provoking.v.color1; } - binner.AddTriangle(subv0, subv1, subv2); + binner.AddTriangle(subv0.v, subv1.v, subv2.v); } } } diff --git a/GPU/Software/Clipper.h b/GPU/Software/Clipper.h index c79fe7d730..587df83a42 100644 --- a/GPU/Software/Clipper.h +++ b/GPU/Software/Clipper.h @@ -26,9 +26,9 @@ class BinManager; namespace Clipper { -void ProcessPoint(const VertexData &v0, BinManager &binner); -void ProcessLine(const VertexData &v0, const VertexData &v1, BinManager &binner); -void ProcessTriangle(const VertexData &v0, const VertexData &v1, const VertexData &v2, const VertexData &provoking, BinManager &binner); -void ProcessRect(const VertexData &v0, const VertexData &v1, BinManager &binner); +void ProcessPoint(const ClipVertexData &v0, BinManager &binner); +void ProcessLine(const ClipVertexData &v0, const ClipVertexData &v1, BinManager &binner); +void ProcessTriangle(const ClipVertexData &v0, const ClipVertexData &v1, const ClipVertexData &v2, const ClipVertexData &provoking, BinManager &binner); +void ProcessRect(const ClipVertexData &v0, const ClipVertexData &v1, BinManager &binner); } diff --git a/GPU/Software/DrawPixel.cpp b/GPU/Software/DrawPixel.cpp index ee47c27b94..f47ea6656a 100644 --- a/GPU/Software/DrawPixel.cpp +++ b/GPU/Software/DrawPixel.cpp @@ -400,6 +400,206 @@ static inline u32 ApplyLogicOp(GELogicOp op, u32 old_color, u32 new_color) { return new_color; } +static inline Vec3 GetSourceFactor(PixelBlendFactor factor, const Vec4 &source, const Vec4 &dst, uint32_t fix) { + switch (factor) { + case PixelBlendFactor::OTHERCOLOR: + return dst.rgb(); + + case PixelBlendFactor::INVOTHERCOLOR: + return Vec3::AssignToAll(255) - dst.rgb(); + + case PixelBlendFactor::SRCALPHA: +#if defined(_M_SSE) + return Vec3(_mm_shuffle_epi32(source.ivec, _MM_SHUFFLE(3, 3, 3, 3))); +#else + return Vec3::AssignToAll(source.a()); +#endif + + case PixelBlendFactor::INVSRCALPHA: +#if defined(_M_SSE) + return Vec3(_mm_sub_epi32(_mm_set1_epi32(255), _mm_shuffle_epi32(source.ivec, _MM_SHUFFLE(3, 3, 3, 3)))); +#else + return Vec3::AssignToAll(255 - source.a()); +#endif + + case PixelBlendFactor::DSTALPHA: + return Vec3::AssignToAll(dst.a()); + + case PixelBlendFactor::INVDSTALPHA: + return Vec3::AssignToAll(255 - dst.a()); + + case PixelBlendFactor::DOUBLESRCALPHA: + return Vec3::AssignToAll(2 * source.a()); + + case PixelBlendFactor::DOUBLEINVSRCALPHA: + return Vec3::AssignToAll(255 - std::min(2 * source.a(), 255)); + + case PixelBlendFactor::DOUBLEDSTALPHA: + return Vec3::AssignToAll(2 * dst.a()); + + case PixelBlendFactor::DOUBLEINVDSTALPHA: + return Vec3::AssignToAll(255 - std::min(2 * dst.a(), 255)); + + case PixelBlendFactor::FIX: + default: + // All other dest factors (> 10) are treated as FIXA. + return Vec3::FromRGB(fix); + + case PixelBlendFactor::ZERO: + return Vec3::AssignToAll(0); + + case PixelBlendFactor::ONE: + return Vec3::AssignToAll(255); + } +} + +static inline Vec3 GetDestFactor(PixelBlendFactor factor, const Vec4 &source, const Vec4 &dst, uint32_t fix) { + switch (factor) { + case PixelBlendFactor::OTHERCOLOR: + return source.rgb(); + + case PixelBlendFactor::INVOTHERCOLOR: + return Vec3::AssignToAll(255) - source.rgb(); + + case PixelBlendFactor::SRCALPHA: +#if defined(_M_SSE) + return Vec3(_mm_shuffle_epi32(source.ivec, _MM_SHUFFLE(3, 3, 3, 3))); +#else + return Vec3::AssignToAll(source.a()); +#endif + + case PixelBlendFactor::INVSRCALPHA: +#if defined(_M_SSE) + return Vec3(_mm_sub_epi32(_mm_set1_epi32(255), _mm_shuffle_epi32(source.ivec, _MM_SHUFFLE(3, 3, 3, 3)))); +#else + return Vec3::AssignToAll(255 - source.a()); +#endif + + case PixelBlendFactor::DSTALPHA: + return Vec3::AssignToAll(dst.a()); + + case PixelBlendFactor::INVDSTALPHA: + return Vec3::AssignToAll(255 - dst.a()); + + case PixelBlendFactor::DOUBLESRCALPHA: + return Vec3::AssignToAll(2 * source.a()); + + case PixelBlendFactor::DOUBLEINVSRCALPHA: + return Vec3::AssignToAll(255 - std::min(2 * source.a(), 255)); + + case PixelBlendFactor::DOUBLEDSTALPHA: + return Vec3::AssignToAll(2 * dst.a()); + + case PixelBlendFactor::DOUBLEINVDSTALPHA: + return Vec3::AssignToAll(255 - std::min(2 * dst.a(), 255)); + + case PixelBlendFactor::FIX: + default: + // All other dest factors (> 10) are treated as FIXB. + return Vec3::FromRGB(fix); + + case PixelBlendFactor::ZERO: + return Vec3::AssignToAll(0); + + case PixelBlendFactor::ONE: + return Vec3::AssignToAll(255); + } +} + +// Removed inline here - it was never chosen to be inlined by the compiler anyway, too complex. +static Vec3 AlphaBlendingResult(const PixelFuncID &pixelID, const Vec4 &source, const Vec4 &dst) { + // Note: These factors cannot go below 0, but they can go above 255 when doubling. + Vec3 srcfactor = GetSourceFactor(pixelID.AlphaBlendSrc(), source, dst, pixelID.cached.alphaBlendSrc); + Vec3 dstfactor = GetDestFactor(pixelID.AlphaBlendDst(), source, dst, pixelID.cached.alphaBlendDst); + + switch (pixelID.AlphaBlendEq()) { + case GE_BLENDMODE_MUL_AND_ADD: + { +#if defined(_M_SSE) + // We switch to 16 bit to use mulhi, and we use 4 bits of decimal to make the 16 bit shift free. + const __m128i half = _mm_set1_epi16(1 << 3); + + const __m128i srgb = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(source.ivec, source.ivec), 4), half); + const __m128i sf = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(srcfactor.ivec, srcfactor.ivec), 4), half); + const __m128i s = _mm_mulhi_epi16(srgb, sf); + + const __m128i drgb = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(dst.ivec, dst.ivec), 4), half); + const __m128i df = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(dstfactor.ivec, dstfactor.ivec), 4), half); + const __m128i d = _mm_mulhi_epi16(drgb, df); + + return Vec3(_mm_unpacklo_epi16(_mm_adds_epi16(s, d), _mm_setzero_si128())); +#else + static constexpr Vec3 half = Vec3::AssignToAll(1); + Vec3 lhs = ((source.rgb() * 2 + half) * (srcfactor * 2 + half)) / 1024; + Vec3 rhs = ((dst.rgb() * 2 + half) * (dstfactor * 2 + half)) / 1024; + return lhs + rhs; +#endif + } + + case GE_BLENDMODE_MUL_AND_SUBTRACT: + { +#if defined(_M_SSE) + const __m128i half = _mm_set1_epi16(1 << 3); + + const __m128i srgb = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(source.ivec, source.ivec), 4), half); + const __m128i sf = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(srcfactor.ivec, srcfactor.ivec), 4), half); + const __m128i s = _mm_mulhi_epi16(srgb, sf); + + const __m128i drgb = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(dst.ivec, dst.ivec), 4), half); + const __m128i df = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(dstfactor.ivec, dstfactor.ivec), 4), half); + const __m128i d = _mm_mulhi_epi16(drgb, df); + + return Vec3(_mm_unpacklo_epi16(_mm_max_epi16(_mm_subs_epi16(s, d), _mm_setzero_si128()), _mm_setzero_si128())); +#else + static constexpr Vec3 half = Vec3::AssignToAll(1); + Vec3 lhs = ((source.rgb() * 2 + half) * (srcfactor * 2 + half)) / 1024; + Vec3 rhs = ((dst.rgb() * 2 + half) * (dstfactor * 2 + half)) / 1024; + return lhs - rhs; +#endif + } + + case GE_BLENDMODE_MUL_AND_SUBTRACT_REVERSE: + { +#if defined(_M_SSE) + const __m128i half = _mm_set1_epi16(1 << 3); + + const __m128i srgb = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(source.ivec, source.ivec), 4), half); + const __m128i sf = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(srcfactor.ivec, srcfactor.ivec), 4), half); + const __m128i s = _mm_mulhi_epi16(srgb, sf); + + const __m128i drgb = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(dst.ivec, dst.ivec), 4), half); + const __m128i df = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(dstfactor.ivec, dstfactor.ivec), 4), half); + const __m128i d = _mm_mulhi_epi16(drgb, df); + + return Vec3(_mm_unpacklo_epi16(_mm_max_epi16(_mm_subs_epi16(d, s), _mm_setzero_si128()), _mm_setzero_si128())); +#else + static constexpr Vec3 half = Vec3::AssignToAll(1); + Vec3 lhs = ((source.rgb() * 2 + half) * (srcfactor * 2 + half)) / 1024; + Vec3 rhs = ((dst.rgb() * 2 + half) * (dstfactor * 2 + half)) / 1024; + return rhs - lhs; +#endif + } + + case GE_BLENDMODE_MIN: + return Vec3(std::min(source.r(), dst.r()), + std::min(source.g(), dst.g()), + std::min(source.b(), dst.b())); + + case GE_BLENDMODE_MAX: + return Vec3(std::max(source.r(), dst.r()), + std::max(source.g(), dst.g()), + std::max(source.b(), dst.b())); + + case GE_BLENDMODE_ABSDIFF: + return Vec3(::abs(source.r() - dst.r()), + ::abs(source.g() - dst.g()), + ::abs(source.b() - dst.b())); + + default: + return source.rgb(); + } +} + template void SOFTRAST_CALL DrawSinglePixel(int x, int y, int z, int fog, Vec4IntArg color_in, const PixelFuncID &pixelID) { Vec4 prim_color = Vec4(color_in).Clamp(0, 255); @@ -673,11 +873,13 @@ void ComputePixelBlendState(PixelBlendState &state, const PixelFuncID &id) { state.srcColorAsFactor = true; break; + case PixelBlendFactor::ZERO: + state.readsDstPixel = state.dstColorAsFactor || state.usesDstAlpha; + break; + default: break; } - - state.dstColorAsFactor = state.dstColorAsFactor || state.usesDstAlpha; } } diff --git a/GPU/Software/DrawPixel.h b/GPU/Software/DrawPixel.h index f813bf61c1..65f6fa1ec5 100644 --- a/GPU/Software/DrawPixel.h +++ b/GPU/Software/DrawPixel.h @@ -51,6 +51,7 @@ struct PixelBlendState { bool dstFactorIsInverse = false; bool srcColorAsFactor = false; bool dstColorAsFactor = false; + bool readsDstPixel = true; }; void ComputePixelBlendState(PixelBlendState &state, const PixelFuncID &id); diff --git a/GPU/Software/DrawPixelX86.cpp b/GPU/Software/DrawPixelX86.cpp index dc53be1c62..53f9c94279 100644 --- a/GPU/Software/DrawPixelX86.cpp +++ b/GPU/Software/DrawPixelX86.cpp @@ -1047,7 +1047,13 @@ bool PixelJitCache::Jit_AlphaBlend(const PixelFuncID &id) { // Step 1: Load and expand dest color. X64Reg dstReg = regCache_.Alloc(RegCache::VEC_TEMP0); - if (id.FBFormat() == GE_FORMAT_8888) { + if (!blendState.readsDstPixel) { + // Let's load colorOff just for registers to be consistent. + X64Reg colorOff = GetColorOff(id); + regCache_.Unlock(colorOff, RegCache::GEN_COLOR_OFF); + + PXOR(dstReg, R(dstReg)); + } else if (id.FBFormat() == GE_FORMAT_8888) { X64Reg colorOff = GetColorOff(id); Describe("AlphaBlend"); MOVD_xmm(dstReg, MatR(colorOff)); @@ -1073,7 +1079,6 @@ bool PixelJitCache::Jit_AlphaBlend(const PixelFuncID &id) { case GE_FORMAT_4444: success = success && Jit_ConvertFrom4444(id, dstGenReg, temp1Reg, temp2Reg, blendState.usesDstAlpha); - break; case GE_FORMAT_8888: @@ -1115,7 +1120,7 @@ bool PixelJitCache::Jit_AlphaBlend(const PixelFuncID &id) { // We also need to add a half bit later, so this gives us space. if (multiplySrc || blendState.srcColorAsFactor) PSLLW(argColorReg, 4); - if (multiplyDst || blendState.dstColorAsFactor) + if (multiplyDst || blendState.dstColorAsFactor || blendState.usesDstAlpha) PSLLW(dstReg, 4); // Okay, now grab our factors. Don't bother if they're known values. @@ -1154,7 +1159,7 @@ bool PixelJitCache::Jit_AlphaBlend(const PixelFuncID &id) { if (id.AlphaBlendEq() == GE_BLENDMODE_MUL_AND_SUBTRACT_REVERSE) PXOR(dstReg, R(dstReg)); } else if (id.AlphaBlendDst() == PixelBlendFactor::ONE) { - if (blendState.dstColorAsFactor) + if (blendState.dstColorAsFactor || blendState.usesDstAlpha) PSRLW(dstReg, 4); } @@ -1219,7 +1224,6 @@ bool PixelJitCache::Jit_AlphaBlend(const PixelFuncID &id) { return success; } - bool PixelJitCache::Jit_BlendFactor(const PixelFuncID &id, RegCache::Reg factorReg, RegCache::Reg dstReg, PixelBlendFactor factor) { X64Reg idReg = INVALID_REG; X64Reg tempReg = INVALID_REG; @@ -1733,6 +1737,7 @@ bool PixelJitCache::Jit_ApplyLogicOp(const PixelFuncID &id, RegCache::Reg colorR } std::vector finishes; + finishes.reserve(11); FixupBranch skipTable = J(true); const u8 *tableValues[16]{}; diff --git a/GPU/Software/FuncId.cpp b/GPU/Software/FuncId.cpp index 501b5aaf27..e1520b8d72 100644 --- a/GPU/Software/FuncId.cpp +++ b/GPU/Software/FuncId.cpp @@ -48,15 +48,15 @@ static inline PixelBlendFactor OptimizeAlphaFactor(uint32_t color) { return PixelBlendFactor::FIX; } -void ComputePixelFuncID(PixelFuncID *id, bool throughMode) { +void ComputePixelFuncID(PixelFuncID *id) { id->fullKey = 0; // TODO: Could this be minz > 0x0000 || maxz < 0xFFFF? Maybe unsafe, depending on verts... - id->applyDepthRange = !throughMode; + id->applyDepthRange = !gstate.isModeThrough(); // Dither happens even in clear mode. id->dithering = gstate.isDitherEnabled(); id->fbFormat = gstate.FrameBufFormat(); - id->useStandardStride = gstate.FrameBufStride() == 512 && gstate.DepthBufStride() == 512; + id->useStandardStride = gstate.FrameBufStride() == 512; id->applyColorWriteMask = gstate.getColorMask() != 0; id->clearMode = gstate.isModeClear(); @@ -168,8 +168,16 @@ void ComputePixelFuncID(PixelFuncID *id, bool throughMode) { id->alphaBlendDst = (uint8_t)OptimizeAlphaFactor(gstate.getFixB()); } + if (id->colorTest && gstate.getColorTestFunction() == GE_COMP_NOTEQUAL && gstate.getColorTestRef() == 0 && gstate.getColorTestMask() == 0xFFFFFF) { + if (!id->depthWrite && !id->stencilTest && id->alphaBlend && id->AlphaBlendEq() == GE_BLENDMODE_MUL_AND_ADD) { + // Might be a pointless color test (seen in Ridge Racer, for example.) + if (id->AlphaBlendDst() == PixelBlendFactor::ONE) + id->colorTest = false; + } + } + id->applyLogicOp = gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY; - id->applyFog = gstate.isFogEnabled() && !throughMode; + id->applyFog = gstate.isFogEnabled() && !gstate.isModeThrough(); id->earlyZChecks = id->DepthTestFunc() != GE_COMP_ALWAYS; if (id->stencilTest && id->earlyZChecks) { @@ -179,6 +187,9 @@ void ComputePixelFuncID(PixelFuncID *id, bool throughMode) { } } + if (id->useStandardStride && (id->depthTestFunc != GE_COMP_ALWAYS || id->depthWrite)) + id->useStandardStride = gstate.DepthBufStride() == 512; + // Cache some values for later convenience. if (id->dithering) { for (int y = 0; y < 4; ++y) { diff --git a/GPU/Software/FuncId.h b/GPU/Software/FuncId.h index 46307fc55c..d9d8e51573 100644 --- a/GPU/Software/FuncId.h +++ b/GPU/Software/FuncId.h @@ -244,7 +244,7 @@ struct hash { }; -void ComputePixelFuncID(PixelFuncID *id, bool throughMode); +void ComputePixelFuncID(PixelFuncID *id); std::string DescribePixelFuncID(const PixelFuncID &id); void ComputeSamplerID(SamplerID *id); diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index 1f4039d05a..d59c1fb0cf 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -93,8 +93,8 @@ static inline Vec4 Interpolate(const float &c0, const float &c1, const fl return Interpolate(c0, c1, c2, w0.Cast(), w1.Cast(), w2.Cast(), wsum_recip); } -void ComputeRasterizerState(RasterizerState *state, bool throughMode) { - ComputePixelFuncID(&state->pixelID, throughMode); +void ComputeRasterizerState(RasterizerState *state) { + ComputePixelFuncID(&state->pixelID); state->drawPixel = Rasterizer::GetSingleFunc(state->pixelID); state->enableTextures = gstate.isTextureMapEnabled() && !state->pixelID.clearMode; @@ -129,10 +129,17 @@ void ComputeRasterizerState(RasterizerState *state, bool throughMode) { state->mipFilt = gstate.isMipmapFilteringEnabled(); state->minFilt = gstate.isMinifyFilteringEnabled(); state->magFilt = gstate.isMagnifyFilteringEnabled(); + state->textureProj = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX; + if (state->textureProj) { + // We may be able to optimize this off. This is actually kinda common. + if (gstate.tgenMatrix[2] == 0.0f && gstate.tgenMatrix[5] == 0.0f && gstate.tgenMatrix[8] == 0.0f && gstate.tgenMatrix[11] == 1.0f) { + state->textureProj = false; + } + } } state->shadeGouraud = gstate.getShadeMode() == GE_SHADE_GOURAUD; - state->throughMode = throughMode; + state->throughMode = gstate.isModeThrough(); state->antialiasLines = gstate.isAntiAliasEnabled(); #if defined(SOFTGPU_MEMORY_TAGGING_DETAILED) || defined(SOFTGPU_MEMORY_TAGGING_BASIC) @@ -143,6 +150,68 @@ void ComputeRasterizerState(RasterizerState *state, bool throughMode) { #endif } +RasterizerState OptimizeFlatRasterizerState(RasterizerState state, const VertexData &v1) { + uint8_t alpha = v1.color0 >> 24; + + bool changedPixelID = false; + bool changedSamplerID = false; + if (!state.pixelID.clearMode) { + auto &pixelID = state.pixelID; + auto &cached = pixelID.cached; + + bool useTextureAlpha = state.enableTextures && state.samplerID.useTextureAlpha; + if (pixelID.alphaBlend && pixelID.AlphaBlendSrc() == PixelBlendFactor::SRCALPHA && !useTextureAlpha) { + // Okay, we may be able to convert this to a fixed value. + if (alpha == 0) { + pixelID.alphaBlendSrc = (uint8_t)PixelBlendFactor::ZERO; + changedPixelID = true; + } else if (alpha == 0xFF) { + pixelID.alphaBlendSrc = (uint8_t)PixelBlendFactor::ONE; + changedPixelID = true; + } + } + if (pixelID.alphaBlend && pixelID.AlphaBlendDst() == PixelBlendFactor::INVSRCALPHA && !useTextureAlpha) { + if (alpha == 0) { + pixelID.alphaBlendDst = (uint8_t)PixelBlendFactor::ONE; + changedPixelID = true; + } else if (alpha == 0xFF) { + pixelID.alphaBlendDst = (uint8_t)PixelBlendFactor::ZERO; + changedPixelID = true; + } + } + if (pixelID.alphaBlend && pixelID.AlphaBlendSrc() == PixelBlendFactor::ONE && pixelID.AlphaBlendDst() == PixelBlendFactor::ZERO) { + if (pixelID.AlphaBlendEq() == GE_BLENDMODE_MUL_AND_ADD) { + pixelID.alphaBlend = false; + changedPixelID = true; + } + } + } + if (state.enableTextures) { + if (v1.color0 == 0xFFFFFFFF) { + // Modulate is common, sometimes even with a fixed color. Replace is cheaper. + if (state.samplerID.TexFunc() == GE_TEXFUNC_MODULATE) { + state.samplerID.texFunc = (uint8_t)GE_TEXFUNC_REPLACE; + changedSamplerID = true; + } + } + } + + if (changedPixelID) + state.drawPixel = Rasterizer::GetSingleFunc(state.pixelID); + if (changedSamplerID) { + state.linear = Sampler::GetLinearFunc(state.samplerID); + state.nearest = Sampler::GetNearestFunc(state.samplerID); + + // Since the definitions are the same, just force this setting using the func pointer. + if (g_Config.iTexFiltering == TEX_FILTER_FORCE_LINEAR) + state.nearest = state.linear; + else if (g_Config.iTexFiltering == TEX_FILTER_FORCE_NEAREST) + state.linear = state.nearest; + } + + return state; +} + static inline u8 ClampFogDepth(float fogdepth) { union FloatBits { float f; @@ -162,12 +231,9 @@ static inline u8 ClampFogDepth(float fogdepth) { } static inline void GetTextureCoordinates(const VertexData& v0, const VertexData& v1, const float p, float &s, float &t) { - // All UV gen modes, by the time they get here, behave the same. - - // TODO: What happens if vertex has no texture coordinates? // Note that for environment mapping, texture coordinates have been calculated during lighting - float q0 = 1.f / v0.clippos.w; - float q1 = 1.f / v1.clippos.w; + float q0 = 1.f / v0.clipw; + float q1 = 1.f / v1.clipw; float wq0 = p * q0; float wq1 = (1.0f - p) * q1; @@ -176,14 +242,26 @@ static inline void GetTextureCoordinates(const VertexData& v0, const VertexData& t = (v0.texturecoords.t() * wq0 + v1.texturecoords.t() * wq1) * q_recip; } -static inline void GetTextureCoordinates(const VertexData &v0, const VertexData &v1, const VertexData &v2, const Vec4 &w0, const Vec4 &w1, const Vec4 &w2, const Vec4 &wsum_recip, Vec4 &s, Vec4 &t) { - // All UV gen modes, by the time they get here, behave the same. +static inline void GetTextureCoordinatesProj(const VertexData& v0, const VertexData& v1, const float p, float &s, float &t) { + // This is for texture matrix projection. + float q0 = 1.f / v0.clipw; + float q1 = 1.f / v1.clipw; + float wq0 = p * q0; + float wq1 = (1.0f - p) * q1; - // TODO: What happens if vertex has no texture coordinates? + float q_recip = 1.0f / (wq0 + wq1); + float q = (v0.texturecoords.q() * wq0 + v1.texturecoords.q() * wq1) * q_recip; + q_recip *= 1.0f / q; + + s = (v0.texturecoords.s() * wq0 + v1.texturecoords.s() * wq1) * q_recip; + t = (v0.texturecoords.t() * wq0 + v1.texturecoords.t() * wq1) * q_recip; +} + +static inline void GetTextureCoordinates(const VertexData &v0, const VertexData &v1, const VertexData &v2, const Vec4 &w0, const Vec4 &w1, const Vec4 &w2, const Vec4 &wsum_recip, Vec4 &s, Vec4 &t) { // Note that for environment mapping, texture coordinates have been calculated during lighting. - float q0 = 1.f / v0.clippos.w; - float q1 = 1.f / v1.clippos.w; - float q2 = 1.f / v2.clippos.w; + float q0 = 1.f / v0.clipw; + float q1 = 1.f / v1.clipw; + float q2 = 1.f / v2.clipw; Vec4 wq0 = w0.Cast() * q0; Vec4 wq1 = w1.Cast() * q1; Vec4 wq2 = w2.Cast() * q2; @@ -193,6 +271,23 @@ static inline void GetTextureCoordinates(const VertexData &v0, const VertexData t = Interpolate(v0.texturecoords.t(), v1.texturecoords.t(), v2.texturecoords.t(), wq0, wq1, wq2, q_recip); } +static inline void GetTextureCoordinatesProj(const VertexData &v0, const VertexData &v1, const VertexData &v2, const Vec4 &w0, const Vec4 &w1, const Vec4 &w2, const Vec4 &wsum_recip, Vec4 &s, Vec4 &t) { + // This is for texture matrix projection. + float q0 = 1.f / v0.clipw; + float q1 = 1.f / v1.clipw; + float q2 = 1.f / v2.clipw; + Vec4 wq0 = w0.Cast() * q0; + Vec4 wq1 = w1.Cast() * q1; + Vec4 wq2 = w2.Cast() * q2; + + Vec4 q_recip = (wq0 + wq1 + wq2).Reciprocal(); + Vec4 q = Interpolate(v0.texturecoords.q(), v1.texturecoords.q(), v2.texturecoords.q(), wq0, wq1, wq2, q_recip); + q_recip = q_recip * q.Reciprocal(); + + s = Interpolate(v0.texturecoords.s(), v1.texturecoords.s(), v2.texturecoords.s(), wq0, wq1, wq2, q_recip); + t = Interpolate(v0.texturecoords.t(), v1.texturecoords.t(), v2.texturecoords.t(), wq0, wq1, wq2, q_recip); +} + static inline void SetPixelDepth(int x, int y, int stride, u16 value) { depthbuf.Set16(x, y, stride, value); } @@ -208,206 +303,6 @@ static inline bool IsRightSideOrFlatBottomLine(const Vec2& vertex, const Ve } } -static inline Vec3 GetSourceFactor(PixelBlendFactor factor, const Vec4 &source, const Vec4 &dst, uint32_t fix) { - switch (factor) { - case PixelBlendFactor::OTHERCOLOR: - return dst.rgb(); - - case PixelBlendFactor::INVOTHERCOLOR: - return Vec3::AssignToAll(255) - dst.rgb(); - - case PixelBlendFactor::SRCALPHA: -#if defined(_M_SSE) - return Vec3(_mm_shuffle_epi32(source.ivec, _MM_SHUFFLE(3, 3, 3, 3))); -#else - return Vec3::AssignToAll(source.a()); -#endif - - case PixelBlendFactor::INVSRCALPHA: -#if defined(_M_SSE) - return Vec3(_mm_sub_epi32(_mm_set1_epi32(255), _mm_shuffle_epi32(source.ivec, _MM_SHUFFLE(3, 3, 3, 3)))); -#else - return Vec3::AssignToAll(255 - source.a()); -#endif - - case PixelBlendFactor::DSTALPHA: - return Vec3::AssignToAll(dst.a()); - - case PixelBlendFactor::INVDSTALPHA: - return Vec3::AssignToAll(255 - dst.a()); - - case PixelBlendFactor::DOUBLESRCALPHA: - return Vec3::AssignToAll(2 * source.a()); - - case PixelBlendFactor::DOUBLEINVSRCALPHA: - return Vec3::AssignToAll(255 - std::min(2 * source.a(), 255)); - - case PixelBlendFactor::DOUBLEDSTALPHA: - return Vec3::AssignToAll(2 * dst.a()); - - case PixelBlendFactor::DOUBLEINVDSTALPHA: - return Vec3::AssignToAll(255 - std::min(2 * dst.a(), 255)); - - case PixelBlendFactor::FIX: - default: - // All other dest factors (> 10) are treated as FIXA. - return Vec3::FromRGB(fix); - - case PixelBlendFactor::ZERO: - return Vec3::AssignToAll(0); - - case PixelBlendFactor::ONE: - return Vec3::AssignToAll(255); - } -} - -static inline Vec3 GetDestFactor(PixelBlendFactor factor, const Vec4 &source, const Vec4 &dst, uint32_t fix) { - switch (factor) { - case PixelBlendFactor::OTHERCOLOR: - return source.rgb(); - - case PixelBlendFactor::INVOTHERCOLOR: - return Vec3::AssignToAll(255) - source.rgb(); - - case PixelBlendFactor::SRCALPHA: -#if defined(_M_SSE) - return Vec3(_mm_shuffle_epi32(source.ivec, _MM_SHUFFLE(3, 3, 3, 3))); -#else - return Vec3::AssignToAll(source.a()); -#endif - - case PixelBlendFactor::INVSRCALPHA: -#if defined(_M_SSE) - return Vec3(_mm_sub_epi32(_mm_set1_epi32(255), _mm_shuffle_epi32(source.ivec, _MM_SHUFFLE(3, 3, 3, 3)))); -#else - return Vec3::AssignToAll(255 - source.a()); -#endif - - case PixelBlendFactor::DSTALPHA: - return Vec3::AssignToAll(dst.a()); - - case PixelBlendFactor::INVDSTALPHA: - return Vec3::AssignToAll(255 - dst.a()); - - case PixelBlendFactor::DOUBLESRCALPHA: - return Vec3::AssignToAll(2 * source.a()); - - case PixelBlendFactor::DOUBLEINVSRCALPHA: - return Vec3::AssignToAll(255 - std::min(2 * source.a(), 255)); - - case PixelBlendFactor::DOUBLEDSTALPHA: - return Vec3::AssignToAll(2 * dst.a()); - - case PixelBlendFactor::DOUBLEINVDSTALPHA: - return Vec3::AssignToAll(255 - std::min(2 * dst.a(), 255)); - - case PixelBlendFactor::FIX: - default: - // All other dest factors (> 10) are treated as FIXB. - return Vec3::FromRGB(fix); - - case PixelBlendFactor::ZERO: - return Vec3::AssignToAll(0); - - case PixelBlendFactor::ONE: - return Vec3::AssignToAll(255); - } -} - -// Removed inline here - it was never chosen to be inlined by the compiler anyway, too complex. -Vec3 AlphaBlendingResult(const PixelFuncID &pixelID, const Vec4 &source, const Vec4 &dst) { - // Note: These factors cannot go below 0, but they can go above 255 when doubling. - Vec3 srcfactor = GetSourceFactor(pixelID.AlphaBlendSrc(), source, dst, pixelID.cached.alphaBlendSrc); - Vec3 dstfactor = GetDestFactor(pixelID.AlphaBlendDst(), source, dst, pixelID.cached.alphaBlendDst); - - switch (pixelID.AlphaBlendEq()) { - case GE_BLENDMODE_MUL_AND_ADD: - { -#if defined(_M_SSE) - // We switch to 16 bit to use mulhi, and we use 4 bits of decimal to make the 16 bit shift free. - const __m128i half = _mm_set1_epi16(1 << 3); - - const __m128i srgb = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(source.ivec, source.ivec), 4), half); - const __m128i sf = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(srcfactor.ivec, srcfactor.ivec), 4), half); - const __m128i s = _mm_mulhi_epi16(srgb, sf); - - const __m128i drgb = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(dst.ivec, dst.ivec), 4), half); - const __m128i df = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(dstfactor.ivec, dstfactor.ivec), 4), half); - const __m128i d = _mm_mulhi_epi16(drgb, df); - - return Vec3(_mm_unpacklo_epi16(_mm_adds_epi16(s, d), _mm_setzero_si128())); -#else - static constexpr Vec3 half = Vec3::AssignToAll(1); - Vec3 lhs = ((source.rgb() * 2 + half) * (srcfactor * 2 + half)) / 1024; - Vec3 rhs = ((dst.rgb() * 2 + half) * (dstfactor * 2 + half)) / 1024; - return lhs + rhs; -#endif - } - - case GE_BLENDMODE_MUL_AND_SUBTRACT: - { -#if defined(_M_SSE) - const __m128i half = _mm_set1_epi16(1 << 3); - - const __m128i srgb = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(source.ivec, source.ivec), 4), half); - const __m128i sf = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(srcfactor.ivec, srcfactor.ivec), 4), half); - const __m128i s = _mm_mulhi_epi16(srgb, sf); - - const __m128i drgb = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(dst.ivec, dst.ivec), 4), half); - const __m128i df = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(dstfactor.ivec, dstfactor.ivec), 4), half); - const __m128i d = _mm_mulhi_epi16(drgb, df); - - return Vec3(_mm_unpacklo_epi16(_mm_max_epi16(_mm_subs_epi16(s, d), _mm_setzero_si128()), _mm_setzero_si128())); -#else - static constexpr Vec3 half = Vec3::AssignToAll(1); - Vec3 lhs = ((source.rgb() * 2 + half) * (srcfactor * 2 + half)) / 1024; - Vec3 rhs = ((dst.rgb() * 2 + half) * (dstfactor * 2 + half)) / 1024; - return lhs - rhs; -#endif - } - - case GE_BLENDMODE_MUL_AND_SUBTRACT_REVERSE: - { -#if defined(_M_SSE) - const __m128i half = _mm_set1_epi16(1 << 3); - - const __m128i srgb = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(source.ivec, source.ivec), 4), half); - const __m128i sf = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(srcfactor.ivec, srcfactor.ivec), 4), half); - const __m128i s = _mm_mulhi_epi16(srgb, sf); - - const __m128i drgb = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(dst.ivec, dst.ivec), 4), half); - const __m128i df = _mm_add_epi16(_mm_slli_epi16(_mm_packs_epi32(dstfactor.ivec, dstfactor.ivec), 4), half); - const __m128i d = _mm_mulhi_epi16(drgb, df); - - return Vec3(_mm_unpacklo_epi16(_mm_max_epi16(_mm_subs_epi16(d, s), _mm_setzero_si128()), _mm_setzero_si128())); -#else - static constexpr Vec3 half = Vec3::AssignToAll(1); - Vec3 lhs = ((source.rgb() * 2 + half) * (srcfactor * 2 + half)) / 1024; - Vec3 rhs = ((dst.rgb() * 2 + half) * (dstfactor * 2 + half)) / 1024; - return rhs - lhs; -#endif - } - - case GE_BLENDMODE_MIN: - return Vec3(std::min(source.r(), dst.r()), - std::min(source.g(), dst.g()), - std::min(source.b(), dst.b())); - - case GE_BLENDMODE_MAX: - return Vec3(std::max(source.r(), dst.r()), - std::max(source.g(), dst.g()), - std::max(source.b(), dst.b())); - - case GE_BLENDMODE_ABSDIFF: - return Vec3(::abs(source.r() - dst.r()), - ::abs(source.g() - dst.g()), - ::abs(source.b() - dst.b())); - - default: - return source.rgb(); - } -} - static inline Vec4IntResult SOFTRAST_CALL ApplyTexturing(float s, float t, int x, int y, Vec4IntArg prim_color, int texlevel, int frac_texlevel, bool bilinear, const RasterizerState &state) { const u8 **tptr0 = const_cast(&state.texptr[texlevel]); const uint16_t *bufw0 = &state.texbufw[texlevel]; @@ -814,6 +709,9 @@ void DrawTriangleSlice( // For levels > 0, mipmapping is always based on level 0. Simpler to scale first. s *= 1.0f / (float)(1 << state.samplerID.width0Shift); t *= 1.0f / (float)(1 << state.samplerID.height0Shift); + } else if (state.textureProj) { + // Texture coordinate interpolation must definitely be perspective-correct. + GetTextureCoordinatesProj(v0, v1, v2, w0, w1, w2, wsum_recip, s, t); } else { // Texture coordinate interpolation must definitely be perspective-correct. GetTextureCoordinates(v0, v1, v2, w0, w1, w2, wsum_recip, s, t); @@ -893,7 +791,7 @@ void DrawTriangle(const VertexData &v0, const VertexData &v1, const VertexData & drawSlice(v0, v1, v2, range.x1, range.y1, range.x2, range.y2, state); } -void DrawRectangle(const VertexData &v0, const VertexData &v1, const BinCoords &range, const RasterizerState &state) { +void DrawRectangle(const VertexData &v0, const VertexData &v1, const BinCoords &range, const RasterizerState &rastState) { int entireX1 = std::min(v0.screenpos.x, v1.screenpos.x); int entireY1 = std::min(v0.screenpos.y, v1.screenpos.y); int entireX2 = std::max(v0.screenpos.x, v1.screenpos.x) - 1; @@ -903,13 +801,16 @@ void DrawRectangle(const VertexData &v0, const VertexData &v1, const BinCoords & int maxX = std::min(entireX2, range.x2); int maxY = std::min(entireY2, range.y2); + RasterizerState state = OptimizeFlatRasterizerState(rastState, v1); + Vec2f rowST(0.0f, 0.0f); // Note: this is double the x or y movement. Vec2f stx(0.0f, 0.0f); Vec2f sty(0.0f, 0.0f); if (state.enableTextures) { - Vec2f tc0 = v0.texturecoords; - Vec2f tc1 = v1.texturecoords; + // Note: texture projection is not handled here, those always turn into triangles. + Vec2f tc0 = v0.texturecoords.uv(); + Vec2f tc1 = v1.texturecoords.uv(); if (state.throughMode) { // For levels > 0, mipmapping is always based on level 0. Simpler to scale first. tc0.s() *= 1.0f / (float)(1 << state.samplerID.width0Shift); @@ -1096,6 +997,8 @@ void DrawPoint(const VertexData &v0, const BinCoords &range, const RasterizerSta if (state.throughMode) { s *= 1.0f / (float)(1 << state.samplerID.width0Shift); t *= 1.0f / (float)(1 << state.samplerID.height0Shift); + } else if (state.textureProj) { + GetTextureCoordinatesProj(v0, v0, 0.0f, s, t); } else { // Texture coordinate interpolation must definitely be perspective-correct. GetTextureCoordinates(v0, v0, 0.0f, s, t); @@ -1136,13 +1039,20 @@ void DrawPoint(const VertexData &v0, const BinCoords &range, const RasterizerSta } void ClearRectangle(const VertexData &v0, const VertexData &v1, const BinCoords &range, const RasterizerState &state) { - DrawingCoords pprime = TransformUnit::ScreenToDrawing(range.x1, range.y1); - DrawingCoords pend = TransformUnit::ScreenToDrawing(range.x2, range.y2); + int entireX1 = std::min(v0.screenpos.x, v1.screenpos.x); + int entireY1 = std::min(v0.screenpos.y, v1.screenpos.y); + int entireX2 = std::max(v0.screenpos.x, v1.screenpos.x) - 1; + int entireY2 = std::max(v0.screenpos.y, v1.screenpos.y) - 1; + int minX = std::max(entireX1, range.x1) | (SCREEN_SCALE_FACTOR / 2 - 1); + int minY = std::max(entireY1, range.y1) | (SCREEN_SCALE_FACTOR / 2 - 1); + int maxX = std::min(entireX2, range.x2); + int maxY = std::min(entireY2, range.y2); + const DrawingCoords pprime = TransformUnit::ScreenToDrawing(minX, minY); + const DrawingCoords pend = TransformUnit::ScreenToDrawing(maxX, maxY); auto &pixelID = state.pixelID; auto &samplerID = state.samplerID; - // Min and max are in PSP fixed point screen coordinates, 16 here is for the 4 subpixel bits. - const int w = (range.x2 - range.x1 + 1) / SCREEN_SCALE_FACTOR; + const int w = pend.x - pprime.x + 1; if (w <= 0) return; @@ -1235,6 +1145,7 @@ void ClearRectangle(const VertexData &v0, const VertexData &v1, const BinCoords case GE_FORMAT_INVALID: case GE_FORMAT_DEPTH16: + case GE_FORMAT_CLUT8: _dbg_assert_msg_(false, "Software: invalid framebuf format."); break; } @@ -1365,7 +1276,7 @@ void DrawLine(const VertexData &v0, const VertexData &v1, const BinCoords &range maskOK = false; } - if (!CheckDepthTestPassed(pixelID.DepthTestFunc(), x, y, pixelID.cached.depthbufStride, z)) { + if (!CheckDepthTestPassed(pixelID.DepthTestFunc(), p.x, p.y, pixelID.cached.depthbufStride, z)) { maskOK = false; } } @@ -1398,13 +1309,16 @@ void DrawLine(const VertexData &v0, const VertexData &v1, const BinCoords &range float s, s1; float t, t1; if (state.throughMode) { - Vec2 tc = (v0.texturecoords * (float)(steps - i) + v1.texturecoords * (float)i) / steps1; - Vec2 tc1 = (v0.texturecoords * (float)(steps - i - 1) + v1.texturecoords * (float)(i + 1)) / steps1; + Vec2 tc = (v0.texturecoords.uv() * (float)(steps - i) + v1.texturecoords.uv() * (float)i) / steps1; + Vec2 tc1 = (v0.texturecoords.uv() * (float)(steps - i - 1) + v1.texturecoords.uv() * (float)(i + 1)) / steps1; s = tc.s() * (1.0f / (float)(1 << state.samplerID.width0Shift)); s1 = tc1.s() * (1.0f / (float)(1 << state.samplerID.width0Shift)); t = tc.t() * (1.0f / (float)(1 << state.samplerID.height0Shift)); t1 = tc1.t() * (1.0f / (float)(1 << state.samplerID.height0Shift)); + } else if (state.textureProj) { + GetTextureCoordinatesProj(v0, v1, (float)(steps - i) / steps1, s, t); + GetTextureCoordinatesProj(v0, v1, (float)(steps - i - 1) / steps1, s1, t1); } else { // Texture coordinate interpolation must definitely be perspective-correct. GetTextureCoordinates(v0, v1, (float)(steps - i) / steps1, s, t); diff --git a/GPU/Software/Rasterizer.h b/GPU/Software/Rasterizer.h index 089f99a657..bd0b820283 100644 --- a/GPU/Software/Rasterizer.h +++ b/GPU/Software/Rasterizer.h @@ -54,6 +54,7 @@ struct RasterizerState { bool minFilt : 1; bool magFilt : 1; bool antialiasLines : 1; + bool textureProj : 1; }; #if defined(SOFTGPU_MEMORY_TAGGING_DETAILED) || defined(SOFTGPU_MEMORY_TAGGING_BASIC) @@ -65,7 +66,7 @@ struct RasterizerState { } }; -void ComputeRasterizerState(RasterizerState *state, bool throughMode); +void ComputeRasterizerState(RasterizerState *state); // Draws a triangle if its vertices are specified in counter-clockwise order void DrawTriangle(const VertexData &v0, const VertexData &v1, const VertexData &v2, const BinCoords &range, const RasterizerState &state); @@ -76,7 +77,4 @@ void ClearRectangle(const VertexData &v0, const VertexData &v1, const BinCoords bool GetCurrentTexture(GPUDebugBuffer &buffer, int level); -// Shared functions with RasterizerRectangle.cpp -Vec3 AlphaBlendingResult(const PixelFuncID &pixelID, const Vec4 &source, const Vec4 &dst); - } // namespace Rasterizer diff --git a/GPU/Software/RasterizerRectangle.cpp b/GPU/Software/RasterizerRectangle.cpp index 4c2e4ad1a9..5cc40566d6 100644 --- a/GPU/Software/RasterizerRectangle.cpp +++ b/GPU/Software/RasterizerRectangle.cpp @@ -32,22 +32,100 @@ extern bool currentDialogActive; namespace Rasterizer { +// This essentially AlphaBlendingResult() with fixed src.a / 1 - src.a factors and ADD equation. +// It allows us to skip round trips between 32-bit and 16-bit color values. +static uint32_t StandardAlphaBlend(uint32_t source, uint32_t dst) { +#if defined(_M_SSE) + const __m128i alpha = _mm_cvtsi32_si128(source >> 24); + // Keep the alpha lane of the srcfactor zero, so we keep dest alpha. + const __m128i srcfactor = _mm_shufflelo_epi16(alpha, _MM_SHUFFLE(1, 0, 0, 0)); + const __m128i dstfactor = _mm_sub_epi16(_mm_set1_epi16(255), srcfactor); + + const __m128i z = _mm_setzero_si128(); + const __m128i sourcevec = _mm_unpacklo_epi8(_mm_cvtsi32_si128(source), z); + const __m128i dstvec = _mm_unpacklo_epi8(_mm_cvtsi32_si128(dst), z); + + // We switch to 16 bit to use mulhi, and we use 4 bits of decimal to make the 16 bit shift free. + const __m128i half = _mm_set1_epi16(1 << 3); + + const __m128i srgb = _mm_add_epi16(_mm_slli_epi16(sourcevec, 4), half); + const __m128i sf = _mm_add_epi16(_mm_slli_epi16(srcfactor, 4), half); + const __m128i s = _mm_mulhi_epi16(srgb, sf); + + const __m128i drgb = _mm_add_epi16(_mm_slli_epi16(dstvec, 4), half); + const __m128i df = _mm_add_epi16(_mm_slli_epi16(dstfactor, 4), half); + const __m128i d = _mm_mulhi_epi16(drgb, df); + + const __m128i blended16 = _mm_adds_epi16(s, d); + return _mm_cvtsi128_si32(_mm_packus_epi16(blended16, blended16)); +#else + Vec3 srcfactor = Vec3::AssignToAll(source >> 24); + Vec3 dstfactor = Vec3::AssignToAll(255 - (source >> 24)); + + static constexpr Vec3 half = Vec3::AssignToAll(1); + Vec3 lhs = ((Vec3::FromRGB(source) * 2 + half) * (srcfactor * 2 + half)) / 1024; + Vec3 rhs = ((Vec3::FromRGB(dst) * 2 + half) * (dstfactor * 2 + half)) / 1024; + Vec3 blended = lhs + rhs; + + return clamp_u8(blended.r()) | (clamp_u8(blended.g()) << 8) | (clamp_u8(blended.b()) << 16); +#endif +} + // Through mode, with the specific Darkstalker settings. -inline void DrawSinglePixel5551(u16 *pixel, const u32 color_in, const PixelFuncID &pixelID) { +inline void DrawSinglePixel5551(u16 *pixel, const u32 color_in) { u32 new_color; + // Because of this check, we only support src.a / 1-src.a blending. if ((color_in >> 24) == 255) { new_color = color_in & 0xFFFFFF; } else { const u32 old_color = RGBA5551ToRGBA8888(*pixel); - const Vec4 dst = Vec4::FromRGBA(old_color); - Vec3 blended = AlphaBlendingResult(pixelID, Vec4::FromRGBA(color_in), dst); - // ToRGB() always automatically clamps. - new_color = blended.ToRGB(); + new_color = StandardAlphaBlend(color_in, old_color); } new_color |= (*pixel & 0x8000) ? 0xff000000 : 0x00000000; *pixel = RGBA8888ToRGBA5551(new_color); } +// Check if we can safely ignore the alpha test, assuming standard alpha blending. +static inline bool AlphaTestIsNeedless(const PixelFuncID &pixelID) { + switch (pixelID.AlphaTestFunc()) { + case GE_COMP_NEVER: + case GE_COMP_EQUAL: + case GE_COMP_LESS: + case GE_COMP_LEQUAL: + return false; + + case GE_COMP_ALWAYS: + return true; + + case GE_COMP_NOTEQUAL: + case GE_COMP_GREATER: + case GE_COMP_GEQUAL: + if (pixelID.alphaTestRef != 0 || pixelID.hasAlphaTestMask) + return false; + return true; + } + + return false; +} + +bool UseDrawSinglePixel5551(const PixelFuncID &pixelID) { + if (pixelID.clearMode || pixelID.colorTest || pixelID.stencilTest) + return false; + if (!AlphaTestIsNeedless(pixelID) || pixelID.DepthTestFunc() != GE_COMP_ALWAYS) + return false; + if (pixelID.FBFormat() != GE_FORMAT_5551 || !pixelID.alphaBlend) + return false; + // We skip blending when alpha = FF, so we can't allow other blend modes. + if (pixelID.AlphaBlendEq() != GE_BLENDMODE_MUL_AND_ADD || pixelID.AlphaBlendSrc() != PixelBlendFactor::SRCALPHA) + return false; + if (pixelID.AlphaBlendDst() != PixelBlendFactor::INVSRCALPHA) + return false; + if (pixelID.dithering || pixelID.applyLogicOp || pixelID.applyColorWriteMask) + return false; + + return true; +} + static inline Vec4IntResult SOFTRAST_CALL ModulateRGBA(Vec4IntArg prim_in, Vec4IntArg texcolor_in, const SamplerID &samplerID) { Vec4 out; Vec4 prim_color = prim_in; @@ -78,27 +156,6 @@ static inline Vec4IntResult SOFTRAST_CALL ModulateRGBA(Vec4IntArg prim_in, Vec4I return ToVec4IntResult(out); } -// Check if we can safely ignore the alpha test. -static inline bool AlphaTestIsNeedless(const PixelFuncID &pixelID) { - switch (pixelID.AlphaTestFunc()) { - case GE_COMP_NEVER: - case GE_COMP_EQUAL: - case GE_COMP_LESS: - case GE_COMP_LEQUAL: - return false; - - case GE_COMP_ALWAYS: - return true; - - case GE_COMP_NOTEQUAL: - case GE_COMP_GREATER: - case GE_COMP_GEQUAL: - return pixelID.alphaBlend && pixelID.alphaTestRef == 0 && !pixelID.hasAlphaTestMask; - } - - return false; -} - void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &range, const RasterizerState &state) { const u8 *texptr = state.texptr[0]; @@ -116,8 +173,12 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &ran DrawingCoords scissorTL = TransformUnit::ScreenToDrawing(range.x1, range.y1); DrawingCoords scissorBR = TransformUnit::ScreenToDrawing(range.x2, range.y2); - int z = v1.screenpos.z; - int fog = 255; + const int z = v1.screenpos.z; + constexpr int fog = 255; + + // Since it's flat, we can check depth range early. Matters for earlyZChecks. + if (pixelID.applyDepthRange && (z < pixelID.cached.minz || z > pixelID.cached.maxz)) + return; bool isWhite = v1.color0 == 0xFFFFFFFF; @@ -148,17 +209,7 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &ran pos0.y = scissorTL.y; } - if (!pixelID.stencilTest && - pixelID.DepthTestFunc() == GE_COMP_ALWAYS && - !pixelID.applyLogicOp && - !pixelID.colorTest && - !pixelID.dithering && - pixelID.alphaBlend && - AlphaTestIsNeedless(pixelID) && - samplerID.useTextureAlpha && - samplerID.TexFunc() == GE_TEXFUNC_MODULATE && - !pixelID.applyColorWriteMask && - pixelID.FBFormat() == GE_FORMAT_5551) { + if (UseDrawSinglePixel5551(pixelID) && samplerID.TexFunc() == GE_TEXFUNC_MODULATE && samplerID.useTextureAlpha) { if (isWhite) { int t = t_start; for (int y = pos0.y; y < pos1.y; y++) { @@ -167,7 +218,7 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &ran for (int x = pos0.x; x < pos1.x; x++) { u32 tex_color = Vec4(fetchFunc(s, t, texptr, texbufw, 0, state.samplerID)).ToRGBA(); if (tex_color & 0xFF000000) { - DrawSinglePixel5551(pixel, tex_color, pixelID); + DrawSinglePixel5551(pixel, tex_color); } s += ds; pixel++; @@ -185,7 +236,7 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &ran Vec4 tex_color = fetchFunc(s, t, texptr, texbufw, 0, state.samplerID); prim_color = Vec4(ModulateRGBA(ToVec4IntArg(prim_color), ToVec4IntArg(tex_color), state.samplerID)); if (prim_color.a() > 0) { - DrawSinglePixel5551(pixel, prim_color.ToRGBA(), pixelID); + DrawSinglePixel5551(pixel, prim_color.ToRGBA()); } s += ds; pixel++; @@ -204,15 +255,31 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &ran float t = tf_start; const Vec4 c0 = Vec4::FromRGBA(v1.color0); - for (int y = pos0.y; y < pos1.y; y++) { - float s = sf_start; - // Not really that fast but faster than triangle. - for (int x = pos0.x; x < pos1.x; x++) { - Vec4 prim_color = state.nearest(s, t, xoff, yoff, ToVec4IntArg(c0), &texptr, &texbufw, 0, 0, state.samplerID); - state.drawPixel(x, y, z, 255, ToVec4IntArg(prim_color), pixelID); - s += dsf; + if (pixelID.earlyZChecks) { + for (int y = pos0.y; y < pos1.y; y++) { + float s = sf_start; + // Not really that fast but faster than triangle. + for (int x = pos0.x; x < pos1.x; x++) { + if (CheckDepthTestPassed(pixelID.DepthTestFunc(), x, y, pixelID.cached.depthbufStride, z)) { + Vec4 prim_color = state.nearest(s, t, xoff, yoff, ToVec4IntArg(c0), &texptr, &texbufw, 0, 0, state.samplerID); + state.drawPixel(x, y, z, fog, ToVec4IntArg(prim_color), pixelID); + } + + s += dsf; + } + t += dtf; + } + } else { + for (int y = pos0.y; y < pos1.y; y++) { + float s = sf_start; + // Not really that fast but faster than triangle. + for (int x = pos0.x; x < pos1.x; x++) { + Vec4 prim_color = state.nearest(s, t, xoff, yoff, ToVec4IntArg(c0), &texptr, &texbufw, 0, 0, state.samplerID); + state.drawPixel(x, y, z, fog, ToVec4IntArg(prim_color), pixelID); + s += dsf; + } + t += dtf; } - t += dtf; } } } else { @@ -220,25 +287,27 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &ran if (pos1.y > scissorBR.y) pos1.y = scissorBR.y + 1; if (pos0.x < scissorTL.x) pos0.x = scissorTL.x; if (pos0.y < scissorTL.y) pos0.y = scissorTL.y; - if (!pixelID.stencilTest && - pixelID.DepthTestFunc() == GE_COMP_ALWAYS && - !pixelID.applyLogicOp && - !pixelID.colorTest && - !pixelID.dithering && - pixelID.alphaBlend && - AlphaTestIsNeedless(pixelID) && - !pixelID.applyColorWriteMask && - pixelID.FBFormat() == GE_FORMAT_5551) { + if (UseDrawSinglePixel5551(pixelID)) { if (Vec4::FromRGBA(v1.color0).a() == 0) return; for (int y = pos0.y; y < pos1.y; y++) { u16 *pixel = fb.Get16Ptr(pos0.x, y, pixelID.cached.framebufStride); for (int x = pos0.x; x < pos1.x; x++) { - DrawSinglePixel5551(pixel, v1.color0, pixelID); + DrawSinglePixel5551(pixel, v1.color0); pixel++; } } + } else if (pixelID.earlyZChecks) { + const Vec4 prim_color = Vec4::FromRGBA(v1.color0); + for (int y = pos0.y; y < pos1.y; y++) { + for (int x = pos0.x; x < pos1.x; x++) { + if (!CheckDepthTestPassed(pixelID.DepthTestFunc(), x, y, pixelID.cached.depthbufStride, z)) + continue; + + state.drawPixel(x, y, z, fog, ToVec4IntArg(prim_color), pixelID); + } + } } else { const Vec4 prim_color = Vec4::FromRGBA(v1.color0); for (int y = pos0.y; y < pos1.y; y++) { @@ -287,7 +356,7 @@ bool RectangleFastPath(const VertexData &v0, const VertexData &v1, BinManager &b // Currently only works for TL/BR, which is the most common but not required. bool orient_check = xdiff >= 0 && ydiff >= 0; // We already have a fast path for clear in ClearRectangle. - bool state_check = state.throughMode && !state.pixelID.clearMode && !state.samplerID.hasAnyMips && NoClampOrWrap(state, v0.texturecoords) && NoClampOrWrap(state, v1.texturecoords); + bool state_check = state.throughMode && !state.pixelID.clearMode && !state.samplerID.hasAnyMips && NoClampOrWrap(state, v0.texturecoords.uv()) && NoClampOrWrap(state, v1.texturecoords.uv()); // This doesn't work well with offset drawing, see #15876. Through never has a subpixel offset. bool subpixel_check = ((v0.screenpos.x | v0.screenpos.y | v1.screenpos.x | v1.screenpos.y) & 0xF) == 0; if ((coord_check || !state.enableTextures) && orient_check && state_check && subpixel_check) { @@ -324,16 +393,19 @@ bool RectangleFastPath(const VertexData &v0, const VertexData &v1, BinManager &b return false; } -static bool AreCoordsRectangleCompatible(const RasterizerState &state, const VertexData &data0, const VertexData &data1) { - if (!(data1.color0 == data0.color0)) +static bool AreCoordsRectangleCompatible(const RasterizerState &state, const ClipVertexData &data0, const ClipVertexData &data1) { + if (data1.v.color0 != data0.v.color0) return false; - if (!(data1.screenpos.z == data0.screenpos.z)) { + if (data1.v.screenpos.z != data0.v.screenpos.z) { // Sometimes, we don't actually care about z. if (state.pixelID.depthWrite || state.pixelID.DepthTestFunc() != GE_COMP_ALWAYS) return false; } if (!state.throughMode) { - if (!state.throughMode && !(data1.color1 == data0.color1)) + if (data1.v.color1 != data0.v.color1) + return false; + // This means it should be culled, outside range. + if (data1.OutsideRange() || data0.OutsideRange()) return false; // Do we have to think about perspective correction or slope mip level? if (state.enableTextures && data1.clippos.w != data0.clippos.w) { @@ -342,26 +414,29 @@ static bool AreCoordsRectangleCompatible(const RasterizerState &state, const Ver if (data1.clippos.w - halftexel > data0.clippos.w || data1.clippos.w + halftexel < data0.clippos.w) return false; } - if (state.pixelID.applyFog && data1.fogdepth != data0.fogdepth) { + // If we're projecting textures, only allow an exact match for simplicity. + if (state.enableTextures && data1.v.texturecoords.q() != data0.v.texturecoords.q()) + return false; + if (state.pixelID.applyFog && data1.v.fogdepth != data0.v.fogdepth) { // Similar to w, this only matters if they're farther apart than 1/255. static constexpr float foghalfstep = 0.5f / 255.0f; - if (data1.fogdepth - foghalfstep > data0.fogdepth || data1.fogdepth + foghalfstep < data0.fogdepth) + if (data1.v.fogdepth - foghalfstep > data0.v.fogdepth || data1.v.fogdepth + foghalfstep < data0.v.fogdepth) return false; } } return true; } -bool DetectRectangleFromStrip(const RasterizerState &state, const VertexData data[4], int *tlIndex, int *brIndex) { +bool DetectRectangleFromStrip(const RasterizerState &state, const ClipVertexData data[4], int *tlIndex, int *brIndex) { // Color and Z must be flat. Also find the TL and BR meanwhile. int tl = 0, br = 0; for (int i = 1; i < 4; ++i) { if (!AreCoordsRectangleCompatible(state, data[i], data[0])) return false; - if (data[i].screenpos.x <= data[tl].screenpos.x && data[i].screenpos.y <= data[tl].screenpos.y) + if (data[i].v.screenpos.x <= data[tl].v.screenpos.x && data[i].v.screenpos.y <= data[tl].v.screenpos.y) tl = i; - if (data[i].screenpos.x >= data[br].screenpos.x && data[i].screenpos.y >= data[br].screenpos.y) + if (data[i].v.screenpos.x >= data[br].v.screenpos.x && data[i].v.screenpos.y >= data[br].v.screenpos.y) br = i; } @@ -370,36 +445,36 @@ bool DetectRectangleFromStrip(const RasterizerState &state, const VertexData dat // OK, now let's look at data to detect rectangles. There are a few possibilities // but we focus on Darkstalkers for now. - if (data[0].screenpos.x == data[1].screenpos.x && - data[0].screenpos.y == data[2].screenpos.y && - data[2].screenpos.x == data[3].screenpos.x && - data[1].screenpos.y == data[3].screenpos.y) { + if (data[0].v.screenpos.x == data[1].v.screenpos.x && + data[0].v.screenpos.y == data[2].v.screenpos.y && + data[2].v.screenpos.x == data[3].v.screenpos.x && + data[1].v.screenpos.y == data[3].v.screenpos.y) { // Okay, this is in the shape of a rectangle, but what about texture? if (!state.enableTextures) return true; - if (data[0].texturecoords.x == data[1].texturecoords.x && - data[0].texturecoords.y == data[2].texturecoords.y && - data[2].texturecoords.x == data[3].texturecoords.x && - data[1].texturecoords.y == data[3].texturecoords.y) { + if (data[0].v.texturecoords.x == data[1].v.texturecoords.x && + data[0].v.texturecoords.y == data[2].v.texturecoords.y && + data[2].v.texturecoords.x == data[3].v.texturecoords.x && + data[1].v.texturecoords.y == data[3].v.texturecoords.y) { // It's a rectangle! return true; } return false; } // There's the other vertex order too... - if (data[0].screenpos.x == data[2].screenpos.x && - data[0].screenpos.y == data[1].screenpos.y && - data[1].screenpos.x == data[3].screenpos.x && - data[2].screenpos.y == data[3].screenpos.y) { + if (data[0].v.screenpos.x == data[2].v.screenpos.x && + data[0].v.screenpos.y == data[1].v.screenpos.y && + data[1].v.screenpos.x == data[3].v.screenpos.x && + data[2].v.screenpos.y == data[3].v.screenpos.y) { // Okay, this is in the shape of a rectangle, but what about texture? if (!state.enableTextures) return true; - if (data[0].texturecoords.x == data[2].texturecoords.x && - data[0].texturecoords.y == data[1].texturecoords.y && - data[1].texturecoords.x == data[3].texturecoords.x && - data[2].texturecoords.y == data[3].texturecoords.y) { + if (data[0].v.texturecoords.x == data[2].v.texturecoords.x && + data[0].v.texturecoords.y == data[1].v.texturecoords.y && + data[1].v.texturecoords.x == data[3].v.texturecoords.x && + data[2].v.texturecoords.y == data[3].v.texturecoords.y) { // It's a rectangle! return true; } @@ -408,7 +483,7 @@ bool DetectRectangleFromStrip(const RasterizerState &state, const VertexData dat return false; } -bool DetectRectangleFromFan(const RasterizerState &state, const VertexData *data, int c, int *tlIndex, int *brIndex) { +bool DetectRectangleFromFan(const RasterizerState &state, const ClipVertexData *data, int c, int *tlIndex, int *brIndex) { // Color and Z must be flat. for (int i = 1; i < c; ++i) { if (!AreCoordsRectangleCompatible(state, data[i], data[0])) @@ -417,8 +492,8 @@ bool DetectRectangleFromFan(const RasterizerState &state, const VertexData *data // Check for the common case: a single TL-TR-BR-BL. if (c == 4) { - const auto &pos0 = data[0].screenpos, &pos1 = data[1].screenpos; - const auto &pos2 = data[2].screenpos, &pos3 = data[3].screenpos; + const auto &pos0 = data[0].v.screenpos, &pos1 = data[1].v.screenpos; + const auto &pos2 = data[2].v.screenpos, &pos3 = data[3].v.screenpos; if (pos0.x == pos3.x && pos1.x == pos2.x && pos0.y == pos1.y && pos3.y == pos2.y) { // Looking like yes. Set TL/BR based on y order first... *tlIndex = pos0.y > pos3.y ? 2 : 0; @@ -433,13 +508,13 @@ bool DetectRectangleFromFan(const RasterizerState &state, const VertexData *data if (!state.enableTextures) return true; - const auto &textl = data[*tlIndex].texturecoords, &textr = data[*tlIndex ^ 1].texturecoords; - const auto &texbl = data[*brIndex ^ 1].texturecoords, &texbr = data[*brIndex].texturecoords; + const auto &textl = data[*tlIndex].v.texturecoords, &textr = data[*tlIndex ^ 1].v.texturecoords; + const auto &texbl = data[*brIndex ^ 1].v.texturecoords, &texbr = data[*brIndex].v.texturecoords; if (textl.x == texbl.x && textr.x == texbr.x && textl.y == textr.y && texbl.y == texbr.y) { // Okay, the texture is also good, but let's avoid rotation issues. - const auto &postl = data[*tlIndex].screenpos; - const auto &posbr = data[*brIndex].screenpos; + const auto &postl = data[*tlIndex].v.screenpos; + const auto &posbr = data[*brIndex].v.screenpos; return textl.y < texbr.y && postl.y < posbr.y && textl.x < texbr.x && postl.x < posbr.x; } } @@ -448,26 +523,26 @@ bool DetectRectangleFromFan(const RasterizerState &state, const VertexData *data return false; } -bool DetectRectangleFromPair(const RasterizerState &state, const VertexData data[6], int *tlIndex, int *brIndex) { +bool DetectRectangleFromPair(const RasterizerState &state, const ClipVertexData data[6], int *tlIndex, int *brIndex) { // Color and Z must be flat. Also find the TL and BR meanwhile. int tl = 0, br = 0; for (int i = 1; i < 6; ++i) { if (!AreCoordsRectangleCompatible(state, data[i], data[0])) return false; - if (data[i].screenpos.x <= data[tl].screenpos.x && data[i].screenpos.y <= data[tl].screenpos.y) + if (data[i].v.screenpos.x <= data[tl].v.screenpos.x && data[i].v.screenpos.y <= data[tl].v.screenpos.y) tl = i; - if (data[i].screenpos.x >= data[br].screenpos.x && data[i].screenpos.y >= data[br].screenpos.y) + if (data[i].v.screenpos.x >= data[br].v.screenpos.x && data[i].v.screenpos.y >= data[br].v.screenpos.y) br = i; } *tlIndex = tl; *brIndex = br; - auto xat = [&](int i) { return data[i].screenpos.x; }; - auto yat = [&](int i) { return data[i].screenpos.y; }; - auto uat = [&](int i) { return data[i].texturecoords.x; }; - auto vat = [&](int i) { return data[i].texturecoords.y; }; + auto xat = [&](int i) { return data[i].v.screenpos.x; }; + auto yat = [&](int i) { return data[i].v.screenpos.y; }; + auto uat = [&](int i) { return data[i].v.texturecoords.x; }; + auto vat = [&](int i) { return data[i].v.texturecoords.y; }; // A likely order would be: TL, TR, BR, TL, BR, BL. We'd have the last index of each. // TODO: Make more generic. @@ -495,12 +570,12 @@ bool DetectRectangleFromPair(const RasterizerState &state, const VertexData data return false; } -bool DetectRectangleThroughModeSlices(const RasterizerState &state, const VertexData data[4]) { +bool DetectRectangleThroughModeSlices(const RasterizerState &state, const ClipVertexData data[4]) { // Color and Z must be flat. for (int i = 1; i < 4; ++i) { - if (!(data[i].color0 == data[0].color0)) + if (!(data[i].v.color0 == data[0].v.color0)) return false; - if (!(data[i].screenpos.z == data[0].screenpos.z)) { + if (!(data[i].v.screenpos.z == data[0].v.screenpos.z)) { // Sometimes, we don't actually care about z. if (state.pixelID.depthWrite || state.pixelID.DepthTestFunc() != GE_COMP_ALWAYS) return false; @@ -508,15 +583,15 @@ bool DetectRectangleThroughModeSlices(const RasterizerState &state, const Vertex } // Games very commonly use vertical strips of rectangles. Detect and combine. - const auto &tl1 = data[0].screenpos, &br1 = data[1].screenpos; - const auto &tl2 = data[2].screenpos, &br2 = data[3].screenpos; + const auto &tl1 = data[0].v.screenpos, &br1 = data[1].v.screenpos; + const auto &tl2 = data[2].v.screenpos, &br2 = data[3].v.screenpos; if (tl1.y == tl2.y && br1.y == br2.y && br1.y > tl1.y) { if (br1.x == tl2.x && tl1.x < br1.x && tl2.x < br2.x) { if (!state.enableTextures) return true; - const auto &textl1 = data[0].texturecoords, &texbr1 = data[1].texturecoords; - const auto &textl2 = data[2].texturecoords, &texbr2 = data[3].texturecoords; + const auto &textl1 = data[0].v.texturecoords, &texbr1 = data[1].v.texturecoords; + const auto &textl2 = data[2].v.texturecoords, &texbr2 = data[3].v.texturecoords; if (textl1.y != textl2.y || texbr1.y != texbr2.y || textl1.y > texbr1.y) return false; if (texbr1.x != textl2.x || textl1.x > texbr1.x || textl2.x > texbr2.x) diff --git a/GPU/Software/RasterizerRectangle.h b/GPU/Software/RasterizerRectangle.h index 6842215338..19e4b42b48 100644 --- a/GPU/Software/RasterizerRectangle.h +++ b/GPU/Software/RasterizerRectangle.h @@ -20,8 +20,8 @@ namespace Rasterizer { bool RectangleFastPath(const VertexData &v0, const VertexData &v1, BinManager &binner); void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &range, const RasterizerState &state); - bool DetectRectangleFromStrip(const RasterizerState &state, const VertexData data[4], int *tlIndex, int *brIndex); - bool DetectRectangleFromFan(const RasterizerState &state, const VertexData *data, int c, int *tlIndex, int *brIndex); - bool DetectRectangleFromPair(const RasterizerState &state, const VertexData data[6], int *tlIndex, int *brIndex); - bool DetectRectangleThroughModeSlices(const RasterizerState &state, const VertexData data[4]); + bool DetectRectangleFromStrip(const RasterizerState &state, const ClipVertexData data[4], int *tlIndex, int *brIndex); + bool DetectRectangleFromFan(const RasterizerState &state, const ClipVertexData *data, int c, int *tlIndex, int *brIndex); + bool DetectRectangleFromPair(const RasterizerState &state, const ClipVertexData data[6], int *tlIndex, int *brIndex); + bool DetectRectangleThroughModeSlices(const RasterizerState &state, const ClipVertexData data[4]); } diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 18cdb0b314..28fe4041fb 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -160,7 +160,7 @@ const SoftwareCommandTableEntry softgpuCommandTable[] = { { GE_CMD_LOGICOP, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_CACHED }, { GE_CMD_LOGICOPENABLE, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_CACHED }, - { GE_CMD_TEXMAPMODE, 0, SoftDirty::TRANSFORM_BASIC }, + { GE_CMD_TEXMAPMODE, 0, SoftDirty::TRANSFORM_BASIC | SoftDirty::RAST_TEX }, // These are read on every SubmitPrim, no need for dirtying or flushing. { GE_CMD_TEXSCALEU }, @@ -341,16 +341,16 @@ const SoftwareCommandTableEntry softgpuCommandTable[] = { { GE_CMD_DITH2, 0, SoftDirty::PIXEL_DITHER }, { GE_CMD_DITH3, 0, SoftDirty::PIXEL_DITHER }, - { GE_CMD_WORLDMATRIXNUMBER }, + { GE_CMD_WORLDMATRIXNUMBER, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_WorldMtxNum }, { GE_CMD_WORLDMATRIXDATA, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_WorldMtxData }, - { GE_CMD_VIEWMATRIXNUMBER }, + { GE_CMD_VIEWMATRIXNUMBER, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_ViewMtxNum }, { GE_CMD_VIEWMATRIXDATA, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_ViewMtxData }, - { GE_CMD_PROJMATRIXNUMBER }, + { GE_CMD_PROJMATRIXNUMBER, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_ProjMtxNum }, { GE_CMD_PROJMATRIXDATA, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_ProjMtxData }, // Currently not state. - { GE_CMD_TGENMATRIXNUMBER }, + { GE_CMD_TGENMATRIXNUMBER, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_TgenMtxNum }, { GE_CMD_TGENMATRIXDATA, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_TgenMtxData }, - { GE_CMD_BONEMATRIXNUMBER }, + { GE_CMD_BONEMATRIXNUMBER, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_BoneMtxNum }, { GE_CMD_BONEMATRIXDATA, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_BoneMtxData }, // Vertex Screen/Texture/Color @@ -361,7 +361,7 @@ const SoftwareCommandTableEntry softgpuCommandTable[] = { { GE_CMD_VTCT }, { GE_CMD_VTCQ }, { GE_CMD_VCV }, - { GE_CMD_VAP, FLAG_EXECUTE, SoftDirty::NONE, &GPUCommon::Execute_ImmVertexAlphaPrim }, + { GE_CMD_VAP, FLAG_EXECUTE, SoftDirty::NONE, &SoftGPU::Execute_ImmVertexAlphaPrim }, { GE_CMD_VFC }, { GE_CMD_VSCV }, @@ -490,14 +490,16 @@ void SoftGPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat for DSStretch g_DarkStalkerStretch; -void SoftGPU::ConvertTextureDescFrom16(Draw::TextureDesc &desc, int srcwidth, int srcheight, u8 *overrideData) { +void SoftGPU::ConvertTextureDescFrom16(Draw::TextureDesc &desc, int srcwidth, int srcheight, const uint16_t *overrideData) { // TODO: This should probably be converted in a shader instead.. fbTexBuffer_.resize(srcwidth * srcheight); - FormatBuffer displayBuffer; - displayBuffer.data = overrideData ? overrideData : Memory::GetPointerWrite(displayFramebuf_); + const uint16_t *displayBuffer = overrideData; + if (!displayBuffer) + displayBuffer = (const uint16_t *)Memory::GetPointer(displayFramebuf_); + for (int y = 0; y < srcheight; ++y) { u32 *buf_line = &fbTexBuffer_[y * srcwidth]; - const u16 *fb_line = &displayBuffer.as16[y * displayStride_]; + const u16 *fb_line = &displayBuffer[y * displayStride_]; switch (displayFormat_) { case GE_FORMAT_565: @@ -557,7 +559,7 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { bool hasPostShader = presentation_ && presentation_->HasPostShader(); if (PSP_CoreParameter().compat.flags().DarkStalkersPresentHack && displayFormat_ == GE_FORMAT_5551 && g_DarkStalkerStretch != DSStretch::Off) { - u8 *data = Memory::GetPointerWrite(0x04088000); + const u8 *data = Memory::GetPointerWrite(0x04088000); bool fillDesc = true; if (draw_->GetDataFormatSupport(Draw::DataFormat::A1B5G5R5_UNORM_PACK16) & Draw::FMT_TEXTURE) { // The perfect one. @@ -567,7 +569,7 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { desc.format = Draw::DataFormat::A1R5G5B5_UNORM_PACK16; outputFlags |= OutputFlags::RB_SWIZZLE; } else { - ConvertTextureDescFrom16(desc, srcwidth, srcheight, data); + ConvertTextureDescFrom16(desc, srcwidth, srcheight, (const uint16_t *)data); fillDesc = false; } if (fillDesc) { @@ -586,13 +588,13 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { hasImage = false; u1 = 1.0f; } else if (displayFormat_ == GE_FORMAT_8888) { - u8 *data = Memory::GetPointerWrite(displayFramebuf_); + const u8 *data = Memory::GetPointer(displayFramebuf_); desc.width = displayStride_ == 0 ? srcwidth : displayStride_; desc.height = srcheight; desc.initData.push_back(data); desc.format = Draw::DataFormat::R8G8B8A8_UNORM; } else if (displayFormat_ == GE_FORMAT_5551) { - const u8 *data = Memory::GetPointerWrite(displayFramebuf_); + const u8 *data = Memory::GetPointer(displayFramebuf_); bool fillDesc = true; if (draw_->GetDataFormatSupport(Draw::DataFormat::A1B5G5R5_UNORM_PACK16) & Draw::FMT_TEXTURE) { // The perfect one. @@ -639,6 +641,7 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { } void SoftGPU::CopyDisplayToOutput(bool reallyDirty) { + drawEngine_->transformUnit.Flush("output"); // The display always shows 480x272. CopyToCurrentFboFromDisplayRam(FB_WIDTH, FB_HEIGHT); MarkDirty(displayFramebuf_, displayStride_, 272, displayFormat_, SoftGPUVRAMDirty::CLEAR); @@ -650,7 +653,7 @@ void SoftGPU::MarkDirty(uint32_t addr, uint32_t stride, uint32_t height, GEBuffe } void SoftGPU::MarkDirty(uint32_t addr, uint32_t bytes, SoftGPUVRAMDirty value) { - // Don't bother tracking if frameskipping. + // Only bother tracking if frameskipping. if (g_Config.iFrameSkip == 0) return; if (!Memory::IsVRAMAddress(addr) || !Memory::IsVRAMAddress(addr + bytes - 1)) @@ -1005,19 +1008,24 @@ void SoftGPU::Execute_LoadClut(u32 op, u32 diff) { void SoftGPU::Execute_FramebufPtr(u32 op, u32 diff) { // We assume fb.data won't change while we're drawing. - drawEngine_->transformUnit.Flush("framebuf"); - fb.data = Memory::GetPointerWrite(gstate.getFrameBufAddress()); + if (diff) { + drawEngine_->transformUnit.Flush("framebuf"); + fb.data = Memory::GetPointerWrite(gstate.getFrameBufAddress()); + } } void SoftGPU::Execute_FramebufFormat(u32 op, u32 diff) { // We should flush, because ranges within bins may change. - drawEngine_->transformUnit.Flush("framebuf"); + if (diff) + drawEngine_->transformUnit.Flush("framebuf"); } void SoftGPU::Execute_ZbufPtr(u32 op, u32 diff) { // We assume depthbuf.data won't change while we're drawing. - drawEngine_->transformUnit.Flush("depthbuf"); - depthbuf.data = Memory::GetPointerWrite(gstate.getDepthBufAddress()); + if (diff) { + drawEngine_->transformUnit.Flush("depthbuf"); + depthbuf.data = Memory::GetPointerWrite(gstate.getDepthBufAddress()); + } } void SoftGPU::Execute_VertexType(u32 op, u32 diff) { @@ -1032,83 +1040,176 @@ void SoftGPU::Execute_VertexType(u32 op, u32 diff) { } } +void SoftGPU::Execute_WorldMtxNum(u32 op, u32 diff) { + // Setting 0xFFFFF0 will reset to 0. + gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | (op & 0xF); +} + +void SoftGPU::Execute_ViewMtxNum(u32 op, u32 diff) { + gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | (op & 0xF); +} + +void SoftGPU::Execute_ProjMtxNum(u32 op, u32 diff) { + gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | (op & 0xF); +} + +void SoftGPU::Execute_TgenMtxNum(u32 op, u32 diff) { + gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | (op & 0xF); +} + +void SoftGPU::Execute_BoneMtxNum(u32 op, u32 diff) { + // Setting any bits outside 0x7F are ignored and resets the internal counter. + gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (op & 0x7F); +} + void SoftGPU::Execute_WorldMtxData(u32 op, u32 diff) { - int num = gstate.worldmtxnum & 0xF; - u32 *target = num < 12 ? (u32 *)&gstate.worldMatrix[num] : (u32 *)&gstate.viewMatrix[num - 12]; - u32 newVal = op << 8; - if (newVal != *target) { - *target = newVal; - dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX; + int num = gstate.worldmtxnum & 0x00FFFFFF; + if (num < 12) { + u32 *target = (u32 *)&gstate.worldMatrix[num]; + u32 newVal = op << 8; + if (newVal != *target) { + *target = newVal; + dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX; + } } + + // Also update the CPU visible values, which update differently. + u32 *target = &matrixVisible.all[12 * 8 + (num & 0xF)]; + *target = op & 0x00FFFFFF; + num++; - gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | (num & 0xF); + gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | (num & 0x00FFFFFF); gstate.worldmtxdata = GE_CMD_WORLDMATRIXDATA << 24; } void SoftGPU::Execute_ViewMtxData(u32 op, u32 diff) { - int num = gstate.viewmtxnum & 0xF; - u32 *target = num < 12 ? (u32 *)&gstate.viewMatrix[num] : (u32 *)&gstate.projMatrix[num - 12]; - u32 newVal = op << 8; - if (newVal != *target) { - *target = newVal; - dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX; + int num = gstate.viewmtxnum & 0x00FFFFFF; + if (num < 12) { + u32 *target = (u32 *)&gstate.viewMatrix[num]; + u32 newVal = op << 8; + if (newVal != *target) { + *target = newVal; + dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX; + } } + + // Also update the CPU visible values, which update differently. + u32 *target = &matrixVisible.all[12 * 8 + 12 + (num & 0xF)]; + *target = op & 0x00FFFFFF; + num++; - gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | (num & 0xF); + gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | (num & 0x00FFFFFF); gstate.viewmtxdata = GE_CMD_VIEWMATRIXDATA << 24; } void SoftGPU::Execute_ProjMtxData(u32 op, u32 diff) { - int num = gstate.projmtxnum & 0xF; - u32 *target = (u32 *)&gstate.projMatrix[num]; - u32 newVal = op << 8; - if (newVal != *target) { - *target = newVal; - dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX; + int num = gstate.projmtxnum & 0x00FFFFFF; + if (num < 16) { + u32 *target = (u32 *)&gstate.projMatrix[num]; + u32 newVal = op << 8; + if (newVal != *target) { + *target = newVal; + dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX; + } } + + // Also update the CPU visible values, which update differently. + u32 *target = &matrixVisible.all[12 * 8 + 12 + 12 + (num & 0xF)]; + *target = op & 0x00FFFFFF; + num++; - gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | (num & 0xF); + gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | (num & 0x00FFFFFF); gstate.projmtxdata = GE_CMD_PROJMATRIXDATA << 24; } void SoftGPU::Execute_TgenMtxData(u32 op, u32 diff) { - int num = gstate.texmtxnum & 0xF; - u32 newVal = op << 8; - // Doesn't wrap to any other matrix. - if (num < 12 && newVal != ((const u32 *)gstate.tgenMatrix)[num]) { - ((u32 *)gstate.tgenMatrix)[num] = newVal; - // No dirtying, read during vertex read. + int num = gstate.texmtxnum & 0x00FFFFFF; + if (num < 12) { + u32 *target = (u32 *)&gstate.tgenMatrix[num]; + u32 newVal = op << 8; + if (newVal != *target) { + *target = newVal; + // This is mainly used in vertex read, but also affects if we enable texture projection. + dirtyFlags_ |= SoftDirty::RAST_TEX; + } } + + // Doesn't wrap to any other matrix. + if ((num & 0xF) < 12) { + matrixVisible.tgen[num & 0xF] = op & 0x00FFFFFF; + } + num++; - gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | (num & 0xF); + gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | (num & 0x00FFFFFF); gstate.texmtxdata = GE_CMD_TGENMATRIXDATA << 24; } void SoftGPU::Execute_BoneMtxData(u32 op, u32 diff) { - int num = gstate.boneMatrixNumber & 0x7F; - u32 *target; + int num = gstate.boneMatrixNumber & 0x00FFFFFF; + if (num < 96) { - target = (u32 *)&gstate.boneMatrix[num]; - } else if (num < 96 + 12) { - target = (u32 *)&gstate.worldMatrix[num - 96]; - } else if (num < 96 + 12 + 12) { - target = (u32 *)&gstate.viewMatrix[num - 96 - 12]; - } else { - target = (u32 *)&gstate.projMatrix[num - 96 - 12 - 12]; + u32 *target = (u32 *)&gstate.boneMatrix[num]; + u32 newVal = op << 8; + // No dirtying, we read bone data during vertex read. + *target = newVal; } - u32 newVal = op << 8; - if (newVal != *target) { - *target = newVal; - // Dirty if it overflowed. We read bone data during vertex read. - if (num >= 96) - dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX; - } + // Also update the CPU visible values, which update differently. + u32 *target = &matrixVisible.all[(num & 0x7F)]; + *target = op & 0x00FFFFFF; + num++; - gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (num & 0x7F); + gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (num & 0x00FFFFFF); gstate.boneMatrixData = GE_CMD_BONEMATRIXDATA << 24; } +static void CopyMatrix24(u32_le *result, const u32 *mtx, u32 count, u32 cmdbits) { + for (u32 i = 0; i < count; ++i) { + result[i] = mtx[i] | cmdbits; + } +} + +bool SoftGPU::GetMatrix24(GEMatrixType type, u32_le *result, u32 cmdbits) { + 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: + CopyMatrix24(result, matrixVisible.bone + (type - GE_MTX_BONE0) * 12, 12, cmdbits); + break; + case GE_MTX_TEXGEN: + CopyMatrix24(result, matrixVisible.tgen, 12, cmdbits); + break; + case GE_MTX_WORLD: + CopyMatrix24(result, matrixVisible.world, 12, cmdbits); + break; + case GE_MTX_VIEW: + CopyMatrix24(result, matrixVisible.view, 12, cmdbits); + break; + case GE_MTX_PROJECTION: + CopyMatrix24(result, matrixVisible.proj, 16, cmdbits); + break; + default: + return false; + } + return true; +} + +void SoftGPU::ResetMatrices() { + GPUCommon::ResetMatrices(); + dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX | SoftDirty::RAST_TEX; +} + +void SoftGPU::Execute_ImmVertexAlphaPrim(u32 op, u32 diff) { + GPUCommon::Execute_ImmVertexAlphaPrim(op, diff); + // We won't flush as often as hardware renderers, so we want to flush right away. + FlushImm(); +} + void SoftGPU::Execute_Call(u32 op, u32 diff) { PROFILE_THIS_SCOPE("gpu_call"); @@ -1138,6 +1239,18 @@ void SoftGPU::FinishDeferred() { drawEngine_->transformUnit.Flush("finish"); } +int SoftGPU::ListSync(int listid, int mode) { + // Take this as a cue that we need to finish drawing. + drawEngine_->transformUnit.Flush("listsync"); + return GPUCommon::ListSync(listid, mode); +} + +u32 SoftGPU::DrawSync(int mode) { + // Take this as a cue that we need to finish drawing. + drawEngine_->transformUnit.Flush("drawsync"); + return GPUCommon::DrawSync(mode); +} + void SoftGPU::GetStats(char *buffer, size_t bufsize) { drawEngine_->transformUnit.GetStats(buffer, bufsize); } @@ -1152,11 +1265,11 @@ void SoftGPU::NotifyVideoUpload(u32 addr, int size, int width, int format) // Ignore. } -bool SoftGPU::PerformMemoryCopy(u32 dest, u32 src, int size) -{ +bool SoftGPU::PerformMemoryCopy(u32 dest, u32 src, int size, GPUCopyFlag flags) { // Nothing to update. InvalidateCache(dest, size, GPU_INVALIDATE_HINT); - GPURecord::NotifyMemcpy(dest, src, size); + if (!(flags & GPUCopyFlag::DEBUG_NOTIFIED)) + GPURecord::NotifyMemcpy(dest, src, size); // Let's just be safe. MarkDirty(dest, size, SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY); return false; @@ -1223,18 +1336,19 @@ bool SoftGPU::GetCurrentFramebuffer(GPUDebugBuffer &buffer, GPUDebugFramebufferT int stride = gstate.FrameBufStride(); DrawingCoords size = GetTargetSize(stride); GEBufferFormat fmt = gstate.FrameBufFormat(); + const u8 *src = fb.data; if (type == GPU_DBG_FRAMEBUF_DISPLAY) { size.x = 480; size.y = 272; stride = displayStride_; fmt = displayFormat_; + src = Memory::GetPointer(displayFramebuf_); } buffer.Allocate(size.x, size.y, fmt); const int depth = fmt == GE_FORMAT_8888 ? 4 : 2; - const u8 *src = fb.data; u8 *dst = buffer.GetData(); const int byteWidth = size.x * depth; for (int16_t y = 0; y < size.y; ++y) { diff --git a/GPU/Software/SoftGpu.h b/GPU/Software/SoftGpu.h index 11d5dd16f3..84732eb43a 100644 --- a/GPU/Software/SoftGpu.h +++ b/GPU/Software/SoftGpu.h @@ -64,17 +64,17 @@ enum class SoftDirty : uint64_t { PIXEL_DITHER = 1ULL << 3, PIXEL_WRITEMASK = 1ULL << 4, PIXEL_CACHED = 1ULL << 5, - PIXEL_ALL = 63ULL << 0, + PIXEL_ALL = 0b111111ULL << 0, SAMPLER_BASIC = 1ULL << 6, SAMPLER_TEXLIST = 1ULL << 7, SAMPLER_CLUT = 1ULL << 8, - SAMPLER_ALL = 7ULL << 6, + SAMPLER_ALL = 0b111ULL << 6, RAST_BASIC = 1ULL << 9, RAST_TEX = 1ULL << 10, RAST_OFFSET = 1ULL << 11, - RAST_ALL = 7ULL << 9, + RAST_ALL = 0b111ULL << 9, LIGHT_BASIC = 1ULL << 12, LIGHT_MATERIAL = 1ULL << 13, @@ -82,13 +82,13 @@ enum class SoftDirty : uint64_t { LIGHT_1 = 1ULL << 15, LIGHT_2 = 1ULL << 16, LIGHT_3 = 1ULL << 17, - LIGHT_ALL = 63ULL << 12, + LIGHT_ALL = 0b111111ULL << 12, TRANSFORM_BASIC = 1ULL << 18, TRANSFORM_MATRIX = 1ULL << 19, TRANSFORM_VIEWPORT = 1ULL << 20, TRANSFORM_FOG = 1ULL << 21, - TRANSFORM_ALL = 31ULL << 18, + TRANSFORM_ALL = 0b1111ULL << 18, BINNER_RANGE = 1ULL << 22, BINNER_OVERLAP = 1ULL << 23, @@ -127,17 +127,19 @@ public: SoftGPU(GraphicsContext *gfxCtx, Draw::DrawContext *draw); ~SoftGPU(); - void CheckGPUFeatures() override {} + u32 CheckGPUFeatures() const override { return 0; } void InitClear() override {} void ExecuteOp(u32 op, u32 diff) override; void FinishDeferred() override; + int ListSync(int listid, int mode) override; + u32 DrawSync(int mode) override; void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) override; void CopyDisplayToOutput(bool reallyDirty) override; void GetStats(char *buffer, size_t bufsize) override; void InvalidateCache(u32 addr, int size, GPUInvalidationType type) override; void NotifyVideoUpload(u32 addr, int size, int width, int format) override; - bool PerformMemoryCopy(u32 dest, u32 src, int size) override; + bool PerformMemoryCopy(u32 dest, u32 src, int size, GPUCopyFlag flags = GPUCopyFlag::NONE) override; bool PerformMemorySet(u32 dest, u8 v, int size) override; bool PerformMemoryDownload(u32 dest, int size) override; bool PerformMemoryUpload(u32 dest, int size) override; @@ -179,18 +181,29 @@ public: // Overridden to change flushing behavior. void Execute_Call(u32 op, u32 diff); + void Execute_WorldMtxNum(u32 op, u32 diff); + void Execute_ViewMtxNum(u32 op, u32 diff); + void Execute_ProjMtxNum(u32 op, u32 diff); + void Execute_TgenMtxNum(u32 op, u32 diff); + void Execute_BoneMtxNum(u32 op, u32 diff); + void Execute_WorldMtxData(u32 op, u32 diff); void Execute_ViewMtxData(u32 op, u32 diff); void Execute_ProjMtxData(u32 op, u32 diff); void Execute_TgenMtxData(u32 op, u32 diff); void Execute_BoneMtxData(u32 op, u32 diff); + bool GetMatrix24(GEMatrixType type, u32_le *result, u32 cmdbits) override; + void ResetMatrices() override; + + void Execute_ImmVertexAlphaPrim(u32 op, u32 diff); + typedef void (SoftGPU::*CmdFunc)(u32 op, u32 diff); protected: void FastRunLoop(DisplayList &list) override; void CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight); - void ConvertTextureDescFrom16(Draw::TextureDesc &desc, int srcwidth, int srcheight, u8 *overrideData = nullptr); + void ConvertTextureDescFrom16(Draw::TextureDesc &desc, int srcwidth, int srcheight, const uint16_t *overrideData = nullptr); private: void MarkDirty(uint32_t addr, uint32_t stride, uint32_t height, GEBufferFormat fmt, SoftGPUVRAMDirty value); diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp index 0cb91dd280..7d1d28d07d 100644 --- a/GPU/Software/TransformUnit.cpp +++ b/GPU/Software/TransformUnit.cpp @@ -54,6 +54,7 @@ SoftwareDrawEngine::SoftwareDrawEngine() { // All this is a LOT of memory, need to see if we can cut down somehow. Used for splines. decoded = (u8 *)AllocateMemoryPages(DECODED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); decIndex = (u16 *)AllocateMemoryPages(DECODED_INDEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); + flushOnParams_ = false; } SoftwareDrawEngine::~SoftwareDrawEngine() { @@ -70,14 +71,68 @@ void SoftwareDrawEngine::DispatchSubmitPrim(const void *verts, const void *inds, transformUnit.SubmitPrimitive(verts, inds, prim, vertexCount, vertTypeID, bytesRead, this); } -void SoftwareDrawEngine::DispatchSubmitImm(const void *verts, const void *inds, GEPrimitiveType prim, int vertexCount, u32 vertTypeID, int cullMode, int *bytesRead) { +void SoftwareDrawEngine::DispatchSubmitImm(GEPrimitiveType prim, TransformedVertex *buffer, int vertexCount, int cullMode, bool continuation) { + uint32_t vertTypeID = GetVertTypeID(gstate.vertType | GE_VTYPE_POS_FLOAT, gstate.getUVGenMode()); + int flipCull = cullMode != gstate.getCullMode() ? 1 : 0; // TODO: For now, just setting all dirty. transformUnit.SetDirty(SoftDirty(-1)); gstate.cullmode ^= flipCull; - transformUnit.SubmitPrimitive(verts, inds, prim, vertexCount, vertTypeID, bytesRead, this); + + // TODO: This is a bit ugly. Should bypass when clipping... + uint32_t xScale = gstate.viewportxscale; + uint32_t xCenter = gstate.viewportxcenter; + uint32_t yScale = gstate.viewportyscale; + uint32_t yCenter = gstate.viewportycenter; + uint32_t zScale = gstate.viewportzscale; + uint32_t zCenter = gstate.viewportzcenter; + + // Force scale to 1 and center to zero. + gstate.viewportxscale = (GE_CMD_VIEWPORTXSCALE << 24) | 0x3F8000; + gstate.viewportxcenter = (GE_CMD_VIEWPORTXCENTER << 24) | 0x000000; + gstate.viewportyscale = (GE_CMD_VIEWPORTYSCALE << 24) | 0x3F8000; + gstate.viewportycenter = (GE_CMD_VIEWPORTYCENTER << 24) | 0x000000; + // Z we scale to 65535 for neg z clipping. + gstate.viewportzscale = (GE_CMD_VIEWPORTZSCALE << 24) | 0x477FFF; + gstate.viewportzcenter = (GE_CMD_VIEWPORTZCENTER << 24) | 0x000000; + + // Before we start, submit 0 prims to reset the prev prim type. + // Following submits will always be KEEP_PREVIOUS. + if (!continuation) + transformUnit.SubmitPrimitive(nullptr, nullptr, prim, 0, vertTypeID, nullptr, this); + + for (int i = 0; i < vertexCount; i++) { + ClipVertexData vert; + vert.clippos = ClipCoords(buffer[i].pos); + vert.v.texturecoords.x = buffer[i].u; + vert.v.texturecoords.y = buffer[i].v; + vert.v.texturecoords.z = buffer[i].uv_w; + if (gstate.isModeThrough()) { + vert.v.texturecoords.x *= gstate.getTextureWidth(0); + vert.v.texturecoords.y *= gstate.getTextureHeight(0); + } else { + vert.clippos.z *= 1.0f / 65535.0f; + } + vert.v.clipw = buffer[i].pos_w; + vert.v.color0 = buffer[i].color0_32; + vert.v.color1 = gstate.isUsingSecondaryColor() && !gstate.isModeThrough() ? buffer[i].color1_32 : 0; + vert.v.fogdepth = buffer[i].fog; + vert.v.screenpos.x = (int)(buffer[i].x * 16.0f); + vert.v.screenpos.y = (int)(buffer[i].y * 16.0f); + vert.v.screenpos.z = (u16)(u32)buffer[i].z; + + transformUnit.SubmitImmVertex(vert, this); + } + + gstate.viewportxscale = xScale; + gstate.viewportxcenter = xCenter; + gstate.viewportyscale = yScale; + gstate.viewportycenter = yCenter; + gstate.viewportzscale = zScale; + gstate.viewportzcenter = zCenter; + gstate.cullmode ^= flipCull; - // TODO: Should really clear, but the vertex type is faked so things might need resetting... + // TODO: Should really clear, but a bunch of values are forced so we this is safest. transformUnit.SetDirty(SoftDirty(-1)); } @@ -171,19 +226,15 @@ ScreenCoords TransformUnit::DrawingToScreen(const DrawingCoords &coords, u16 z) } enum class MatrixMode { - NONE = 0, POS_TO_CLIP = 1, - POS_TO_VIEW = 2, - WORLD_TO_CLIP = 3, + WORLD_TO_CLIP = 2, }; struct TransformState { Lighting::State lightingState; - float fogEnd; - float fogSlope; - float matrix[16]; + Vec4f posToFog; Vec3f screenScale; Vec3f screenAdd; @@ -210,22 +261,11 @@ void ComputeTransformState(TransformState *state, const VertexReader &vreader) { state->negateNormals = gstate.areNormalsReversed(); state->uvGenMode = gstate.getUVGenMode(); + if (state->uvGenMode == GE_TEXMAP_UNKNOWN) + state->uvGenMode = GE_TEXMAP_TEXTURE_COORDS; if (state->enableTransform) { - if (state->enableFog) { - state->fogEnd = getFloat24(gstate.fog1); - state->fogSlope = getFloat24(gstate.fog2); - // Same fixup as in ShaderManagerGLES.cpp - if (my_isnanorinf(state->fogEnd)) { - state->fogEnd = std::signbit(state->fogEnd) ? -INFINITY : INFINITY; - } - if (my_isnanorinf(state->fogSlope)) { - state->fogSlope = std::signbit(state->fogSlope) ? -INFINITY : INFINITY; - } - } - bool canSkipWorldPos = true; - bool canSkipViewPos = !state->enableFog; if (state->enableLighting) { Lighting::ComputeState(&state->lightingState, vreader.hasColor0()); for (int i = 0; i < 4; ++i) { @@ -238,29 +278,35 @@ void ComputeTransformState(TransformState *state, const VertexReader &vreader) { float world[16]; float view[16]; - if (canSkipWorldPos && canSkipViewPos) { - state->matrixMode = (uint8_t)MatrixMode::POS_TO_CLIP; - + float worldview[16]; + ConvertMatrix4x3To4x4(view, gstate.viewMatrix); + if (state->enableFog || canSkipWorldPos) { ConvertMatrix4x3To4x4(world, gstate.worldMatrix); - ConvertMatrix4x3To4x4(view, gstate.viewMatrix); - - float worldview[16]; Matrix4ByMatrix4(worldview, world, view); + } + + if (canSkipWorldPos) { + state->matrixMode = (uint8_t)MatrixMode::POS_TO_CLIP; Matrix4ByMatrix4(state->matrix, worldview, gstate.projMatrix); - } else if (canSkipWorldPos) { - state->matrixMode = (uint8_t)MatrixMode::POS_TO_VIEW; - - ConvertMatrix4x3To4x4(world, gstate.worldMatrix); - ConvertMatrix4x3To4x4(view, gstate.viewMatrix); - - Matrix4ByMatrix4(state->matrix, world, view); - } else if (canSkipViewPos) { - state->matrixMode = (uint8_t)MatrixMode::WORLD_TO_CLIP; - - ConvertMatrix4x3To4x4(view, gstate.viewMatrix); - Matrix4ByMatrix4(state->matrix, view, gstate.projMatrix); } else { - state->matrixMode = (uint8_t)MatrixMode::NONE; + state->matrixMode = (uint8_t)MatrixMode::WORLD_TO_CLIP; + Matrix4ByMatrix4(state->matrix, view, gstate.projMatrix); + } + + if (state->enableFog) { + float fogEnd = getFloat24(gstate.fog1); + float fogSlope = getFloat24(gstate.fog2); + // Same fixup as in ShaderManagerGLES.cpp + if (my_isnanorinf(fogEnd)) { + fogEnd = std::signbit(fogEnd) ? -INFINITY : INFINITY; + } + if (my_isnanorinf(fogSlope)) { + fogSlope = std::signbit(fogSlope) ? -INFINITY : INFINITY; + } + + // We bake fog end and slope into the dot product. + state->posToFog = Vec4f(worldview[2], worldview[6], worldview[10], worldview[14] + fogEnd); + state->posToFog *= fogSlope; } state->screenScale = Vec3f(gstate.getViewportXScale(), gstate.getViewportYScale(), gstate.getViewportZScale()); @@ -273,26 +319,34 @@ void ComputeTransformState(TransformState *state, const VertexReader &vreader) { state->roundToScreen = &ClipToScreenInternal; } -VertexData TransformUnit::ReadVertex(VertexReader &vreader, const TransformState &state, bool &outside_range_flag) { +ClipVertexData TransformUnit::ReadVertex(VertexReader &vreader, const TransformState &state) { PROFILE_THIS_SCOPE("read_vert"); - VertexData vertex; + // If we ever thread this, we'll have to change this. + ClipVertexData vertex; ModelCoords pos; // VertexDecoder normally scales z, but we want it unscaled. vreader.ReadPosThroughZ16(pos.AsArray()); + static Vec3Packedf lastTC; if (state.readUV) { - vreader.ReadUV(vertex.texturecoords.AsArray()); + vreader.ReadUV(vertex.v.texturecoords.AsArray()); + vertex.v.texturecoords.q() = 0.0f; + lastTC = vertex.v.texturecoords; } else { - vertex.texturecoords.SetZero(); + vertex.v.texturecoords = lastTC; } - Vec3 normal; + Vec3f normal; + static Vec3f lastnormal; if (vreader.hasNormal()) { vreader.ReadNrm(normal.AsArray()); + lastnormal = normal; if (state.negateNormals) normal = -normal; + } else { + normal = lastnormal; } if (state.readWeights) { @@ -317,37 +371,21 @@ VertexData TransformUnit::ReadVertex(VertexReader &vreader, const TransformState } if (vreader.hasColor0()) { - vreader.ReadColor0_8888((u8 *)&vertex.color0); + vreader.ReadColor0_8888((u8 *)&vertex.v.color0); } else { - vertex.color0 = gstate.getMaterialAmbientRGBA(); + vertex.v.color0 = gstate.getMaterialAmbientRGBA(); } - vertex.color1 = 0; + vertex.v.color1 = 0; if (state.enableTransform) { WorldCoords worldpos; - ModelCoords viewpos; switch (MatrixMode(state.matrixMode)) { - case MatrixMode::NONE: - worldpos = TransformUnit::ModelToWorld(pos); - viewpos = TransformUnit::WorldToView(worldpos); - vertex.clippos = TransformUnit::ViewToClip(viewpos); - break; - case MatrixMode::POS_TO_CLIP: vertex.clippos = Vec3ByMatrix44(pos, state.matrix); break; - case MatrixMode::POS_TO_VIEW: -#ifdef _M_SSE - viewpos = Vec3ByMatrix44(pos, state.matrix).vec; -#else - viewpos = Vec3ByMatrix44(pos, state.matrix).rgb(); -#endif - vertex.clippos = TransformUnit::ViewToClip(viewpos); - break; - case MatrixMode::WORLD_TO_CLIP: worldpos = TransformUnit::ModelToWorld(pos); vertex.clippos = Vec3ByMatrix44(worldpos, state.matrix); @@ -362,15 +400,20 @@ VertexData TransformUnit::ReadVertex(VertexReader &vreader, const TransformState #else screenScaled = vertex.clippos.xyz() * state.screenScale / vertex.clippos.w + state.screenAdd; #endif - vertex.screenpos = state.roundToScreen(screenScaled, vertex.clippos, &outside_range_flag); - if (outside_range_flag) + bool outside_range_flag = false; + vertex.v.screenpos = state.roundToScreen(screenScaled, vertex.clippos, &outside_range_flag); + if (outside_range_flag) { + // We use this, essentially, as the flag. + vertex.v.screenpos.x = 0x7FFFFFFF; return vertex; + } if (state.enableFog) { - vertex.fogdepth = (viewpos.z + state.fogEnd) * state.fogSlope; + vertex.v.fogdepth = Dot(state.posToFog, Vec4f(pos, 1.0f)); } else { - vertex.fogdepth = 1.0f; + vertex.v.fogdepth = 1.0f; } + vertex.v.clipw = vertex.clippos.w; Vec3 worldnormal; if (vreader.hasNormal()) { @@ -389,40 +432,35 @@ VertexData TransformUnit::ReadVertex(VertexReader &vreader, const TransformState break; case GE_PROJMAP_UV: - source = Vec3f(vertex.texturecoords, 0.0f); + source = Vec3f(vertex.v.texturecoords.uv(), 0.0f); break; case GE_PROJMAP_NORMALIZED_NORMAL: - source = normal.NormalizedOr001(cpu_info.bSSE4_1); + // This does not use 0, 0, 1 if length is zero. + source = normal.Normalized(cpu_info.bSSE4_1); break; case GE_PROJMAP_NORMAL: source = normal; break; - - default: - source = Vec3f::AssignToAll(0.0f); - ERROR_LOG_REPORT(G3D, "Software: Unsupported UV projection mode %x", gstate.getUVProjMode()); - break; } - // TODO: What about uv scale and offset? + // Note that UV scale/offset are not used in this mode. Vec3 stq = Vec3ByMatrix43(source, gstate.tgenMatrix); - float z_recip = 1.0f / stq.z; - vertex.texturecoords = Vec2f(stq.x * z_recip, stq.y * z_recip); + vertex.v.texturecoords = Vec3Packedf(stq.x, stq.y, stq.z); } else if (state.uvGenMode == GE_TEXMAP_ENVIRONMENT_MAP) { - Lighting::GenerateLightST(vertex, worldnormal); + Lighting::GenerateLightST(vertex.v, worldnormal); } PROFILE_THIS_SCOPE("light"); if (state.enableLighting) - Lighting::Process(vertex, worldpos, worldnormal, state.lightingState); + Lighting::Process(vertex.v, worldpos, worldnormal, state.lightingState); } else { - vertex.screenpos.x = (int)(pos[0] * SCREEN_SCALE_FACTOR); - vertex.screenpos.y = (int)(pos[1] * SCREEN_SCALE_FACTOR); - vertex.screenpos.z = pos[2]; - vertex.clippos.w = 1.f; - vertex.fogdepth = 1.f; + vertex.v.screenpos.x = (int)(pos[0] * SCREEN_SCALE_FACTOR); + vertex.v.screenpos.y = (int)(pos[1] * SCREEN_SCALE_FACTOR); + vertex.v.screenpos.z = pos[2]; + vertex.v.clipw = 1.0f; + vertex.v.fogdepth = 1.0f; } return vertex; @@ -435,10 +473,75 @@ SoftDirty TransformUnit::GetDirty() { return binner_->GetDirty(); } +class SoftwareVertexReader { +public: + SoftwareVertexReader(u8 *base, VertexDecoder &vdecoder, u32 vertex_type, int vertex_count, const void *vertices, const void *indices, const TransformState &transformState, TransformUnit &transform) + : vreader_(base, vdecoder.GetDecVtxFmt(), vertex_type), conv_(vertex_type, indices), transformState_(transformState), transform_(transform) { + useIndices_ = indices != nullptr; + lowerBound_ = 0; + upperBound_ = vertex_count == 0 ? 0 : vertex_count - 1; + + if (useIndices_) + GetIndexBounds(indices, vertex_count, vertex_type, &lowerBound_, &upperBound_); + if (vertex_count != 0) + vdecoder.DecodeVerts(base, vertices, lowerBound_, upperBound_); + + // If we're only using a subset of verts, it's better to decode with random access (usually.) + // However, if we're reusing a lot of verts, we should read and cache them. + useCache_ = useIndices_ && vertex_count > (upperBound_ - lowerBound_ + 1); + if (useCache_ && cached_.size() < upperBound_ - lowerBound_ + 1) + cached_.resize(std::max(128, upperBound_ - lowerBound_ + 1)); + } + + const VertexReader &GetVertexReader() const { + return vreader_; + } + + bool IsThrough() const { + return vreader_.isThrough(); + } + + void UpdateCache() { + if (!useCache_) + return; + + for (int i = 0; i < upperBound_ - lowerBound_ + 1; ++i) { + vreader_.Goto(i); + cached_[i] = transform_.ReadVertex(vreader_, transformState_); + } + } + + inline ClipVertexData Read(int vtx) { + if (useIndices_) { + if (useCache_) { + return cached_[conv_(vtx) - lowerBound_]; + } + vreader_.Goto(conv_(vtx) - lowerBound_); + } else { + vreader_.Goto(vtx); + } + + return transform_.ReadVertex(vreader_, transformState_); + }; + +protected: + VertexReader vreader_; + const IndexConverter conv_; + const TransformState &transformState_; + TransformUnit &transform_; + uint16_t lowerBound_; + uint16_t upperBound_; + static std::vector cached_; + bool useIndices_ = false; + bool useCache_ = false; +}; + +// Static to reduce allocations mid-frame. +std::vector SoftwareVertexReader::cached_; + void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, GEPrimitiveType prim_type, int vertex_count, u32 vertex_type, int *bytesRead, SoftwareDrawEngine *drawEngine) { VertexDecoder &vdecoder = *drawEngine->FindVertexDecoder(vertex_type); - const DecVtxFormat &vtxfmt = vdecoder.GetDecVtxFmt(); if (bytesRead) *bytesRead = vertex_count * vdecoder.VertexSize(); @@ -447,22 +550,13 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G if (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) { return; } - // Throughmode never draws 8-bit primitives, maybe because they can't fully specify the screen? - if ((vertex_type & GE_VTYPE_THROUGH_MASK) != 0 && (vertex_type & GE_VTYPE_POS_MASK) == GE_VTYPE_POS_8BIT) - return; // Vertices without position are just entirely culled. + // Note: Throughmode does draw 8-bit primitives, but positions are always zero - handled in decode. if ((vertex_type & GE_VTYPE_POS_MASK) == 0) return; - u16 index_lower_bound = 0; - u16 index_upper_bound = vertex_count - 1; - IndexConverter ConvertIndex(vertex_type, indices); - - if (indices) - GetIndexBounds(indices, vertex_count, vertex_type, &index_lower_bound, &index_upper_bound); - vdecoder.DecodeVerts(decoded_, vertices, index_lower_bound, index_upper_bound); - - VertexReader vreader(decoded_, vtxfmt, vertex_type); + static TransformState transformState; + SoftwareVertexReader vreader(decoded_, vdecoder, vertex_type, vertex_count, vertices, indices, transformState, *this); if (prim_type != GE_PRIM_KEEP_PREVIOUS) { data_index_ = 0; @@ -471,55 +565,29 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G prim_type = prev_prim_; } - int vtcs_per_prim; - switch (prim_type) { - case GE_PRIM_POINTS: vtcs_per_prim = 1; break; - case GE_PRIM_LINES: vtcs_per_prim = 2; break; - case GE_PRIM_TRIANGLES: vtcs_per_prim = 3; break; - case GE_PRIM_RECTANGLES: vtcs_per_prim = 2; break; - default: vtcs_per_prim = 0; break; - } + binner_->UpdateState(); + hasDraws_ = true; - // TODO: Do this in two passes - first process the vertices (before indexing/stripping), - // then resolve the indices. This lets us avoid transforming shared vertices twice. - - binner_->UpdateState(vreader.isThrough()); - - static TransformState transformState; if (binner_->HasDirty(SoftDirty::LIGHT_ALL | SoftDirty::TRANSFORM_ALL)) { - ComputeTransformState(&transformState, vreader); + ComputeTransformState(&transformState, vreader.GetVertexReader()); binner_->ClearDirty(SoftDirty::LIGHT_ALL | SoftDirty::TRANSFORM_ALL); } + vreader.UpdateCache(); bool skipCull = !gstate.isCullEnabled() || gstate.isModeClear(); const CullType cullType = skipCull ? CullType::OFF : (gstate.getCullMode() ? CullType::CCW : CullType::CW); - bool outside_range_flag = false; - - if (vreader.isThrough() && cullType == CullType::OFF && prim_type == GE_PRIM_TRIANGLES && data_index_ + vertex_count >= 6 && ((data_index_ + vertex_count) % 6) == 0) { + if (vreader.IsThrough() && cullType == CullType::OFF && prim_type == GE_PRIM_TRIANGLES && data_index_ == 0 && vertex_count >= 6 && ((vertex_count) % 6) == 0) { // Some games send rectangles as a series of regular triangles. // We look for this, but only in throughmode. - VertexData buf[6]; + ClipVertexData buf[6]; int buf_index = data_index_; for (int i = 0; i < data_index_; ++i) { buf[i] = data_[i]; } for (int vtx = 0; vtx < vertex_count; ++vtx) { - if (indices) { - vreader.Goto(ConvertIndex(vtx) - index_lower_bound); - } else { - vreader.Goto(vtx); - } - - buf[buf_index++] = ReadVertex(vreader, transformState, outside_range_flag); - if (buf_index >= 3 && outside_range_flag) { - // Cull, just pretend it didn't happen. - buf_index -= 3; - outside_range_flag = false; - continue; - } - + buf[buf_index++] = vreader.Read(vtx); if (buf_index < 6) continue; @@ -552,75 +620,56 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G return; } + // Note: intentionally, these allow for the case of vertex_count == 0, but data_index_ > 0. + // This is used for immediate-mode primitives. switch (prim_type) { case GE_PRIM_POINTS: - case GE_PRIM_LINES: - case GE_PRIM_TRIANGLES: - { - for (int vtx = 0; vtx < vertex_count; ++vtx) { - if (indices) { - vreader.Goto(ConvertIndex(vtx) - index_lower_bound); - } else { - vreader.Goto(vtx); - } - - data_[data_index_++] = ReadVertex(vreader, transformState, outside_range_flag); - if (data_index_ < vtcs_per_prim) { - // Keep reading. Note: an incomplete prim will stay read for GE_PRIM_KEEP_PREVIOUS. - continue; - } - - // Okay, we've got enough verts. Reset the index for next time. - data_index_ = 0; - if (outside_range_flag) { - // Cull the prim if it was outside, and move to the next prim. - outside_range_flag = false; - continue; - } - - switch (prim_type) { - case GE_PRIM_TRIANGLES: - SendTriangle(cullType, &data_[0]); - break; - - case GE_PRIM_LINES: - Clipper::ProcessLine(data_[0], data_[1], *binner_); - break; - - case GE_PRIM_POINTS: - Clipper::ProcessPoint(data_[0], *binner_); - break; - - default: - _dbg_assert_msg_(false, "Unexpected prim type: %d", prim_type); - } - } - break; + for (int i = 0; i < data_index_; ++i) + Clipper::ProcessPoint(data_[i], *binner_); + data_index_ = 0; + for (int vtx = 0; vtx < vertex_count; ++vtx) { + data_[0] = vreader.Read(vtx); + Clipper::ProcessPoint(data_[0], *binner_); } + break; + + case GE_PRIM_LINES: + for (int i = 0; i < data_index_ - 1; i += 2) + Clipper::ProcessLine(data_[i + 0], data_[i + 1], *binner_); + data_index_ &= 1; + for (int vtx = 0; vtx < vertex_count; ++vtx) { + data_[data_index_++] = vreader.Read(vtx); + if (data_index_ == 2) { + Clipper::ProcessLine(data_[0], data_[1], *binner_); + data_index_ = 0; + } + } + break; + + case GE_PRIM_TRIANGLES: + for (int vtx = 0; vtx < vertex_count; ++vtx) { + data_[data_index_++] = vreader.Read(vtx); + if (data_index_ < 3) { + // Keep reading. Note: an incomplete prim will stay read for GE_PRIM_KEEP_PREVIOUS. + continue; + } + // Okay, we've got enough verts. Reset the index for next time. + data_index_ = 0; + + SendTriangle(cullType, &data_[0]); + } + // In case vertex_count was 0. + if (data_index_ >= 3) { + SendTriangle(cullType, &data_[0]); + data_index_ = 0; + } + break; case GE_PRIM_RECTANGLES: for (int vtx = 0; vtx < vertex_count; ++vtx) { - if (indices) { - vreader.Goto(ConvertIndex(vtx) - index_lower_bound); - } else { - vreader.Goto(vtx); - } + data_[data_index_++] = vreader.Read(vtx); - data_[data_index_++] = ReadVertex(vreader, transformState, outside_range_flag); - if (outside_range_flag) { - outside_range_flag = false; - // Note: this is the post increment index. If odd, we set the first vert. - if (data_index_ & 1) { - // Skip the next one and forget this one. - vtx++; - data_index_--; - } else { - // Forget both of the last 2. - data_index_ -= 2; - } - } - - if (data_index_ == 4 && vreader.isThrough() && cullType == CullType::OFF) { + if (data_index_ == 4 && vreader.IsThrough() && cullType == CullType::OFF) { if (Rasterizer::DetectRectangleThroughModeSlices(binner_->State(), data_)) { data_[1] = data_[3]; data_index_ = 2; @@ -646,19 +695,7 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G // If data_index_ is 1 or 2, etc., it means we're continuing a line strip. int skip_count = data_index_ == 0 ? 1 : 0; for (int vtx = 0; vtx < vertex_count; ++vtx) { - if (indices) { - vreader.Goto(ConvertIndex(vtx) - index_lower_bound); - } else { - vreader.Goto(vtx); - } - - data_[(data_index_++) & 1] = ReadVertex(vreader, transformState, outside_range_flag); - if (outside_range_flag) { - // Drop all primitives containing the current vertex - skip_count = 2; - outside_range_flag = false; - continue; - } + data_[(data_index_++) & 1] = vreader.Read(vtx); if (skip_count) { --skip_count; @@ -667,6 +704,9 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G Clipper::ProcessLine(data_[data_index_ & 1], data_[(data_index_ & 1) ^ 1], *binner_); } } + // If this is from immediate-mode drawing, we always had one new vert (already in data_.) + if (isImmDraw_ && data_index_ >= 2) + Clipper::ProcessLine(data_[data_index_ & 1], data_[(data_index_ & 1) ^ 1], *binner_); break; } @@ -681,19 +721,15 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G if (data_index_ == 0 && vertex_count >= 4 && (vertex_count & 1) == 0 && cullType == CullType::OFF) { for (int base = 0; base < vertex_count - 2; base += 2) { for (int vtx = base == 0 ? 0 : 2; vtx < 4; ++vtx) { - if (indices) { - vreader.Goto(ConvertIndex(base + vtx) - index_lower_bound); - } else { - vreader.Goto(base + vtx); - } - data_[vtx] = ReadVertex(vreader, transformState, outside_range_flag); + data_[vtx] = vreader.Read(base + vtx); } // If a strip is effectively a rectangle, draw it as such! int tl = -1, br = -1; - if (!outside_range_flag && Rasterizer::DetectRectangleFromStrip(binner_->State(), data_, &tl, &br)) { + if (Rasterizer::DetectRectangleFromStrip(binner_->State(), data_, &tl, &br)) { Clipper::ProcessRect(data_[tl], data_[br], *binner_); start_vtx += 2; + skip_count = 2; if (base + 4 >= vertex_count) { start_vtx = vertex_count; break; @@ -703,6 +739,7 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G // TODO: Maybe should give detection two halves? data_[0] = data_[2]; data_[1] = data_[3]; + data_index_ = 2; } else { // Go into triangle mode. Unfortunately, we re-read the verts. break; @@ -710,32 +747,29 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G } } - outside_range_flag = false; - for (int vtx = start_vtx; vtx < vertex_count; ++vtx) { - if (indices) { - vreader.Goto(ConvertIndex(vtx) - index_lower_bound); - } else { - vreader.Goto(vtx); - } - + for (int vtx = start_vtx; vtx < vertex_count && skip_count > 0; ++vtx) { int provoking_index = (data_index_++) % 3; - data_[provoking_index] = ReadVertex(vreader, transformState, outside_range_flag); - if (outside_range_flag) { - // Drop all primitives containing the current vertex - skip_count = 2; - outside_range_flag = false; - continue; - } + data_[provoking_index] = vreader.Read(vtx); + --skip_count; + ++start_vtx; + } - if (skip_count) { - --skip_count; - continue; - } + for (int vtx = start_vtx; vtx < vertex_count; ++vtx) { + int provoking_index = (data_index_++) % 3; + data_[provoking_index] = vreader.Read(vtx); int wind = (data_index_ - 1) % 2; CullType altCullType = cullType == CullType::OFF ? cullType : CullType((int)cullType ^ wind); SendTriangle(altCullType, &data_[0], provoking_index); } + + // If this is from immediate-mode drawing, we always had one new vert (already in data_.) + if (isImmDraw_ && data_index_ >= 3) { + int provoking_index = (data_index_ - 1) % 3; + int wind = (data_index_ - 1) % 2; + CullType altCullType = cullType == CullType::OFF ? cullType : CullType((int)cullType ^ wind); + SendTriangle(altCullType, &data_[0], provoking_index); + } break; } @@ -747,64 +781,47 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G int start_vtx = 0; // Only read the central vertex if we're not continuing. - if (data_index_ == 0) { - if (indices) { - vreader.Goto(ConvertIndex(0) - index_lower_bound); - } else { - vreader.Goto(0); - } - data_[0] = ReadVertex(vreader, transformState, outside_range_flag); + if (data_index_ == 0 && vertex_count > 0) { + data_[0] = vreader.Read(0); data_index_++; start_vtx = 1; - - // If the central vertex is outside range, all the points are toast. - if (outside_range_flag) - break; } if (data_index_ == 1 && vertex_count == 4 && cullType == CullType::OFF) { for (int vtx = start_vtx; vtx < vertex_count; ++vtx) { - if (indices) { - vreader.Goto(ConvertIndex(vtx) - index_lower_bound); - } else { - vreader.Goto(vtx); - } - data_[vtx] = ReadVertex(vreader, transformState, outside_range_flag); + data_[vtx] = vreader.Read(vtx); } int tl = -1, br = -1; - if (!outside_range_flag && Rasterizer::DetectRectangleFromFan(binner_->State(), data_, vertex_count, &tl, &br)) { + if (Rasterizer::DetectRectangleFromFan(binner_->State(), data_, vertex_count, &tl, &br)) { Clipper::ProcessRect(data_[tl], data_[br], *binner_); break; } } - outside_range_flag = false; - for (int vtx = start_vtx; vtx < vertex_count; ++vtx) { - if (indices) { - vreader.Goto(ConvertIndex(vtx) - index_lower_bound); - } else { - vreader.Goto(vtx); - } - + for (int vtx = start_vtx; vtx < vertex_count && skip_count > 0; ++vtx) { int provoking_index = 2 - ((data_index_++) % 2); - data_[provoking_index] = ReadVertex(vreader, transformState, outside_range_flag); - if (outside_range_flag) { - // Drop all primitives containing the current vertex - skip_count = 2; - outside_range_flag = false; - continue; - } + data_[provoking_index] = vreader.Read(vtx); + --skip_count; + ++start_vtx; + } - if (skip_count) { - --skip_count; - continue; - } + for (int vtx = start_vtx; vtx < vertex_count; ++vtx) { + int provoking_index = 2 - ((data_index_++) % 2); + data_[provoking_index] = vreader.Read(vtx); int wind = (data_index_ - 1) % 2; CullType altCullType = cullType == CullType::OFF ? cullType : CullType((int)cullType ^ wind); SendTriangle(altCullType, &data_[0], provoking_index); } + + // If this is from immediate-mode drawing, we always had one new vert (already in data_.) + if (isImmDraw_ && data_index_ >= 3) { + int wind = (data_index_ - 1) % 2; + int provoking_index = 2 - wind; + CullType altCullType = cullType == CullType::OFF ? cullType : CullType((int)cullType ^ wind); + SendTriangle(altCullType, &data_[0], provoking_index); + } break; } @@ -814,7 +831,48 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G } } -void TransformUnit::SendTriangle(CullType cullType, const VertexData *verts, int provoking) { +void TransformUnit::SubmitImmVertex(const ClipVertexData &vert, SoftwareDrawEngine *drawEngine) { + // Where we put it is different for STRIP/FAN types. + switch (prev_prim_) { + case GE_PRIM_POINTS: + case GE_PRIM_LINES: + case GE_PRIM_TRIANGLES: + case GE_PRIM_RECTANGLES: + // This is the easy one. SubmitPrimitive resets data_index_. + data_[data_index_++] = vert; + break; + + case GE_PRIM_LINE_STRIP: + // This one alternates, and data_index_ > 0 means it draws a segment. + data_[(data_index_++) & 1] = vert; + break; + + case GE_PRIM_TRIANGLE_STRIP: + data_[(data_index_++) % 3] = vert; + break; + + case GE_PRIM_TRIANGLE_FAN: + if (data_index_ == 0) { + data_[data_index_++] = vert; + } else { + int provoking_index = 2 - ((data_index_++) % 2); + data_[provoking_index] = vert; + } + break; + + default: + _assert_msg_(false, "Invalid prim type: %d", (int)prev_prim_); + break; + } + + uint32_t vertTypeID = GetVertTypeID(gstate.vertType | GE_VTYPE_POS_FLOAT, gstate.getUVGenMode()); + // This now processes the step with shared logic, given the existing data_. + isImmDraw_ = true; + SubmitPrimitive(nullptr, nullptr, GE_PRIM_KEEP_PREVIOUS, 0, vertTypeID, nullptr, drawEngine); + isImmDraw_ = false; +} + +void TransformUnit::SendTriangle(CullType cullType, const ClipVertexData *verts, int provoking) { if (cullType == CullType::OFF) { Clipper::ProcessTriangle(verts[0], verts[1], verts[2], verts[provoking], *binner_); Clipper::ProcessTriangle(verts[2], verts[1], verts[0], verts[provoking], *binner_); @@ -826,8 +884,12 @@ void TransformUnit::SendTriangle(CullType cullType, const VertexData *verts, int } void TransformUnit::Flush(const char *reason) { + if (!hasDraws_) + return; + binner_->Flush(reason); GPUDebug::NotifyDraw(); + hasDraws_ = false; } void TransformUnit::GetStats(char *buffer, size_t bufsize) { @@ -836,6 +898,9 @@ void TransformUnit::GetStats(char *buffer, size_t bufsize) { } void TransformUnit::FlushIfOverlap(const char *reason, bool modifying, uint32_t addr, uint32_t stride, uint32_t w, uint32_t h) { + if (!hasDraws_) + return; + if (binner_->HasPendingWrite(addr, stride, w, h)) Flush(reason); if (modifying && binner_->HasPendingRead(addr, stride, w, h)) @@ -916,6 +981,9 @@ bool TransformUnit::GetCurrentSimpleVertices(int count, std::vector= 0xFFFF ? z : (float)screenPos.z; } if (gstate.vertType & GE_VTYPE_COL_MASK) { diff --git a/GPU/Software/TransformUnit.h b/GPU/Software/TransformUnit.h index fd6e047350..3c022523d2 100644 --- a/GPU/Software/TransformUnit.h +++ b/GPU/Software/TransformUnit.h @@ -78,29 +78,39 @@ struct DrawingCoords { s16 y; }; -struct VertexData { - void Lerp(float t, const VertexData &a, const VertexData &b) { +struct alignas(16) VertexData { + Vec3Packedf texturecoords; + float clipw; + uint32_t color0; + uint32_t color1; + ScreenCoords screenpos; + float fogdepth; +}; + +struct ClipVertexData { + void Lerp(float t, const ClipVertexData &a, const ClipVertexData &b) { clippos = ::Lerp(a.clippos, b.clippos, t); // Ignore screenpos because Lerp() is only used pre-calculation of screenpos. - texturecoords = ::Lerp(a.texturecoords, b.texturecoords, t); - fogdepth = ::Lerp(a.fogdepth, b.fogdepth, t); + v.texturecoords = ::Lerp(a.v.texturecoords, b.v.texturecoords, t); + v.fogdepth = ::Lerp(a.v.fogdepth, b.v.fogdepth, t); u16 t_int = (u16)(t * 256); - color0 = LerpInt, 256>(Vec4::FromRGBA(a.color0), Vec4::FromRGBA(b.color0), t_int).ToRGBA(); - color1 = LerpInt, 256>(Vec3::FromRGB(a.color1), Vec3::FromRGB(b.color1), t_int).ToRGB(); + v.color0 = LerpInt, 256>(Vec4::FromRGBA(a.v.color0), Vec4::FromRGBA(b.v.color0), t_int).ToRGBA(); + v.color1 = LerpInt, 256>(Vec3::FromRGB(a.v.color1), Vec3::FromRGB(b.v.color1), t_int).ToRGB(); + } + + bool OutsideRange() const { + return v.screenpos.x == 0x7FFFFFFF; } ClipCoords clippos; - Vec2 texturecoords; - uint32_t color0; - uint32_t color1; - ScreenCoords screenpos; // TODO: Shouldn't store this ? - float fogdepth; + VertexData v; }; class VertexReader; class SoftwareDrawEngine; +class SoftwareVertexReader; class TransformUnit { public: @@ -125,6 +135,7 @@ public: static ScreenCoords DrawingToScreen(const DrawingCoords &coords, u16 z); void SubmitPrimitive(const void* vertices, const void* indices, GEPrimitiveType prim_type, int vertex_count, u32 vertex_type, int *bytesRead, SoftwareDrawEngine *drawEngine); + void SubmitImmVertex(const ClipVertexData &vert, SoftwareDrawEngine *drawEngine); bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices); @@ -138,17 +149,21 @@ public: SoftDirty GetDirty(); private: - VertexData ReadVertex(VertexReader &vreader, const TransformState &lstate, bool &outside_range_flag); - void SendTriangle(CullType cullType, const VertexData *verts, int provoking = 2); + ClipVertexData ReadVertex(VertexReader &vreader, const TransformState &state); + void SendTriangle(CullType cullType, const ClipVertexData *verts, int provoking = 2); u8 *decoded_ = nullptr; BinManager *binner_ = nullptr; // Normally max verts per prim is 3, but we temporarily need 4 to detect rectangles from strips. - VertexData data_[4]; + ClipVertexData data_[4]; // This is the index of the next vert in data (or higher, may need modulus.) int data_index_ = 0; GEPrimitiveType prev_prim_ = GE_PRIM_POINTS; + bool hasDraws_ = false; + bool isImmDraw_ = false; + + friend SoftwareVertexReader; }; class SoftwareDrawEngine : public DrawEngineCommon { @@ -158,13 +173,14 @@ public: void DispatchFlush() override; void DispatchSubmitPrim(const void *verts, const void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int cullMode, int *bytesRead) override; - void DispatchSubmitImm(const void *verts, const void *inds, GEPrimitiveType prim, int vertexCount, u32 vertTypeID, int cullMode, int *bytesRead) override; + void DispatchSubmitImm(GEPrimitiveType prim, TransformedVertex *buffer, int vertexCount, int cullMode, bool continuation) override; VertexDecoder *FindVertexDecoder(u32 vtype); TransformUnit transformUnit; #if PPSSPP_ARCH(32BIT) +#undef new void *operator new(size_t s) { return AllocateAlignedMemory(s, 16); } diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index e8ebd485d1..b0e88aa0f2 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -71,6 +71,7 @@ enum { DRAW_BINDING_TESS_STORAGE_BUF = 6, DRAW_BINDING_TESS_STORAGE_BUF_WU = 7, DRAW_BINDING_TESS_STORAGE_BUF_WV = 8, + DRAW_BINDING_INPUT_ATTACHMENT = 9, }; enum { @@ -88,13 +89,14 @@ DrawEngineVulkan::DrawEngineVulkan(Draw::DrawContext *draw) decIndex = (u16 *)AllocateMemoryPages(DECODED_INDEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); indexGen.Setup(decIndex); - - InitDeviceObjects(); } void DrawEngineVulkan::InitDeviceObjects() { // All resources we need for PSP drawing. Usually only bindings 0 and 2-4 are populated. - VkDescriptorSetLayoutBinding bindings[9]{}; + + // TODO: Make things more flexible, so we at least have specialized layouts for input attachments and tess. + // Note that it becomes a support matrix.. + VkDescriptorSetLayoutBinding bindings[10]{}; bindings[0].descriptorCount = 1; bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; @@ -110,6 +112,8 @@ void DrawEngineVulkan::InitDeviceObjects() { bindings[3].descriptorCount = 1; bindings[3].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; bindings[3].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + if (gstate_c.Supports(GPU_SUPPORTS_GS_CULLING)) + bindings[3].stageFlags |= VK_SHADER_STAGE_GEOMETRY_BIT; bindings[3].binding = DRAW_BINDING_DYNUBO_BASE; bindings[4].descriptorCount = 1; bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; @@ -132,6 +136,10 @@ void DrawEngineVulkan::InitDeviceObjects() { bindings[8].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; bindings[8].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; bindings[8].binding = DRAW_BINDING_TESS_STORAGE_BUF_WV; + bindings[9].descriptorCount = 1; + bindings[9].descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + bindings[9].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + bindings[9].binding = DRAW_BINDING_INPUT_ATTACHMENT; VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT); VkDevice device = vulkan->GetDevice(); @@ -145,13 +153,15 @@ void DrawEngineVulkan::InitDeviceObjects() { static constexpr int DEFAULT_DESC_POOL_SIZE = 512; std::vector dpTypes; - dpTypes.resize(3); + dpTypes.resize(4); dpTypes[0].descriptorCount = DEFAULT_DESC_POOL_SIZE * 3; dpTypes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; dpTypes[1].descriptorCount = DEFAULT_DESC_POOL_SIZE * 3; // Don't use these for tess anymore, need max three per set. dpTypes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; dpTypes[2].descriptorCount = DEFAULT_DESC_POOL_SIZE * 3; // TODO: Use a separate layout when no spline stuff is needed to reduce the need for these. dpTypes[2].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + dpTypes[3].descriptorCount = DEFAULT_DESC_POOL_SIZE; // TODO: Use a separate layout when no spline stuff is needed to reduce the need for these. + dpTypes[3].type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; VkDescriptorPoolCreateInfo dp{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; // Don't want to mess around with individually freeing these. @@ -371,7 +381,7 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView _dbg_assert_(light != VK_NULL_HANDLE); _dbg_assert_(bone != VK_NULL_HANDLE); - DescriptorSetKey key; + DescriptorSetKey key{}; key.imageView_ = imageView; key.sampler_ = sampler; key.secondaryImageView_ = boundSecondary_; @@ -379,6 +389,7 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView key.base_ = base; key.light_ = light; key.bone_ = bone; + key.secondaryIsInputAttachment = boundSecondaryIsInputAttachment_; FrameData &frame = GetCurFrame(); // See if we already have this descriptor set cached. @@ -417,15 +428,15 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView } if (boundSecondary_) { - tex[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + tex[1].imageLayout = key.secondaryIsInputAttachment ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; tex[1].imageView = boundSecondary_; tex[1].sampler = samplerSecondaryNearest_; writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[n].pNext = nullptr; - writes[n].dstBinding = DRAW_BINDING_2ND_TEXTURE; + writes[n].dstBinding = key.secondaryIsInputAttachment ? DRAW_BINDING_INPUT_ATTACHMENT : DRAW_BINDING_2ND_TEXTURE; writes[n].pImageInfo = &tex[1]; writes[n].descriptorCount = 1; - writes[n].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writes[n].descriptorType = key.secondaryIsInputAttachment ? VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT : VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writes[n].dstSet = desc; n++; } @@ -558,7 +569,7 @@ void DrawEngineVulkan::DoFlush() { textureCache_->SetTexture(); gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); textureNeedsApply = true; - } else if (gstate.getTextureAddress(0) == ((gstate.getFrameBufRawAddress() | 0x04000000) & 0x3FFFFFFF)) { + } else if (gstate.getTextureAddress(0) == (gstate.getFrameBufRawAddress() | 0x04000000)) { // This catches the case of clearing a texture. gstate_c.Dirty(DIRTY_TEXTURE_IMAGE); } @@ -570,6 +581,7 @@ void DrawEngineVulkan::DoFlush() { VulkanVertexShader *vshader = nullptr; VulkanFragmentShader *fshader = nullptr; + VulkanGeometryShader *gshader = nullptr; uint32_t ibOffset; uint32_t vbOffset; @@ -759,19 +771,19 @@ void DrawEngineVulkan::DoFlush() { sampler = nullSampler_; } - if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { + if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE) || prim != lastPrim_) { if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } - shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, true, useHWTessellation_, decOptions_.expandAllWeightsToFloat); // usehwtransform + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, &gshader, pipelineState_, true, useHWTessellation_, decOptions_.expandAllWeightsToFloat); // usehwtransform if (!vshader) { // We're screwed. return; } _dbg_assert_msg_(vshader->UseHWTransform(), "Bad vshader"); - VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, true, 0); + VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, gshader, true, 0); if (!pipeline || !pipeline->pipeline) { // Already logged, let's bail out. return; @@ -788,7 +800,7 @@ void DrawEngineVulkan::DoFlush() { lastRenderStepId_ = curRenderStepId; } - renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags, pipelineLayout_); + renderManager->BindPipeline(pipeline->pipeline, pipeline->pipelineFlags, pipelineLayout_); if (pipeline != lastPipeline_) { if (lastPipeline_ && !(lastPipeline_->UsesBlendConstant() && pipeline->UsesBlendConstant())) { gstate_c.Dirty(DIRTY_BLEND_STATE); @@ -890,13 +902,13 @@ void DrawEngineVulkan::DoFlush() { if (sampler == VK_NULL_HANDLE) sampler = nullSampler_; } - if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { + if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE) || prim != lastPrim_) { if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } - shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, &gshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform _dbg_assert_msg_(!vshader->UseHWTransform(), "Bad vshader"); - VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, false, 0); + VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, gshader, false, 0); if (!pipeline || !pipeline->pipeline) { // Already logged, let's bail out. decodedVerts_ = 0; @@ -916,7 +928,7 @@ void DrawEngineVulkan::DoFlush() { lastRenderStepId_ = curRenderStepId; } - renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags, pipelineLayout_); + renderManager->BindPipeline(pipeline->pipeline, pipeline->pipelineFlags, pipelineLayout_); if (pipeline != lastPipeline_) { if (lastPipeline_ && !lastPipeline_->UsesBlendConstant() && pipeline->UsesBlendConstant()) { gstate_c.Dirty(DIRTY_BLEND_STATE); diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 531e05c4ed..ebf50e33b5 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -81,8 +81,8 @@ public: VAI_UNRELIABLE, // never cache }; - uint64_t hash; - u32 minihash; + uint64_t hash = 0; + u32 minihash = 0; // These will probably always be the same, but whatever. VkBuffer vb = VK_NULL_HANDLE; @@ -127,6 +127,9 @@ public: DrawEngineVulkan(Draw::DrawContext *draw); virtual ~DrawEngineVulkan(); + // We reference feature flags, so this is called after construction. + void InitDeviceObjects(); + void SetShaderManager(ShaderManagerVulkan *shaderManager) { shaderManager_ = shaderManager; } @@ -196,7 +199,6 @@ private: void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState); void BindShaderBlendTex(); - void InitDeviceObjects(); void DestroyDeviceObjects(); void DecodeVertsToPushBuffer(VulkanPushBuffer *push, uint32_t *bindOffset, VkBuffer *vkbuf); @@ -217,6 +219,8 @@ private: // Secondary texture for shader blending VkImageView boundSecondary_ = VK_NULL_HANDLE; + bool boundSecondaryIsInputAttachment_ = false; + // CLUT texture for shader depal VkImageView boundDepal_ = VK_NULL_HANDLE; bool boundDepalSmoothed_ = false; @@ -234,6 +238,7 @@ private: VkSampler sampler_; VkBuffer base_, light_, bone_; // All three UBO slots will be set to this. This will usually be identical // for all draws in a frame, except when the buffer has to grow. + bool secondaryIsInputAttachment; }; // We alternate between these. @@ -281,7 +286,7 @@ private: VulkanDynamicState dynState_{}; int tessOffset_ = 0; - bool fboTexNeedsBind_ = false; + FBOTexState fboTexBindState_ = FBO_TEX_NONE; // Hardware tessellation TessellationDataTransferVulkan *tessDataTransferVulkan; diff --git a/GPU/Vulkan/FramebufferManagerVulkan.h b/GPU/Vulkan/FramebufferManagerVulkan.h index 0f5d7c4f53..d3370fafb7 100644 --- a/GPU/Vulkan/FramebufferManagerVulkan.h +++ b/GPU/Vulkan/FramebufferManagerVulkan.h @@ -33,7 +33,7 @@ class VulkanPushBuffer; class FramebufferManagerVulkan : public FramebufferManagerCommon { public: - FramebufferManagerVulkan(Draw::DrawContext *draw); + explicit FramebufferManagerVulkan(Draw::DrawContext *draw); ~FramebufferManagerVulkan(); // If within a render pass, this will just issue a regular clear. If beginning a new render pass, diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index f96d282fc7..f285af82d0 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -52,7 +52,8 @@ GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw) : GPUCommon(gfxCtx, draw), drawEngine_(draw) { - CheckGPUFeatures(); + gstate_c.featureFlags = CheckGPUFeatures(); + drawEngine_.InitDeviceObjects(); VulkanContext *vulkan = (VulkanContext *)gfxCtx->GetAPIContext(); @@ -182,8 +183,8 @@ GPU_Vulkan::~GPU_Vulkan() { delete framebufferManagerVulkan_; } -void GPU_Vulkan::CheckGPUFeatures() { - uint32_t features = 0; +u32 GPU_Vulkan::CheckGPUFeatures() const { + uint32_t features = GPUCommon::CheckGPUFeatures(); VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT); switch (vulkan->GetPhysicalDeviceProperties().properties.vendorID) { @@ -222,44 +223,25 @@ void GPU_Vulkan::CheckGPUFeatures() { // Mandatory features on Vulkan, which may be checked in "centralized" code features |= GPU_SUPPORTS_TEXTURE_LOD_CONTROL; - features |= GPU_SUPPORTS_BLEND_MINMAX; - features |= GPU_SUPPORTS_TEXTURE_NPOT; features |= GPU_SUPPORTS_INSTANCE_RENDERING; features |= GPU_SUPPORTS_VERTEX_TEXTURE_FETCH; features |= GPU_SUPPORTS_TEXTURE_FLOAT; - features |= GPU_SUPPORTS_DEPTH_TEXTURE; auto &enabledFeatures = vulkan->GetDeviceFeatures().enabled; if (enabledFeatures.depthClamp) { features |= GPU_SUPPORTS_DEPTH_CLAMP; } - if (enabledFeatures.shaderClipDistance) { - features |= GPU_SUPPORTS_CLIP_DISTANCE; - } - if (enabledFeatures.shaderCullDistance) { - // Must support at least 8 if feature supported, so we're fine. - features |= GPU_SUPPORTS_CULL_DISTANCE; - } - if (!draw_->GetBugs().Has(Draw::Bugs::BROKEN_NAN_IN_CONDITIONAL)) { - // Ignore the compat setting if clip and cull are both enabled. - // When supported, we can do the depth side of range culling more correctly. - const bool supported = draw_->GetDeviceCaps().clipDistanceSupported && draw_->GetDeviceCaps().cullDistanceSupported; - const bool disabled = PSP_CoreParameter().compat.flags().DisableRangeCulling; - if (supported || !disabled) { - features |= GPU_SUPPORTS_VS_RANGE_CULLING; + + // Fall back to geometry shader culling if we can't do vertex range culling. + if (enabledFeatures.geometryShader) { + const bool useGeometry = g_Config.bUseGeometryShader && !draw_->GetBugs().Has(Draw::Bugs::GEOMETRY_SHADERS_SLOW); + const bool vertexSupported = draw_->GetDeviceCaps().clipDistanceSupported && draw_->GetDeviceCaps().cullDistanceSupported; + if (useGeometry && (!vertexSupported || (features & GPU_SUPPORTS_VS_RANGE_CULLING) == 0)) { + // Switch to culling via the geometry shader if not fully supported in vertex. + features |= GPU_SUPPORTS_GS_CULLING; + features &= ~GPU_SUPPORTS_VS_RANGE_CULLING; } } - if (enabledFeatures.dualSrcBlend) { - if (!g_Config.bVendorBugChecksEnabled || !draw_->GetBugs().Has(Draw::Bugs::DUAL_SOURCE_BLENDING_BROKEN)) { - features |= GPU_SUPPORTS_DUALSOURCE_BLEND; - } - } - if (draw_->GetDeviceCaps().logicOpSupported) { - features |= GPU_SUPPORTS_LOGIC_OP; - } - if (draw_->GetDeviceCaps().anisoSupported) { - features |= GPU_SUPPORTS_ANISOTROPY; - } // These are VULKAN_4444_FORMAT and friends. uint32_t fmt4444 = draw_->GetDataFormatSupport(Draw::DataFormat::B4G4R4A4_UNORM_PACK16); @@ -275,10 +257,6 @@ void GPU_Vulkan::CheckGPUFeatures() { INFO_LOG(G3D, "Deficient texture format support: 4444: %d 1555: %d 565: %d", fmt4444, fmt1555, fmt565); } - if (PSP_CoreParameter().compat.flags().ClearToRAM) { - features |= GPU_USE_CLEAR_RAM_HACK; - } - if (!g_Config.bHighQualityDepth && (features & GPU_SUPPORTS_ACCURATE_DEPTH) != 0) { features |= GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT; } @@ -290,7 +268,7 @@ void GPU_Vulkan::CheckGPUFeatures() { features |= GPU_ROUND_DEPTH_TO_16BIT; } - gstate_c.featureFlags = features; + return features; } void GPU_Vulkan::BeginHostFrame() { @@ -298,7 +276,7 @@ void GPU_Vulkan::BeginHostFrame() { UpdateCmdInfo(); if (resized_) { - CheckGPUFeatures(); + gstate_c.featureFlags = CheckGPUFeatures(); // In case the GPU changed. BuildReportingInfo(); framebufferManager_->Resized(); @@ -537,7 +515,7 @@ void GPU_Vulkan::DeviceRestore() { GPUCommon::DeviceRestore(); InitDeviceObjects(); - CheckGPUFeatures(); + gstate_c.featureFlags = CheckGPUFeatures(); BuildReportingInfo(); UpdateCmdInfo(); @@ -600,7 +578,7 @@ std::vector GPU_Vulkan::DebugGetShaderIDs(DebugShaderType type) { return pipelineManager_->DebugGetObjectIDs(type); } else if (type == SHADER_TYPE_TEXTURE) { return textureCache_->GetTextureShaderCache()->DebugGetShaderIDs(type); - } else if (type == SHADER_TYPE_VERTEX || type == SHADER_TYPE_FRAGMENT) { + } else if (type == SHADER_TYPE_VERTEX || type == SHADER_TYPE_FRAGMENT || type == SHADER_TYPE_GEOMETRY) { return shaderManagerVulkan_->DebugGetShaderIDs(type); } else if (type == SHADER_TYPE_SAMPLER) { return textureCacheVulkan_->DebugGetSamplerIDs(); @@ -618,7 +596,7 @@ std::string GPU_Vulkan::DebugGetShaderString(std::string id, DebugShaderType typ return textureCache_->GetTextureShaderCache()->DebugGetShaderString(id, type, stringType); } else if (type == SHADER_TYPE_SAMPLER) { return textureCacheVulkan_->DebugGetSamplerString(id, stringType); - } else if (type == SHADER_TYPE_VERTEX || type == SHADER_TYPE_FRAGMENT) { + } else if (type == SHADER_TYPE_VERTEX || type == SHADER_TYPE_FRAGMENT || type == SHADER_TYPE_GEOMETRY) { return shaderManagerVulkan_->DebugGetShaderString(id, type, stringType); } else { return std::string(); diff --git a/GPU/Vulkan/GPU_Vulkan.h b/GPU/Vulkan/GPU_Vulkan.h index 3c13d57621..3fbd329aae 100644 --- a/GPU/Vulkan/GPU_Vulkan.h +++ b/GPU/Vulkan/GPU_Vulkan.h @@ -38,7 +38,7 @@ public: ~GPU_Vulkan(); // This gets called on startup and when we get back from settings. - void CheckGPUFeatures() override; + u32 CheckGPUFeatures() const override; bool IsReady() override; void CancelReady() override; diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index a88fe235a5..3d7745b452 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -170,8 +170,8 @@ static std::string CutFromMain(std::string str) { } static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, VkPipelineCache pipelineCache, - VkPipelineLayout layout, const VulkanPipelineRasterStateKey &key, - const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantBitmask) { + VkPipelineLayout layout, PipelineFlags pipelineFlags, const VulkanPipelineRasterStateKey &key, + const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, VulkanGeometryShader *gs, bool useHwTransform, u32 variantBitmask) { VulkanPipeline *vulkanPipeline = new VulkanPipeline(); VKRGraphicsPipelineDesc *desc = &vulkanPipeline->desc; desc->pipelineCache = pipelineCache; @@ -221,7 +221,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, VkDynamicState *dynamicStates = &desc->dynamicStates[0]; int numDyn = 0; if (key.blendEnable && - (UsesBlendConstant(key.srcAlpha) || UsesBlendConstant(key.srcColor) || UsesBlendConstant(key.destAlpha) || UsesBlendConstant(key.destColor))) { + (UsesBlendConstant(key.srcAlpha) || UsesBlendConstant(key.srcColor) || UsesBlendConstant(key.destAlpha) || UsesBlendConstant(key.destColor))) { dynamicStates[numDyn++] = VK_DYNAMIC_STATE_BLEND_CONSTANTS; useBlendConstant = true; } @@ -232,12 +232,12 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK; dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE; } - + VkPipelineDynamicStateCreateInfo &ds = desc->ds; ds.flags = 0; ds.pDynamicStates = dynamicStates; ds.dynamicStateCount = numDyn; - + VkPipelineRasterizationStateCreateInfo &rs = desc->rs; rs.flags = 0; rs.depthBiasEnable = false; @@ -254,6 +254,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, desc->fragmentShader = fs->GetModule(); desc->vertexShader = vs->GetModule(); + desc->geometryShader = gs ? gs->GetModule() : nullptr; VkPipelineInputAssemblyStateCreateInfo &inputAssembly = desc->inputAssembly; inputAssembly.flags = 0; @@ -270,8 +271,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, } else { bool needsUV = vs->GetID().Bit(VS_BIT_DO_TEXTURE); bool needsColor1 = vs->GetID().Bit(VS_BIT_LMODE); - bool needsFog = vs->GetID().Bit(VS_BIT_ENABLE_FOG); - attributeCount = SetupVertexAttribsPretransformed(attrs, needsUV, needsColor1, needsFog); + attributeCount = SetupVertexAttribsPretransformed(attrs, needsUV, needsColor1, true); vertexStride = (int)sizeof(TransformedVertex); } @@ -296,21 +296,23 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, desc->pipelineLayout = layout; - VKRGraphicsPipeline *pipeline = renderManager->CreateGraphicsPipeline(desc, variantBitmask, "game"); + VKRGraphicsPipeline *pipeline = renderManager->CreateGraphicsPipeline(desc, pipelineFlags, variantBitmask, "game"); vulkanPipeline->pipeline = pipeline; - vulkanPipeline->flags = 0; - if (useBlendConstant) - vulkanPipeline->flags |= PIPELINE_FLAG_USES_BLEND_CONSTANT; - if (key.topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || key.topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP) - vulkanPipeline->flags |= PIPELINE_FLAG_USES_LINES; - if (dss.depthTestEnable || dss.stencilTestEnable) { - vulkanPipeline->flags |= PIPELINE_FLAG_USES_DEPTH_STENCIL; + if (useBlendConstant) { + pipelineFlags |= PipelineFlags::USES_BLEND_CONSTANT; } + if (gs) { + pipelineFlags |= PipelineFlags::USES_GEOMETRY_SHADER; + } + if (dss.depthTestEnable || dss.stencilTestEnable) { + pipelineFlags |= PipelineFlags::USES_DEPTH_STENCIL; + } + vulkanPipeline->pipelineFlags = pipelineFlags; return vulkanPipeline; } -VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantBitmask) { +VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, VulkanGeometryShader *gs, bool useHwTransform, u32 variantBitmask) { if (!pipelineCache_) { VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO }; VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_); @@ -323,15 +325,21 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager * key.useHWTransform = useHwTransform; key.vShader = vs->GetModule(); key.fShader = fs->GetModule(); + key.gShader = gs ? gs->GetModule() : VK_NULL_HANDLE; key.vtxFmtId = useHwTransform ? decFmt->id : 0; auto iter = pipelines_.Get(key); if (iter) return iter; + PipelineFlags pipelineFlags = (PipelineFlags)0; + if (fs->Flags() & FragmentShaderFlags::INPUT_ATTACHMENT) { + pipelineFlags |= PipelineFlags::USES_INPUT_ATTACHMENT; + } + VulkanPipeline *pipeline = CreateVulkanPipeline( - renderManager, pipelineCache_, layout, - rasterKey, decFmt, vs, fs, useHwTransform, variantBitmask); + renderManager, pipelineCache_, layout, pipelineFlags, + rasterKey, decFmt, vs, fs, gs, useHwTransform, variantBitmask); pipelines_.Insert(key, pipeline); // Don't return placeholder null pipelines. @@ -536,6 +544,7 @@ struct StoredVulkanPipelineKey { VulkanPipelineRasterStateKey raster; VShaderID vShaderID; FShaderID fShaderID; + GShaderID gShaderID; uint32_t vtxFmtId; uint32_t variants; bool useHWTransform; // TODO: Still needed? @@ -586,7 +595,13 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha return; VulkanVertexShader *vshader = shaderManager->GetVertexShaderFromModule(pkey.vShader->BlockUntilReady()); VulkanFragmentShader *fshader = shaderManager->GetFragmentShaderFromModule(pkey.fShader->BlockUntilReady()); - if (!vshader || !fshader) { + VulkanGeometryShader *gshader = nullptr; + if (pkey.gShader) { + gshader = shaderManager->GetGeometryShaderFromModule(pkey.gShader->BlockUntilReady()); + if (!gshader) + failed = true; + } + if (!vshader || !fshader || failed) { failed = true; return; } @@ -595,6 +610,7 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha key.useHWTransform = pkey.useHWTransform; key.fShaderID = fshader->GetID(); key.vShaderID = vshader->GetID(); + key.gShaderID = gshader ? gshader->GetID() : GShaderID(); key.variants = value->GetVariantsBitmask(); if (key.useHWTransform) { // NOTE: This is not a vtype, but a decoded vertex format. @@ -699,7 +715,8 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha } VulkanVertexShader *vs = shaderManager->GetVertexShaderFromID(key.vShaderID); VulkanFragmentShader *fs = shaderManager->GetFragmentShaderFromID(key.fShaderID); - if (!vs || !fs) { + VulkanGeometryShader *gs = shaderManager->GetGeometryShaderFromID(key.gShaderID); + if (!vs || !fs || (!gs && key.gShaderID.Bit(GS_BIT_ENABLED))) { failed = true; ERROR_LOG(G3D, "Failed to find vs or fs in of pipeline %d in cache", (int)i); continue; @@ -707,7 +724,7 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha DecVtxFormat fmt; fmt.InitializeFromID(key.vtxFmtId); - VulkanPipeline *pipeline = GetOrCreatePipeline(rm, layout, key.raster, key.useHWTransform ? &fmt : 0, vs, fs, key.useHWTransform, key.variants); + VulkanPipeline *pipeline = GetOrCreatePipeline(rm, layout, key.raster, key.useHWTransform ? &fmt : 0, vs, fs, gs, key.useHWTransform, key.variants); if (!pipeline) { pipelineCreateFailCount += 1; } diff --git a/GPU/Vulkan/PipelineManagerVulkan.h b/GPU/Vulkan/PipelineManagerVulkan.h index af32aa81bd..32e45c3faf 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.h +++ b/GPU/Vulkan/PipelineManagerVulkan.h @@ -38,6 +38,7 @@ struct VulkanPipelineKey { VKRRenderPass *renderPass; Promise *vShader; Promise *fShader; + Promise *gShader; uint32_t vtxFmtId; bool useHWTransform; @@ -55,11 +56,12 @@ struct VulkanPipelineKey { struct VulkanPipeline { VKRGraphicsPipeline *pipeline; VKRGraphicsPipelineDesc desc; - int flags; // PipelineFlags enum above. + PipelineFlags pipelineFlags; // PipelineFlags enum above. - bool UsesBlendConstant() const { return (flags & PIPELINE_FLAG_USES_BLEND_CONSTANT) != 0; } - bool UsesLines() const { return (flags & PIPELINE_FLAG_USES_LINES) != 0; } - bool UsesDepthStencil() const { return (flags & PIPELINE_FLAG_USES_DEPTH_STENCIL) != 0; } + bool UsesBlendConstant() const { return (pipelineFlags & PipelineFlags::USES_BLEND_CONSTANT) != 0; } + bool UsesDepthStencil() const { return (pipelineFlags & PipelineFlags::USES_DEPTH_STENCIL) != 0; } + bool UsesInputAttachment() const { return (pipelineFlags & PipelineFlags::USES_INPUT_ATTACHMENT) != 0; } + bool UsesGeometryShader() const { return (pipelineFlags & PipelineFlags::USES_GEOMETRY_SHADER) != 0; } u32 GetVariantsBitmask() const; }; @@ -67,6 +69,7 @@ struct VulkanPipeline { class VulkanContext; class VulkanVertexShader; class VulkanFragmentShader; +class VulkanGeometryShader; class ShaderManagerVulkan; class DrawEngineCommon; @@ -76,7 +79,7 @@ public: ~PipelineManagerVulkan(); // variantMask is only used when loading pipelines from cache. - VulkanPipeline *GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantMask); + VulkanPipeline *GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, VulkanGeometryShader *gs, bool useHwTransform, u32 variantMask); int GetNumPipelines() const { return (int)pipelines_.size(); } void Clear(); diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 132b4fba20..c7724da670 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -38,6 +38,7 @@ #include "GPU/ge_constants.h" #include "GPU/Common/FragmentShaderGenerator.h" #include "GPU/Common/VertexShaderGenerator.h" +#include "GPU/Common/GeometryShaderGenerator.h" #include "GPU/Vulkan/ShaderManagerVulkan.h" #include "GPU/Vulkan/DrawEngineVulkan.h" #include "GPU/Vulkan/FramebufferManagerVulkan.h" @@ -73,14 +74,22 @@ static Promise *CompileShaderModuleAsync(VulkanContext *vulkan, VkShaderModule shaderModule = VK_NULL_HANDLE; if (success) { - success = vulkan->CreateShaderModule(spirv, &shaderModule, stage == VK_SHADER_STAGE_VERTEX_BIT ? "game_vertex" : "game_fragment"); + const char *createTag = tag ? tag->c_str() : nullptr; + if (!createTag) { + switch (stage) { + case VK_SHADER_STAGE_VERTEX_BIT: createTag = "game_vertex"; break; + case VK_SHADER_STAGE_FRAGMENT_BIT: createTag = "game_fragment"; break; + case VK_SHADER_STAGE_GEOMETRY_BIT: createTag = "game_geometry"; break; + case VK_SHADER_STAGE_COMPUTE_BIT: createTag = "game_compute"; break; + } + } + + success = vulkan->CreateShaderModule(spirv, &shaderModule, createTag); #ifdef SHADERLOG OutputDebugStringA("OK"); #endif - if (tag) { - vulkan->SetDebugName(shaderModule, VK_OBJECT_TYPE_SHADER_MODULE, tag->c_str()); + if (tag) delete tag; - } } return shaderModule; @@ -128,7 +137,7 @@ std::string VulkanFragmentShader::GetShaderString(DebugShaderStringType type) co VulkanVertexShader::VulkanVertexShader(VulkanContext *vulkan, VShaderID id, const char *code, bool useHWTransform) : vulkan_(vulkan), useHWTransform_(useHWTransform), id_(id) { source_ = code; - module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_VERTEX_BIT, source_.c_str(), new std::string(VertexShaderDesc(id).c_str())); + module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_VERTEX_BIT, source_.c_str(), new std::string(VertexShaderDesc(id))); if (!module_) { failed_ = true; } else { @@ -155,9 +164,41 @@ std::string VulkanVertexShader::GetShaderString(DebugShaderStringType type) cons } } +VulkanGeometryShader::VulkanGeometryShader(VulkanContext *vulkan, GShaderID id, const char *code) + : vulkan_(vulkan), id_(id) { + source_ = code; + module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_GEOMETRY_BIT, source_.c_str(), new std::string(GeometryShaderDesc(id).c_str())); + if (!module_) { + failed_ = true; + } else { + VERBOSE_LOG(G3D, "Compiled geometry shader:\n%s\n", (const char *)code); + } +} + +VulkanGeometryShader::~VulkanGeometryShader() { + if (module_) { + VkShaderModule shaderModule = module_->BlockUntilReady(); + vulkan_->Delete().QueueDeleteShaderModule(shaderModule); + delete module_; + } +} + +std::string VulkanGeometryShader::GetShaderString(DebugShaderStringType type) const { + switch (type) { + case SHADER_STRING_SOURCE_CODE: + return source_; + case SHADER_STRING_SHORT_DESC: + return GeometryShaderDesc(id_); + default: + return "N/A"; + } +} + +static constexpr size_t CODE_BUFFER_SIZE = 32768; + ShaderManagerVulkan::ShaderManagerVulkan(Draw::DrawContext *draw) - : ShaderManagerCommon(draw), compat_(GLSL_VULKAN), fsCache_(16), vsCache_(16) { - codeBuffer_ = new char[16384]; + : ShaderManagerCommon(draw), compat_(GLSL_VULKAN), fsCache_(16), vsCache_(16), gsCache_(16) { + codeBuffer_ = new char[CODE_BUFFER_SIZE]; VulkanContext *vulkan = (VulkanContext *)draw->GetNativeObject(Draw::NativeObject::CONTEXT); uboAlignment_ = vulkan->GetPhysicalDeviceProperties().properties.limits.minUniformBufferOffsetAlignment; memset(&ub_base, 0, sizeof(ub_base)); @@ -191,30 +232,37 @@ void ShaderManagerVulkan::Clear() { vsCache_.Iterate([&](const VShaderID &key, VulkanVertexShader *shader) { delete shader; }); + gsCache_.Iterate([&](const GShaderID &key, VulkanGeometryShader *shader) { + delete shader; + }); fsCache_.Clear(); vsCache_.Clear(); + gsCache_.Clear(); lastFSID_.set_invalid(); lastVSID_.set_invalid(); - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); + lastGSID_.set_invalid(); + gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); } void ShaderManagerVulkan::ClearShaders() { Clear(); DirtyShader(); - gstate_c.Dirty(DIRTY_ALL_UNIFORMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); + gstate_c.Dirty(DIRTY_ALL_UNIFORMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); } void ShaderManagerVulkan::DirtyShader() { // Forget the last shader ID lastFSID_.set_invalid(); lastVSID_.set_invalid(); + lastGSID_.set_invalid(); DirtyLastShader(); } void ShaderManagerVulkan::DirtyLastShader() { lastVShader_ = nullptr; lastFShader_ = nullptr; - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); + lastGShader_ = nullptr; + gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); } uint64_t ShaderManagerVulkan::UpdateUniforms(bool useBufferedRendering) { @@ -231,7 +279,7 @@ uint64_t ShaderManagerVulkan::UpdateUniforms(bool useBufferedRendering) { return dirty; } -void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) { +void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, VulkanGeometryShader **gshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) { VShaderID VSID; if (gstate_c.IsDirty(DIRTY_VERTEXSHADER_STATE)) { gstate_c.Clean(DIRTY_VERTEXSHADER_STATE); @@ -248,15 +296,28 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader FSID = lastFSID_; } + GShaderID GSID; + if (gstate_c.IsDirty(DIRTY_GEOMETRYSHADER_STATE)) { + gstate_c.Clean(DIRTY_GEOMETRYSHADER_STATE); + ComputeGeometryShaderID(&GSID, draw_->GetBugs(), prim); + } else { + GSID = lastGSID_; + } + _dbg_assert_(FSID.Bit(FS_BIT_LMODE) == VSID.Bit(VS_BIT_LMODE)); _dbg_assert_(FSID.Bit(FS_BIT_DO_TEXTURE) == VSID.Bit(VS_BIT_DO_TEXTURE)); - _dbg_assert_(FSID.Bit(FS_BIT_ENABLE_FOG) == VSID.Bit(VS_BIT_ENABLE_FOG)); _dbg_assert_(FSID.Bit(FS_BIT_FLATSHADE) == VSID.Bit(VS_BIT_FLATSHADE)); + if (GSID.Bit(GS_BIT_ENABLED)) { + _dbg_assert_(GSID.Bit(GS_BIT_LMODE) == VSID.Bit(VS_BIT_LMODE)); + _dbg_assert_(GSID.Bit(GS_BIT_DO_TEXTURE) == VSID.Bit(VS_BIT_DO_TEXTURE)); + } + // Just update uniforms if this is the same shader as last time. - if (lastVShader_ != nullptr && lastFShader_ != nullptr && VSID == lastVSID_ && FSID == lastFSID_) { + if (lastVShader_ != nullptr && lastFShader_ != nullptr && VSID == lastVSID_ && FSID == lastFSID_ && GSID == lastGSID_) { *vshader = lastVShader_; *fshader = lastFShader_; + *gshader = lastGShader_; _dbg_assert_msg_((*vshader)->UseHWTransform() == useHWTransform, "Bad vshader was cached"); // Already all set, no need to look up in shader maps. return; @@ -271,31 +332,51 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader uint32_t attributeMask = 0; // Not used bool success = GenerateVertexShader(VSID, codeBuffer_, compat_, draw_->GetBugs(), &attributeMask, &uniformMask, &genErrorString); _assert_msg_(success, "VS gen error: %s", genErrorString.c_str()); + _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "VS length error: %d", (int)strlen(codeBuffer_)); vs = new VulkanVertexShader(vulkan, VSID, codeBuffer_, useHWTransform); vsCache_.Insert(VSID, vs); } - lastVSID_ = VSID; VulkanFragmentShader *fs = fsCache_.Get(FSID); if (!fs) { - // uint32_t vendorID = vulkan->GetPhysicalDeviceProperties().properties.vendorID; // Fragment shader not in cache. Let's compile it. std::string genErrorString; uint64_t uniformMask = 0; // Not used FragmentShaderFlags flags; bool success = GenerateFragmentShader(FSID, codeBuffer_, compat_, draw_->GetBugs(), &uniformMask, &flags, &genErrorString); _assert_msg_(success, "FS gen error: %s", genErrorString.c_str()); + _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "FS length error: %d", (int)strlen(codeBuffer_)); fs = new VulkanFragmentShader(vulkan, FSID, flags, codeBuffer_); fsCache_.Insert(FSID, fs); } + VulkanGeometryShader *gs; + if (GSID.Bit(GS_BIT_ENABLED)) { + gs = gsCache_.Get(GSID); + if (!gs) { + // Geometry shader not in cache. Let's compile it. + std::string genErrorString; + bool success = GenerateGeometryShader(GSID, codeBuffer_, compat_, draw_->GetBugs(), &genErrorString); + _assert_msg_(success, "GS gen error: %s", genErrorString.c_str()); + _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "GS length error: %d", (int)strlen(codeBuffer_)); + gs = new VulkanGeometryShader(vulkan, GSID, codeBuffer_); + gsCache_.Insert(GSID, gs); + } + } else { + gs = nullptr; + } + + lastVSID_ = VSID; lastFSID_ = FSID; + lastGSID_ = GSID; lastVShader_ = vs; lastFShader_ = fs; + lastGShader_ = gs; *vshader = vs; *fshader = fs; + *gshader = gs; _dbg_assert_msg_((*vshader)->UseHWTransform() == useHWTransform, "Bad vshader was computed"); } @@ -320,6 +401,15 @@ std::vector ShaderManagerVulkan::DebugGetShaderIDs(DebugShaderType }); break; } + case SHADER_TYPE_GEOMETRY: + { + gsCache_.Iterate([&](const GShaderID &id, VulkanGeometryShader *shader) { + std::string idstr; + id.ToString(&idstr); + ids.push_back(idstr); + }); + break; + } default: break; } @@ -335,12 +425,16 @@ std::string ShaderManagerVulkan::DebugGetShaderString(std::string id, DebugShade VulkanVertexShader *vs = vsCache_.Get(VShaderID(shaderId)); return vs ? vs->GetShaderString(stringType) : ""; } - case SHADER_TYPE_FRAGMENT: { VulkanFragmentShader *fs = fsCache_.Get(FShaderID(shaderId)); return fs ? fs->GetShaderString(stringType) : ""; } + case SHADER_TYPE_GEOMETRY: + { + VulkanGeometryShader *gs = gsCache_.Get(GShaderID(shaderId)); + return gs ? gs->GetShaderString(stringType) : ""; + } default: return "N/A"; } @@ -368,6 +462,17 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM return fs; } +VulkanGeometryShader *ShaderManagerVulkan::GetGeometryShaderFromModule(VkShaderModule module) { + VulkanGeometryShader *gs = nullptr; + gsCache_.Iterate([&](const GShaderID &id, VulkanGeometryShader *shader) { + Promise *p = shader->GetModule(); + VkShaderModule m = p->BlockUntilReady(); + if (m == module) + gs = shader; + }); + return gs; +} + // Shader cache. // // We simply store the IDs of the shaders used during gameplay. On next startup of @@ -377,7 +482,7 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM // instantaneous. #define CACHE_HEADER_MAGIC 0xff51f420 -#define CACHE_VERSION 26 +#define CACHE_VERSION 29 struct VulkanCacheHeader { uint32_t magic; uint32_t version; @@ -385,6 +490,7 @@ struct VulkanCacheHeader { uint32_t reserved; int numVertexShaders; int numFragmentShaders; + int numGeometryShaders; }; bool ShaderManagerVulkan::LoadCache(FILE *f) { @@ -411,6 +517,7 @@ bool ShaderManagerVulkan::LoadCache(FILE *f) { if (!GenerateVertexShader(id, codeBuffer_, compat_, draw_->GetBugs(), &attributeMask, &uniformMask, &genErrorString)) { return false; } + _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "VS length error: %d", (int)strlen(codeBuffer_)); VulkanVertexShader *vs = new VulkanVertexShader(vulkan, id, codeBuffer_, useHWTransform); vsCache_.Insert(id, vs); } @@ -428,10 +535,26 @@ bool ShaderManagerVulkan::LoadCache(FILE *f) { if (!GenerateFragmentShader(id, codeBuffer_, compat_, draw_->GetBugs(), &uniformMask, &flags, &genErrorString)) { return false; } + _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "FS length error: %d", (int)strlen(codeBuffer_)); VulkanFragmentShader *fs = new VulkanFragmentShader(vulkan, id, flags, codeBuffer_); fsCache_.Insert(id, fs); } + for (int i = 0; i < header.numGeometryShaders; i++) { + GShaderID id; + if (fread(&id, sizeof(id), 1, f) != 1) { + ERROR_LOG(G3D, "Vulkan shader cache truncated"); + break; + } + std::string genErrorString; + if (!GenerateGeometryShader(id, codeBuffer_, compat_, draw_->GetBugs(), &genErrorString)) { + return false; + } + _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "GS length error: %d", (int)strlen(codeBuffer_)); + VulkanGeometryShader *gs = new VulkanGeometryShader(vulkan, id, codeBuffer_); + gsCache_.Insert(id, gs); + } + NOTICE_LOG(G3D, "Loaded %d vertex and %d fragment shaders", header.numVertexShaders, header.numFragmentShaders); return true; } @@ -444,6 +567,7 @@ void ShaderManagerVulkan::SaveCache(FILE *f) { header.reserved = 0; header.numVertexShaders = (int)vsCache_.size(); header.numFragmentShaders = (int)fsCache_.size(); + header.numGeometryShaders = (int)gsCache_.size(); bool writeFailed = fwrite(&header, sizeof(header), 1, f) != 1; vsCache_.Iterate([&](const VShaderID &id, VulkanVertexShader *vs) { writeFailed = writeFailed || fwrite(&id, sizeof(id), 1, f) != 1; @@ -451,6 +575,9 @@ void ShaderManagerVulkan::SaveCache(FILE *f) { fsCache_.Iterate([&](const FShaderID &id, VulkanFragmentShader *fs) { writeFailed = writeFailed || fwrite(&id, sizeof(id), 1, f) != 1; }); + gsCache_.Iterate([&](const GShaderID &id, VulkanGeometryShader *gs) { + writeFailed = writeFailed || fwrite(&id, sizeof(id), 1, f) != 1; + }); if (writeFailed) { ERROR_LOG(G3D, "Failed to write Vulkan shader cache, disk full?"); } else { diff --git a/GPU/Vulkan/ShaderManagerVulkan.h b/GPU/Vulkan/ShaderManagerVulkan.h index 6593a366f4..4c89f91ef5 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.h +++ b/GPU/Vulkan/ShaderManagerVulkan.h @@ -83,7 +83,27 @@ protected: VShaderID id_; }; -class VulkanPushBuffer; +class VulkanGeometryShader { +public: + VulkanGeometryShader(VulkanContext *vulkan, GShaderID id, const char *code); + ~VulkanGeometryShader(); + + const std::string &source() const { return source_; } + + bool Failed() const { return failed_; } + + std::string GetShaderString(DebugShaderStringType type) const; + Promise *GetModule() const { return module_; } + const GShaderID &GetID() { return id_; } + +protected: + Promise *module_ = nullptr; + + VulkanContext *vulkan_; + std::string source_; + bool failed_ = false; + GShaderID id_; +}; class ShaderManagerVulkan : public ShaderManagerCommon { public: @@ -93,19 +113,22 @@ public: void DeviceLost(); void DeviceRestore(Draw::DrawContext *draw); - void GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat); + void GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, VulkanGeometryShader **gshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat); void ClearShaders(); void DirtyShader(); void DirtyLastShader() override; int GetNumVertexShaders() const { return (int)vsCache_.size(); } int GetNumFragmentShaders() const { return (int)fsCache_.size(); } + int GetNumGeometryShaders() const { return (int)gsCache_.size(); } // Used for saving/loading the cache. Don't need to be particularly fast. VulkanVertexShader *GetVertexShaderFromID(VShaderID id) { return vsCache_.Get(id); } VulkanFragmentShader *GetFragmentShaderFromID(FShaderID id) { return fsCache_.Get(id); } + VulkanGeometryShader *GetGeometryShaderFromID(GShaderID id) { return gsCache_.Get(id); } VulkanVertexShader *GetVertexShaderFromModule(VkShaderModule module); VulkanFragmentShader *GetFragmentShaderFromModule(VkShaderModule module); + VulkanGeometryShader *GetGeometryShaderFromModule(VkShaderModule module); std::vector DebugGetShaderIDs(DebugShaderType type); std::string DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType); @@ -143,6 +166,9 @@ private: typedef DenseHashMap VSCache; VSCache vsCache_; + typedef DenseHashMap GSCache; + GSCache gsCache_; + char *codeBuffer_; uint64_t uboAlignment_; @@ -153,7 +179,9 @@ private: VulkanFragmentShader *lastFShader_ = nullptr; VulkanVertexShader *lastVShader_ = nullptr; + VulkanGeometryShader *lastGShader_ = nullptr; FShaderID lastFSID_; VShaderID lastVSID_; + GShaderID lastGSID_; }; diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index 6457b99ad9..929482a88d 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -152,8 +152,8 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag GenericBlendState &blendState = pipelineState_.blendState; GenericLogicState &logicState = pipelineState_.logicState; - if (pipelineState_.FramebufferRead()) { - ApplyFramebufferRead(&fboTexNeedsBind_); + if (pipelineState_.FramebufferRead() && useBufferedRendering) { + ApplyFramebufferRead(&fboTexBindState_); // The shader takes over the responsibility for blending, so recompute. // We might still end up using blend to write something to alpha. ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState); @@ -364,15 +364,23 @@ void DrawEngineVulkan::BindShaderBlendTex() { // TODO: At this point, we know if the vertices are full alpha or not. // Set the nearest/linear here (since we correctly know if alpha/color tests are needed)? if (!gstate.isModeClear()) { - if (fboTexNeedsBind_) { + if (fboTexBindState_ == FBO_TEX_COPY_BIND_TEX) { bool bindResult = framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); _dbg_assert_(bindResult); boundSecondary_ = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE1_IMAGEVIEW); + boundSecondaryIsInputAttachment_ = false; fboTexBound_ = true; - fboTexNeedsBind_ = false; + fboTexBindState_ = FBO_TEX_NONE; // Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects. dirtyRequiresRecheck_ |= DIRTY_BLEND_STATE; + } else if (fboTexBindState_ == FBO_TEX_READ_FRAMEBUFFER) { + draw_->BindCurrentFramebufferForColorInput(); + boundSecondary_ = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_FRAMEBUFFER_COLOR_IMAGEVIEW); + boundSecondaryIsInputAttachment_ = true; + fboTexBindState_ = FBO_TEX_NONE; + } else { + boundSecondary_ = VK_NULL_HANDLE; } } } diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index a67210cfc7..75b067cc40 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -107,6 +107,15 @@ void main() { )"; +static int VkFormatBytesPerPixel(VkFormat format) { + switch (format) { + case VULKAN_8888_FORMAT: return 4; + case VULKAN_CLUT8_FORMAT: return 1; + default: break; + } + return 2; +} + SamplerCache::~SamplerCache() { DeviceLost(); } @@ -448,6 +457,8 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { if (plan.scaleFactor > 1) { // Whether hardware or software scaling, this is the dest format. dstFmt = VULKAN_8888_FORMAT; + } else if (plan.decodeToClut8) { + dstFmt = VULKAN_CLUT8_FORMAT; } // We don't generate mipmaps for 512x512 textures because they're almost exclusively used for menu backgrounds @@ -479,7 +490,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { case VULKAN_4444_FORMAT: mapping = &VULKAN_4444_SWIZZLE; break; case VULKAN_1555_FORMAT: mapping = &VULKAN_1555_SWIZZLE; break; case VULKAN_565_FORMAT: mapping = &VULKAN_565_SWIZZLE; break; - default: mapping = &VULKAN_8888_SWIZZLE; break; + default: mapping = &VULKAN_8888_SWIZZLE; break; // no swizzle } VkImageLayout imageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; @@ -562,7 +573,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { int mipHeight; plan.GetMipSize(i, &mipWidth, &mipHeight); - int bpp = actualFmt == VULKAN_8888_FORMAT ? 4 : 2; // output bpp + int bpp = VkFormatBytesPerPixel(actualFmt); int stride = (mipWidth * bpp + 15) & ~15; // output stride int uploadSize = stride * mipHeight; @@ -602,7 +613,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { loadLevel(uploadSize, i, stride, plan.scaleFactor); entry->vkTex->UploadMip(cmdInit, 0, mipWidth, mipHeight, i, texBuf, bufferOffset, stride / bpp); } else if (computeUpload) { - int srcBpp = dstFmt == VULKAN_8888_FORMAT ? 4 : 2; + int srcBpp = VkFormatBytesPerPixel(dstFmt); int srcStride = mipUnscaledWidth * srcBpp; int srcSize = srcStride * mipUnscaledHeight; loadLevel(srcSize, i == 0 ? plan.baseLevelSrc : i, srcStride, 1); @@ -698,20 +709,6 @@ VkFormat TextureCacheVulkan::GetDestFormat(GETextureFormat format, GEPaletteForm } } -CheckAlphaResult TextureCacheVulkan::CheckAlpha(const u32 *pixelData, VkFormat dstFmt, int w) { - switch (dstFmt) { - case VULKAN_4444_FORMAT: - return CheckAlpha16((const u16 *)pixelData, w, 0xF000); - case VULKAN_1555_FORMAT: - return CheckAlpha16((const u16 *)pixelData, w, 0x8000); - case VULKAN_565_FORMAT: - // Never has any alpha. - return CHECKALPHA_FULL; - default: - return CheckAlpha32(pixelData, w, 0xFF000000); - } -} - void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePtr, int rowPitch, int level, int scaleFactor, VkFormat dstFmt) { int w = gstate.getTextureWidth(level); int h = gstate.getTextureHeight(level); @@ -723,7 +720,7 @@ void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePt _assert_msg_(texaddr != 0, "Can't load a texture from address null") int bufw = GetTextureBufw(level, texaddr, tfmt); - int bpp = dstFmt == VULKAN_8888_FORMAT ? 4 : 2; + int bpp = VkFormatBytesPerPixel(dstFmt); u32 *pixelData; int decPitch; @@ -732,6 +729,9 @@ void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePt if (!gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || scaleFactor > 1 || dstFmt == VULKAN_8888_FORMAT) { texDecFlags |= TexDecodeFlags::EXPAND32; } + if (entry.status & TexCacheEntry::STATUS_CLUT_GPU) { + texDecFlags |= TexDecodeFlags::TO_CLUT8; + } if (scaleFactor > 1) { tmpTexBufRearrange_.resize(std::max(bufw, w) * h); @@ -857,3 +857,8 @@ std::vector TextureCacheVulkan::DebugGetSamplerIDs() const { std::string TextureCacheVulkan::DebugGetSamplerString(std::string id, DebugShaderStringType stringType) { return samplerCache_.DebugGetSamplerString(id, stringType); } + +void *TextureCacheVulkan::GetNativeTextureView(const TexCacheEntry *entry) { + VkImageView view = entry->vkTex->GetImageView(); + return (void *)view; +} diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index 64b9da9eab..5deeb30ea9 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -97,11 +97,11 @@ protected: void BindAsClutTexture(Draw::Texture *tex, bool smooth) override; void ApplySamplingParams(const SamplerCacheKey &key) override; void BoundFramebufferTexture() override; + void *GetNativeTextureView(const TexCacheEntry *entry) override; private: void LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePtr, int rowPitch, int level, int scaleFactor, VkFormat dstFmt); VkFormat GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const; - static CheckAlphaResult CheckAlpha(const u32 *pixelData, VkFormat dstFmt, int w); void UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBase, bool clutIndexIsSimple) override; void BuildTexture(TexCacheEntry *const entry) override; diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index 2753fd79eb..b31fdb75f9 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -36,6 +36,7 @@ extern const VkComponentMapping VULKAN_8888_SWIZZLE; #define VULKAN_1555_FORMAT VK_FORMAT_A1R5G5B5_UNORM_PACK16 #define VULKAN_565_FORMAT VK_FORMAT_B5G6R5_UNORM_PACK16 // TODO: Does not actually have mandatory support, though R5G6B5 does! See #14602 #define VULKAN_8888_FORMAT VK_FORMAT_R8G8B8A8_UNORM +#define VULKAN_CLUT8_FORMAT VK_FORMAT_R8_UNORM // Manager for compute shaders that upload things (and those have two bindings: a storage buffer to read from and an image to write to). class VulkanComputeShaderManager { diff --git a/GPU/ge_constants.h b/GPU/ge_constants.h index 91ef35b3ec..cdb7cbce58 100644 --- a/GPU/ge_constants.h +++ b/GPU/ge_constants.h @@ -278,20 +278,6 @@ enum GECommand { GE_CMD_NOP_FF = 0xFF, }; -enum GEBufferFormat : uint8_t { - GE_FORMAT_565 = 0, - GE_FORMAT_5551 = 1, - GE_FORMAT_4444 = 2, - GE_FORMAT_8888 = 3, - GE_FORMAT_DEPTH16 = 4, // Virtual format, just used to pass into Depal - GE_FORMAT_INVALID = 0xFF, -}; - -const char *GeBufferFormatToString(GEBufferFormat fmt); -inline bool IsGeBufferFormat16BitColor(GEBufferFormat fmt) { - return (int)fmt < 3; -} - #define GE_VTYPE_TRANSFORM (0<<23) #define GE_VTYPE_THROUGH (1<<23) #define GE_VTYPE_THROUGH_MASK (1<<23) @@ -413,8 +399,10 @@ enum GELightComputation GE_LIGHTCOMP_ONLYPOWDIFFUSE = 2, }; -enum GETextureFormat : uint8_t -{ +// TODO: Consolidate the below three to one enum? The first four are the same in all, +// and we sometimes need to interpret back and forth between them. + +enum GETextureFormat : uint8_t { GE_TFMT_5650 = 0, GE_TFMT_5551 = 1, GE_TFMT_4444 = 2, @@ -428,6 +416,32 @@ enum GETextureFormat : uint8_t GE_TFMT_DXT5 = 10, }; +enum GEBufferFormat : uint8_t { + GE_FORMAT_565 = 0, + GE_FORMAT_5551 = 1, + GE_FORMAT_4444 = 2, + GE_FORMAT_8888 = 3, + GE_FORMAT_DEPTH16 = 4, // Virtual format, just used to pass into Depal + GE_FORMAT_CLUT8 = 5, // Virtual format, for pre-decoded static textures with dynamic CLUT + GE_FORMAT_INVALID = 0xFF, +}; + +enum GEPaletteFormat : uint8_t { + GE_CMODE_16BIT_BGR5650, + GE_CMODE_16BIT_ABGR5551, + GE_CMODE_16BIT_ABGR4444, + GE_CMODE_32BIT_ABGR8888, +}; + +const char *GEPaletteFormatToString(GEPaletteFormat pfmt); +const char *GeTextureFormatToString(GETextureFormat tfmt); +const char *GeTextureFormatToString(GETextureFormat tfmt, GEPaletteFormat pfmt); + +const char *GeBufferFormatToString(GEBufferFormat fmt); +inline bool IsGeBufferFormat16BitColor(GEBufferFormat fmt) { + return (int)fmt < 3; +} + inline bool IsClutFormat(GETextureFormat tfmt) { return tfmt == GE_TFMT_CLUT4 || tfmt == GE_TFMT_CLUT8 || tfmt == GE_TFMT_CLUT16 || tfmt == GE_TFMT_CLUT32; } @@ -445,7 +459,12 @@ inline bool IsTextureFormat16Bit(GETextureFormat tfmt) { } inline int BufferFormatBytesPerPixel(GEBufferFormat format) { - return format == GE_FORMAT_8888 ? 4 : 2; // applies to depth as well. + switch (format) { + case GE_FORMAT_8888: return 4; // applies to depth as well. + case GE_FORMAT_CLUT8: return 1; + default: + return 2; + } } inline bool TextureFormatMatchesBufferFormat(GETextureFormat fmt, GEBufferFormat bfmt) { @@ -613,15 +632,3 @@ inline GEPrimitiveType PatchPrimToPrim(GEPatchPrimType type) { case GE_PATCHPRIM_UNKNOWN: default: return GE_PRIM_POINTS; // Treated as points. } } - -enum GEPaletteFormat -{ - GE_CMODE_16BIT_BGR5650, - GE_CMODE_16BIT_ABGR5551, - GE_CMODE_16BIT_ABGR4444, - GE_CMODE_32BIT_ABGR8888, -}; - -const char *GEPaletteFormatToString(GEPaletteFormat pfmt); -const char *GeTextureFormatToString(GETextureFormat tfmt); -const char *GeTextureFormatToString(GETextureFormat tfmt, GEPaletteFormat pfmt); diff --git a/SDL/SDLVulkanGraphicsContext.cpp b/SDL/SDLVulkanGraphicsContext.cpp index 433f35b03f..b7eb932b11 100644 --- a/SDL/SDLVulkanGraphicsContext.cpp +++ b/SDL/SDLVulkanGraphicsContext.cpp @@ -116,7 +116,7 @@ bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, return false; } - draw_ = Draw::T3DCreateVulkanContext(vulkan_, false); + draw_ = Draw::T3DCreateVulkanContext(vulkan_); SetGPUBackend(GPUBackend::VULKAN); bool success = draw_->CreatePresets(); _assert_(success); diff --git a/UI/ChatScreen.cpp b/UI/ChatScreen.cpp index 01421ee733..3a12597d11 100644 --- a/UI/ChatScreen.cpp +++ b/UI/ChatScreen.cpp @@ -138,7 +138,7 @@ void ChatMenu::UpdateChat() { uint32_t textcolor = 0xFFFFFF; uint32_t infocolor = 0xFDD835; - std::string name = g_Config.sNickName.c_str(); + std::string name = g_Config.sNickName; std::string displayname = i.substr(0, i.find(':')); std::string chattext = i.substr(displayname.length()); diff --git a/UI/ComboKeyMappingScreen.cpp b/UI/ComboKeyMappingScreen.cpp index 3a4bc277d1..d4c8a814b6 100644 --- a/UI/ComboKeyMappingScreen.cpp +++ b/UI/ComboKeyMappingScreen.cpp @@ -56,6 +56,8 @@ public: parent->Add(scroll); } + const char *tag() const override { return "ButtonShape"; } + private: int *setting_; }; @@ -84,6 +86,8 @@ public: parent->Add(scroll); } + const char *tag() const override { return "ButtonIcon"; } + private: int *setting_; }; diff --git a/UI/ComboKeyMappingScreen.h b/UI/ComboKeyMappingScreen.h index 6eb271211d..c2ed6396d8 100644 --- a/UI/ComboKeyMappingScreen.h +++ b/UI/ComboKeyMappingScreen.h @@ -28,6 +28,8 @@ class ComboKeyScreen : public UIDialogScreenWithBackground { public: ComboKeyScreen(int id): id_(id) {} + const char *tag() const override { return "ComboKey"; } + void CreateViews() override; void onFinish(DialogResult result) override; diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 7acdd36f05..f6255ad4e3 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -313,7 +313,7 @@ UI::EventReturn ControlMappingScreen::OnVisualizeMapping(UI::EventParams ¶ms } void ControlMappingScreen::dialogFinished(const Screen *dialog, DialogResult result) { - if (result == DR_OK && dialog->tag() == "listpopup") { + if (result == DR_OK && std::string(dialog->tag()) == "listpopup") { ListPopupScreen *popup = (ListPopupScreen *)dialog; KeyMap::AutoConfForPad(popup->GetChoiceString()); } diff --git a/UI/ControlMappingScreen.h b/UI/ControlMappingScreen.h index add89bfdfa..29f1360fbe 100644 --- a/UI/ControlMappingScreen.h +++ b/UI/ControlMappingScreen.h @@ -35,7 +35,7 @@ class SingleControlMapper; class ControlMappingScreen : public UIDialogScreenWithBackground { public: ControlMappingScreen() {} - std::string tag() const override { return "control mapping"; } + const char *tag() const override { return "ControlMapping"; } protected: void CreateViews() override; @@ -47,7 +47,7 @@ private: UI::EventReturn OnAutoConfigure(UI::EventParams ¶ms); UI::EventReturn OnVisualizeMapping(UI::EventParams ¶ms); - virtual void dialogFinished(const Screen *dialog, DialogResult result) override; + void dialogFinished(const Screen *dialog, DialogResult result) override; UI::ScrollView *rightScroll_; std::vector mappers_; @@ -61,17 +61,19 @@ public: pspBtn_ = btn; } - virtual bool key(const KeyInput &key) override; - virtual bool axis(const AxisInput &axis) override; + const char *tag() const override { return "KeyMappingNewKey"; } + + bool key(const KeyInput &key) override; + bool axis(const AxisInput &axis) override; void SetDelay(float t); protected: void CreatePopupContents(UI::ViewGroup *parent) override; - virtual bool FillVertical() const override { return false; } - virtual bool ShowButtons() const override { return true; } - virtual void OnCompleted(DialogResult result) override {} + bool FillVertical() const override { return false; } + bool ShowButtons() const override { return true; } + void OnCompleted(DialogResult result) override {} private: int pspBtn_; @@ -87,6 +89,8 @@ public: pspBtn_ = btn; } + const char *tag() const override { return "KeyMappingNewMouseKey"; } + bool key(const KeyInput &key) override; bool axis(const AxisInput &axis) override; @@ -114,6 +118,8 @@ public: void update() override; + const char *tag() const override { return "AnalogSetup"; } + protected: void CreateViews() override; @@ -144,6 +150,8 @@ public: bool key(const KeyInput &key) override; bool axis(const AxisInput &axis) override; + const char *tag() const override { return "TouchTest"; } + protected: struct TrackedTouch { int id; @@ -171,6 +179,8 @@ class VisualMappingScreen : public UIDialogScreenWithBackground { public: VisualMappingScreen() {} + const char *tag() const override { return "VisualMapping"; } + protected: void CreateViews() override; diff --git a/UI/CwCheatScreen.cpp b/UI/CwCheatScreen.cpp index 8b4ba9d146..dee7a11166 100644 --- a/UI/CwCheatScreen.cpp +++ b/UI/CwCheatScreen.cpp @@ -199,7 +199,6 @@ UI::EventReturn CwCheatScreen::OnImportCheat(UI::EventParams ¶ms) { WARN_LOG(COMMON, "CWCHEAT: Incorrect ID(%s) - can't import cheats.", gameID_.c_str()); return UI::EVENT_DONE; } - std::string line; std::vector title; std::vector newList; diff --git a/UI/CwCheatScreen.h b/UI/CwCheatScreen.h index e2e167c771..27a28a1b51 100644 --- a/UI/CwCheatScreen.h +++ b/UI/CwCheatScreen.h @@ -41,6 +41,8 @@ public: void update() override; void onFinish(DialogResult result) override; + const char *tag() const override { return "CwCheat"; } + protected: void CreateViews() override; diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index a804eb98f0..5dea5e0921 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -85,7 +85,7 @@ static const char *logLevelList[] = { "Verb." }; -void DevMenu::CreatePopupContents(UI::ViewGroup *parent) { +void DevMenuScreen::CreatePopupContents(UI::ViewGroup *parent) { using namespace UI; auto dev = GetI18NCategory("Developer"); auto sy = GetI18NCategory("System"); @@ -94,25 +94,25 @@ void DevMenu::CreatePopupContents(UI::ViewGroup *parent) { LinearLayout *items = new LinearLayout(ORIENT_VERTICAL); #if !defined(MOBILE_DEVICE) - items->Add(new Choice(dev->T("Log View")))->OnClick.Handle(this, &DevMenu::OnLogView); + items->Add(new Choice(dev->T("Log View")))->OnClick.Handle(this, &DevMenuScreen::OnLogView); #endif - items->Add(new Choice(dev->T("Logging Channels")))->OnClick.Handle(this, &DevMenu::OnLogConfig); - items->Add(new Choice(sy->T("Developer Tools")))->OnClick.Handle(this, &DevMenu::OnDeveloperTools); - items->Add(new Choice(dev->T("Jit Compare")))->OnClick.Handle(this, &DevMenu::OnJitCompare); - items->Add(new Choice(dev->T("Shader Viewer")))->OnClick.Handle(this, &DevMenu::OnShaderView); + items->Add(new Choice(dev->T("Logging Channels")))->OnClick.Handle(this, &DevMenuScreen::OnLogConfig); + items->Add(new Choice(sy->T("Developer Tools")))->OnClick.Handle(this, &DevMenuScreen::OnDeveloperTools); + items->Add(new Choice(dev->T("Jit Compare")))->OnClick.Handle(this, &DevMenuScreen::OnJitCompare); + items->Add(new Choice(dev->T("Shader Viewer")))->OnClick.Handle(this, &DevMenuScreen::OnShaderView); if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) { // TODO: Make a new allocator visualizer for VMA. // items->Add(new CheckBox(&g_Config.bShowAllocatorDebug, dev->T("Allocator Viewer"))); items->Add(new CheckBox(&g_Config.bShowGpuProfile, dev->T("GPU Profile"))); } - items->Add(new Choice(dev->T("Toggle Freeze")))->OnClick.Handle(this, &DevMenu::OnFreezeFrame); - items->Add(new Choice(dev->T("Dump Frame GPU Commands")))->OnClick.Handle(this, &DevMenu::OnDumpFrame); - items->Add(new Choice(dev->T("Toggle Audio Debug")))->OnClick.Handle(this, &DevMenu::OnToggleAudioDebug); + items->Add(new Choice(dev->T("Toggle Freeze")))->OnClick.Handle(this, &DevMenuScreen::OnFreezeFrame); + items->Add(new Choice(dev->T("Dump Frame GPU Commands")))->OnClick.Handle(this, &DevMenuScreen::OnDumpFrame); + items->Add(new Choice(dev->T("Toggle Audio Debug")))->OnClick.Handle(this, &DevMenuScreen::OnToggleAudioDebug); #ifdef USE_PROFILER items->Add(new CheckBox(&g_Config.bShowFrameProfiler, dev->T("Frame Profiler"), "")); #endif items->Add(new CheckBox(&g_Config.bDrawFrameGraph, dev->T("Draw Frametimes Graph"))); - items->Add(new Choice(dev->T("Reset limited logging")))->OnClick.Handle(this, &DevMenu::OnResetLimitedLogging); + items->Add(new Choice(dev->T("Reset limited logging")))->OnClick.Handle(this, &DevMenuScreen::OnResetLimitedLogging); scroll->Add(items); parent->Add(scroll); @@ -123,48 +123,48 @@ void DevMenu::CreatePopupContents(UI::ViewGroup *parent) { } } -UI::EventReturn DevMenu::OnToggleAudioDebug(UI::EventParams &e) { +UI::EventReturn DevMenuScreen::OnToggleAudioDebug(UI::EventParams &e) { g_Config.bShowAudioDebug = !g_Config.bShowAudioDebug; return UI::EVENT_DONE; } -UI::EventReturn DevMenu::OnResetLimitedLogging(UI::EventParams &e) { +UI::EventReturn DevMenuScreen::OnResetLimitedLogging(UI::EventParams &e) { Reporting::ResetCounts(); return UI::EVENT_DONE; } -UI::EventReturn DevMenu::OnLogView(UI::EventParams &e) { +UI::EventReturn DevMenuScreen::OnLogView(UI::EventParams &e) { UpdateUIState(UISTATE_PAUSEMENU); screenManager()->push(new LogScreen()); return UI::EVENT_DONE; } -UI::EventReturn DevMenu::OnLogConfig(UI::EventParams &e) { +UI::EventReturn DevMenuScreen::OnLogConfig(UI::EventParams &e) { UpdateUIState(UISTATE_PAUSEMENU); screenManager()->push(new LogConfigScreen()); return UI::EVENT_DONE; } -UI::EventReturn DevMenu::OnDeveloperTools(UI::EventParams &e) { +UI::EventReturn DevMenuScreen::OnDeveloperTools(UI::EventParams &e) { UpdateUIState(UISTATE_PAUSEMENU); screenManager()->push(new DeveloperToolsScreen()); return UI::EVENT_DONE; } -UI::EventReturn DevMenu::OnJitCompare(UI::EventParams &e) { +UI::EventReturn DevMenuScreen::OnJitCompare(UI::EventParams &e) { UpdateUIState(UISTATE_PAUSEMENU); screenManager()->push(new JitCompareScreen()); return UI::EVENT_DONE; } -UI::EventReturn DevMenu::OnShaderView(UI::EventParams &e) { +UI::EventReturn DevMenuScreen::OnShaderView(UI::EventParams &e) { UpdateUIState(UISTATE_PAUSEMENU); if (gpu) // Avoid crashing if chosen while the game is being loaded. screenManager()->push(new ShaderListScreen()); return UI::EVENT_DONE; } -UI::EventReturn DevMenu::OnFreezeFrame(UI::EventParams &e) { +UI::EventReturn DevMenuScreen::OnFreezeFrame(UI::EventParams &e) { if (PSP_CoreParameter().frozen) { PSP_CoreParameter().frozen = false; } else { @@ -173,12 +173,12 @@ UI::EventReturn DevMenu::OnFreezeFrame(UI::EventParams &e) { return UI::EVENT_DONE; } -UI::EventReturn DevMenu::OnDumpFrame(UI::EventParams &e) { +UI::EventReturn DevMenuScreen::OnDumpFrame(UI::EventParams &e) { gpu->DumpNextFrame(); return UI::EVENT_DONE; } -void DevMenu::dialogFinished(const Screen *dialog, DialogResult result) { +void DevMenuScreen::dialogFinished(const Screen *dialog, DialogResult result) { UpdateUIState(UISTATE_INGAME); // Close when a subscreen got closed. // TODO: a bug in screenmanager causes this not to work here. @@ -514,7 +514,15 @@ void SystemInfoScreen::CreateViews() { const std::string apiNameKey = draw->GetInfoString(InfoField::APINAME); const char *apiName = gr->T(apiNameKey); deviceSpecs->Add(new InfoItem(si->T("3D API"), apiName)); - deviceSpecs->Add(new InfoItem(si->T("Vendor"), draw->GetInfoString(InfoField::VENDORSTRING))); + + // TODO: Not really vendor, on most APIs it's a device name (GL calls it vendor though). + std::string vendorString; + if (draw->GetDeviceCaps().deviceID != 0) { + vendorString = StringFromFormat("%s (%08x)", draw->GetInfoString(InfoField::VENDORSTRING).c_str(), draw->GetDeviceCaps().deviceID); + } else { + vendorString = draw->GetInfoString(InfoField::VENDORSTRING); + } + deviceSpecs->Add(new InfoItem(si->T("Vendor"), vendorString)); std::string vendor = draw->GetInfoString(InfoField::VENDOR); if (vendor.size()) deviceSpecs->Add(new InfoItem(si->T("Vendor (detected)"), vendor)); @@ -1136,7 +1144,7 @@ int ShaderListScreen::ListShaders(DebugShaderType shaderType, UI::LinearLayout * using namespace UI; std::vector shaderIds_ = gpu->DebugGetShaderIDs(shaderType); int count = 0; - for (auto id : shaderIds_) { + for (const auto &id : shaderIds_) { Choice *choice = view->Add(new Choice(gpu->DebugGetShaderString(id, shaderType, SHADER_STRING_SHORT_DESC))); choice->SetTag(id); choice->OnClick.Handle(this, &ShaderListScreen::OnShaderClick); @@ -1148,7 +1156,7 @@ int ShaderListScreen::ListShaders(DebugShaderType shaderType, UI::LinearLayout * struct { DebugShaderType type; const char *name; } shaderTypes[] = { { SHADER_TYPE_VERTEX, "Vertex" }, { SHADER_TYPE_FRAGMENT, "Fragment" }, - // { SHADER_TYPE_GEOMETRY, "Geometry" }, + { SHADER_TYPE_GEOMETRY, "Geometry" }, { SHADER_TYPE_VERTEXLOADER, "VertexLoader" }, { SHADER_TYPE_PIPELINE, "Pipeline" }, { SHADER_TYPE_TEXTURE, "Texture" }, diff --git a/UI/DevScreens.h b/UI/DevScreens.h index 4e7065307e..0df3d73ce3 100644 --- a/UI/DevScreens.h +++ b/UI/DevScreens.h @@ -28,9 +28,11 @@ #include "UI/MiscScreens.h" #include "GPU/Common/ShaderCommon.h" -class DevMenu : public PopupScreen { +class DevMenuScreen : public PopupScreen { public: - DevMenu(std::shared_ptr i18n) : PopupScreen(i18n->T("Dev Tools")) {} + DevMenuScreen(std::shared_ptr i18n) : PopupScreen(i18n->T("Dev Tools")) {} + + const char *tag() const override { return "DevMenu"; } void CreatePopupContents(UI::ViewGroup *parent) override; void dialogFinished(const Screen *dialog, DialogResult result) override; @@ -50,7 +52,9 @@ protected: class JitDebugScreen : public UIDialogScreenWithBackground { public: JitDebugScreen() {} - virtual void CreateViews() override; + void CreateViews() override; + + const char *tag() const override { return "JitDebug"; } private: UI::EventReturn OnEnableAll(UI::EventParams &e); @@ -60,7 +64,9 @@ private: class LogConfigScreen : public UIDialogScreenWithBackground { public: LogConfigScreen() {} - virtual void CreateViews() override; + void CreateViews() override; + + const char *tag() const override { return "LogConfig"; } private: UI::EventReturn OnToggleAll(UI::EventParams &e); @@ -76,6 +82,8 @@ public: void CreateViews() override; void update() override; + const char *tag() const override { return "Log"; } + private: void UpdateLog(); UI::EventReturn OnSubmit(UI::EventParams &e); @@ -89,14 +97,16 @@ class LogLevelScreen : public ListPopupScreen { public: LogLevelScreen(const std::string &title); -private: - virtual void OnCompleted(DialogResult result); + const char *tag() const override { return "LogLevel"; } +private: + void OnCompleted(DialogResult result) override; }; class SystemInfoScreen : public UIDialogScreenWithBackground { public: - SystemInfoScreen() {} + const char *tag() const override { return "SystemInfo"; } + void CreateViews() override; }; @@ -106,13 +116,15 @@ public: memset(buttons_, 0, sizeof(buttons_)); } - virtual bool key(const KeyInput &key) override; + const char *tag() const override { return "AddressPrompt"; } + + bool key(const KeyInput &key) override; UI::Event OnChoice; protected: - virtual void CreatePopupContents(UI::ViewGroup *parent) override; - virtual void OnCompleted(DialogResult result) override; + void CreatePopupContents(UI::ViewGroup *parent) override; + void OnCompleted(DialogResult result) override; UI::EventReturn OnDigitButton(UI::EventParams &e); UI::EventReturn OnBackspace(UI::EventParams &e); @@ -128,8 +140,9 @@ private: class JitCompareScreen : public UIDialogScreenWithBackground { public: - JitCompareScreen() : currentBlock_(-1) {} - virtual void CreateViews() override; + void CreateViews() override; + + const char *tag() const override { return "JitCompare"; } private: void UpdateDisasm(); @@ -146,7 +159,7 @@ private: UI::EventReturn OnAddressChange(UI::EventParams &e); UI::EventReturn OnShowStats(UI::EventParams &e); - int currentBlock_; + int currentBlock_ = -1; UI::TextView *blockName_; UI::TextEdit *blockAddr_; @@ -158,9 +171,10 @@ private: class ShaderListScreen : public UIDialogScreenWithBackground { public: - ShaderListScreen() {} void CreateViews() override; + const char *tag() const override { return "ShaderList"; } + private: int ListShaders(DebugShaderType shaderType, UI::LinearLayout *view); @@ -175,6 +189,9 @@ public: : id_(id), type_(type) {} void CreateViews() override; + + const char *tag() const override { return "ShaderView"; } + private: std::string id_; DebugShaderType type_; @@ -188,6 +205,8 @@ public: void CreateViews() override; void update() override; + const char *tag() const override { return "FrameDumpTest"; } + private: UI::EventReturn OnLoadDump(UI::EventParams &e); diff --git a/UI/DisplayLayoutScreen.h b/UI/DisplayLayoutScreen.h index 15835258eb..06730742d4 100644 --- a/UI/DisplayLayoutScreen.h +++ b/UI/DisplayLayoutScreen.h @@ -31,7 +31,7 @@ public: virtual void dialogFinished(const Screen *dialog, DialogResult result) override; virtual void onFinish(DialogResult reason) override; virtual void resized() override; - std::string tag() const override { return "display layout screen"; } + const char *tag() const override { return "DisplayLayout"; } protected: virtual UI::EventReturn OnCenter(UI::EventParams &e); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 1b98b91bb8..c29d8e67c6 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -224,6 +224,15 @@ bool EmuScreen::bootAllowStorage(const Path &filename) { } void EmuScreen::bootGame(const Path &filename) { + if (PSP_IsRebooting()) + return; + if (PSP_IsInited()) { + bootPending_ = false; + invalid_ = false; + bootComplete(); + return; + } + if (PSP_IsIniting()) { std::string error_string; bootPending_ = !PSP_InitUpdate(&error_string); @@ -955,7 +964,7 @@ void EmuScreen::CreateViews() { UI::EventReturn EmuScreen::OnDevTools(UI::EventParams ¶ms) { auto dev = GetI18NCategory("Developer"); - DevMenu *devMenu = new DevMenu(dev); + DevMenuScreen *devMenu = new DevMenuScreen(dev); if (params.v) devMenu->SetPopupOrigin(params.v); screenManager()->push(devMenu); @@ -1049,7 +1058,7 @@ void EmuScreen::update() { errLoadingFile.append(err->T(errorMessage_.c_str())); screenManager()->push(new PromptScreen(errLoadingFile, "OK", "")); - errorMessage_ = ""; + errorMessage_.clear(); quit_ = true; return; } @@ -1097,7 +1106,12 @@ void EmuScreen::update() { } void EmuScreen::checkPowerDown() { - if (coreState == CORE_POWERDOWN && !PSP_IsIniting()) { + if (PSP_IsRebooting()) { + bootPending_ = true; + invalid_ = true; + } + + if (coreState == CORE_POWERDOWN && !PSP_IsIniting() && !PSP_IsRebooting()) { if (PSP_IsInited()) { PSP_Shutdown(); } diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index ff41bd81a4..1fd41ffb9d 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -40,6 +40,8 @@ public: EmuScreen(const Path &filename); ~EmuScreen(); + const char *tag() const override { return "Emu"; } + void update() override; void render() override; void preRender() override; diff --git a/UI/GPUDriverTestScreen.cpp b/UI/GPUDriverTestScreen.cpp index b46fd7e42e..023b9e1def 100644 --- a/UI/GPUDriverTestScreen.cpp +++ b/UI/GPUDriverTestScreen.cpp @@ -122,7 +122,7 @@ static const std::vector vsAdrenoLogicTest = { "} myBufferVals;\n" "layout (location = 0) in vec4 pos;\n" "layout (location = 1) in vec4 inColor;\n" - "layout (location = 2) in vec2 inTexCoord;\n" + "layout (location = 3) in vec2 inTexCoord;\n" "layout (location = 0) out vec4 outColor;\n" "layout (location = 1) out highp vec2 outTexCoord;\n" "out gl_PerVertex { vec4 gl_Position; };\n" @@ -219,7 +219,7 @@ static const std::vector vsFlat = { "} myBufferVals;\n" "layout (location = 0) in vec4 pos;\n" "layout (location = 1) in vec4 inColor;\n" - "layout (location = 2) in vec2 inTexCoord;\n" + "layout (location = 3) in vec2 inTexCoord;\n" "layout (location = 0) flat out lowp vec4 outColor;\n" "layout (location = 1) out highp vec2 outTexCoord;\n" "out gl_PerVertex { vec4 gl_Position; };\n" @@ -231,6 +231,8 @@ static const std::vector vsFlat = { } }; +static_assert(Draw::SEM_TEXCOORD0 == 3, "Semantic shader hardcoded in glsl above."); + GPUDriverTestScreen::GPUDriverTestScreen() { using namespace Draw; } diff --git a/UI/GPUDriverTestScreen.h b/UI/GPUDriverTestScreen.h index f2c5796be1..38ec2c33e1 100644 --- a/UI/GPUDriverTestScreen.h +++ b/UI/GPUDriverTestScreen.h @@ -17,6 +17,8 @@ public: void CreateViews() override; void render() override; + const char *tag() const override { return "GPUDriverTest"; } + private: void DiscardTest(); void ShaderTest(); diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 15d4073f98..edf519f46b 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -443,6 +443,7 @@ UI::EventReturn GameScreen::OnRemoveFromRecent(UI::EventParams &e) { class SetBackgroundPopupScreen : public PopupScreen { public: SetBackgroundPopupScreen(const std::string &title, const Path &gamePath); + const char *tag() const override { return "SetBackgroundPopup"; } protected: bool FillVertical() const override { return false; } diff --git a/UI/GameScreen.h b/UI/GameScreen.h index 9379c8f134..cff0c48366 100644 --- a/UI/GameScreen.h +++ b/UI/GameScreen.h @@ -38,7 +38,7 @@ public: void render() override; - std::string tag() const override { return "game"; } + const char *tag() const override { return "Game"; } protected: void CreateViews() override; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 6c702bd7f0..4d1d6c6798 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -256,7 +256,7 @@ void GameSettingsScreen::CreateViews() { root_->Add(settingInfo_); // Show it again if we recreated the view - if (oldSettingInfo_ != "") { + if (!oldSettingInfo_.empty()) { settingInfo_->Show(oldSettingInfo_, nullptr); } @@ -469,10 +469,19 @@ void GameSettingsScreen::CreateViews() { if (GetGPUBackend() == GPUBackend::VULKAN || GetGPUBackend() == GPUBackend::OPENGL) { static const char *bufferOptions[] = { "No buffer", "Up to 1", "Up to 2" }; - PopupMultiChoice *inflightChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iInflightFrames, gr->T("Buffer graphics commands (faster, input lag)"), bufferOptions, 0, ARRAY_SIZE(bufferOptions), gr->GetName(), screenManager())); + PopupMultiChoice *inflightChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iInflightFrames, gr->T("Buffer graphics commands (faster, input lag)"), bufferOptions, 1, ARRAY_SIZE(bufferOptions), gr->GetName(), screenManager())); inflightChoice->OnChoice.Handle(this, &GameSettingsScreen::OnInflightFramesChoice); } + if (GetGPUBackend() == GPUBackend::VULKAN) { + const bool usable = !draw->GetBugs().Has(Draw::Bugs::GEOMETRY_SHADERS_SLOW); + const bool vertexSupported = draw->GetDeviceCaps().clipDistanceSupported && draw->GetDeviceCaps().cullDistanceSupported; + if (usable && !vertexSupported) { + CheckBox *geometryCulling = graphicsSettings->Add(new CheckBox(&g_Config.bUseGeometryShader, gr->T("Geometry shader culling"))); + geometryCulling->SetDisabledPtr(&g_Config.bSoftwareRendering); + } + } + if (deviceType != DEVICE_TYPE_VR) { CheckBox *hwTransform = graphicsSettings->Add(new CheckBox(&g_Config.bHardwareTransform, gr->T("Hardware Transform"))); hwTransform->SetDisabledPtr(&g_Config.bSoftwareRendering); @@ -1222,7 +1231,7 @@ UI::EventReturn GameSettingsScreen::OnChangeMemStickDir(UI::EventParams &e) { } UI::EventReturn GameSettingsScreen::OnOpenMemStick(UI::EventParams &e) { - OpenDirectory(File::ResolvePath(g_Config.memStickDirectory.ToString().c_str()).c_str()); + OpenDirectory(File::ResolvePath(g_Config.memStickDirectory.ToString()).c_str()); return UI::EVENT_DONE; } @@ -1488,7 +1497,7 @@ void GameSettingsScreen::TriggerRestart(const char *why) { std::string param = "--gamesettings"; if (editThenRestore_) { // We won't pass the gameID, so don't resume back into settings. - param = ""; + param.clear(); } else if (!gamePath_.empty()) { param += " \"" + ReplaceAll(ReplaceAll(gamePath_.ToString(), "\\", "\\\\"), "\"", "\\\"") + "\""; } diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 087b05740e..82b4cd859e 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -32,7 +32,7 @@ public: void update() override; void onFinish(DialogResult result) override; - std::string tag() const override { return "settings"; } + const char *tag() const override { return "GameSettings"; } protected: void sendMessage(const char *message, const char *value) override; @@ -157,10 +157,11 @@ private: class DeveloperToolsScreen : public UIDialogScreenWithBackground { public: - DeveloperToolsScreen() {} void update() override; void onFinish(DialogResult result) override; + const char *tag() const override { return "DeveloperTools"; } + protected: void CreateViews() override; @@ -205,6 +206,8 @@ public: void CreatePopupContents(UI::ViewGroup *parent) override; + const char *tag() const override { return "HostnameSelect"; } + protected: void OnCompleted(DialogResult result) override; bool CanComplete(DialogResult result) override; @@ -247,4 +250,6 @@ private: class GestureMappingScreen : public UIDialogScreenWithBackground { public: void CreateViews() override; + + const char *tag() const override { return "GestureMapping"; } }; diff --git a/UI/InstallZipScreen.h b/UI/InstallZipScreen.h index 6daa13bf7f..140b82fec2 100644 --- a/UI/InstallZipScreen.h +++ b/UI/InstallZipScreen.h @@ -30,6 +30,8 @@ public: virtual void update() override; virtual bool key(const KeyInput &key) override; + const char *tag() const override { return "InstallZip"; } + protected: virtual void CreateViews() override; diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 3e025d06ba..d5ed3ab0a4 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -1432,11 +1432,12 @@ UI::EventReturn MainScreen::OnExit(UI::EventParams &e) { } void MainScreen::dialogFinished(const Screen *dialog, DialogResult result) { - if (dialog->tag() == "store") { + std::string tag = dialog->tag(); + if (tag == "Store") { backFromStore_ = true; RecreateViews(); } - if (dialog->tag() == "game") { + if (tag == "Game") { if (!restoreFocusGamePath_.empty() && UI::IsFocusMovementEnabled()) { // Prevent the background from fading, since we just were displaying it. highlightedGamePath_ = restoreFocusGamePath_; diff --git a/UI/MainScreen.h b/UI/MainScreen.h index 6b15f06427..b81e193b93 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -102,6 +102,8 @@ public: bool isTopLevel() const override { return true; } + const char *tag() const override { return "Main"; } + // Horrible hack to show the demos & homebrew tab after having installed a game from a zip file. static bool showHomebrewTab; @@ -153,7 +155,7 @@ protected: class UmdReplaceScreen : public UIDialogScreenWithBackground { public: - UmdReplaceScreen() {} + const char *tag() const override { return "UmdReplace"; } protected: void CreateViews() override; @@ -174,6 +176,8 @@ public: void CreatePopupContents(UI::ViewGroup *parent) override; UI::Event OnRecentChanged; + const char *tag() const override { return "GridSettings"; } + private: UI::EventReturn GridPlusClick(UI::EventParams &e); UI::EventReturn GridMinusClick(UI::EventParams &e); diff --git a/UI/MemStickScreen.cpp b/UI/MemStickScreen.cpp index ba61ad193a..78c4cf24fd 100644 --- a/UI/MemStickScreen.cpp +++ b/UI/MemStickScreen.cpp @@ -259,7 +259,7 @@ void MemStickScreen::CreateViews() { privateString = StringFromFormat("%s (%s)", iz->T("Skip for now"), privateString.c_str()); } - leftColumn->Add(new RadioButton(&choice_, CHOICE_PRIVATE_DIRECTORY, privateString.c_str()))->OnClick.Handle(this, &MemStickScreen::OnChoiceClick); + leftColumn->Add(new RadioButton(&choice_, CHOICE_PRIVATE_DIRECTORY, privateString))->OnClick.Handle(this, &MemStickScreen::OnChoiceClick); if (choice_ == CHOICE_PRIVATE_DIRECTORY) { AddExplanation(leftColumn, (MemStickScreen::Choice)choice_); } diff --git a/UI/MemStickScreen.h b/UI/MemStickScreen.h index f315467c56..378f4f3ed0 100644 --- a/UI/MemStickScreen.h +++ b/UI/MemStickScreen.h @@ -36,7 +36,7 @@ public: MemStickScreen(bool initialSetup); ~MemStickScreen() {} - std::string tag() const override { return "game"; } + const char *tag() const override { return "MemStick"; } enum Choice { CHOICE_BROWSE_FOLDER, @@ -112,6 +112,9 @@ class ConfirmMemstickMoveScreen : public UIDialogScreenWithBackground { public: ConfirmMemstickMoveScreen(Path newMemstickFolder, bool initialSetup); ~ConfirmMemstickMoveScreen(); + + const char *tag() const override { return "ConfirmMemstickMove"; } + protected: void update() override; void CreateViews() override; diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 2a0ea9bfe7..d90d92f76d 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -387,13 +387,13 @@ void HandleCommonMessages(const char *message, const char *value, ScreenManager MIPSComp::jit->ClearCache(); } currentMIPS->UpdateCore((CPUCore)g_Config.iCpuCore); - } else if (!strcmp(message, "control mapping") && isActiveScreen && activeScreen->tag() != "control mapping") { + } else if (!strcmp(message, "control mapping") && isActiveScreen && std::string(activeScreen->tag()) != "ControlMapping") { UpdateUIState(UISTATE_MENU); manager->push(new ControlMappingScreen()); - } else if (!strcmp(message, "display layout editor") && isActiveScreen && activeScreen->tag() != "display layout screen") { + } else if (!strcmp(message, "display layout editor") && isActiveScreen && std::string(activeScreen->tag()) != "DisplayLayout") { UpdateUIState(UISTATE_MENU); manager->push(new DisplayLayoutScreen()); - } else if (!strcmp(message, "settings") && isActiveScreen && activeScreen->tag() != "settings") { + } else if (!strcmp(message, "settings") && isActiveScreen && std::string(activeScreen->tag()) != "GameSettings") { UpdateUIState(UISTATE_MENU); manager->push(new GameSettingsScreen(Path())); } else if (!strcmp(message, "language screen") && isActiveScreen) { diff --git a/UI/MiscScreens.h b/UI/MiscScreens.h index d094b6d3db..164a06cfec 100644 --- a/UI/MiscScreens.h +++ b/UI/MiscScreens.h @@ -82,6 +82,8 @@ public: void TriggerFinish(DialogResult result) override; + const char *tag() const override { return "Prompt"; } + private: UI::EventReturn OnYes(UI::EventParams &e); UI::EventReturn OnNo(UI::EventParams &e); @@ -96,6 +98,8 @@ class NewLanguageScreen : public ListPopupScreen { public: NewLanguageScreen(const std::string &title); + const char *tag() const override { return "NewLanguage"; } + private: void OnCompleted(DialogResult result) override; bool ShowButtons() const override { return true; } @@ -110,6 +114,8 @@ public: void CreateViews() override; + const char *tag() const override { return "PostProc"; } + private: void OnCompleted(DialogResult result) override; bool ShowButtons() const override { return true; } @@ -123,6 +129,8 @@ public: void CreateViews() override; + const char *tag() const override { return "TextureShader"; } + private: void OnCompleted(DialogResult result) override; bool ShowButtons() const override { return true; } @@ -146,6 +154,8 @@ public: void sendMessage(const char *message, const char *value) override; void CreateViews() override {} + const char *tag() const override { return "Logo"; } + private: void Next(); int frames_ = 0; @@ -162,6 +172,8 @@ public: void CreateViews() override; + const char *tag() const override { return "Credits"; } + private: UI::EventReturn OnOK(UI::EventParams &e); diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index 339409ed6e..748fcdf86d 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -172,9 +172,7 @@ public: return slot_; } - std::string tag() const override { - return "screenshot"; - } + const char *tag() const override { return "ScreenshotView"; } protected: bool FillVertical() const override { return false; } @@ -460,7 +458,7 @@ UI::EventReturn GamePauseScreen::OnState(UI::EventParams &e) { void GamePauseScreen::dialogFinished(const Screen *dialog, DialogResult dr) { std::string tag = dialog->tag(); - if (tag == "screenshot" && dr == DR_OK) { + if (tag == "ScreenshotView" && dr == DR_OK) { finishNextFrame_ = true; } else { // There may have been changes to our savestates, so let's recreate. diff --git a/UI/PauseScreen.h b/UI/PauseScreen.h index 15b22174e2..3c66609285 100644 --- a/UI/PauseScreen.h +++ b/UI/PauseScreen.h @@ -33,6 +33,8 @@ public: virtual void dialogFinished(const Screen *dialog, DialogResult dr) override; + const char *tag() const override { return "GamePause"; } + protected: virtual void CreateViews() override; virtual void update() override; diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 4497f169db..7d60e507a4 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -176,7 +176,7 @@ bool RemoteISOConnectScreen::FindServer(std::string &resultHost, int &resultPort }; // Try last server first, if it is set - if (g_Config.iLastRemoteISOPort && g_Config.sLastRemoteISOServer != "") { + if (g_Config.iLastRemoteISOPort && !g_Config.sLastRemoteISOServer.empty()) { if (TryServer(g_Config.sLastRemoteISOServer.c_str(), g_Config.iLastRemoteISOPort)) { return true; } diff --git a/UI/RemoteISOScreen.h b/UI/RemoteISOScreen.h index bfcac815be..b8676e4832 100644 --- a/UI/RemoteISOScreen.h +++ b/UI/RemoteISOScreen.h @@ -29,6 +29,8 @@ class RemoteISOScreen : public UIScreenWithBackground { public: RemoteISOScreen(); + const char *tag() const override { return "RemoteISO"; } + protected: void update() override; void CreateViews() override; @@ -57,6 +59,8 @@ public: RemoteISOConnectScreen(); ~RemoteISOConnectScreen() override; + const char *tag() const override { return "RemoteISOConnect"; } + protected: void update() override; void CreateViews() override; @@ -83,6 +87,8 @@ class RemoteISOBrowseScreen : public MainScreen { public: RemoteISOBrowseScreen(const std::string &url, const std::vector &games); + const char *tag() const override { return "RemoteISOBrowse"; } + protected: void CreateViews() override; @@ -94,6 +100,8 @@ class RemoteISOSettingsScreen : public UIDialogScreenWithBackground { public: RemoteISOSettingsScreen(); + const char *tag() const override { return "RemoteISOSettings"; } + UI::EventReturn OnClickRemoteISOSubdir(UI::EventParams &e); UI::EventReturn OnClickRemoteServer(UI::EventParams &e); protected: diff --git a/UI/ReportScreen.h b/UI/ReportScreen.h index a7d132b2c8..006d90e6f5 100644 --- a/UI/ReportScreen.h +++ b/UI/ReportScreen.h @@ -37,6 +37,8 @@ class ReportScreen : public UIDialogScreenWithGameBackground { public: ReportScreen(const Path &gamePath); + const char *tag() const override { return "Report"; } + protected: void postRender() override; void update() override; @@ -75,6 +77,8 @@ class ReportFinishScreen : public UIDialogScreenWithGameBackground { public: ReportFinishScreen(const Path &gamePath, ReportingOverallScore score); + const char *tag() const override { return "ReportFinish"; } + protected: void update() override; void CreateViews() override; diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index 731e3d5d95..0172b6a19b 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -78,8 +78,9 @@ static std::string TrimString(const std::string &str) { class SavedataPopupScreen : public PopupScreen { public: - SavedataPopupScreen(std::string savePath, std::string title) : PopupScreen(TrimString(title)), savePath_(savePath) { - } + SavedataPopupScreen(std::string savePath, std::string title) : PopupScreen(TrimString(title)), savePath_(savePath) { } + + const char *tag() const override { return "SavedataPopup"; } void CreatePopupContents(UI::ViewGroup *parent) override { using namespace UI; diff --git a/UI/SavedataScreen.h b/UI/SavedataScreen.h index 1f9509e687..1bb2f42140 100644 --- a/UI/SavedataScreen.h +++ b/UI/SavedataScreen.h @@ -74,6 +74,8 @@ public: void dialogFinished(const Screen *dialog, DialogResult result) override; void sendMessage(const char *message, const char *value) override; + const char *tag() const override { return "Savedata"; } + protected: UI::EventReturn OnSavedataButtonClick(UI::EventParams &e); UI::EventReturn OnSortClick(UI::EventParams &e); diff --git a/UI/Store.cpp b/UI/Store.cpp index 8329ed22d9..86a084da95 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -502,7 +502,7 @@ void StoreScreen::CreateViews() { selectedItem->Press(); } else { - lastSelectedName_ = ""; + lastSelectedName_.clear(); } } root_->Add(content); diff --git a/UI/Store.h b/UI/Store.h index 263e5dbde1..8f02f1a615 100644 --- a/UI/Store.h +++ b/UI/Store.h @@ -67,7 +67,7 @@ public: ~StoreScreen(); void update() override; - std::string tag() const override { return "store"; } + const char *tag() const override { return "Store"; } protected: void CreateViews() override; diff --git a/UI/TextureUtil.cpp b/UI/TextureUtil.cpp index 9a5b377a90..80dfb59e1a 100644 --- a/UI/TextureUtil.cpp +++ b/UI/TextureUtil.cpp @@ -151,7 +151,7 @@ bool ManagedTexture::LoadFromFile(const std::string &filename, ImageFileType typ size_t fileSize; uint8_t *buffer = VFSReadFile(filename.c_str(), &fileSize); if (!buffer) { - filename_ = ""; + filename_.clear(); ERROR_LOG(IO, "Failed to read file '%s'", filename.c_str()); return false; } @@ -159,7 +159,7 @@ bool ManagedTexture::LoadFromFile(const std::string &filename, ImageFileType typ if (retval) { filename_ = filename; } else { - filename_ = ""; + filename_.clear(); ERROR_LOG(IO, "Failed to load texture '%s'", filename.c_str()); } delete[] buffer; diff --git a/UI/Theme.cpp b/UI/Theme.cpp index 978eeb3d0b..464c4ab2db 100644 --- a/UI/Theme.cpp +++ b/UI/Theme.cpp @@ -139,7 +139,7 @@ static void LoadThemeInfo(const std::vector &directories) { std::string tmpPath; section.Get("UIAtlas", &tmpPath, ""); - if (tmpPath != "") { + if (!tmpPath.empty()) { tmpPath = (path / tmpPath).ToString(); File::FileInfo tmpInfo; diff --git a/UI/TiltAnalogSettingsScreen.h b/UI/TiltAnalogSettingsScreen.h index 3aeece6eb9..832a436842 100644 --- a/UI/TiltAnalogSettingsScreen.h +++ b/UI/TiltAnalogSettingsScreen.h @@ -27,6 +27,8 @@ public: void CreateViews() override; bool axis(const AxisInput &axis) override; + const char *tag() const override { return "TiltAnalogSettings"; } + private: UI::EventReturn OnCalibrate(UI::EventParams &e); float currentTiltX_ = 0.0f; diff --git a/UI/TouchControlLayoutScreen.h b/UI/TouchControlLayoutScreen.h index 3db9394258..9834f217f0 100644 --- a/UI/TouchControlLayoutScreen.h +++ b/UI/TouchControlLayoutScreen.h @@ -33,6 +33,8 @@ public: virtual void update() override; virtual void resized() override; + const char *tag() const override { return "TouchControlLayout"; } + protected: virtual UI::EventReturn OnReset(UI::EventParams &e); virtual UI::EventReturn OnVisibility(UI::EventParams &e); diff --git a/UI/TouchControlVisibilityScreen.h b/UI/TouchControlVisibilityScreen.h index f44e333395..52d6d00eea 100644 --- a/UI/TouchControlVisibilityScreen.h +++ b/UI/TouchControlVisibilityScreen.h @@ -36,6 +36,8 @@ public: void CreateViews() override; void onFinish(DialogResult result) override; + const char *tag() const override { return "TouchControlVisibility"; } + protected: UI::EventReturn OnToggleAll(UI::EventParams &e); @@ -47,4 +49,6 @@ private: class RightAnalogMappingScreen : public UIDialogScreenWithBackground { public: void CreateViews() override; + + const char *tag() const override { return "RightAnalogMapping"; } }; diff --git a/UWP/Armips_UWP/Armips_UWP.vcxproj b/UWP/Armips_UWP/Armips_UWP.vcxproj index a0fcbffe0c..a836263550 100644 --- a/UWP/Armips_UWP/Armips_UWP.vcxproj +++ b/UWP/Armips_UWP/Armips_UWP.vcxproj @@ -116,41 +116,43 @@ false - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips; + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips;$(ProjectDir)..\..\ext\armips\ext\filesystem\include;$(ProjectDir)..\..\ext\armips\ext\tinyformat; false - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips; + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips;$(ProjectDir)..\..\ext\armips\ext\filesystem\include;$(ProjectDir)..\..\ext\armips\ext\tinyformat; false - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips; + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips;$(ProjectDir)..\..\ext\armips\ext\filesystem\include;$(ProjectDir)..\..\ext\armips\ext\tinyformat; false - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips; + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips;$(ProjectDir)..\..\ext\armips\ext\filesystem\include;$(ProjectDir)..\..\ext\armips\ext\tinyformat; false - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips; + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips;$(ProjectDir)..\..\ext\armips\ext\filesystem\include;$(ProjectDir)..\..\ext\armips\ext\tinyformat; false - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips; + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips;$(ProjectDir)..\..\ext\armips\ext\filesystem\include;$(ProjectDir)..\..\ext\armips\ext\tinyformat; false - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips; + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips;$(ProjectDir)..\..\ext\armips\ext\filesystem\include;$(ProjectDir)..\..\ext\armips\ext\tinyformat; false - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips; + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips;$(ProjectDir)..\..\ext\armips\ext\filesystem\include;$(ProjectDir)..\..\ext\armips\ext\tinyformat; NotUsing false false + stdcpp17 + _UNICODE;UNICODE;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) Console @@ -163,6 +165,8 @@ NotUsing false false + stdcpp17 + _UNICODE;UNICODE;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) Console @@ -175,6 +179,8 @@ NotUsing false false + stdcpp17 + _UNICODE;UNICODE;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) Console @@ -187,6 +193,8 @@ NotUsing false false + stdcpp17 + _UNICODE;UNICODE;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) Console @@ -199,6 +207,8 @@ NotUsing false false + stdcpp17 + _UNICODE;UNICODE;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) Console @@ -211,6 +221,8 @@ NotUsing false false + stdcpp17 + _UNICODE;UNICODE;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) Console @@ -223,6 +235,8 @@ NotUsing false false + stdcpp17 + _UNICODE;UNICODE;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) Console @@ -235,6 +249,8 @@ NotUsing false false + stdcpp17 + _UNICODE;UNICODE;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) Console @@ -285,7 +301,6 @@ - @@ -322,25 +337,28 @@ + + + - + Create diff --git a/UWP/Armips_UWP/Armips_UWP.vcxproj.filters b/UWP/Armips_UWP/Armips_UWP.vcxproj.filters index 463cc812fe..37d23a6b09 100644 --- a/UWP/Armips_UWP/Armips_UWP.vcxproj.filters +++ b/UWP/Armips_UWP/Armips_UWP.vcxproj.filters @@ -140,7 +140,10 @@ Util - + + + + @@ -286,7 +289,6 @@ Util - diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 0777e28251..60c5d6bf8a 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -205,7 +205,8 @@ false pch.h ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) + stdcpp17 Console @@ -220,7 +221,8 @@ false pch.h ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) + stdcpp17 Console @@ -235,7 +237,8 @@ false pch.h ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) + stdcpp17 Console @@ -250,7 +253,8 @@ false pch.h ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;ARMIPS_USE_STD_FILESYSTEM;%(ClCompile.PreprocessorDefinitions) + stdcpp17 Console @@ -265,7 +269,8 @@ false pch.h ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;ARMIPS_USE_STD_FILESYSTEM;%(ClCompile.PreprocessorDefinitions) + stdcpp17 Console @@ -280,7 +285,8 @@ false pch.h ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;ARMIPS_USE_STD_FILESYSTEM;%(ClCompile.PreprocessorDefinitions) + stdcpp17 Console @@ -295,7 +301,8 @@ false pch.h ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;ARMIPS_USE_STD_FILESYSTEM;%(ClCompile.PreprocessorDefinitions) + stdcpp17 Console @@ -310,7 +317,8 @@ false pch.h ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;ARMIPS_USE_STD_FILESYSTEM;%(ClCompile.PreprocessorDefinitions) + stdcpp17 Console @@ -325,7 +333,8 @@ false pch.h ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;ARMIPS_USE_STD_FILESYSTEM;%(ClCompile.PreprocessorDefinitions) + stdcpp17 Console @@ -340,7 +349,8 @@ false pch.h ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) + stdcpp17 Console @@ -355,7 +365,8 @@ false pch.h ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) + stdcpp17 Console @@ -370,7 +381,8 @@ false pch.h ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) - USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) + USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) + stdcpp17 Console @@ -628,7 +640,20 @@ - + + ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + @@ -809,7 +834,20 @@ - + + ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;../../ext/armips/;../../ext/armips/ext/filesystem/include/;../../ext/armips/ext/tinyformat/;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj b/UWP/GPU_UWP/GPU_UWP.vcxproj index 87aac4d38b..05b7839736 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj @@ -385,6 +385,7 @@ + @@ -446,6 +447,7 @@ + diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj.filters b/UWP/GPU_UWP/GPU_UWP.vcxproj.filters index dbce632981..2296b16a16 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj.filters +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj.filters @@ -55,6 +55,7 @@ + @@ -116,6 +117,7 @@ + diff --git a/UWP/PackageGold.appxmanifest b/UWP/PackageGold.appxmanifest index c2b898a502..d428bcd738 100644 --- a/UWP/PackageGold.appxmanifest +++ b/UWP/PackageGold.appxmanifest @@ -35,6 +35,5 @@ - diff --git a/UWP/PackageNormal.appxmanifest b/UWP/PackageNormal.appxmanifest index 3ab4dbe2f8..ef79928e4c 100644 --- a/UWP/PackageNormal.appxmanifest +++ b/UWP/PackageNormal.appxmanifest @@ -29,8 +29,7 @@ - + - diff --git a/Windows/Debugger/CtrlDisAsmView.cpp b/Windows/Debugger/CtrlDisAsmView.cpp index 5aca43fbcf..7faf3b6f2b 100644 --- a/Windows/Debugger/CtrlDisAsmView.cpp +++ b/Windows/Debugger/CtrlDisAsmView.cpp @@ -191,7 +191,7 @@ CtrlDisAsmView::CtrlDisAsmView(HWND _wnd) matchAddress = -1; searching = false; - searchQuery = ""; + searchQuery.clear(); windowStart = curAddress; whiteBackground = false; displaySymbols = true; @@ -326,7 +326,7 @@ void CtrlDisAsmView::assembleOpcode(u32 address, std::string defaultText) redraw(); } else { - std::wstring error = MIPSAsm::GetAssembleError(); + std::wstring error = ConvertUTF8ToWString(MIPSAsm::GetAssembleError()); MessageBox(wnd,error.c_str(),L"Error",MB_OK); } } diff --git a/Windows/Debugger/CtrlMemView.cpp b/Windows/Debugger/CtrlMemView.cpp index c8f8a3f8df..d40bdaf483 100644 --- a/Windows/Debugger/CtrlMemView.cpp +++ b/Windows/Debugger/CtrlMemView.cpp @@ -49,7 +49,7 @@ CtrlMemView::CtrlMemView(HWND _wnd) curAddress = 0; debugger = 0; - searchQuery = ""; + searchQuery.clear(); matchAddress = -1; searching = false; @@ -767,10 +767,11 @@ std::vector CtrlMemView::searchString(const std::string &searchQuery) { return searchResAddrs; std::vector> memoryAreas; - memoryAreas.push_back(std::pair(PSP_GetScratchpadMemoryBase(), PSP_GetScratchpadMemoryEnd())); + memoryAreas.reserve(3); + memoryAreas.emplace_back(PSP_GetScratchpadMemoryBase(), PSP_GetScratchpadMemoryEnd()); // Ignore the video memory mirrors. - memoryAreas.push_back(std::pair(PSP_GetVidMemBase(), 0x04200000)); - memoryAreas.push_back(std::pair(PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd())); + memoryAreas.emplace_back(PSP_GetVidMemBase(), 0x04200000); + memoryAreas.emplace_back(PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd()); for (const auto &area : memoryAreas) { const u32 segmentStart = area.first; @@ -821,10 +822,11 @@ void CtrlMemView::search(bool continueSearch) } std::vector> memoryAreas; + memoryAreas.reserve(3); // Ignore the video memory mirrors. - memoryAreas.push_back(std::pair(PSP_GetVidMemBase(), 0x04200000)); - memoryAreas.push_back(std::pair(PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd())); - memoryAreas.push_back(std::pair(PSP_GetScratchpadMemoryBase(), PSP_GetScratchpadMemoryEnd())); + memoryAreas.emplace_back(PSP_GetVidMemBase(), 0x04200000); + memoryAreas.emplace_back(PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd()); + memoryAreas.emplace_back(PSP_GetScratchpadMemoryBase(), PSP_GetScratchpadMemoryEnd()); searching = true; redraw(); // so the cursor is disabled diff --git a/Windows/Debugger/Debugger_MemoryDlg.cpp b/Windows/Debugger/Debugger_MemoryDlg.cpp index d5358ab1bc..d016afaafa 100644 --- a/Windows/Debugger/Debugger_MemoryDlg.cpp +++ b/Windows/Debugger/Debugger_MemoryDlg.cpp @@ -195,7 +195,7 @@ BOOL CMemoryDlg::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { switch (HIWORD(wParam)) { case BN_CLICKED: GetWindowText(searchBoxHdl, temp, 255); - std::vector results = memView->searchString(ConvertWStringToUTF8(temp).c_str()); + std::vector results = memView->searchString(ConvertWStringToUTF8(temp)); if (results.size() > 0){ searchBoxRedraw(results); } diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index 62026e6f07..59e8fb018d 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -281,16 +281,31 @@ void CGEDebugger::SetupPreviews() { primaryWindow->SetHoverCallback([&] (int x, int y) { PrimaryPreviewHover(x, y); }); - primaryWindow->SetRightClickMenu(ContextMenuID::GEDBG_PREVIEW, [&] (int cmd) { + primaryWindow->SetRightClickMenu(ContextMenuID::GEDBG_PREVIEW, [&] (int cmd, int x, int y) { HMENU subMenu = GetContextMenu(ContextMenuID::GEDBG_PREVIEW); switch (cmd) { case 0: // Setup. CheckMenuItem(subMenu, ID_GEDBG_ENABLE_PREVIEW, MF_BYCOMMAND | ((previewsEnabled_ & 1) ? MF_CHECKED : MF_UNCHECKED)); + EnableMenuItem(subMenu, ID_GEDBG_TRACK_PIXEL_STOP, primaryTrackX_ == 0xFFFFFFFF ? MF_GRAYED : MF_ENABLED); break; case ID_GEDBG_EXPORT_IMAGE: PreviewExport(primaryBuffer_); break; + case ID_GEDBG_COPY_IMAGE: + PreviewToClipboard(primaryBuffer_, false); + break; + case ID_GEDBG_COPY_IMAGE_ALPHA: + PreviewToClipboard(primaryBuffer_, true); + break; + case ID_GEDBG_TRACK_PIXEL: + primaryTrackX_ = x; + primaryTrackY_ = y; + break; + case ID_GEDBG_TRACK_PIXEL_STOP: + primaryTrackX_ = 0xFFFFFFFF; + primaryTrackX_ = 0xFFFFFFFF; + break; case ID_GEDBG_ENABLE_PREVIEW: previewsEnabled_ ^= 1; primaryWindow->Redraw(); @@ -311,16 +326,31 @@ void CGEDebugger::SetupPreviews() { secondWindow->SetHoverCallback([&] (int x, int y) { SecondPreviewHover(x, y); }); - secondWindow->SetRightClickMenu(ContextMenuID::GEDBG_PREVIEW, [&] (int cmd) { + secondWindow->SetRightClickMenu(ContextMenuID::GEDBG_PREVIEW, [&] (int cmd, int x, int y) { HMENU subMenu = GetContextMenu(ContextMenuID::GEDBG_PREVIEW); switch (cmd) { case 0: // Setup. CheckMenuItem(subMenu, ID_GEDBG_ENABLE_PREVIEW, MF_BYCOMMAND | ((previewsEnabled_ & 2) ? MF_CHECKED : MF_UNCHECKED)); + EnableMenuItem(subMenu, ID_GEDBG_TRACK_PIXEL_STOP, secondTrackX_ == 0xFFFFFFFF ? MF_GRAYED : MF_ENABLED); break; case ID_GEDBG_EXPORT_IMAGE: PreviewExport(secondBuffer_); break; + case ID_GEDBG_COPY_IMAGE: + PreviewToClipboard(secondBuffer_, false); + break; + case ID_GEDBG_COPY_IMAGE_ALPHA: + PreviewToClipboard(secondBuffer_, true); + break; + case ID_GEDBG_TRACK_PIXEL: + secondTrackX_ = x; + secondTrackY_ = y; + break; + case ID_GEDBG_TRACK_PIXEL_STOP: + secondTrackX_ = 0xFFFFFFFF; + secondTrackX_ = 0xFFFFFFFF; + break; case ID_GEDBG_ENABLE_PREVIEW: previewsEnabled_ ^= 2; secondWindow->Redraw(); @@ -338,6 +368,12 @@ void CGEDebugger::SetupPreviews() { } void CGEDebugger::DescribePrimaryPreview(const GPUgstate &state, char desc[256]) { + if (primaryTrackX_ < primaryBuffer_->GetStride() && primaryTrackY_ < primaryBuffer_->GetHeight()) { + u32 pix = primaryBuffer_->GetRawPixel(primaryTrackX_, primaryTrackY_); + DescribePixel(pix, primaryBuffer_->GetFormat(), primaryTrackX_, primaryTrackY_, desc); + return; + } + if (showClut_) { // In this case, we're showing the texture here. snprintf(desc, 256, "Texture L%d: 0x%08x (%dx%d)", textureLevel_, state.getTextureAddress(textureLevel_), state.getTextureWidth(textureLevel_), state.getTextureHeight(textureLevel_)); @@ -362,6 +398,22 @@ void CGEDebugger::DescribePrimaryPreview(const GPUgstate &state, char desc[256]) } void CGEDebugger::DescribeSecondPreview(const GPUgstate &state, char desc[256]) { + if (secondTrackX_ != 0xFFFFFFFF) { + uint32_t x = secondTrackX_; + uint32_t y = secondTrackY_; + if (showClut_) { + uint32_t clutWidth = secondBuffer_->GetStride() / 16; + x = y * clutWidth + x; + y = 0; + } + + if (x < secondBuffer_->GetStride() && y < secondBuffer_->GetHeight()) { + u32 pix = secondBuffer_->GetRawPixel(x, y); + DescribePixel(pix, secondBuffer_->GetFormat(), x, y, desc); + return; + } + } + if (showClut_) { snprintf(desc, 256, "CLUT: 0x%08x (%d)", state.getClutAddress(), state.getClutPaletteFormat()); } else { @@ -393,6 +445,97 @@ void CGEDebugger::PreviewExport(const GPUDebugBuffer *dbgBuffer) { } } +void CGEDebugger::PreviewToClipboard(const GPUDebugBuffer *dbgBuffer, bool saveAlpha) { + if (!OpenClipboard(GetDlgHandle())) { + return; + } + EmptyClipboard(); + + uint8_t *flipbuffer = nullptr; + uint32_t w = (uint32_t)-1; + uint32_t h = (uint32_t)-1; + const uint8_t *buffer = ConvertBufferToScreenshot(*dbgBuffer, saveAlpha, flipbuffer, w, h); + if (buffer == nullptr) { + delete [] flipbuffer; + CloseClipboard(); + return; + } + + uint32_t pixelSize = saveAlpha ? 4 : 3; + uint32_t byteStride = pixelSize * w; + while ((byteStride & 3) != 0) + ++byteStride; + + // Various apps don't support alpha well, so also copy as PNG. + std::vector png; + if (saveAlpha) { + // Overallocate if we can. + png.resize(byteStride * h); + Save8888RGBAScreenshot(png, buffer, w, h); + + W32Util::ClipboardData png1("PNG", png.size()); + W32Util::ClipboardData png2("image/png", png.size()); + if (!png.empty() && png1 && png2) { + memcpy(png1.data, png.data(), png.size()); + memcpy(png2.data, png.data(), png.size()); + png1.Set(); + png2.Set(); + } + } + + W32Util::ClipboardData bitmap(CF_DIBV5, sizeof(BITMAPV5HEADER) + byteStride * h); + if (!bitmap) { + delete [] flipbuffer; + CloseClipboard(); + return; + } + + BITMAPV5HEADER *header = (BITMAPV5HEADER *)bitmap.data; + header->bV5Size = sizeof(BITMAPV5HEADER); + header->bV5Width = w; + header->bV5Height = h; + header->bV5Planes = 1; + header->bV5BitCount = saveAlpha ? 32 : 24; + header->bV5Compression = saveAlpha ? BI_BITFIELDS : BI_RGB; + header->bV5SizeImage = byteStride * h; + header->bV5CSType = LCS_WINDOWS_COLOR_SPACE; + header->bV5Intent = LCS_GM_GRAPHICS; + + if (saveAlpha) { + header->bV5RedMask = 0x000000FF; + header->bV5GreenMask = 0x0000FF00; + header->bV5BlueMask = 0x00FF0000; + // Only some applications respect the alpha mask... + header->bV5AlphaMask = 0xFF000000; + } + + uint8_t *pixels = (uint8_t *)(header + 1); + for (uint32_t y = 0; y < h; ++y) { + const uint8_t *src = buffer + y * pixelSize * w; + uint8_t *dst = pixels + (h - y - 1) * byteStride; + + if (saveAlpha) { + // No RB swap needed. + memcpy(dst, src, pixelSize * w); + continue; + } + + for (uint32_t x = 0; x < w; ++x) { + // Have to swap B/R again for the bitmap, unfortunate. + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; + src += pixelSize; + dst += pixelSize; + } + } + + delete [] flipbuffer; + + bitmap.Set(); + CloseClipboard(); +} + void CGEDebugger::UpdatePreviews() { auto memLock = Memory::Lock(); if (!PSP_IsInited()) { diff --git a/Windows/GEDebugger/GEDebugger.h b/Windows/GEDebugger/GEDebugger.h index c7d70dc3ee..9488c6ec21 100644 --- a/Windows/GEDebugger/GEDebugger.h +++ b/Windows/GEDebugger/GEDebugger.h @@ -113,6 +113,7 @@ private: void PrimaryPreviewHover(int x, int y); void SecondPreviewHover(int x, int y); void PreviewExport(const GPUDebugBuffer *buffer); + void PreviewToClipboard(const GPUDebugBuffer *buffer, bool saveAlpha); static void DescribePixel(u32 pix, GPUDebugBufferFormat fmt, int x, int y, char desc[256]); static void DescribePixelRGBA(u32 pix, GPUDebugBufferFormat fmt, int x, int y, char desc[256]); void UpdateMenus(); @@ -134,11 +135,16 @@ private: int textureLevel_ = 0; bool showClut_ = false; bool forceOpaque_ = false; - bool autoFlush_ = false; + bool autoFlush_ = true; // The most recent primary/framebuffer and texture buffers. const GPUDebugBuffer *primaryBuffer_ = nullptr; const GPUDebugBuffer *secondBuffer_ = nullptr; + uint32_t primaryTrackX_ = 0xFFFFFFFF; + uint32_t primaryTrackY_ = 0xFFFFFFFF; + uint32_t secondTrackX_ = 0xFFFFFFFF; + uint32_t secondTrackY_ = 0xFFFFFFFF; + bool updating_ = false; int previewsEnabled_ = 3; int minWidth_; diff --git a/Windows/GEDebugger/SimpleGLWindow.cpp b/Windows/GEDebugger/SimpleGLWindow.cpp index e04053ed3d..6f0464a87c 100644 --- a/Windows/GEDebugger/SimpleGLWindow.cpp +++ b/Windows/GEDebugger/SimpleGLWindow.cpp @@ -519,25 +519,34 @@ bool SimpleGLWindow::ToggleZoom() { return true; } -bool SimpleGLWindow::Hover(int mouseX, int mouseY) { - if (hoverCallback_ == nullptr) { - return false; - } - +POINT SimpleGLWindow::PosFromMouse(int mouseX, int mouseY) { float fw, fh; float x, y; GetContentSize(x, y, fw, fh); if (mouseX < x || mouseX >= x + fw || mouseY < y || mouseY >= y + fh) { // Outside of bounds. - hoverCallback_(-1, -1); - return true; + return POINT{ -1, -1 }; } float tx = (mouseX - x) * (tw_ / fw); float ty = (mouseY - y) * (th_ / fh); - hoverCallback_((int)tx, (int)ty); + return POINT{ (int)tx, (int)ty }; +} + +bool SimpleGLWindow::Hover(int mouseX, int mouseY) { + if (hoverCallback_ == nullptr) { + return false; + } + + POINT pos = PosFromMouse(mouseX, mouseY); + hoverCallback_(pos.x, pos.y); + + if (pos.x == -1 || pos.y == -1) { + // Outside of bounds, don't track. + return true; + } // Find out when they are done. TRACKMOUSEEVENT tracking = {0}; @@ -564,11 +573,12 @@ bool SimpleGLWindow::RightClick(int mouseX, int mouseY) { } POINT pt{mouseX, mouseY}; + POINT pos = PosFromMouse(mouseX, mouseY); - rightClickCallback_(0); + rightClickCallback_(0, pos.x, pos.y); int result = TriggerContextMenu(rightClickMenu_, hWnd_, ContextPoint::FromClient(pt)); if (result > 0) { - rightClickCallback_(result); + rightClickCallback_(result, pos.x, pos.y); } return true; diff --git a/Windows/GEDebugger/SimpleGLWindow.h b/Windows/GEDebugger/SimpleGLWindow.h index caf90b8b6d..605fb0c37f 100644 --- a/Windows/GEDebugger/SimpleGLWindow.h +++ b/Windows/GEDebugger/SimpleGLWindow.h @@ -117,7 +117,7 @@ struct SimpleGLWindow { } // Called first with 0 that it's opening, then the selected item. - void SetRightClickMenu(ContextMenuID menu, std::function callback) { + void SetRightClickMenu(ContextMenuID menu, std::function callback) { rightClickCallback_ = callback; rightClickMenu_ = menu; } @@ -136,6 +136,7 @@ protected: bool Leave(); bool RightClick(int mouseX, int mouseY); bool ToggleZoom(); + POINT PosFromMouse(int mouseX, int mouseY); const u8 *Reformat(const u8 *data, Format fmt, u32 numPixels); HWND hWnd_; @@ -173,6 +174,6 @@ protected: std::function redrawCallback_; std::function hoverCallback_; - std::function rightClickCallback_; + std::function rightClickCallback_; ContextMenuID rightClickMenu_; }; diff --git a/Windows/GEDebugger/TabState.cpp b/Windows/GEDebugger/TabState.cpp index 63a7199ed6..0a50b4fd19 100644 --- a/Windows/GEDebugger/TabState.cpp +++ b/Windows/GEDebugger/TabState.cpp @@ -447,7 +447,7 @@ void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enab swprintf(dest, 255, L"%S", texformats[value]); } else if ((value & 0xF) < (u32)ARRAY_SIZE(texformats)) { - swprintf(dest, 255, L"%S (extra bits %06x)", texformats[value & 0xF], value); + swprintf(dest, 255, L"%S (extra bits %06x)", texformats[value & 0xF], value & ~0xF); } else { swprintf(dest, 255, L"%06x", value); diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index 5618e2f1f5..818f398420 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -131,9 +131,7 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m return false; } - bool splitSubmit = g_Config.bGfxDebugSplitSubmit; - - draw_ = Draw::T3DCreateVulkanContext(vulkan_, splitSubmit); + draw_ = Draw::T3DCreateVulkanContext(vulkan_); SetGPUBackend(GPUBackend::VULKAN, vulkan_->GetPhysicalDeviceProperties(deviceNum).properties.deviceName); bool success = draw_->CreatePresets(); _assert_msg_(success, "Failed to compile preset shaders"); diff --git a/Windows/InputBox.cpp b/Windows/InputBox.cpp index 1d4fd02e7e..632a8134fe 100644 --- a/Windows/InputBox.cpp +++ b/Windows/InputBox.cpp @@ -49,12 +49,12 @@ bool InputBox_GetString(HINSTANCE hInst, HWND hParent, const wchar_t *title, con if (defaultValue.size() < 255) textBoxContents = ConvertUTF8ToWString(defaultValue); else - textBoxContents = L""; + textBoxContents.clear(); if (title != NULL) windowTitle = title; else - windowTitle = L""; + windowTitle.clear(); if (IDOK == DialogBox(hInst, (LPCWSTR)IDD_INPUTBOX, hParent, InputBoxFunc)) { outvalue = ConvertWStringToUTF8(out); diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index f483a55d9f..733e270db3 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -334,9 +334,6 @@ namespace MainWindow dwStyle &= ~WS_POPUP; // Re-add caption and border styles. dwStyle |= WS_OVERLAPPEDWINDOW; - - // Put back the menu bar. - ::SetMenu(hWnd, menu); } else { // If the window was maximized before going fullscreen, make sure to restore first // in order not to have the taskbar show up on top of PPSSPP. @@ -353,8 +350,8 @@ namespace MainWindow ::SetWindowLong(hWnd, GWL_STYLE, dwStyle); - // Remove the menu bar. This can trigger WM_SIZE - ::SetMenu(hWnd, goingFullscreen ? NULL : menu); + // Remove the menu bar. This can trigger WM_SIZE because the contents change size. + ::SetMenu(hWnd, goingFullscreen || !g_Config.bShowMenuBar ? NULL : menu); if (g_Config.UseFullScreen() != goingFullscreen) { g_Config.bFullScreen = goingFullscreen; diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index 6aba9d275a..384bb9a0f7 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -911,7 +911,7 @@ namespace MainWindow { if (lastSlash) { fn = lastSlash + 1; } else { - fn = ""; + fn.clear(); } PSPFileInfo info = pspFileSystem.GetFileInfo(filename); diff --git a/Windows/W32Util/Misc.cpp b/Windows/W32Util/Misc.cpp index 657a0193d2..4d9d101e41 100644 --- a/Windows/W32Util/Misc.cpp +++ b/Windows/W32Util/Misc.cpp @@ -59,7 +59,8 @@ namespace W32Util } BOOL CopyTextToClipboard(HWND hwnd, const std::wstring &wtext) { - OpenClipboard(hwnd); + if (!OpenClipboard(hwnd)) + return FALSE; EmptyClipboard(); HANDLE hglbCopy = GlobalAlloc(GMEM_MOVEABLE, (wtext.size() + 1) * sizeof(wchar_t)); if (hglbCopy == NULL) { @@ -156,6 +157,31 @@ namespace W32Util } ShellExecute(nullptr, nullptr, moduleFilename.c_str(), cmdline, workingDirectory.c_str(), SW_SHOW); } + + ClipboardData::ClipboardData(const char *format, size_t sz) { + format_ = RegisterClipboardFormatA(format); + handle_ = format_ != 0 ? GlobalAlloc(GHND, sz) : 0; + data = handle_ != 0 ? GlobalLock(handle_) : nullptr; + } + + ClipboardData::ClipboardData(UINT format, size_t sz) { + format_ = format; + handle_ = GlobalAlloc(GHND, sz); + data = handle_ != 0 ? GlobalLock(handle_) : nullptr; + } + + ClipboardData::~ClipboardData() { + if (handle_ != 0) { + GlobalUnlock(handle_); + GlobalFree(handle_); + } + } + + void ClipboardData::Set() { + if (format_ == 0 || handle_ == 0 || data == 0) + return; + SetClipboardData(format_, handle_); + } } static constexpr UINT_PTR IDT_UPDATE = 0xC0DE0042; @@ -288,9 +314,49 @@ int GenericListControl::HandleNotify(LPARAM lParam) { return 0; } + if (mhdr->code == LVN_INCREMENTALSEARCH) { + NMLVFINDITEM *request = (NMLVFINDITEM *)lParam; + uint32_t supported = LVFI_WRAP | LVFI_STRING | LVFI_PARTIAL | LVFI_SUBSTRING; + if ((request->lvfi.flags & ~supported) == 0 && (request->lvfi.flags & LVFI_STRING) != 0) { + bool wrap = (request->lvfi.flags & LVFI_WRAP) != 0; + bool partial = (request->lvfi.flags & (LVFI_PARTIAL | LVFI_SUBSTRING)) != 0; + + // It seems like 0 is always sent for start, let's override. + int startRow = request->iStart; + if (startRow == 0) + startRow = GetSelectedIndex(); + int result = OnIncrementalSearch(startRow, request->lvfi.psz, wrap, partial); + if (result != -1) { + request->lvfi.flags = LVFI_PARAM; + request->lvfi.lParam = (LPARAM)result; + } + } + } + return 0; } +int GenericListControl::OnIncrementalSearch(int startRow, const wchar_t *str, bool wrap, bool partial) { + int size = GetRowCount(); + size_t searchlen = wcslen(str); + if (!wrap) + size -= startRow; + + // We start with the earliest column, preferring matches on the leftmost columns by default. + for (int c = 0; c < columnCount; ++c) { + for (int i = 0; i < size; ++i) { + int r = (startRow + i) % size; + stringBuffer[0] = 0; + GetColumnText(stringBuffer, r, c); + int difference = partial ? _wcsnicmp(str, stringBuffer, searchlen) : _wcsicmp(str, stringBuffer); + if (difference == 0) + return r; + } + } + + return -1; +} + void GenericListControl::Update() { if (!updateScheduled_) { SetTimer(handle, IDT_UPDATE, UPDATE_DELAY, nullptr); diff --git a/Windows/W32Util/Misc.h b/Windows/W32Util/Misc.h index 8a43c55b59..d20998fdc9 100644 --- a/Windows/W32Util/Misc.h +++ b/Windows/W32Util/Misc.h @@ -14,6 +14,22 @@ namespace W32Util void ExitAndRestart(bool overrideArgs = false, const std::string &args = ""); void SpawnNewInstance(bool overrideArgs = false, const std::string &args = ""); void GetSelfExecuteParams(std::wstring &workingDirectory, std::wstring &moduleFilename); + + struct ClipboardData { + ClipboardData(const char *format, size_t sz); + ClipboardData(UINT format, size_t sz); + ~ClipboardData(); + + void Set(); + + operator bool() { + return data != nullptr; + } + + UINT format_; + HANDLE handle_; + void *data; + }; } struct GenericListViewColumn @@ -68,6 +84,8 @@ protected: virtual bool OnRowPrePaint(int row, LPNMLVCUSTOMDRAW msg) { return false; } virtual bool OnColPrePaint(int row, int col, LPNMLVCUSTOMDRAW msg) { return false; } + virtual int OnIncrementalSearch(int startRow, const wchar_t *str, bool wrap, bool partial); + private: static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); void ProcessUpdate(); diff --git a/Windows/W32Util/ShellUtil.cpp b/Windows/W32Util/ShellUtil.cpp index c6d3a2bc49..bd53935d27 100644 --- a/Windows/W32Util/ShellUtil.cpp +++ b/Windows/W32Util/ShellUtil.cpp @@ -144,7 +144,7 @@ namespace W32Util files.push_back(directory); } else { while (*temp) { - files.push_back(directory + "\\" + ConvertWStringToUTF8(temp)); + files.emplace_back(directory + "\\" + ConvertWStringToUTF8(temp)); temp += wcslen(temp) + 1; } } @@ -199,7 +199,7 @@ namespace W32Util switch (type_) { case DIR: filename_ = BrowseForFolder(parent_, title_.c_str()); - result_ = filename_ != ""; + result_ = !filename_.empty(); complete_ = true; break; diff --git a/Windows/main.cpp b/Windows/main.cpp index 4c0752bda8..90e216f283 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -34,6 +34,7 @@ #include "Common/System/Display.h" #include "Common/System/NativeApp.h" #include "Common/System/System.h" +#include "Common/File/FileUtil.h" #include "Common/File/VFS/VFS.h" #include "Common/File/VFS/AssetReader.h" #include "Common/Data/Text/I18n.h" @@ -109,9 +110,12 @@ static std::thread inputBoxThread; static bool inputBoxRunning = false; void OpenDirectory(const char *path) { + // SHParseDisplayName can't handle relative paths, so normalize first. + std::string resolved = ReplaceAll(File::ResolvePath(path), "/", "\\"); + SFGAOF flags; PIDLIST_ABSOLUTE pidl = nullptr; - HRESULT hr = SHParseDisplayName(ConvertUTF8ToWString(ReplaceAll(path, "/", "\\")).c_str(), nullptr, &pidl, 0, &flags); + HRESULT hr = SHParseDisplayName(ConvertUTF8ToWString(resolved).c_str(), nullptr, &pidl, 0, &flags); if (pidl) { if (SUCCEEDED(hr)) @@ -207,7 +211,7 @@ std::string System_GetProperty(SystemProperty prop) { if (wstr) retval = ConvertWStringToUTF8(wstr); else - retval = ""; + retval.clear(); GlobalUnlock(handle); CloseClipboard(); } diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index 9ffc1747f7..c04801767e 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -784,6 +784,10 @@ BEGIN POPUP "gepreviewoptions" BEGIN MENUITEM "Export Image...", ID_GEDBG_EXPORT_IMAGE + MENUITEM "Copy Opaque Image", ID_GEDBG_COPY_IMAGE + MENUITEM "Copy Image With Alpha", ID_GEDBG_COPY_IMAGE_ALPHA + MENUITEM "Track Pixel", ID_GEDBG_TRACK_PIXEL + MENUITEM "Stop Tracking Pixel", ID_GEDBG_TRACK_PIXEL_STOP MENUITEM "Show Prim Preview", ID_GEDBG_ENABLE_PREVIEW END POPUP "matrixoptions" diff --git a/Windows/resource.h b/Windows/resource.h index 9ba6bc7946..ab68ee494f 100644 --- a/Windows/resource.h +++ b/Windows/resource.h @@ -333,6 +333,10 @@ #define ID_GEDBG_SHOWONTOPRIGHT 40221 #define IDC_GEDBG_STEPVSYNC 40222 #define ID_GEDBG_SETCOND 40223 +#define ID_GEDBG_COPY_IMAGE 40224 +#define ID_GEDBG_COPY_IMAGE_ALPHA 40225 +#define ID_GEDBG_TRACK_PIXEL 40226 +#define ID_GEDBG_TRACK_PIXEL_STOP 40227 // Dummy option to let the buffered rendering hotkey cycle through all the options. @@ -346,7 +350,7 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 256 -#define _APS_NEXT_COMMAND_VALUE 40224 +#define _APS_NEXT_COMMAND_VALUE 40228 #define _APS_NEXT_CONTROL_VALUE 1202 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/android/PicoManifest.xml b/android/PicoManifest.xml new file mode 100644 index 0000000000..081b497ec9 --- /dev/null +++ b/android/PicoManifest.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/android/build.gradle b/android/build.gradle index 14b7d5da92..47eef16fd7 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -103,7 +103,10 @@ android { gold { res.srcDirs = ['gold/res'] } - quest { + vr_pico { + manifest.srcFile 'PicoManifest.xml' + } + vr_quest { manifest.srcFile 'QuestManifest.xml' } } @@ -145,7 +148,7 @@ android { abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } } - quest { + vr_pico { applicationId 'org.ppsspp.ppsspp' dimension "variant" externalNativeBuild { @@ -157,6 +160,27 @@ android { '-DANDROID_CPP_FEATURES=', '-DANDROID_STL=c++_static', '-DANDROID_ARM_NEON=TRUE', + '-DOPENXR_PLATFORM_PICO=TRUE', + '-DOPENXR=TRUE' + } + } + ndk { + abiFilters 'arm64-v8a' + } + } + vr_quest { + applicationId 'org.ppsspp.ppsspp' + dimension "variant" + externalNativeBuild { + cmake { + // Available arguments listed at https://developer.android.com/ndk/guides/cmake.html + arguments '-DANDROID=true', + '-DANDROID_PLATFORM=android-16', + '-DANDROID_TOOLCHAIN=clang', + '-DANDROID_CPP_FEATURES=', + '-DANDROID_STL=c++_static', + '-DANDROID_ARM_NEON=TRUE', + '-DOPENXR_PLATFORM_QUEST=TRUE', '-DOPENXR=TRUE' } } @@ -171,9 +195,12 @@ android { 'normalOptimized', // for testing 'normalRelease', // for Google Play releases 'goldRelease', // for Google Play releases - 'questDebug', // for VR debugging - 'questOptimized', // for VR testing - 'questRelease', // for VR releases + 'vr_picoDebug', // for VR debugging on Pico + 'vr_picoOptimized', // for VR testing on Pico + 'vr_picoRelease', // for VR releases on Pico + 'vr_questDebug', // for VR debugging on Quest + 'vr_questOptimized',// for VR testing on Quest + 'vr_questRelease', // for VR releases on Quest ] variant.setIgnore(!needed) } diff --git a/android/jni/Android.mk b/android/jni/Android.mk index b47db9d5cc..42cae5cb28 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -51,6 +51,7 @@ VULKAN_FILES := \ $(SRC)/Common/GPU/Vulkan/thin3d_vulkan.cpp \ $(SRC)/Common/GPU/Vulkan/VulkanQueueRunner.cpp \ $(SRC)/Common/GPU/Vulkan/VulkanRenderManager.cpp \ + $(SRC)/Common/GPU/Vulkan/VulkanFrameData.cpp \ $(SRC)/Common/GPU/Vulkan/VulkanLoader.cpp \ $(SRC)/Common/GPU/Vulkan/VulkanContext.cpp \ $(SRC)/Common/GPU/Vulkan/VulkanDebug.cpp \ @@ -349,6 +350,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/GPU/Common/PostShader.cpp \ $(SRC)/GPU/Common/ShaderUniforms.cpp \ $(SRC)/GPU/Common/VertexShaderGenerator.cpp \ + $(SRC)/GPU/Common/GeometryShaderGenerator.cpp \ $(SRC)/GPU/Debugger/Breakpoints.cpp \ $(SRC)/GPU/Debugger/Debugger.cpp \ $(SRC)/GPU/Debugger/GECommandTable.cpp \ @@ -583,14 +585,17 @@ LIBARMIPS_FILES := \ $(SRC)/ext/armips/Commands/CommandSequence.cpp \ $(SRC)/ext/armips/Core/ELF/ElfFile.cpp \ $(SRC)/ext/armips/Core/ELF/ElfRelocator.cpp \ + $(SRC)/ext/armips/Core/Allocations.cpp \ $(SRC)/ext/armips/Core/Assembler.cpp \ $(SRC)/ext/armips/Core/Common.cpp \ $(SRC)/ext/armips/Core/Expression.cpp \ + $(SRC)/ext/armips/Core/ExpressionFunctionHandler.cpp \ $(SRC)/ext/armips/Core/ExpressionFunctions.cpp \ $(SRC)/ext/armips/Core/FileManager.cpp \ $(SRC)/ext/armips/Core/Misc.cpp \ $(SRC)/ext/armips/Core/SymbolData.cpp \ $(SRC)/ext/armips/Core/SymbolTable.cpp \ + $(SRC)/ext/armips/Core/Types.cpp \ $(SRC)/ext/armips/Parser/DirectivesParser.cpp \ $(SRC)/ext/armips/Parser/ExpressionParser.cpp \ $(SRC)/ext/armips/Parser/Parser.cpp \ @@ -599,6 +604,7 @@ LIBARMIPS_FILES := \ $(SRC)/ext/armips/Util/CRC.cpp \ $(SRC)/ext/armips/Util/EncodingTable.cpp \ $(SRC)/ext/armips/Util/FileClasses.cpp \ + $(SRC)/ext/armips/Util/FileSystem.cpp \ $(SRC)/ext/armips/Util/Util.cpp LOCAL_MODULE := libarmips diff --git a/android/jni/AndroidVulkanContext.cpp b/android/jni/AndroidVulkanContext.cpp index dc68a8d75a..af8a87e751 100644 --- a/android/jni/AndroidVulkanContext.cpp +++ b/android/jni/AndroidVulkanContext.cpp @@ -101,7 +101,7 @@ bool AndroidVulkanContext::InitFromRenderThread(ANativeWindow *wnd, int desiredB bool success = true; if (g_Vulkan->InitSwapchain()) { - draw_ = Draw::T3DCreateVulkanContext(g_Vulkan, g_Config.bGfxDebugSplitSubmit); + draw_ = Draw::T3DCreateVulkanContext(g_Vulkan); SetGPUBackend(GPUBackend::VULKAN); success = draw_->CreatePresets(); // Doesn't fail, we ship the compiler. _assert_msg_(success, "Failed to compile preset shaders"); diff --git a/android/jni/Locals.mk b/android/jni/Locals.mk index f589c4f7e2..ef0d118462 100644 --- a/android/jni/Locals.mk +++ b/android/jni/Locals.mk @@ -4,7 +4,7 @@ LOCAL_CFLAGS := -DUSE_FFMPEG -DWITH_UPNP -DUSING_GLES2 -DMOBILE_DEVICE -O3 -fsigned-char -Wall -Wno-multichar -Wno-unused-variable -fno-strict-aliasing -D__STDC_CONSTANT_MACROS -DSPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS # yes, it's really CPPFLAGS for C++ # deprecated-register is generated by Android default code and causes noise. -LOCAL_CPPFLAGS := -fno-exceptions -std=gnu++11 -fno-rtti -Wno-reorder -Wno-deprecated-register -Wno-nullability-completeness +LOCAL_CPPFLAGS := -fexceptions -std=gnu++17 -fno-rtti -Wno-reorder -Wno-deprecated-register -Wno-nullability-completeness LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/../../Common \ $(LOCAL_PATH)/../.. \ @@ -16,6 +16,9 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/../../ext/libpng17 \ $(LOCAL_PATH)/../../ext/libzip \ $(LOCAL_PATH)/../../ext/zstd/lib \ + $(LOCAL_PATH)/../../ext/armips \ + $(LOCAL_PATH)/../../ext/armips/ext/filesystem/include \ + $(LOCAL_PATH)/../../ext/armips/ext/tinyformat \ $(LOCAL_PATH) LOCAL_STATIC_LIBRARIES := libzip glslang-build miniupnp-build diff --git a/android/jni/TestRunner.cpp b/android/jni/TestRunner.cpp index d032a9db75..64e6f7761c 100644 --- a/android/jni/TestRunner.cpp +++ b/android/jni/TestRunner.cpp @@ -107,7 +107,7 @@ bool RunTests() { // Never report from tests. std::string savedReportHost = g_Config.sReportHost; - g_Config.sReportHost = ""; + g_Config.sReportHost.clear(); for (size_t i = 0; i < ARRAY_SIZE(testsToRun); i++) { std::string testName = testsToRun[i]; @@ -116,7 +116,7 @@ bool RunTests() { INFO_LOG(SYSTEM, "Preparing to execute '%s'", testName.c_str()); std::string error_string; - output = ""; + output.clear(); if (!PSP_Init(coreParam, &error_string)) { ERROR_LOG(SYSTEM, "Failed to init unittest %s : %s", testsToRun[i], error_string.c_str()); PSP_CoreParameter().pixelWidth = pixel_xres; diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index d430322afc..365bbef903 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -1057,16 +1057,20 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, SetCurrentThreadName("AndroidRender"); } + if (IsVRBuild() && !StartVRRender()) + return; + if (useCPUThread) { // This is the "GPU thread". - if (graphicsContext) - graphicsContext->ThreadFrame(); + if (!graphicsContext || !graphicsContext->ThreadFrame()) + return; } else { UpdateRunLoopAndroid(env); } if (IsVRBuild()) { UpdateVRInput(NativeKey, NativeTouch, g_Config.bHapticFeedback, dp_xscale, dp_yscale); + FinishVRRender(); } } diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 758d7eb6a8..bed2de57e3 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -1547,6 +1547,6 @@ public abstract class NativeActivity extends Activity { } private boolean isVRDevice() { - return BuildConfig.FLAVOR.compareTo("quest") == 0; + return BuildConfig.FLAVOR.startsWith("vr_"); } } diff --git a/assets/compat.ini b/assets/compat.ini index 3d2846d377..2fbe053168 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -156,89 +156,6 @@ NPJH90062 = true # Phantasy Star Portable Infinity Demo NPJH90157 = true # Infinity demo -[DisableRangeCulling] -# Phantasy Star games also have range culling issues. -# Phantasy Star Portable 2 and Infinity -ULJM05309 = true -ULUS10410 = true -ULES01218 = true -ULJM08023 = true -ULES01218 = true -# Phantasy Star Portable 1 Demo -NPUH90023 = true -# Phantasy Star Portable 2 -ULES01439 = true -ULUS10529 = true -ULJM05493 = true -NPJH50043 = true -ULJM08030 = true -NPUH90023 = true -ULJM91014 = true -NPJH90002 = true -ULJM05732 = true -NPJH50332 = true -# Phantasy Star Portable 2 JP Demo -ULJM91018 = true -NPJH90062 = true -# Phantasy Star Portable Infinity Demo -NPJH90157 = true # Infinity demo - -# Test Drive Unlimited -ULET00386 = true -ULES00637 = true -ULKS46126 = true -ULUS10249 = true - -# Metal Gear Solid : Peace Walker (see issue #12348) -ULUS10509 = true -ULES01372 = true -ULJM08038 = true -NPJH50045 = true -ULJM05630 = true -NPJH90082 = true -NPJH90063 = true - -# Metal Gear Solid : Peace Walker Demo Ops (see issue #12348) -NPUH90066 = true -NPHH00145 = true -NPEH90023 = true - -# Metal Gear Solid: Portable Ops (see issue #12348) -ULES00645 = true -ULJM05193 = true -ULJM05227 = true -ULUS10202 = true -ULES01003 = true -ULJM05261 = true -ULUS10290 = true -ULED90040 = true -ULJM05284 = true -ULES00645 = true - -# Splinter Cell Essentials (#13035) -ULES00281 = true -ULUS10070 = true - -# Asphalt 2 (#14299) -ULES00719 = true -# No US release, it seems - -# Star Wars: Lethal Alliance (#11551) -ULES00599 = true -ULUS10188 = true - -# Digimon World Re:Digitize -ULJS00496 = true -NPJH50588 = true -ULAS42319 = true - -# NOVA: Near Orbit Vanguard Alliance -NPUZ00179 = true -NPEZ00222 = true - -# Street Riders (only region found. See #14746) -ULES00276 = true - [ClearToRAM] # SOCOM Navy Seals games require this. See issue #8973. # Navy Seals : Tactical Strike @@ -520,6 +437,14 @@ ULES01086 = true # LEGO Batman: The Videogame ULUS10380 = true ULES01151 = true +# Burnout Dominator +ULUS10236 = true +ULES00750 = true +ULJM05242 = true +ULJM05371 = true +NPJH50304 = true +ULES00703 = true + # TODO: There are many more. [RequireBlockTransfer] @@ -584,54 +509,6 @@ NPJH50304 = true ULES00703 = true ULAS42095 = true -[MaliDepthStencilBugWorkaround] -# See issue #13833 where the map is supposed to be round but is not. - -# Midnight Club: LA Remix -ULUS10383 = true -ULES01144 = true -ULJS00180 = true -ULJS00267 = true -ULJM05904 = true -NPJH50440 = true -# Midnight Club 3 : DUB edition -ULUS10021 = true -ULES00108 = true - -# Tales of Phantasia - Narikiri Dungeon X. See #15526 -ULJS00293 = true - -# The Simpsons Game - issue #9830 -ULUS10295 = true -ULES00975 = true -ULES00979 = true -ULES00978 = true -ULES00977 = true -ULES00976 = true - -# Surf's Up - see issue #15016 -ULES00816 = true -ULES00817 = true -ULUS10262 = true - -# Kenka Bancho - Badass Rumble - see issue #15016 -ULUS10442 = true -ULJS00166 = true -UCAS40231 = true -ULJS19030 = true -NPJH50223 = true -ULJS19082 = true - -# Nanoha Magical Girl - see issue #15016 -ULJS00384 = true -ULJS00385 = true -ULJS19078 = true - -# Suikoden Woven Web of The Centuries / Genso Suikoden: Tsumugareshi Hyakunen no Toki -ULJM05886 = true -ULJM08063 = true -NPJH50535 = true - [RequireDefaultCPUClock] # GOW : Ghost of Sparta UCUS98737 = true @@ -749,6 +626,22 @@ ULUS10428 = true ULES01375 = true ULUS10429 = true +# Silent Hill: Origins +# Avoids readback. +ULES00869 = true +ULUS10285 = true +ULKS46161 = true +ULJM05281 = true +NPJH50051 = true + +# Silent Hill: Shattered Memories +# Avoids readback. +ULUS10450 = true +ULES01352 = true +ULJM05564 = true +NPJH50148 = true +ULAS42189 = true + [IntraVRAMBlockTransferAllowCreateFB] # Final Fantasy - Type 0 ULJM05900 = true @@ -1152,6 +1045,17 @@ ULES00262 = true ULUS10064 = true ULKS46087 = true +# Burnout Dominator - lens flare effect (issue #11100) +ULUS10236 = true +ULES00703 = true + +# Need for Speed - Shift (same as Burnout Dominator) +ULUS10462 = true +ULES01275 = true +ULJM05494 = true +NPJH50143 = true +ULJM05738 = true + [DisableFirstFrameReadback] # Wipeout Pure: Temporary workaround for lens flare flicker. See #13344 UCUS98612 = true @@ -1300,5 +1204,39 @@ ULES00262 = true ULUS10064 = true ULKS46087 = true +# Burnout Dominator - lens flare effect (issue #11100) +# Some of the steps don't work at high resolution yet. +ULUS10236 = true +ULES00703 = true + +# Need for Speed - Shift (same as Burnout Dominator) +ULUS10462 = true +ULES01275 = true +ULJM05494 = true +NPJH50143 = true +ULJM05738 = true + +[NearestFilteringOnFramebufferCreate] +# Ridge Racer speedometer dynamic CLUT problem - they rely on some palette entries +# from memory, and render to the rest of the palette. The palette entries loaded from memory +# must not be blurred by filtering, so nearest it is. See issue #8509 + +# Ridge Racer +ULJS00001 = true +ULUS10001 = true +UCKS45002 = true +UCES00002 = true +ULJS19002 = true +UCKS45053 = true +NPJH50140 = true + +# Ridge Racer 2 +ULJS00080 = true +UCKS45032 = true +UCES00422 = true +UCAS40273 = true +NPJH50366 = true + [AllowDownloadCLUT] -# Temporary compatibility option, while developing a GPU CLUT-from-framebuffer path. +# Temporary compatibility option, while working on the GPU CLUT-from-framebuffer path. +# Not required for any games now that it works, but might be useful for development. diff --git a/assets/compatvr.ini b/assets/compatvr.ini index 4c47ddffa9..c3ec3f926a 100644 --- a/assets/compatvr.ini +++ b/assets/compatvr.ini @@ -29,6 +29,24 @@ # Issue numbers refer to issues on https://github.com/hrydgard/ppsspp/issues # ======================================================================================== + +[Skyplane] +# Grand Theft Auto: Liberty City Stories +ULUS10041 = true +ULES00151 = true +ULJM05255 = true +ULJM05359 = true +ULJM05885 = true +NPJH50825 = true + +# Grand Theft Auto: Vice City Stories +ULES00502 = true +ULUS10160 = true +ULJM05297 = true +ULJM05395 = true +ULJM05884 = true +NPJH50827 = true + [UnitsPerMeter] # Values exported from https://github.com/CarlKenner/ppsspp/tree/VR/Sys/GameSettings ELF000000 = 10.0 diff --git a/assets/lang/pl_PL.ini b/assets/lang/pl_PL.ini index 4493f6db83..e963535aee 100644 --- a/assets/lang/pl_PL.ini +++ b/assets/lang/pl_PL.ini @@ -1,20 +1,20 @@ [Audio] Alternate speed volume = Alternate speed volume Audio backend = Sterownik dźwięku (wymagany restart) -Audio Error = Audio Error +Audio Error = Błąd Audio AudioBufferingForBluetooth = Bufory dźwięku dost. do Bluetooth (wolniejsze) Auto = Automatyczny -Device = Device -Disabled = Disabled +Device = Urządzenie +Disabled = Wyłączone DSound (compatible) = DSound (kompatybilny) Enable Sound = Włącz dźwięk Global volume = Głośność globalna -Microphone = Microphone -Microphone Device = Microphone device -Mute = Mute -Reverb volume = Reverb volume -Use new audio devices automatically = Use new audio devices automatically -Use global volume = Use global volume +Microphone = Mikrofon +Microphone Device = Mikrofon +Mute = Wycisz +Reverb volume = Pogłos +Use new audio devices automatically = Automatycznie używaj nowego urządzenia audio +Use global volume = Używaj głośności globalnej WASAPI (fast) = WASAPI (szybki) [Controls] @@ -27,44 +27,44 @@ AnalogLimiter Tip = Jeśli "ogranicznik analoga" jest aktywny Auto = Automatyczny Auto-centering analog stick = Autocentrowanie gałki analogowej Auto-hide buttons after seconds = Automatyczne ukrywanie przycisków po: -Auto-rotation speed = Auto-rotation speed +Auto-rotation speed = Prędkość automatycznego obracania Auto-switch = Auto-switch Base tilt position = Base tilt position Binds = Binds Button Binding = Button Binding Button Opacity = Przeźroczystość przycisków Button style = Styl przycisków -Calibrate Analog Stick = Calibrate Analog Stick +Calibrate Analog Stick = Kalibracja Gałki Analogowej Calibrate D-Pad = Kalibracja krzyżaka -Calibrated = Calibrated +Calibrated = Skalibrowany Calibration = Kalibracja Circular stick input = Circular stick input Classic = Klasyczny Confine Mouse = Zablokuj myszkę w oknie Control Mapping = Mapowanie przycisków -Custom Key Setting = Custom Key Setting -Customize = Customize +Custom Key Setting = Niestandardowe Ustawienia Klawiszy +Customize = Ustawienia Niestandardowe Customize Touch Controls = Edycja układu przycisków ekranowych... Customize tilt = Zmodyfikuj przechył... D-PAD = Krzyżak Deadzone radius = Promień martwej strefy Disable D-Pad diagonals (4-way touch) = Wył. przekątne krzyżaka (pozostaw 4 kierunki) -Disable diagonal input = Disable diagonal input -Double tap = Double tap -Enable gesture control = Enable gesture control -Enable standard shortcut keys = Enable standard shortcut keys -Gesture = Gesture -Gesture mapping = Gesture mapping +Disable diagonal input = Wyłącz wejście cyfrowe +Double tap = Dwukrotne dotknięcie +Enable gesture control = Włącz kontrolę kestami +Enable standard shortcut keys = Włącz standardowe klawisze skrótów +Gesture = Gesty +Gesture mapping = Konfiguracja gestów Glowing borders = Świecące krawędzie HapticFeedback = Wibracje Hide touch analog stick background circle = Hide touch analog stick background circle -Icon = Icon +Icon = Ikona Ignore gamepads when not focused = Ignoruj przyciski pada gdy okno nieaktywne Ignore Windows Key = Ignoruj klawisz Windows Invert Axes = Odwrócone osie Invert Tilt along X axis = Odwróć przechył wzdłuż osi X Invert Tilt along Y axis = Odwróć przechył wzdłuż osi Y -Keep this button pressed when right analog is pressed = Keep this button pressed when right analog is pressed +Keep this button pressed when right analog is pressed = Kontynuuj naciskanie tego klawisza gdy prawa Gałka Analogowa wciśnięta Keyboard = Ustawienia klawiatury L/R Trigger Buttons = Przyciski L/R Landscape = Poziomo @@ -83,12 +83,12 @@ Portrait Reversed = Odwrócone pionowo PSP Action Buttons = Klawisze akcji PSP Raw input = Raw input Repeat mode = Repeat mode -Reset to defaults = Reset to defaults +Reset to defaults = Domyślne Screen aligned to ground = Screen aligned to ground Screen at right angle to ground = Screen at right angle to ground Screen Rotation = Obrót ekranu seconds, 0 : off = sekundy, 0 = wył. -Sensitivity (scale) = Sensitivity (scale) +Sensitivity (scale) = Czułość (dostosuj) Sensitivity = Czułość Shape = Shape Show Touch Pause Menu Button = Pokaż przycisk Menu Pauzy @@ -101,7 +101,7 @@ Tilt Input Type = Typ przechyłu Tilt Sensitivity along X axis = Czułość przechyłu wzdłuż osi X Tilt Sensitivity along Y axis = Czułość przechyłu wzdłuż osi Y To Calibrate = Aby skalibrować, połóż urządzenie na płaskiej powierzchni i wciśnij "Kalibracja". -Toggle mode = Toggle mode +Toggle mode = Zmień tryb Touch Control Visibility = Kontroler dotykowy (widoczny) Use custom right analog = Use custom right analog Use Mouse Control = Włącz kontrolę myszką @@ -175,11 +175,11 @@ More Settings... = Więcej ustawień... Nearest = Najbliższe Non-Buffered Rendering = &Nie renderuj efektów z bufora (niebuforowane, szybsze) Off = Wyłącz -Open Chat = Open Chat +Open Chat = Otwórz Chat Open Directory... = Otwórz katalog... Open from MS:/PSP/GAME... = Otwórz z MS:/PSP/GAME... Open Memory Stick = Otwórz Kartę Pamięci -Open New Instance = Open new instance +Open New Instance = Otwórz nową instancję OpenGL = &OpenGL Pause = Pauza Pause When Not Focused = Pauzuj, gdy okno nieaktywne @@ -234,15 +234,15 @@ Disabled JIT functionality = Wyłącz funkcje JIT Draw Frametimes Graph = Draw frametimes graph Dump Decrypted Eboot = Zachowuj odszyfrowany EBOOT.BIN przy starcie Dump Frame GPU Commands = Zrzuć klatkę komend GPU -Enable driver bug workarounds = Enable driver bug workarounds +Enable driver bug workarounds = Uruchom obejścia błędów sterownika Enable Logging = Włącz dziennik zdarzeń debugera Enter address = Wprowadź adres FPU = FPU Framedump tests = Framedump tests Frame Profiler = Profiler klatki GPU Driver Test = Test sterownika GPU -GPU Log Profiler = GPU Log Profiler -GPU Profile = GPU profile +GPU Log Profiler = Profilowanie logów GPU +GPU Profile = Profil GPU Jit Compare = Porównywanie JIT JIT debug tools = Narzędzia debugowania JIT Language = Język @@ -260,7 +260,7 @@ Reset = Reset Reset limited logging = Reset limited logging RestoreDefaultSettings = Czy na pewno chcesz przywrócić domyślne ustawienia?\nUstawienia sterowania pozostaną niezmienione.\n\n\nNie można tego cofnąć.\nZrestartuj PPSSPP, aby zastosować zmiany. RestoreGameDefaultSettings = Czy na pewno chcesz przywrócić ustawienia specyficzne dla danych gier\npowrót do ustawień domyślnych PPSSPP? -Resume = Resume +Resume = Wznów Run CPU Tests = Uruchom testy CPU Save language ini = Zapisz plik ini języka Save new textures = Zapisz nową teksturę @@ -269,11 +269,11 @@ Show Developer Menu = Pokaż przycisk menu dewelopera Show on-screen messages = Show on-screen messages Stats = Statystyki System Information = Informacje o systemie -Texture ini file created = Texture ini file created +Texture ini file created = Stworzono plik ini Tekstury Texture Replacement = Podmiana tekstur Toggle Audio Debug = Wł./Wył. debugowanie audio Toggle Freeze = Wł./Wył. zamrożenie -Touchscreen Test = Touchscreen test +Touchscreen Test = Test ekranu dotykowego VFPU = VFPU [Dialog] @@ -283,16 +283,16 @@ Back = Powrót Cancel = Anuluj Center = Wyśrodkuj ChangingGPUBackends = Zmiana sterownika grafiki wymaga restartu PPSSPP. Zrestartować teraz? -ChangingInflightFrames = Changing graphics command buffering requires PPSSPP to restart. Restart now? -Channel: = Channel: +ChangingInflightFrames = Zmiana komend buffera grafiki wymaka restartu PPSSPP. Zrestartować teraz? +Channel: = Kanał: Choose PPSSPP save folder = Wybierz folder do zapisu stanów PPSSPP Confirm Overwrite = Czy chcesz nadpisać te dane? Confirm Save = Czy chcesz zapisać te dane? ConfirmLoad = Wczytać te dane? -ConnectingAP = Connecting to the access point.\nPlease wait... -ConnectingPleaseWait = Connecting.\nPlease wait... -ConnectionName = Connection name -Corrupted Data = Corrupted data +ConnectingAP = Łączenie do punktu dostępowego.\nProszę czekać... +ConnectingPleaseWait = Łączenie.\nProszę czekać... +ConnectionName = Nazwa Połączenia +Corrupted Data = Uszkodzone dane Delete = Usuń Delete all = Usuń wszystko Delete completed = Usuwanie zakończone. @@ -304,15 +304,15 @@ DeleteFailed = Nie można usunąć danych. Deleting = Usuwanie\nProszę czekać... Disable All = Wyłącz wszystko Dumps = Dumps -Edit = Edit +Edit = Edytuj Enable All = Włącz wszystko Enter = Dalej -Filter = Filter +Filter = Filtruj Finish = Zakończ GE Frame Dumps = GE Frame Dumps -Grid = Grid +Grid = Siatka Inactive = Nieaktywny -InternalError = An internal error has occurred. +InternalError = Występił wewnętrzny błąd aplikacji Load = Wczytaj Load completed = Ładowanie zakończone. Loading = Ładowanie\nProszę czekać... @@ -321,7 +321,7 @@ Move = Przenieś Network Connection = Połączenie sieciowe NEW DATA = NOWE DANE No = Nie -ObtainingIP = Obtaining IP address.\nPlease wait... +ObtainingIP = Uzyskiwanie adresu IP.\nProszę czekać... OK = OK Old savedata detected = Wykryto stary zapis gry Options = Opcje @@ -332,10 +332,10 @@ Save = Zapisz Save completed = Zapisywanie zakończone. Saving = Zapisywanie\nProszę czekać... SavingFailed = Nie można zapisać danych. -Search = Search +Search = Szukaj Select = Wybierz Shift = Shift -Skip = Skip +Skip = Pomiń Snap = Snap Space = Spacja SSID = SSID @@ -366,7 +366,7 @@ Error loading file = Wystąpił błąd: Error reading file = Błąd podczas odczytywania pliku. Failed initializing CPU/Memory = Failed initializing CPU or memory Failed to load executable: = Failed to load executable: -File corrupt = File corrupt +File corrupt = Uszkodzony plik Game disc read error - ISO corrupt = Nie można odczytać danych z dysku - plik ISO uszkodzony. GenericAllStartupError = PPSSPP failed to start up with any graphics backend. Try upgrading your graphics and other drivers. GenericBackendSwitchCrash = PPSSPP crashed while starting.\n\nThis usually means a graphics driver problem. Try upgrading your graphics drivers.\n\nGraphics backend has been switched: @@ -579,11 +579,11 @@ xBRZ = xBRZ Delete ZIP file = Usuń plik ZIP Install = Zainstaluj Install game from ZIP file? = Zainstalować grę z pliku ZIP? -Install textures from ZIP file? = Install textures from ZIP file? +Install textures from ZIP file? = Czy zainstalować teksturę z archiwum ZIP? Installed! = Zainstalowano! Texture pack doesn't support install = Texture pack doesn't support install Zip archive corrupt = ZIP archive corrupt -Zip file does not contain PSP software = ZIP file does not contain PSP software +Zip file does not contain PSP software = Archiwum ZIP nie zawiera aplikacji dla PSP [KeyMapping] Autoconfigure = Autokonfiguracja @@ -725,7 +725,7 @@ DataWillBeLostOnUninstall = Warning! Data will be lost when you uninstall PPSSPP DataWillStay = Data will stay even if you uninstall PPSSPP. Done! = Done! EasyUSBAccess = Easy USB access -Failed to move some files! = Failed to move some files! +Failed to move some files! = Nie udało się przenieść niektórych plików! Failed to save config = Failed to save config Free space = Free space Manually specify PSP folder = Manually specify PSP folder @@ -737,8 +737,8 @@ PPSSPP will restart after the change = PPSSPP will restart after the change Skip for now = Skip for now Starting move... = Starting move... That folder doesn't work as a memstick folder. = That folder doesn't work as a memstick folder. -USBAccessThrough = USB access through Android/data/org.ppsspp.ppsspp/files -USBAccessThroughGold = USB access through Android/data/org.ppsspp.ppssppgold/files +USBAccessThrough = Dostęp do USB poprzez Android/data/org.ppsspp.ppsspp/files +USBAccessThroughGold = Dostęþ do USB poprzez Android/data/org.ppsspp.ppssppgold/files Use App Private Data = Use App Private Data Use PSP folder at root of storage = Use PSP folder at root of storage Welcome to PPSSPP! = Welcome to PPSSPP! @@ -968,7 +968,7 @@ View Feedback = Zobacz opinie [Savedata] Date = Data -Filename = Nazwa +Filename = Nazwa pliku No screenshot = Brak obrazka None yet. Things will appear here after you save. = Na razie nic tu nie ma. Wpisy pojawią się, gdy zapiszesz grę. Nothing matching '%1' was found. = Nothing matching '%1' was found. @@ -1100,7 +1100,7 @@ Error: load undo state is from a different game = Error: load undo state is from Failed to load state for load undo. Error in the file system. = Failed to load state for load undo. Error in the file system. Floating symbols = Floating symbols Memory Stick folder = Zmień folder Karty Pamięci -Memory Stick size = Memory Stick size +Memory Stick size = Rozmiar Karty Pamięci Change Nickname = Zmień pseudonim ChangingMemstickPath = Zapisy gier, zapisy stanu i inne dane nie zostaną skopiowane do tego folderu.\n\nZmienić ścieżkę do folderu Karty Pamięci? ChangingMemstickPathInvalid = Ta ścieżka nie może zostać użyta do zapisu plików Karty Pamięci. @@ -1177,10 +1177,10 @@ Slot 4 = Slot 4 Slot 5 = Slot 5 Storage full = Dysk zapełniony Sustained performance mode = Tryb wysokiej wydajności -Theme = Theme +Theme = Motyw Time Format = Format czasu UI = Interfejs użytkownika -UI background animation = UI background animation +UI background animation = Animacja tła UI Sound = UI sound undo %c = backup %c USB = USB @@ -1202,7 +1202,7 @@ Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] -Default = Default +Default = Domyślny [UI Elements] %1 button = %1 button diff --git a/assets/mime/ppsspp.xml b/assets/mime/ppsspp.xml new file mode 100644 index 0000000000..26b62e56e9 --- /dev/null +++ b/assets/mime/ppsspp.xml @@ -0,0 +1,14 @@ + + + + + + + + Compressed ISO Image + Komprimiertes ISO‑Abbild + Skomprymowany obraz ISO + CSO + + + diff --git a/assets/shaders/tex_2xbrz.csh b/assets/shaders/tex_2xbrz.csh index f4a12c4cfe..639efb582e 100644 --- a/assets/shaders/tex_2xbrz.csh +++ b/assets/shaders/tex_2xbrz.csh @@ -100,11 +100,6 @@ void applyScaling(uvec2 origxy) { ivec4 blendResult = ivec4(BLEND_NONE); - // Preserve old alpha-ignoring behavior? Keep or delete? - for (int i = 0; i < 9; i++) { - v[i] &= 0xFFFFFF; - } - // Preprocess corners // Pixel Tap Mapping: --|--|--|--|-- // --|--|07|08|-- diff --git a/assets/shaders/tex_4xbrz.csh b/assets/shaders/tex_4xbrz.csh index a19a28a5f5..bd22097ef8 100644 --- a/assets/shaders/tex_4xbrz.csh +++ b/assets/shaders/tex_4xbrz.csh @@ -100,11 +100,6 @@ void applyScaling(uvec2 origxy) { ivec4 blendResult = ivec4(BLEND_NONE); - // Preserve old alpha-ignoring behavior? Keep or delete? - for (int i = 0; i < 9; i++) { - v[i] &= 0xFFFFFF; - } - // Preprocess corners // Pixel Tap Mapping: --|--|--|--|-- // --|--|07|08|-- diff --git a/cmake/Toolchains/ios.cmake b/cmake/Toolchains/ios.cmake index 4198706e0c..45a8a219bc 100644 --- a/cmake/Toolchains/ios.cmake +++ b/cmake/Toolchains/ios.cmake @@ -12,7 +12,7 @@ # PPSSPP platform flags set(MOBILE_DEVICE ON) set(USING_GLES2 ON) -set(IPHONEOS_DEPLOYMENT_TARGET 6.0) +set(IPHONEOS_DEPLOYMENT_TARGET 11.0) add_definitions( -DGL_ETC1_RGB8_OES=0 -U__STRICT_ANSI__ @@ -27,7 +27,7 @@ set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") # Standard settings set(CMAKE_SYSTEM_NAME Darwin) set(CMAKE_SYSTEM_VERSION 1) -set(CMAKE_SYSTEM_PROCESSOR armv7) +set(CMAKE_SYSTEM_PROCESSOR aarch64) set(IOS ON) set(CMAKE_CROSSCOMPILING ON) set(CMAKE_MACOSX_BUNDLE YES) @@ -66,9 +66,7 @@ set(CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS sup # set the architecture for iOS if(IOS_PLATFORM STREQUAL "OS") - # When ffmpeg has been rebuilt for arm64 use: - set(IOS_ARCH "armv7;arm64") - #set(IOS_ARCH "armv7") + set(IOS_ARCH "arm64") else() set(IOS_ARCH "i386;x86_64") endif() @@ -95,4 +93,3 @@ set(CMAKE_SYSTEM_FRAMEWORK_PATH # only search the iOS sdks, not the remainder of the host filesystem set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) - diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index 440f3c362c..e5159c989a 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -1,4 +1,8 @@ set(ARMIPS_REGEXP OFF CACHE BOOL "" FORCE) +set(ARMIPS_LIBRARY_ONLY ON CACHE BOOL "" FORCE) +if(WIN32) + set(ARMIPS_USE_STD_FILESYSTEM ON CACHE BOOL "" FORCE) +endif() add_subdirectory(armips) if(NOT USING_GLES2) diff --git a/ext/armips b/ext/armips index 7885552b20..b4335f04fa 160000 --- a/ext/armips +++ b/ext/armips @@ -1 +1 @@ -Subproject commit 7885552b208493a6a0f21663770c446c3ba65576 +Subproject commit b4335f04fa62c1b4f7833c70fbb15505b6772274 diff --git a/ext/libarmips.vcxproj b/ext/libarmips.vcxproj index b3d288f799..c0484e63d1 100644 --- a/ext/libarmips.vcxproj +++ b/ext/libarmips.vcxproj @@ -187,88 +187,88 @@ - .;$(IncludePath) + .;.\armips;.\armips\ext\filesystem\include;.\armips\ext\tinyformat;$(IncludePath) _Output\ _Output\$(ProjectName)\$(Configuration)\ $(ProjectName)d $(ProjectName)d64 - .;$(IncludePath) + .;.\armips;.\armips\ext\filesystem\include;.\armips\ext\tinyformat;$(IncludePath) _Output\ _Output\$(ProjectName)\$(Configuration)64\ $(ProjectName)dARM64 - .;$(IncludePath) + .;.\armips;.\armips\ext\filesystem\include;.\armips\ext\tinyformat;$(IncludePath) _Output\ _Output\$(ProjectName)\$(Configuration)ARM64\ $(ProjectName)dARM - .;$(IncludePath) + .;.\armips;.\armips\ext\filesystem\include;.\armips\ext\tinyformat;$(IncludePath) _Output\ _Output\$(ProjectName)\$(Configuration)ARM\ - .;$(IncludePath) + .;.\armips;.\armips\ext\filesystem\include;.\armips\ext\tinyformat;$(IncludePath) _Output\ _Output\$(ProjectName)\$(Configuration)\ $(ProjectName)tests - .;$(IncludePath) + .;.\armips;.\armips\ext\filesystem\include;.\armips\ext\tinyformat;$(IncludePath) $(ProjectName)tests64 _Output\ _Output\$(ProjectName)\$(Configuration)64\ - .;$(IncludePath) + .;.\armips;.\armips\ext\filesystem\include;.\armips\ext\tinyformat;$(IncludePath) $(ProjectName)testsARM64 _Output\ _Output\$(ProjectName)\$(Configuration)ARM64\ - .;$(IncludePath) + .;.\armips;.\armips\ext\filesystem\include;.\armips\ext\tinyformat;$(IncludePath) $(ProjectName)testsARM _Output\ _Output\$(ProjectName)\$(Configuration)ARM\ - .;$(IncludePath) + .;.\armips;.\armips\ext\filesystem\include;.\armips\ext\tinyformat;$(IncludePath) _Output\ _Output\$(ProjectName)\$(Configuration)\ $(ProjectName) $(ProjectName)64 - .;$(IncludePath) + .;.\armips;.\armips\ext\filesystem\include;.\armips\ext\tinyformat;$(IncludePath) _Output\ _Output\$(ProjectName)\$(Configuration)64\ $(ProjectName)ARM64 - .;$(IncludePath) + .;.\armips;.\armips\ext\filesystem\include;.\armips\ext\tinyformat;$(IncludePath) _Output\ _Output\$(ProjectName)\$(Configuration)ARM64\ $(ProjectName)ARM - .;$(IncludePath) + .;.\armips;.\armips\ext\filesystem\include;.\armips\ext\tinyformat;$(IncludePath) _Output\ _Output\$(ProjectName)\$(Configuration)ARM\ - Use Level3 - _CRTDBG_MAP_ALLOC;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) MultiThreadedDebug armips true ProgramDatabase + stdcpp17 Windows @@ -280,9 +280,8 @@ - Use Level3 - _CRTDBG_MAP_ALLOC;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) MultiThreadedDebug armips false @@ -290,6 +289,7 @@ true ProgramDatabase + stdcpp17 Windows @@ -301,9 +301,8 @@ - Use Level3 - _CRTDBG_MAP_ALLOC;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) MultiThreadedDebug armips false @@ -311,6 +310,7 @@ true ProgramDatabase + stdcpp17 Windows @@ -322,9 +322,8 @@ - Use Level3 - _CRTDBG_MAP_ALLOC;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + _CRTDBG_MAP_ALLOC;WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) MultiThreadedDebug armips false @@ -332,6 +331,7 @@ true ProgramDatabase + stdcpp17 Windows @@ -341,11 +341,11 @@ - Use Level3 Disabled - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) armips + stdcpp17 Windows @@ -354,11 +354,11 @@ - Use Level3 Disabled - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) armips + stdcpp17 Windows @@ -367,11 +367,11 @@ - Use Level3 Disabled - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) armips + stdcpp17 Windows @@ -380,12 +380,12 @@ - Use Level3 Disabled - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) armips MultiThreadedDebug + stdcpp17 Windows @@ -395,16 +395,16 @@ Level3 - Use MaxSpeed true true - WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) MultiThreaded armips true false ProgramDatabase + stdcpp17 Windows @@ -416,11 +416,10 @@ Level3 - Use MaxSpeed true true - WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) MultiThreaded armips false @@ -428,6 +427,7 @@ true false ProgramDatabase + stdcpp17 Windows @@ -439,11 +439,10 @@ Level3 - Use MaxSpeed true true - WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) MultiThreaded armips false @@ -451,6 +450,7 @@ true false ProgramDatabase + stdcpp17 Windows @@ -462,11 +462,10 @@ Level3 - Use MaxSpeed true true - WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions) MultiThreaded armips false @@ -474,6 +473,7 @@ true false ProgramDatabase + stdcpp17 Windows @@ -510,38 +510,28 @@ + + + - - Create - Create - Create - Create - Create - Create - Create - Create - Create - Create - Create - Create - + @@ -572,27 +562,30 @@ + + + - + diff --git a/ext/libarmips.vcxproj.filters b/ext/libarmips.vcxproj.filters index e1c8ef9a3a..61282421bb 100644 --- a/ext/libarmips.vcxproj.filters +++ b/ext/libarmips.vcxproj.filters @@ -132,7 +132,6 @@ Util - Core @@ -172,6 +171,18 @@ Archs\MIPS + + Core + + + Util + + + Core + + + Core + @@ -276,7 +287,6 @@ Util - Core @@ -319,5 +329,17 @@ Archs\MIPS + + Core + + + Util + + + Core + + + Core + \ No newline at end of file diff --git a/ext/openxr/README.md b/ext/openxr/README.md new file mode 100644 index 0000000000..13db1d3ade --- /dev/null +++ b/ext/openxr/README.md @@ -0,0 +1,14 @@ +### Introduction + +OpenXR is an interface defined by Khronos Group. Implementation of the interface is done by each VR headset vendor differently. + +### Files + +The used interface (headers in C language) is an older version because Pico doesn't support the latest one yet. +The Implementations are taken from the vendor specific SDKs. + +### Links + +- Khronos OpenXR SDK: https://github.com/KhronosGroup/OpenXR-SDK/ +- Oculus OpenXR SDK: https://developer.oculus.com/downloads/package/oculus-openxr-mobile-sdk/ +- Pico OpenXR SDK https://developer-global.pico-interactive.com/sdk?deviceId=1&platformId=3&itemId=11 diff --git a/ext/openxr/libs/pico/arm64-v8a/libopenxr_loader.so b/ext/openxr/libs/pico/arm64-v8a/libopenxr_loader.so new file mode 100644 index 0000000000..784337e64e Binary files /dev/null and b/ext/openxr/libs/pico/arm64-v8a/libopenxr_loader.so differ diff --git a/ext/openxr/libs/arm64-v8a/libopenxr_loader.so b/ext/openxr/libs/quest/arm64-v8a/libopenxr_loader.so similarity index 100% rename from ext/openxr/libs/arm64-v8a/libopenxr_loader.so rename to ext/openxr/libs/quest/arm64-v8a/libopenxr_loader.so diff --git a/ext/openxr/openxr.h b/ext/openxr/openxr.h old mode 100755 new mode 100644 index 8798e5a6e0..02ec7cfbcb --- a/ext/openxr/openxr.h +++ b/ext/openxr/openxr.h @@ -2,7 +2,7 @@ #define OPENXR_H_ 1 /* -** Copyright (c) 2017-2022, The Khronos Group Inc. +** Copyright (c) 2017-2021, The Khronos Group Inc. ** ** SPDX-License-Identifier: Apache-2.0 OR MIT */ @@ -25,7 +25,7 @@ extern "C" { ((((major) & 0xffffULL) << 48) | (((minor) & 0xffffULL) << 32) | ((patch) & 0xffffffffULL)) // OpenXR current version number. -#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 0, 22) +#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 0, 14) #define XR_VERSION_MAJOR(version) (uint16_t)(((uint64_t)(version) >> 48)& 0xffffULL) #define XR_VERSION_MINOR(version) (uint16_t)(((uint64_t)(version) >> 32) & 0xffffULL) @@ -181,33 +181,13 @@ typedef enum XrResult { XR_ERROR_LOCALIZED_NAME_DUPLICATED = -48, XR_ERROR_LOCALIZED_NAME_INVALID = -49, XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING = -50, - XR_ERROR_RUNTIME_UNAVAILABLE = -51, XR_ERROR_ANDROID_THREAD_SETTINGS_ID_INVALID_KHR = -1000003000, XR_ERROR_ANDROID_THREAD_SETTINGS_FAILURE_KHR = -1000003001, XR_ERROR_CREATE_SPATIAL_ANCHOR_FAILED_MSFT = -1000039001, XR_ERROR_SECONDARY_VIEW_CONFIGURATION_TYPE_NOT_ENABLED_MSFT = -1000053000, XR_ERROR_CONTROLLER_MODEL_KEY_INVALID_MSFT = -1000055000, - XR_ERROR_REPROJECTION_MODE_UNSUPPORTED_MSFT = -1000066000, - XR_ERROR_COMPUTE_NEW_SCENE_NOT_COMPLETED_MSFT = -1000097000, - XR_ERROR_SCENE_COMPONENT_ID_INVALID_MSFT = -1000097001, - XR_ERROR_SCENE_COMPONENT_TYPE_MISMATCH_MSFT = -1000097002, - XR_ERROR_SCENE_MESH_BUFFER_ID_INVALID_MSFT = -1000097003, - XR_ERROR_SCENE_COMPUTE_FEATURE_INCOMPATIBLE_MSFT = -1000097004, - XR_ERROR_SCENE_COMPUTE_CONSISTENCY_MISMATCH_MSFT = -1000097005, XR_ERROR_DISPLAY_REFRESH_RATE_UNSUPPORTED_FB = -1000101000, XR_ERROR_COLOR_SPACE_UNSUPPORTED_FB = -1000108000, - XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB = -1000118000, - XR_ERROR_FEATURE_ALREADY_CREATED_PASSTHROUGH_FB = -1000118001, - XR_ERROR_FEATURE_REQUIRED_PASSTHROUGH_FB = -1000118002, - XR_ERROR_NOT_PERMITTED_PASSTHROUGH_FB = -1000118003, - XR_ERROR_INSUFFICIENT_RESOURCES_PASSTHROUGH_FB = -1000118004, - XR_ERROR_UNKNOWN_PASSTHROUGH_FB = -1000118050, - XR_ERROR_RENDER_MODEL_KEY_INVALID_FB = -1000119000, - XR_RENDER_MODEL_UNAVAILABLE_FB = 1000119020, - XR_ERROR_MARKER_NOT_TRACKED_VARJO = -1000124000, - XR_ERROR_MARKER_ID_INVALID_VARJO = -1000124001, - XR_ERROR_SPATIAL_ANCHOR_NAME_NOT_FOUND_MSFT = -1000142001, - XR_ERROR_SPATIAL_ANCHOR_NAME_INVALID_MSFT = -1000142002, XR_RESULT_MAX_ENUM = 0x7FFFFFFF } XrResult; @@ -262,6 +242,17 @@ typedef enum XrStructureType { XR_TYPE_ACTIONS_SYNC_INFO = 61, XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO = 62, XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO = 63, + //pico add new type + XR_TYPE_EVENT_CONTROLLER_STATE_CHANGED = 1200006064, + XR_TYPE_EVENT_SEETHROUGH_STATE_CHANGED = 1200006065, + XR_TYPE_EVENT_HARDIPD_STATE_CHANGED = 1200006066, + XR_TYPE_EVENT_KEY_EVENT = 1200006067, + XR_TYPE_EVENT_FOVEATION_LEVEL_CHANGED = 1200006068, + XR_TYPE_EVENT_FRUSTUM_STATE_CHANGED = 1200006069, + XR_TYPE_EVENT_RENDER_TEXTURE_CHANGED = 1200006070, + XR_TYPE_EVENT_TARGET_FRAME_RATE_STATE_CHANGED = 1200006071, + XR_TYPE_EVENT_MRC_STATUS_CHANGED = 1200006072, + XR_TYPE_EVENT_REFRESH_RATE_CHANGE = 1200006073, XR_TYPE_COMPOSITION_LAYER_CUBE_KHR = 1000006000, XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR = 1000008000, XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR = 1000010000, @@ -300,8 +291,6 @@ typedef enum XrStructureType { XR_TYPE_COMPOSITION_LAYER_COLOR_SCALE_BIAS_KHR = 1000034000, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_MSFT = 1000039000, XR_TYPE_SPATIAL_ANCHOR_SPACE_CREATE_INFO_MSFT = 1000039001, - XR_TYPE_COMPOSITION_LAYER_IMAGE_LAYOUT_FB = 1000040000, - XR_TYPE_COMPOSITION_LAYER_ALPHA_BLEND_FB = 1000041001, XR_TYPE_VIEW_CONFIGURATION_DEPTH_RANGE_EXT = 1000046000, XR_TYPE_GRAPHICS_BINDING_EGL_MNDX = 1000048004, XR_TYPE_SPATIAL_GRAPH_NODE_SPACE_CREATE_INFO_MSFT = 1000049000, @@ -328,90 +317,16 @@ typedef enum XrStructureType { XR_TYPE_CONTROLLER_MODEL_STATE_MSFT = 1000055004, XR_TYPE_VIEW_CONFIGURATION_VIEW_FOV_EPIC = 1000059000, XR_TYPE_HOLOGRAPHIC_WINDOW_ATTACHMENT_MSFT = 1000063000, - XR_TYPE_COMPOSITION_LAYER_REPROJECTION_INFO_MSFT = 1000066000, - XR_TYPE_COMPOSITION_LAYER_REPROJECTION_PLANE_OVERRIDE_MSFT = 1000066001, XR_TYPE_ANDROID_SURFACE_SWAPCHAIN_CREATE_INFO_FB = 1000070000, - XR_TYPE_COMPOSITION_LAYER_SECURE_CONTENT_FB = 1000072000, XR_TYPE_INTERACTION_PROFILE_ANALOG_THRESHOLD_VALVE = 1000079000, - XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT = 1000080000, XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR = 1000089000, XR_TYPE_VULKAN_INSTANCE_CREATE_INFO_KHR = 1000090000, XR_TYPE_VULKAN_DEVICE_CREATE_INFO_KHR = 1000090001, XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR = 1000090003, XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR = 1000091000, - XR_TYPE_SCENE_OBSERVER_CREATE_INFO_MSFT = 1000097000, - XR_TYPE_SCENE_CREATE_INFO_MSFT = 1000097001, - XR_TYPE_NEW_SCENE_COMPUTE_INFO_MSFT = 1000097002, - XR_TYPE_VISUAL_MESH_COMPUTE_LOD_INFO_MSFT = 1000097003, - XR_TYPE_SCENE_COMPONENTS_MSFT = 1000097004, - XR_TYPE_SCENE_COMPONENTS_GET_INFO_MSFT = 1000097005, - XR_TYPE_SCENE_COMPONENT_LOCATIONS_MSFT = 1000097006, - XR_TYPE_SCENE_COMPONENTS_LOCATE_INFO_MSFT = 1000097007, - XR_TYPE_SCENE_OBJECTS_MSFT = 1000097008, - XR_TYPE_SCENE_COMPONENT_PARENT_FILTER_INFO_MSFT = 1000097009, - XR_TYPE_SCENE_OBJECT_TYPES_FILTER_INFO_MSFT = 1000097010, - XR_TYPE_SCENE_PLANES_MSFT = 1000097011, - XR_TYPE_SCENE_PLANE_ALIGNMENT_FILTER_INFO_MSFT = 1000097012, - XR_TYPE_SCENE_MESHES_MSFT = 1000097013, - XR_TYPE_SCENE_MESH_BUFFERS_GET_INFO_MSFT = 1000097014, - XR_TYPE_SCENE_MESH_BUFFERS_MSFT = 1000097015, - XR_TYPE_SCENE_MESH_VERTEX_BUFFER_MSFT = 1000097016, - XR_TYPE_SCENE_MESH_INDICES_UINT32_MSFT = 1000097017, - XR_TYPE_SCENE_MESH_INDICES_UINT16_MSFT = 1000097018, - XR_TYPE_SERIALIZED_SCENE_FRAGMENT_DATA_GET_INFO_MSFT = 1000098000, - XR_TYPE_SCENE_DESERIALIZE_INFO_MSFT = 1000098001, XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB = 1000101000, - XR_TYPE_VIVE_TRACKER_PATHS_HTCX = 1000103000, - XR_TYPE_EVENT_DATA_VIVE_TRACKER_CONNECTED_HTCX = 1000103001, - XR_TYPE_SYSTEM_FACIAL_TRACKING_PROPERTIES_HTC = 1000104000, - XR_TYPE_FACIAL_TRACKER_CREATE_INFO_HTC = 1000104001, - XR_TYPE_FACIAL_EXPRESSIONS_HTC = 1000104002, XR_TYPE_SYSTEM_COLOR_SPACE_PROPERTIES_FB = 1000108000, - XR_TYPE_HAND_TRACKING_MESH_FB = 1000110001, - XR_TYPE_HAND_TRACKING_SCALE_FB = 1000110003, - XR_TYPE_HAND_TRACKING_AIM_STATE_FB = 1000111001, - XR_TYPE_HAND_TRACKING_CAPSULES_STATE_FB = 1000112000, - XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB = 1000114000, - XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB = 1000114001, - XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB = 1000114002, - XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB = 1000115000, - XR_TYPE_KEYBOARD_SPACE_CREATE_INFO_FB = 1000116009, - XR_TYPE_KEYBOARD_TRACKING_QUERY_FB = 1000116004, - XR_TYPE_SYSTEM_KEYBOARD_TRACKING_PROPERTIES_FB = 1000116002, - XR_TYPE_TRIANGLE_MESH_CREATE_INFO_FB = 1000117001, - XR_TYPE_SYSTEM_PASSTHROUGH_PROPERTIES_FB = 1000118000, - XR_TYPE_PASSTHROUGH_CREATE_INFO_FB = 1000118001, - XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB = 1000118002, - XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB = 1000118003, - XR_TYPE_GEOMETRY_INSTANCE_CREATE_INFO_FB = 1000118004, - XR_TYPE_GEOMETRY_INSTANCE_TRANSFORM_FB = 1000118005, - XR_TYPE_PASSTHROUGH_STYLE_FB = 1000118020, - XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_RGBA_FB = 1000118021, - XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_MONO_FB = 1000118022, - XR_TYPE_EVENT_DATA_PASSTHROUGH_STATE_CHANGED_FB = 1000118030, - XR_TYPE_RENDER_MODEL_PATH_INFO_FB = 1000119000, - XR_TYPE_RENDER_MODEL_PROPERTIES_FB = 1000119001, - XR_TYPE_RENDER_MODEL_BUFFER_FB = 1000119002, - XR_TYPE_RENDER_MODEL_LOAD_INFO_FB = 1000119003, - XR_TYPE_SYSTEM_RENDER_MODEL_PROPERTIES_FB = 1000119004, XR_TYPE_BINDING_MODIFICATIONS_KHR = 1000120000, - XR_TYPE_VIEW_LOCATE_FOVEATED_RENDERING_VARJO = 1000121000, - XR_TYPE_FOVEATED_VIEW_CONFIGURATION_VIEW_VARJO = 1000121001, - XR_TYPE_SYSTEM_FOVEATED_RENDERING_PROPERTIES_VARJO = 1000121002, - XR_TYPE_COMPOSITION_LAYER_DEPTH_TEST_VARJO = 1000122000, - XR_TYPE_SYSTEM_MARKER_TRACKING_PROPERTIES_VARJO = 1000124000, - XR_TYPE_EVENT_DATA_MARKER_TRACKING_UPDATE_VARJO = 1000124001, - XR_TYPE_MARKER_SPACE_CREATE_INFO_VARJO = 1000124002, - XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT = 1000142000, - XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT = 1000142001, - XR_TYPE_SWAPCHAIN_IMAGE_FOVEATION_VULKAN_FB = 1000160000, - XR_TYPE_SWAPCHAIN_STATE_ANDROID_SURFACE_DIMENSIONS_FB = 1000161000, - XR_TYPE_SWAPCHAIN_STATE_SAMPLER_OPENGL_ES_FB = 1000162000, - XR_TYPE_SWAPCHAIN_STATE_SAMPLER_VULKAN_FB = 1000163000, - XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB = 1000171000, - XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB = 1000171001, - XR_TYPE_DIGITAL_LENS_CONTROL_ALMALENCE = 1000196000, - XR_TYPE_PASSTHROUGH_KEYBOARD_HANDS_INTENSITY_FB = 1000203002, XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR, XR_TYPE_SWAPCHAIN_IMAGE_VULKAN2_KHR = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR, XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR, @@ -444,7 +359,6 @@ typedef enum XrReferenceSpaceType { XR_REFERENCE_SPACE_TYPE_LOCAL = 2, XR_REFERENCE_SPACE_TYPE_STAGE = 3, XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT = 1000038000, - XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO = 1000121000, XR_REFERENCE_SPACE_TYPE_MAX_ENUM = 0x7FFFFFFF } XrReferenceSpaceType; @@ -488,15 +402,6 @@ typedef enum XrObjectType { XR_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT = 1000019000, XR_OBJECT_TYPE_SPATIAL_ANCHOR_MSFT = 1000039000, XR_OBJECT_TYPE_HAND_TRACKER_EXT = 1000051000, - XR_OBJECT_TYPE_SCENE_OBSERVER_MSFT = 1000097000, - XR_OBJECT_TYPE_SCENE_MSFT = 1000097001, - XR_OBJECT_TYPE_FACIAL_TRACKER_HTC = 1000104000, - XR_OBJECT_TYPE_FOVEATION_PROFILE_FB = 1000114000, - XR_OBJECT_TYPE_TRIANGLE_MESH_FB = 1000117000, - XR_OBJECT_TYPE_PASSTHROUGH_FB = 1000118000, - XR_OBJECT_TYPE_PASSTHROUGH_LAYER_FB = 1000118002, - XR_OBJECT_TYPE_GEOMETRY_INSTANCE_FB = 1000118004, - XR_OBJECT_TYPE_SPATIAL_ANCHOR_STORE_CONNECTION_MSFT = 1000142000, XR_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF } XrObjectType; typedef XrFlags64 XrInstanceCreateFlags; @@ -538,7 +443,6 @@ static const XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_TRANSFER_DST_BIT = 0x00000 static const XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_SAMPLED_BIT = 0x00000020; static const XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT = 0x00000040; static const XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_MND = 0x00000080; -static const XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_KHR = 0x00000080; // alias of XR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_MND typedef XrFlags64 XrCompositionLayerFlags; @@ -651,7 +555,6 @@ typedef struct XrVector3f { float z; } XrVector3f; -// XrSpaceVelocity extends XrSpaceLocation typedef struct XrSpaceVelocity { XrStructureType type; void* XR_MAY_ALIAS next; @@ -1404,7 +1307,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrApplyHapticFeedback( XRAPI_ATTR XrResult XRAPI_CALL xrStopHapticFeedback( XrSession session, const XrHapticActionInfo* hapticActionInfo); -#endif /* !XR_NO_PROTOTYPES */ +#endif #define XR_KHR_composition_layer_cube 1 @@ -1426,7 +1329,6 @@ typedef struct XrCompositionLayerCubeKHR { #define XR_KHR_composition_layer_depth 1 #define XR_KHR_composition_layer_depth_SPEC_VERSION 5 #define XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME "XR_KHR_composition_layer_depth" -// XrCompositionLayerDepthInfoKHR extends XrCompositionLayerProjectionView typedef struct XrCompositionLayerDepthInfoKHR { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -1507,21 +1409,18 @@ typedef struct XrEventDataVisibilityMaskChangedKHR { typedef XrResult (XRAPI_PTR *PFN_xrGetVisibilityMaskKHR)(XrSession session, XrViewConfigurationType viewConfigurationType, uint32_t viewIndex, XrVisibilityMaskTypeKHR visibilityMaskType, XrVisibilityMaskKHR* visibilityMask); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrGetVisibilityMaskKHR( XrSession session, XrViewConfigurationType viewConfigurationType, uint32_t viewIndex, XrVisibilityMaskTypeKHR visibilityMaskType, XrVisibilityMaskKHR* visibilityMask); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #define XR_KHR_composition_layer_color_scale_bias 1 #define XR_KHR_composition_layer_color_scale_bias_SPEC_VERSION 5 #define XR_KHR_COMPOSITION_LAYER_COLOR_SCALE_BIAS_EXTENSION_NAME "XR_KHR_composition_layer_color_scale_bias" -// XrCompositionLayerColorScaleBiasKHR extends XrCompositionLayerBaseHeader typedef struct XrCompositionLayerColorScaleBiasKHR { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -1542,11 +1441,9 @@ typedef struct XR_MAY_ALIAS XrLoaderInitInfoBaseHeaderKHR { typedef XrResult (XRAPI_PTR *PFN_xrInitializeLoaderKHR)(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrInitializeLoaderKHR( const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #define XR_KHR_composition_layer_equirect2 1 @@ -1571,12 +1468,11 @@ typedef struct XrCompositionLayerEquirect2KHR { #define XR_KHR_binding_modification 1 #define XR_KHR_binding_modification_SPEC_VERSION 1 #define XR_KHR_BINDING_MODIFICATION_EXTENSION_NAME "XR_KHR_binding_modification" -typedef struct XR_MAY_ALIAS XrBindingModificationBaseHeaderKHR { +typedef struct XrBindingModificationBaseHeaderKHR { XrStructureType type; const void* XR_MAY_ALIAS next; } XrBindingModificationBaseHeaderKHR; -// XrBindingModificationsKHR extends XrInteractionProfileSuggestedBinding typedef struct XrBindingModificationsKHR { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -1586,13 +1482,8 @@ typedef struct XrBindingModificationsKHR { -#define XR_KHR_swapchain_usage_input_attachment_bit 1 -#define XR_KHR_swapchain_usage_input_attachment_bit_SPEC_VERSION 3 -#define XR_KHR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_EXTENSION_NAME "XR_KHR_swapchain_usage_input_attachment_bit" - - #define XR_EXT_performance_settings 1 -#define XR_EXT_performance_settings_SPEC_VERSION 3 +#define XR_EXT_performance_settings_SPEC_VERSION 1 #define XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME "XR_EXT_performance_settings" typedef enum XrPerfSettingsDomainEXT { @@ -1634,35 +1525,31 @@ typedef struct XrEventDataPerfSettingsEXT { typedef XrResult (XRAPI_PTR *PFN_xrPerfSettingsSetPerformanceLevelEXT)(XrSession session, XrPerfSettingsDomainEXT domain, XrPerfSettingsLevelEXT level); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrPerfSettingsSetPerformanceLevelEXT( XrSession session, XrPerfSettingsDomainEXT domain, XrPerfSettingsLevelEXT level); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #define XR_EXT_thermal_query 1 -#define XR_EXT_thermal_query_SPEC_VERSION 2 +#define XR_EXT_thermal_query_SPEC_VERSION 1 #define XR_EXT_THERMAL_QUERY_EXTENSION_NAME "XR_EXT_thermal_query" typedef XrResult (XRAPI_PTR *PFN_xrThermalGetTemperatureTrendEXT)(XrSession session, XrPerfSettingsDomainEXT domain, XrPerfSettingsNotificationLevelEXT* notificationLevel, float* tempHeadroom, float* tempSlope); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrThermalGetTemperatureTrendEXT( XrSession session, XrPerfSettingsDomainEXT domain, XrPerfSettingsNotificationLevelEXT* notificationLevel, float* tempHeadroom, float* tempSlope); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #define XR_EXT_debug_utils 1 XR_DEFINE_HANDLE(XrDebugUtilsMessengerEXT) -#define XR_EXT_debug_utils_SPEC_VERSION 4 +#define XR_EXT_debug_utils_SPEC_VERSION 3 #define XR_EXT_DEBUG_UTILS_EXTENSION_NAME "XR_EXT_debug_utils" typedef XrFlags64 XrDebugUtilsMessageSeverityFlagsEXT; @@ -1713,7 +1600,6 @@ typedef XrBool32 (XRAPI_PTR *PFN_xrDebugUtilsMessengerCallbackEXT)( void* userData); -// XrDebugUtilsMessengerCreateInfoEXT extends XrInstanceCreateInfo typedef struct XrDebugUtilsMessengerCreateInfoEXT { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -1732,7 +1618,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrSessionEndDebugUtilsLabelRegionEXT)(XrSession typedef XrResult (XRAPI_PTR *PFN_xrSessionInsertDebugUtilsLabelEXT)(XrSession session, const XrDebugUtilsLabelEXT* labelInfo); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrSetDebugUtilsObjectNameEXT( XrInstance instance, const XrDebugUtilsObjectNameInfoEXT* nameInfo); @@ -1761,21 +1646,18 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSessionEndDebugUtilsLabelRegionEXT( XRAPI_ATTR XrResult XRAPI_CALL xrSessionInsertDebugUtilsLabelEXT( XrSession session, const XrDebugUtilsLabelEXT* labelInfo); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #define XR_EXT_eye_gaze_interaction 1 #define XR_EXT_eye_gaze_interaction_SPEC_VERSION 1 #define XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME "XR_EXT_eye_gaze_interaction" -// XrSystemEyeGazeInteractionPropertiesEXT extends XrSystemProperties typedef struct XrSystemEyeGazeInteractionPropertiesEXT { XrStructureType type; void* XR_MAY_ALIAS next; XrBool32 supportsEyeGazeInteraction; } XrSystemEyeGazeInteractionPropertiesEXT; -// XrEyeGazeSampleTimeEXT extends XrSpaceLocation typedef struct XrEyeGazeSampleTimeEXT { XrStructureType type; void* XR_MAY_ALIAS next; @@ -1785,18 +1667,18 @@ typedef struct XrEyeGazeSampleTimeEXT { #define XR_EXTX_overlay 1 -#define XR_EXTX_overlay_SPEC_VERSION 5 +#define XR_EXTX_overlay_SPEC_VERSION 4 #define XR_EXTX_OVERLAY_EXTENSION_NAME "XR_EXTX_overlay" typedef XrFlags64 XrOverlaySessionCreateFlagsEXTX; // Flag bits for XrOverlaySessionCreateFlagsEXTX +static const XrOverlaySessionCreateFlagsEXTX XR_OVERLAY_SESSION_CREATE_RELAXED_DISPLAY_TIME_BIT_EXTX = 0x00000001; typedef XrFlags64 XrOverlayMainSessionFlagsEXTX; // Flag bits for XrOverlayMainSessionFlagsEXTX static const XrOverlayMainSessionFlagsEXTX XR_OVERLAY_MAIN_SESSION_ENABLED_COMPOSITION_LAYER_INFO_DEPTH_BIT_EXTX = 0x00000001; -// XrSessionCreateInfoOverlayEXTX extends XrSessionCreateInfo typedef struct XrSessionCreateInfoOverlayEXTX { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -1825,7 +1707,7 @@ typedef struct XrEventDataMainSessionVisibilityChangedEXTX { #define XR_MSFT_spatial_anchor 1 XR_DEFINE_HANDLE(XrSpatialAnchorMSFT) -#define XR_MSFT_spatial_anchor_SPEC_VERSION 2 +#define XR_MSFT_spatial_anchor_SPEC_VERSION 1 #define XR_MSFT_SPATIAL_ANCHOR_EXTENSION_NAME "XR_MSFT_spatial_anchor" typedef struct XrSpatialAnchorCreateInfoMSFT { XrStructureType type; @@ -1847,7 +1729,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorSpaceMSFT)(XrSession sessi typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialAnchorMSFT)(XrSpatialAnchorMSFT anchor); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorMSFT( XrSession session, const XrSpatialAnchorCreateInfoMSFT* createInfo, @@ -1860,50 +1741,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorSpaceMSFT( XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialAnchorMSFT( XrSpatialAnchorMSFT anchor); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_FB_composition_layer_image_layout 1 -#define XR_FB_composition_layer_image_layout_SPEC_VERSION 1 -#define XR_FB_COMPOSITION_LAYER_IMAGE_LAYOUT_EXTENSION_NAME "XR_FB_composition_layer_image_layout" -typedef XrFlags64 XrCompositionLayerImageLayoutFlagsFB; - -// Flag bits for XrCompositionLayerImageLayoutFlagsFB -static const XrCompositionLayerImageLayoutFlagsFB XR_COMPOSITION_LAYER_IMAGE_LAYOUT_VERTICAL_FLIP_BIT_FB = 0x00000001; - -// XrCompositionLayerImageLayoutFB extends XrCompositionLayerBaseHeader -typedef struct XrCompositionLayerImageLayoutFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrCompositionLayerImageLayoutFlagsFB flags; -} XrCompositionLayerImageLayoutFB; - - - -#define XR_FB_composition_layer_alpha_blend 1 -#define XR_FB_composition_layer_alpha_blend_SPEC_VERSION 2 -#define XR_FB_COMPOSITION_LAYER_ALPHA_BLEND_EXTENSION_NAME "XR_FB_composition_layer_alpha_blend" - -typedef enum XrBlendFactorFB { - XR_BLEND_FACTOR_ZERO_FB = 0, - XR_BLEND_FACTOR_ONE_FB = 1, - XR_BLEND_FACTOR_SRC_ALPHA_FB = 2, - XR_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA_FB = 3, - XR_BLEND_FACTOR_DST_ALPHA_FB = 4, - XR_BLEND_FACTOR_ONE_MINUS_DST_ALPHA_FB = 5, - XR_BLEND_FACTOR_MAX_ENUM_FB = 0x7FFFFFFF -} XrBlendFactorFB; -// XrCompositionLayerAlphaBlendFB extends XrCompositionLayerBaseHeader -typedef struct XrCompositionLayerAlphaBlendFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBlendFactorFB srcFactorColor; - XrBlendFactorFB dstFactorColor; - XrBlendFactorFB srcFactorAlpha; - XrBlendFactorFB dstFactorAlpha; -} XrCompositionLayerAlphaBlendFB; - +#endif #define XR_MND_headless 1 @@ -1919,7 +1757,6 @@ typedef struct XrCompositionLayerAlphaBlendFB { #define XR_EXT_view_configuration_depth_range 1 #define XR_EXT_view_configuration_depth_range_SPEC_VERSION 1 #define XR_EXT_VIEW_CONFIGURATION_DEPTH_RANGE_EXTENSION_NAME "XR_EXT_view_configuration_depth_range" -// XrViewConfigurationDepthRangeEXT extends XrViewConfigurationView typedef struct XrViewConfigurationDepthRangeEXT { XrStructureType type; void* XR_MAY_ALIAS next; @@ -1932,7 +1769,7 @@ typedef struct XrViewConfigurationDepthRangeEXT { #define XR_EXT_conformance_automation 1 -#define XR_EXT_conformance_automation_SPEC_VERSION 3 +#define XR_EXT_conformance_automation_SPEC_VERSION 1 #define XR_EXT_CONFORMANCE_AUTOMATION_EXTENSION_NAME "XR_EXT_conformance_automation" typedef XrResult (XRAPI_PTR *PFN_xrSetInputDeviceActiveEXT)(XrSession session, XrPath interactionProfile, XrPath topLevelPath, XrBool32 isActive); typedef XrResult (XRAPI_PTR *PFN_xrSetInputDeviceStateBoolEXT)(XrSession session, XrPath topLevelPath, XrPath inputSourcePath, XrBool32 state); @@ -1941,7 +1778,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrSetInputDeviceStateVector2fEXT)(XrSession ses typedef XrResult (XRAPI_PTR *PFN_xrSetInputDeviceLocationEXT)(XrSession session, XrPath topLevelPath, XrPath inputSourcePath, XrSpace space, XrPosef pose); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrSetInputDeviceActiveEXT( XrSession session, XrPath interactionProfile, @@ -1972,8 +1808,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetInputDeviceLocationEXT( XrPath inputSourcePath, XrSpace space, XrPosef pose); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #define XR_MSFT_spatial_graph_bridge 1 @@ -1996,13 +1831,11 @@ typedef struct XrSpatialGraphNodeSpaceCreateInfoMSFT { typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialGraphNodeSpaceMSFT)(XrSession session, const XrSpatialGraphNodeSpaceCreateInfoMSFT* createInfo, XrSpace* space); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialGraphNodeSpaceMSFT( XrSession session, const XrSpatialGraphNodeSpaceCreateInfoMSFT* createInfo, XrSpace* space); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #define XR_MSFT_hand_interaction 1 @@ -2015,7 +1848,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialGraphNodeSpaceMSFT( #define XR_HAND_JOINT_COUNT_EXT 26 XR_DEFINE_HANDLE(XrHandTrackerEXT) -#define XR_EXT_hand_tracking_SPEC_VERSION 4 +#define XR_EXT_hand_tracking_SPEC_VERSION 2 #define XR_EXT_HAND_TRACKING_EXTENSION_NAME "XR_EXT_hand_tracking" typedef enum XrHandEXT { @@ -2058,7 +1891,6 @@ typedef enum XrHandJointSetEXT { XR_HAND_JOINT_SET_DEFAULT_EXT = 0, XR_HAND_JOINT_SET_MAX_ENUM_EXT = 0x7FFFFFFF } XrHandJointSetEXT; -// XrSystemHandTrackingPropertiesEXT extends XrSystemProperties typedef struct XrSystemHandTrackingPropertiesEXT { XrStructureType type; void* XR_MAY_ALIAS next; @@ -2099,7 +1931,6 @@ typedef struct XrHandJointLocationsEXT { XrHandJointLocationEXT* jointLocations; } XrHandJointLocationsEXT; -// XrHandJointVelocitiesEXT extends XrHandJointLocationsEXT typedef struct XrHandJointVelocitiesEXT { XrStructureType type; void* XR_MAY_ALIAS next; @@ -2112,7 +1943,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrDestroyHandTrackerEXT)(XrHandTrackerEXT handT typedef XrResult (XRAPI_PTR *PFN_xrLocateHandJointsEXT)(XrHandTrackerEXT handTracker, const XrHandJointsLocateInfoEXT* locateInfo, XrHandJointLocationsEXT* locations); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrCreateHandTrackerEXT( XrSession session, const XrHandTrackerCreateInfoEXT* createInfo, @@ -2125,12 +1955,11 @@ XRAPI_ATTR XrResult XRAPI_CALL xrLocateHandJointsEXT( XrHandTrackerEXT handTracker, const XrHandJointsLocateInfoEXT* locateInfo, XrHandJointLocationsEXT* locations); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #define XR_MSFT_hand_tracking_mesh 1 -#define XR_MSFT_hand_tracking_mesh_SPEC_VERSION 4 +#define XR_MSFT_hand_tracking_mesh_SPEC_VERSION 2 #define XR_MSFT_HAND_TRACKING_MESH_EXTENSION_NAME "XR_MSFT_hand_tracking_mesh" typedef enum XrHandPoseTypeMSFT { @@ -2138,7 +1967,6 @@ typedef enum XrHandPoseTypeMSFT { XR_HAND_POSE_TYPE_REFERENCE_OPEN_PALM_MSFT = 1, XR_HAND_POSE_TYPE_MAX_ENUM_MSFT = 0x7FFFFFFF } XrHandPoseTypeMSFT; -// XrSystemHandTrackingMeshPropertiesMSFT extends XrSystemProperties typedef struct XrSystemHandTrackingMeshPropertiesMSFT { XrStructureType type; void* XR_MAY_ALIAS next; @@ -2190,7 +2018,6 @@ typedef struct XrHandMeshMSFT { XrHandMeshVertexBufferMSFT vertexBuffer; } XrHandMeshMSFT; -// XrHandPoseTypeInfoMSFT extends XrHandTrackerCreateInfoEXT typedef struct XrHandPoseTypeInfoMSFT { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -2201,7 +2028,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateHandMeshSpaceMSFT)(XrHandTrackerEXT han typedef XrResult (XRAPI_PTR *PFN_xrUpdateHandMeshMSFT)(XrHandTrackerEXT handTracker, const XrHandMeshUpdateInfoMSFT* updateInfo, XrHandMeshMSFT* handMesh); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrCreateHandMeshSpaceMSFT( XrHandTrackerEXT handTracker, const XrHandMeshSpaceCreateInfoMSFT* createInfo, @@ -2211,14 +2037,12 @@ XRAPI_ATTR XrResult XRAPI_CALL xrUpdateHandMeshMSFT( XrHandTrackerEXT handTracker, const XrHandMeshUpdateInfoMSFT* updateInfo, XrHandMeshMSFT* handMesh); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #define XR_MSFT_secondary_view_configuration 1 #define XR_MSFT_secondary_view_configuration_SPEC_VERSION 1 #define XR_MSFT_SECONDARY_VIEW_CONFIGURATION_EXTENSION_NAME "XR_MSFT_secondary_view_configuration" -// XrSecondaryViewConfigurationSessionBeginInfoMSFT extends XrSessionBeginInfo typedef struct XrSecondaryViewConfigurationSessionBeginInfoMSFT { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -2233,7 +2057,6 @@ typedef struct XrSecondaryViewConfigurationStateMSFT { XrBool32 active; } XrSecondaryViewConfigurationStateMSFT; -// XrSecondaryViewConfigurationFrameStateMSFT extends XrFrameState typedef struct XrSecondaryViewConfigurationFrameStateMSFT { XrStructureType type; void* XR_MAY_ALIAS next; @@ -2250,7 +2073,6 @@ typedef struct XrSecondaryViewConfigurationLayerInfoMSFT { const XrCompositionLayerBaseHeader* const* layers; } XrSecondaryViewConfigurationLayerInfoMSFT; -// XrSecondaryViewConfigurationFrameEndInfoMSFT extends XrFrameEndInfo typedef struct XrSecondaryViewConfigurationFrameEndInfoMSFT { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -2258,7 +2080,6 @@ typedef struct XrSecondaryViewConfigurationFrameEndInfoMSFT { const XrSecondaryViewConfigurationLayerInfoMSFT* viewConfigurationLayersInfo; } XrSecondaryViewConfigurationFrameEndInfoMSFT; -// XrSecondaryViewConfigurationSwapchainCreateInfoMSFT extends XrSwapchainCreateInfo typedef struct XrSecondaryViewConfigurationSwapchainCreateInfoMSFT { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -2321,7 +2142,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrGetControllerModelPropertiesMSFT)(XrSession s typedef XrResult (XRAPI_PTR *PFN_xrGetControllerModelStateMSFT)(XrSession session, XrControllerModelKeyMSFT modelKey, XrControllerModelStateMSFT* state); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrGetControllerModelKeyMSFT( XrSession session, XrPath topLevelUserPath, @@ -2343,8 +2163,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetControllerModelStateMSFT( XrSession session, XrControllerModelKeyMSFT modelKey, XrControllerModelStateMSFT* state); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #define XR_EXT_win32_appcontainer_compatible 1 @@ -2355,7 +2174,6 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetControllerModelStateMSFT( #define XR_EPIC_view_configuration_fov 1 #define XR_EPIC_view_configuration_fov_SPEC_VERSION 2 #define XR_EPIC_VIEW_CONFIGURATION_FOV_EXTENSION_NAME "XR_EPIC_view_configuration_fov" -// XrViewConfigurationViewFovEPIC extends XrViewConfigurationView typedef struct XrViewConfigurationViewFovEPIC { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -2365,97 +2183,13 @@ typedef struct XrViewConfigurationViewFovEPIC { -#define XR_MSFT_composition_layer_reprojection 1 -#define XR_MSFT_composition_layer_reprojection_SPEC_VERSION 1 -#define XR_MSFT_COMPOSITION_LAYER_REPROJECTION_EXTENSION_NAME "XR_MSFT_composition_layer_reprojection" - -typedef enum XrReprojectionModeMSFT { - XR_REPROJECTION_MODE_DEPTH_MSFT = 1, - XR_REPROJECTION_MODE_PLANAR_FROM_DEPTH_MSFT = 2, - XR_REPROJECTION_MODE_PLANAR_MANUAL_MSFT = 3, - XR_REPROJECTION_MODE_ORIENTATION_ONLY_MSFT = 4, - XR_REPROJECTION_MODE_MAX_ENUM_MSFT = 0x7FFFFFFF -} XrReprojectionModeMSFT; -// XrCompositionLayerReprojectionInfoMSFT extends XrCompositionLayerProjection -typedef struct XrCompositionLayerReprojectionInfoMSFT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrReprojectionModeMSFT reprojectionMode; -} XrCompositionLayerReprojectionInfoMSFT; - -// XrCompositionLayerReprojectionPlaneOverrideMSFT extends XrCompositionLayerProjection -typedef struct XrCompositionLayerReprojectionPlaneOverrideMSFT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrVector3f position; - XrVector3f normal; - XrVector3f velocity; -} XrCompositionLayerReprojectionPlaneOverrideMSFT; - -typedef XrResult (XRAPI_PTR *PFN_xrEnumerateReprojectionModesMSFT)(XrInstance instance, XrSystemId systemId, XrViewConfigurationType viewConfigurationType, uint32_t modeCapacityInput, uint32_t* modeCountOutput, XrReprojectionModeMSFT* modes); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateReprojectionModesMSFT( - XrInstance instance, - XrSystemId systemId, - XrViewConfigurationType viewConfigurationType, - uint32_t modeCapacityInput, - uint32_t* modeCountOutput, - XrReprojectionModeMSFT* modes); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - #define XR_HUAWEI_controller_interaction 1 #define XR_HUAWEI_controller_interaction_SPEC_VERSION 1 #define XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_HUAWEI_controller_interaction" -#define XR_FB_swapchain_update_state 1 -#define XR_FB_swapchain_update_state_SPEC_VERSION 3 -#define XR_FB_SWAPCHAIN_UPDATE_STATE_EXTENSION_NAME "XR_FB_swapchain_update_state" -typedef struct XR_MAY_ALIAS XrSwapchainStateBaseHeaderFB { - XrStructureType type; - void* XR_MAY_ALIAS next; -} XrSwapchainStateBaseHeaderFB; - -typedef XrResult (XRAPI_PTR *PFN_xrUpdateSwapchainFB)(XrSwapchain swapchain, const XrSwapchainStateBaseHeaderFB* state); -typedef XrResult (XRAPI_PTR *PFN_xrGetSwapchainStateFB)(XrSwapchain swapchain, XrSwapchainStateBaseHeaderFB* state); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrUpdateSwapchainFB( - XrSwapchain swapchain, - const XrSwapchainStateBaseHeaderFB* state); - -XRAPI_ATTR XrResult XRAPI_CALL xrGetSwapchainStateFB( - XrSwapchain swapchain, - XrSwapchainStateBaseHeaderFB* state); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_FB_composition_layer_secure_content 1 -#define XR_FB_composition_layer_secure_content_SPEC_VERSION 1 -#define XR_FB_COMPOSITION_LAYER_SECURE_CONTENT_EXTENSION_NAME "XR_FB_composition_layer_secure_content" -typedef XrFlags64 XrCompositionLayerSecureContentFlagsFB; - -// Flag bits for XrCompositionLayerSecureContentFlagsFB -static const XrCompositionLayerSecureContentFlagsFB XR_COMPOSITION_LAYER_SECURE_CONTENT_EXCLUDE_LAYER_BIT_FB = 0x00000001; -static const XrCompositionLayerSecureContentFlagsFB XR_COMPOSITION_LAYER_SECURE_CONTENT_REPLACE_LAYER_BIT_FB = 0x00000002; - -// XrCompositionLayerSecureContentFB extends XrCompositionLayerBaseHeader -typedef struct XrCompositionLayerSecureContentFB { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrCompositionLayerSecureContentFlagsFB flags; -} XrCompositionLayerSecureContentFB; - - - #define XR_VALVE_analog_threshold 1 -#define XR_VALVE_analog_threshold_SPEC_VERSION 2 +#define XR_VALVE_analog_threshold_SPEC_VERSION 1 #define XR_VALVE_ANALOG_THRESHOLD_EXTENSION_NAME "XR_VALVE_analog_threshold" typedef struct XrInteractionProfileAnalogThresholdVALVE { XrStructureType type; @@ -2470,24 +2204,6 @@ typedef struct XrInteractionProfileAnalogThresholdVALVE { -#define XR_EXT_hand_joints_motion_range 1 -#define XR_EXT_hand_joints_motion_range_SPEC_VERSION 1 -#define XR_EXT_HAND_JOINTS_MOTION_RANGE_EXTENSION_NAME "XR_EXT_hand_joints_motion_range" - -typedef enum XrHandJointsMotionRangeEXT { - XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT = 1, - XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT = 2, - XR_HAND_JOINTS_MOTION_RANGE_MAX_ENUM_EXT = 0x7FFFFFFF -} XrHandJointsMotionRangeEXT; -// XrHandJointsMotionRangeInfoEXT extends XrHandJointsLocateInfoEXT -typedef struct XrHandJointsMotionRangeInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrHandJointsMotionRangeEXT handJointsMotionRange; -} XrHandJointsMotionRangeInfoEXT; - - - #define XR_EXT_samsung_odyssey_controller 1 #define XR_EXT_samsung_odyssey_controller_SPEC_VERSION 1 #define XR_EXT_SAMSUNG_ODYSSEY_CONTROLLER_EXTENSION_NAME "XR_EXT_samsung_odyssey_controller" @@ -2503,374 +2219,6 @@ typedef struct XrHandJointsMotionRangeInfoEXT { #define XR_MND_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_EXTENSION_NAME "XR_MND_swapchain_usage_input_attachment_bit" -#define XR_MSFT_scene_understanding 1 - - XR_DEFINE_HANDLE(XrSceneObserverMSFT) - - - XR_DEFINE_HANDLE(XrSceneMSFT) - -#define XR_MSFT_scene_understanding_SPEC_VERSION 1 -#define XR_MSFT_SCENE_UNDERSTANDING_EXTENSION_NAME "XR_MSFT_scene_understanding" - -typedef enum XrSceneComputeFeatureMSFT { - XR_SCENE_COMPUTE_FEATURE_PLANE_MSFT = 1, - XR_SCENE_COMPUTE_FEATURE_PLANE_MESH_MSFT = 2, - XR_SCENE_COMPUTE_FEATURE_VISUAL_MESH_MSFT = 3, - XR_SCENE_COMPUTE_FEATURE_COLLIDER_MESH_MSFT = 4, - XR_SCENE_COMPUTE_FEATURE_SERIALIZE_SCENE_MSFT = 1000098000, - XR_SCENE_COMPUTE_FEATURE_MAX_ENUM_MSFT = 0x7FFFFFFF -} XrSceneComputeFeatureMSFT; - -typedef enum XrSceneComputeConsistencyMSFT { - XR_SCENE_COMPUTE_CONSISTENCY_SNAPSHOT_COMPLETE_MSFT = 1, - XR_SCENE_COMPUTE_CONSISTENCY_SNAPSHOT_INCOMPLETE_FAST_MSFT = 2, - XR_SCENE_COMPUTE_CONSISTENCY_OCCLUSION_OPTIMIZED_MSFT = 3, - XR_SCENE_COMPUTE_CONSISTENCY_MAX_ENUM_MSFT = 0x7FFFFFFF -} XrSceneComputeConsistencyMSFT; - -typedef enum XrMeshComputeLodMSFT { - XR_MESH_COMPUTE_LOD_COARSE_MSFT = 1, - XR_MESH_COMPUTE_LOD_MEDIUM_MSFT = 2, - XR_MESH_COMPUTE_LOD_FINE_MSFT = 3, - XR_MESH_COMPUTE_LOD_UNLIMITED_MSFT = 4, - XR_MESH_COMPUTE_LOD_MAX_ENUM_MSFT = 0x7FFFFFFF -} XrMeshComputeLodMSFT; - -typedef enum XrSceneComponentTypeMSFT { - XR_SCENE_COMPONENT_TYPE_INVALID_MSFT = -1, - XR_SCENE_COMPONENT_TYPE_OBJECT_MSFT = 1, - XR_SCENE_COMPONENT_TYPE_PLANE_MSFT = 2, - XR_SCENE_COMPONENT_TYPE_VISUAL_MESH_MSFT = 3, - XR_SCENE_COMPONENT_TYPE_COLLIDER_MESH_MSFT = 4, - XR_SCENE_COMPONENT_TYPE_SERIALIZED_SCENE_FRAGMENT_MSFT = 1000098000, - XR_SCENE_COMPONENT_TYPE_MAX_ENUM_MSFT = 0x7FFFFFFF -} XrSceneComponentTypeMSFT; - -typedef enum XrSceneObjectTypeMSFT { - XR_SCENE_OBJECT_TYPE_UNCATEGORIZED_MSFT = -1, - XR_SCENE_OBJECT_TYPE_BACKGROUND_MSFT = 1, - XR_SCENE_OBJECT_TYPE_WALL_MSFT = 2, - XR_SCENE_OBJECT_TYPE_FLOOR_MSFT = 3, - XR_SCENE_OBJECT_TYPE_CEILING_MSFT = 4, - XR_SCENE_OBJECT_TYPE_PLATFORM_MSFT = 5, - XR_SCENE_OBJECT_TYPE_INFERRED_MSFT = 6, - XR_SCENE_OBJECT_TYPE_MAX_ENUM_MSFT = 0x7FFFFFFF -} XrSceneObjectTypeMSFT; - -typedef enum XrScenePlaneAlignmentTypeMSFT { - XR_SCENE_PLANE_ALIGNMENT_TYPE_NON_ORTHOGONAL_MSFT = 0, - XR_SCENE_PLANE_ALIGNMENT_TYPE_HORIZONTAL_MSFT = 1, - XR_SCENE_PLANE_ALIGNMENT_TYPE_VERTICAL_MSFT = 2, - XR_SCENE_PLANE_ALIGNMENT_TYPE_MAX_ENUM_MSFT = 0x7FFFFFFF -} XrScenePlaneAlignmentTypeMSFT; - -typedef enum XrSceneComputeStateMSFT { - XR_SCENE_COMPUTE_STATE_NONE_MSFT = 0, - XR_SCENE_COMPUTE_STATE_UPDATING_MSFT = 1, - XR_SCENE_COMPUTE_STATE_COMPLETED_MSFT = 2, - XR_SCENE_COMPUTE_STATE_COMPLETED_WITH_ERROR_MSFT = 3, - XR_SCENE_COMPUTE_STATE_MAX_ENUM_MSFT = 0x7FFFFFFF -} XrSceneComputeStateMSFT; -typedef struct XrUuidMSFT { - uint8_t bytes[16]; -} XrUuidMSFT; - -typedef struct XrSceneObserverCreateInfoMSFT { - XrStructureType type; - const void* XR_MAY_ALIAS next; -} XrSceneObserverCreateInfoMSFT; - -typedef struct XrSceneCreateInfoMSFT { - XrStructureType type; - const void* XR_MAY_ALIAS next; -} XrSceneCreateInfoMSFT; - -typedef struct XrSceneSphereBoundMSFT { - XrVector3f center; - float radius; -} XrSceneSphereBoundMSFT; - -typedef struct XrSceneOrientedBoxBoundMSFT { - XrPosef pose; - XrVector3f extents; -} XrSceneOrientedBoxBoundMSFT; - -typedef struct XrSceneFrustumBoundMSFT { - XrPosef pose; - XrFovf fov; - float farDistance; -} XrSceneFrustumBoundMSFT; - -typedef struct XrSceneBoundsMSFT { - XrSpace space; - XrTime time; - uint32_t sphereCount; - const XrSceneSphereBoundMSFT* spheres; - uint32_t boxCount; - const XrSceneOrientedBoxBoundMSFT* boxes; - uint32_t frustumCount; - const XrSceneFrustumBoundMSFT* frustums; -} XrSceneBoundsMSFT; - -typedef struct XrNewSceneComputeInfoMSFT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t requestedFeatureCount; - const XrSceneComputeFeatureMSFT* requestedFeatures; - XrSceneComputeConsistencyMSFT consistency; - XrSceneBoundsMSFT bounds; -} XrNewSceneComputeInfoMSFT; - -// XrVisualMeshComputeLodInfoMSFT extends XrNewSceneComputeInfoMSFT -typedef struct XrVisualMeshComputeLodInfoMSFT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrMeshComputeLodMSFT lod; -} XrVisualMeshComputeLodInfoMSFT; - -typedef struct XrSceneComponentMSFT { - XrSceneComponentTypeMSFT componentType; - XrUuidMSFT id; - XrUuidMSFT parentId; - XrTime updateTime; -} XrSceneComponentMSFT; - -typedef struct XrSceneComponentsMSFT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t componentCapacityInput; - uint32_t componentCountOutput; - XrSceneComponentMSFT* components; -} XrSceneComponentsMSFT; - -typedef struct XrSceneComponentsGetInfoMSFT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSceneComponentTypeMSFT componentType; -} XrSceneComponentsGetInfoMSFT; - -typedef struct XrSceneComponentLocationMSFT { - XrSpaceLocationFlags flags; - XrPosef pose; -} XrSceneComponentLocationMSFT; - -typedef struct XrSceneComponentLocationsMSFT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t locationCount; - XrSceneComponentLocationMSFT* locations; -} XrSceneComponentLocationsMSFT; - -typedef struct XrSceneComponentsLocateInfoMSFT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpace baseSpace; - XrTime time; - uint32_t componentIdCount; - const XrUuidMSFT* componentIds; -} XrSceneComponentsLocateInfoMSFT; - -typedef struct XrSceneObjectMSFT { - XrSceneObjectTypeMSFT objectType; -} XrSceneObjectMSFT; - -// XrSceneObjectsMSFT extends XrSceneComponentsMSFT -typedef struct XrSceneObjectsMSFT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t sceneObjectCount; - XrSceneObjectMSFT* sceneObjects; -} XrSceneObjectsMSFT; - -// XrSceneComponentParentFilterInfoMSFT extends XrSceneComponentsGetInfoMSFT -typedef struct XrSceneComponentParentFilterInfoMSFT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrUuidMSFT parentId; -} XrSceneComponentParentFilterInfoMSFT; - -// XrSceneObjectTypesFilterInfoMSFT extends XrSceneComponentsGetInfoMSFT -typedef struct XrSceneObjectTypesFilterInfoMSFT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t objectTypeCount; - const XrSceneObjectTypeMSFT* objectTypes; -} XrSceneObjectTypesFilterInfoMSFT; - -typedef struct XrScenePlaneMSFT { - XrScenePlaneAlignmentTypeMSFT alignment; - XrExtent2Df size; - uint64_t meshBufferId; - XrBool32 supportsIndicesUint16; -} XrScenePlaneMSFT; - -// XrScenePlanesMSFT extends XrSceneComponentsMSFT -typedef struct XrScenePlanesMSFT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t scenePlaneCount; - XrScenePlaneMSFT* scenePlanes; -} XrScenePlanesMSFT; - -// XrScenePlaneAlignmentFilterInfoMSFT extends XrSceneComponentsGetInfoMSFT -typedef struct XrScenePlaneAlignmentFilterInfoMSFT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t alignmentCount; - const XrScenePlaneAlignmentTypeMSFT* alignments; -} XrScenePlaneAlignmentFilterInfoMSFT; - -typedef struct XrSceneMeshMSFT { - uint64_t meshBufferId; - XrBool32 supportsIndicesUint16; -} XrSceneMeshMSFT; - -// XrSceneMeshesMSFT extends XrSceneComponentsMSFT -typedef struct XrSceneMeshesMSFT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t sceneMeshCount; - XrSceneMeshMSFT* sceneMeshes; -} XrSceneMeshesMSFT; - -typedef struct XrSceneMeshBuffersGetInfoMSFT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint64_t meshBufferId; -} XrSceneMeshBuffersGetInfoMSFT; - -typedef struct XrSceneMeshBuffersMSFT { - XrStructureType type; - void* XR_MAY_ALIAS next; -} XrSceneMeshBuffersMSFT; - -typedef struct XrSceneMeshVertexBufferMSFT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t vertexCapacityInput; - uint32_t vertexCountOutput; - XrVector3f* vertices; -} XrSceneMeshVertexBufferMSFT; - -typedef struct XrSceneMeshIndicesUint32MSFT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t indexCapacityInput; - uint32_t indexCountOutput; - uint32_t* indices; -} XrSceneMeshIndicesUint32MSFT; - -typedef struct XrSceneMeshIndicesUint16MSFT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t indexCapacityInput; - uint32_t indexCountOutput; - uint16_t* indices; -} XrSceneMeshIndicesUint16MSFT; - -typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSceneComputeFeaturesMSFT)(XrInstance instance, XrSystemId systemId, uint32_t featureCapacityInput, uint32_t* featureCountOutput, XrSceneComputeFeatureMSFT* features); -typedef XrResult (XRAPI_PTR *PFN_xrCreateSceneObserverMSFT)(XrSession session, const XrSceneObserverCreateInfoMSFT* createInfo, XrSceneObserverMSFT* sceneObserver); -typedef XrResult (XRAPI_PTR *PFN_xrDestroySceneObserverMSFT)(XrSceneObserverMSFT sceneObserver); -typedef XrResult (XRAPI_PTR *PFN_xrCreateSceneMSFT)(XrSceneObserverMSFT sceneObserver, const XrSceneCreateInfoMSFT* createInfo, XrSceneMSFT* scene); -typedef XrResult (XRAPI_PTR *PFN_xrDestroySceneMSFT)(XrSceneMSFT scene); -typedef XrResult (XRAPI_PTR *PFN_xrComputeNewSceneMSFT)(XrSceneObserverMSFT sceneObserver, const XrNewSceneComputeInfoMSFT* computeInfo); -typedef XrResult (XRAPI_PTR *PFN_xrGetSceneComputeStateMSFT)(XrSceneObserverMSFT sceneObserver, XrSceneComputeStateMSFT* state); -typedef XrResult (XRAPI_PTR *PFN_xrGetSceneComponentsMSFT)(XrSceneMSFT scene, const XrSceneComponentsGetInfoMSFT* getInfo, XrSceneComponentsMSFT* components); -typedef XrResult (XRAPI_PTR *PFN_xrLocateSceneComponentsMSFT)(XrSceneMSFT scene, const XrSceneComponentsLocateInfoMSFT* locateInfo, XrSceneComponentLocationsMSFT* locations); -typedef XrResult (XRAPI_PTR *PFN_xrGetSceneMeshBuffersMSFT)(XrSceneMSFT scene, const XrSceneMeshBuffersGetInfoMSFT* getInfo, XrSceneMeshBuffersMSFT* buffers); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSceneComputeFeaturesMSFT( - XrInstance instance, - XrSystemId systemId, - uint32_t featureCapacityInput, - uint32_t* featureCountOutput, - XrSceneComputeFeatureMSFT* features); - -XRAPI_ATTR XrResult XRAPI_CALL xrCreateSceneObserverMSFT( - XrSession session, - const XrSceneObserverCreateInfoMSFT* createInfo, - XrSceneObserverMSFT* sceneObserver); - -XRAPI_ATTR XrResult XRAPI_CALL xrDestroySceneObserverMSFT( - XrSceneObserverMSFT sceneObserver); - -XRAPI_ATTR XrResult XRAPI_CALL xrCreateSceneMSFT( - XrSceneObserverMSFT sceneObserver, - const XrSceneCreateInfoMSFT* createInfo, - XrSceneMSFT* scene); - -XRAPI_ATTR XrResult XRAPI_CALL xrDestroySceneMSFT( - XrSceneMSFT scene); - -XRAPI_ATTR XrResult XRAPI_CALL xrComputeNewSceneMSFT( - XrSceneObserverMSFT sceneObserver, - const XrNewSceneComputeInfoMSFT* computeInfo); - -XRAPI_ATTR XrResult XRAPI_CALL xrGetSceneComputeStateMSFT( - XrSceneObserverMSFT sceneObserver, - XrSceneComputeStateMSFT* state); - -XRAPI_ATTR XrResult XRAPI_CALL xrGetSceneComponentsMSFT( - XrSceneMSFT scene, - const XrSceneComponentsGetInfoMSFT* getInfo, - XrSceneComponentsMSFT* components); - -XRAPI_ATTR XrResult XRAPI_CALL xrLocateSceneComponentsMSFT( - XrSceneMSFT scene, - const XrSceneComponentsLocateInfoMSFT* locateInfo, - XrSceneComponentLocationsMSFT* locations); - -XRAPI_ATTR XrResult XRAPI_CALL xrGetSceneMeshBuffersMSFT( - XrSceneMSFT scene, - const XrSceneMeshBuffersGetInfoMSFT* getInfo, - XrSceneMeshBuffersMSFT* buffers); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_MSFT_scene_understanding_serialization 1 -#define XR_MSFT_scene_understanding_serialization_SPEC_VERSION 1 -#define XR_MSFT_SCENE_UNDERSTANDING_SERIALIZATION_EXTENSION_NAME "XR_MSFT_scene_understanding_serialization" -typedef struct XrSerializedSceneFragmentDataGetInfoMSFT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrUuidMSFT sceneFragmentId; -} XrSerializedSceneFragmentDataGetInfoMSFT; - -typedef struct XrDeserializeSceneFragmentMSFT { - uint32_t bufferSize; - const uint8_t* buffer; -} XrDeserializeSceneFragmentMSFT; - -typedef struct XrSceneDeserializeInfoMSFT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t fragmentCount; - const XrDeserializeSceneFragmentMSFT* fragments; -} XrSceneDeserializeInfoMSFT; - -typedef XrResult (XRAPI_PTR *PFN_xrDeserializeSceneMSFT)(XrSceneObserverMSFT sceneObserver, const XrSceneDeserializeInfoMSFT* deserializeInfo); -typedef XrResult (XRAPI_PTR *PFN_xrGetSerializedSceneFragmentDataMSFT)(XrSceneMSFT scene, const XrSerializedSceneFragmentDataGetInfoMSFT* getInfo, uint32_t countInput, uint32_t* readOutput, uint8_t* buffer); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrDeserializeSceneMSFT( - XrSceneObserverMSFT sceneObserver, - const XrSceneDeserializeInfoMSFT* deserializeInfo); - -XRAPI_ATTR XrResult XRAPI_CALL xrGetSerializedSceneFragmentDataMSFT( - XrSceneMSFT scene, - const XrSerializedSceneFragmentDataGetInfoMSFT* getInfo, - uint32_t countInput, - uint32_t* readOutput, - uint8_t* buffer); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - #define XR_FB_display_refresh_rate 1 #define XR_FB_display_refresh_rate_SPEC_VERSION 1 #define XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME "XR_FB_display_refresh_rate" @@ -2886,7 +2234,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrGetDisplayRefreshRateFB)(XrSession session, f typedef XrResult (XRAPI_PTR *PFN_xrRequestDisplayRefreshRateFB)(XrSession session, float displayRefreshRate); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateDisplayRefreshRatesFB( XrSession session, uint32_t displayRefreshRateCapacityInput, @@ -2900,8 +2247,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetDisplayRefreshRateFB( XRAPI_ATTR XrResult XRAPI_CALL xrRequestDisplayRefreshRateFB( XrSession session, float displayRefreshRate); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #define XR_HTC_vive_cosmos_controller_interaction 1 @@ -2909,161 +2255,8 @@ XRAPI_ATTR XrResult XRAPI_CALL xrRequestDisplayRefreshRateFB( #define XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_HTC_vive_cosmos_controller_interaction" -#define XR_HTCX_vive_tracker_interaction 1 -#define XR_HTCX_vive_tracker_interaction_SPEC_VERSION 1 -#define XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME "XR_HTCX_vive_tracker_interaction" -typedef struct XrViveTrackerPathsHTCX { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrPath persistentPath; - XrPath rolePath; -} XrViveTrackerPathsHTCX; - -typedef struct XrEventDataViveTrackerConnectedHTCX { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrViveTrackerPathsHTCX* paths; -} XrEventDataViveTrackerConnectedHTCX; - -typedef XrResult (XRAPI_PTR *PFN_xrEnumerateViveTrackerPathsHTCX)(XrInstance instance, uint32_t pathCapacityInput, uint32_t* pathCountOutput, XrViveTrackerPathsHTCX* paths); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateViveTrackerPathsHTCX( - XrInstance instance, - uint32_t pathCapacityInput, - uint32_t* pathCountOutput, - XrViveTrackerPathsHTCX* paths); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_HTC_facial_tracking 1 - -#define XR_FACIAL_EXPRESSION_EYE_COUNT_HTC 14 - - -#define XR_FACIAL_EXPRESSION_LIP_COUNT_HTC 37 - -XR_DEFINE_HANDLE(XrFacialTrackerHTC) -#define XR_HTC_facial_tracking_SPEC_VERSION 1 -#define XR_HTC_FACIAL_TRACKING_EXTENSION_NAME "XR_HTC_facial_tracking" - -typedef enum XrEyeExpressionHTC { - XR_EYE_EXPRESSION_LEFT_BLINK_HTC = 0, - XR_EYE_EXPRESSION_LEFT_WIDE_HTC = 1, - XR_EYE_EXPRESSION_RIGHT_BLINK_HTC = 2, - XR_EYE_EXPRESSION_RIGHT_WIDE_HTC = 3, - XR_EYE_EXPRESSION_LEFT_SQUEEZE_HTC = 4, - XR_EYE_EXPRESSION_RIGHT_SQUEEZE_HTC = 5, - XR_EYE_EXPRESSION_LEFT_DOWN_HTC = 6, - XR_EYE_EXPRESSION_RIGHT_DOWN_HTC = 7, - XR_EYE_EXPRESSION_LEFT_OUT_HTC = 8, - XR_EYE_EXPRESSION_RIGHT_IN_HTC = 9, - XR_EYE_EXPRESSION_LEFT_IN_HTC = 10, - XR_EYE_EXPRESSION_RIGHT_OUT_HTC = 11, - XR_EYE_EXPRESSION_LEFT_UP_HTC = 12, - XR_EYE_EXPRESSION_RIGHT_UP_HTC = 13, - XR_EYE_EXPRESSION_MAX_ENUM_HTC = 0x7FFFFFFF -} XrEyeExpressionHTC; - -typedef enum XrLipExpressionHTC { - XR_LIP_EXPRESSION_JAW_RIGHT_HTC = 0, - XR_LIP_EXPRESSION_JAW_LEFT_HTC = 1, - XR_LIP_EXPRESSION_JAW_FORWARD_HTC = 2, - XR_LIP_EXPRESSION_JAW_OPEN_HTC = 3, - XR_LIP_EXPRESSION_MOUTH_APE_SHAPE_HTC = 4, - XR_LIP_EXPRESSION_MOUTH_UPPER_RIGHT_HTC = 5, - XR_LIP_EXPRESSION_MOUTH_UPPER_LEFT_HTC = 6, - XR_LIP_EXPRESSION_MOUTH_LOWER_RIGHT_HTC = 7, - XR_LIP_EXPRESSION_MOUTH_LOWER_LEFT_HTC = 8, - XR_LIP_EXPRESSION_MOUTH_UPPER_OVERTURN_HTC = 9, - XR_LIP_EXPRESSION_MOUTH_LOWER_OVERTURN_HTC = 10, - XR_LIP_EXPRESSION_MOUTH_POUT_HTC = 11, - XR_LIP_EXPRESSION_MOUTH_SMILE_RIGHT_HTC = 12, - XR_LIP_EXPRESSION_MOUTH_SMILE_LEFT_HTC = 13, - XR_LIP_EXPRESSION_MOUTH_SAD_RIGHT_HTC = 14, - XR_LIP_EXPRESSION_MOUTH_SAD_LEFT_HTC = 15, - XR_LIP_EXPRESSION_CHEEK_PUFF_RIGHT_HTC = 16, - XR_LIP_EXPRESSION_CHEEK_PUFF_LEFT_HTC = 17, - XR_LIP_EXPRESSION_CHEEK_SUCK_HTC = 18, - XR_LIP_EXPRESSION_MOUTH_UPPER_UPRIGHT_HTC = 19, - XR_LIP_EXPRESSION_MOUTH_UPPER_UPLEFT_HTC = 20, - XR_LIP_EXPRESSION_MOUTH_LOWER_DOWNRIGHT_HTC = 21, - XR_LIP_EXPRESSION_MOUTH_LOWER_DOWNLEFT_HTC = 22, - XR_LIP_EXPRESSION_MOUTH_UPPER_INSIDE_HTC = 23, - XR_LIP_EXPRESSION_MOUTH_LOWER_INSIDE_HTC = 24, - XR_LIP_EXPRESSION_MOUTH_LOWER_OVERLAY_HTC = 25, - XR_LIP_EXPRESSION_TONGUE_LONGSTEP1_HTC = 26, - XR_LIP_EXPRESSION_TONGUE_LEFT_HTC = 27, - XR_LIP_EXPRESSION_TONGUE_RIGHT_HTC = 28, - XR_LIP_EXPRESSION_TONGUE_UP_HTC = 29, - XR_LIP_EXPRESSION_TONGUE_DOWN_HTC = 30, - XR_LIP_EXPRESSION_TONGUE_ROLL_HTC = 31, - XR_LIP_EXPRESSION_TONGUE_LONGSTEP2_HTC = 32, - XR_LIP_EXPRESSION_TONGUE_UPRIGHT_MORPH_HTC = 33, - XR_LIP_EXPRESSION_TONGUE_UPLEFT_MORPH_HTC = 34, - XR_LIP_EXPRESSION_TONGUE_DOWNRIGHT_MORPH_HTC = 35, - XR_LIP_EXPRESSION_TONGUE_DOWNLEFT_MORPH_HTC = 36, - XR_LIP_EXPRESSION_MAX_ENUM_HTC = 0x7FFFFFFF -} XrLipExpressionHTC; - -typedef enum XrFacialTrackingTypeHTC { - XR_FACIAL_TRACKING_TYPE_EYE_DEFAULT_HTC = 1, - XR_FACIAL_TRACKING_TYPE_LIP_DEFAULT_HTC = 2, - XR_FACIAL_TRACKING_TYPE_MAX_ENUM_HTC = 0x7FFFFFFF -} XrFacialTrackingTypeHTC; -// XrSystemFacialTrackingPropertiesHTC extends XrSystemProperties -typedef struct XrSystemFacialTrackingPropertiesHTC { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportEyeFacialTracking; - XrBool32 supportLipFacialTracking; -} XrSystemFacialTrackingPropertiesHTC; - -typedef struct XrFacialExpressionsHTC { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrBool32 isActive; - XrTime sampleTime; - uint32_t expressionCount; - float* expressionWeightings; -} XrFacialExpressionsHTC; - -typedef struct XrFacialTrackerCreateInfoHTC { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrFacialTrackingTypeHTC facialTrackingType; -} XrFacialTrackerCreateInfoHTC; - -typedef XrResult (XRAPI_PTR *PFN_xrCreateFacialTrackerHTC)(XrSession session, const XrFacialTrackerCreateInfoHTC* createInfo, XrFacialTrackerHTC* facialTracker); -typedef XrResult (XRAPI_PTR *PFN_xrDestroyFacialTrackerHTC)(XrFacialTrackerHTC facialTracker); -typedef XrResult (XRAPI_PTR *PFN_xrGetFacialExpressionsHTC)(XrFacialTrackerHTC facialTracker, XrFacialExpressionsHTC* facialExpressions); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrCreateFacialTrackerHTC( - XrSession session, - const XrFacialTrackerCreateInfoHTC* createInfo, - XrFacialTrackerHTC* facialTracker); - -XRAPI_ATTR XrResult XRAPI_CALL xrDestroyFacialTrackerHTC( - XrFacialTrackerHTC facialTracker); - -XRAPI_ATTR XrResult XRAPI_CALL xrGetFacialExpressionsHTC( - XrFacialTrackerHTC facialTracker, - XrFacialExpressionsHTC* facialExpressions); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_HTC_vive_focus3_controller_interaction 1 -#define XR_HTC_vive_focus3_controller_interaction_SPEC_VERSION 1 -#define XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_HTC_vive_focus3_controller_interaction" - - #define XR_FB_color_space 1 -#define XR_FB_color_space_SPEC_VERSION 2 +#define XR_FB_color_space_SPEC_VERSION 1 #define XR_FB_COLOR_SPACE_EXTENSION_NAME "XR_FB_color_space" typedef enum XrColorSpaceFB { @@ -3077,7 +2270,6 @@ typedef enum XrColorSpaceFB { XR_COLOR_SPACE_ADOBE_RGB_FB = 7, XR_COLOR_SPACE_MAX_ENUM_FB = 0x7FFFFFFF } XrColorSpaceFB; -// XrSystemColorSpacePropertiesFB extends XrSystemProperties typedef struct XrSystemColorSpacePropertiesFB { XrStructureType type; void* XR_MAY_ALIAS next; @@ -3088,7 +2280,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrEnumerateColorSpacesFB)(XrSession session, ui typedef XrResult (XRAPI_PTR *PFN_xrSetColorSpaceFB)(XrSession session, const XrColorSpaceFB colorspace); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateColorSpacesFB( XrSession session, uint32_t colorSpaceCapacityInput, @@ -3098,825 +2289,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateColorSpacesFB( XRAPI_ATTR XrResult XRAPI_CALL xrSetColorSpaceFB( XrSession session, const XrColorSpaceFB colorspace); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_FB_hand_tracking_mesh 1 -#define XR_FB_hand_tracking_mesh_SPEC_VERSION 1 -#define XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME "XR_FB_hand_tracking_mesh" -typedef struct XrVector4sFB { - int16_t x; - int16_t y; - int16_t z; - int16_t w; -} XrVector4sFB; - -typedef struct XrHandTrackingMeshFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t jointCapacityInput; - uint32_t jointCountOutput; - XrPosef* jointBindPoses; - float* jointRadii; - XrHandJointEXT* jointParents; - uint32_t vertexCapacityInput; - uint32_t vertexCountOutput; - XrVector3f* vertexPositions; - XrVector3f* vertexNormals; - XrVector2f* vertexUVs; - XrVector4sFB* vertexBlendIndices; - XrVector4f* vertexBlendWeights; - uint32_t indexCapacityInput; - uint32_t indexCountOutput; - int16_t* indices; -} XrHandTrackingMeshFB; - -// XrHandTrackingScaleFB extends XrHandJointsLocateInfoEXT -typedef struct XrHandTrackingScaleFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - float sensorOutput; - float currentOutput; - XrBool32 overrideHandScale; - float overrideValueInput; -} XrHandTrackingScaleFB; - -typedef XrResult (XRAPI_PTR *PFN_xrGetHandMeshFB)(XrHandTrackerEXT handTracker, XrHandTrackingMeshFB* mesh); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrGetHandMeshFB( - XrHandTrackerEXT handTracker, - XrHandTrackingMeshFB* mesh); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_FB_hand_tracking_aim 1 -#define XR_FB_hand_tracking_aim_SPEC_VERSION 1 -#define XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME "XR_FB_hand_tracking_aim" -typedef XrFlags64 XrHandTrackingAimFlagsFB; - -// Flag bits for XrHandTrackingAimFlagsFB -static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_COMPUTED_BIT_FB = 0x00000001; -static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_VALID_BIT_FB = 0x00000002; -static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_INDEX_PINCHING_BIT_FB = 0x00000004; -static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_MIDDLE_PINCHING_BIT_FB = 0x00000008; -static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_RING_PINCHING_BIT_FB = 0x00000010; -static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_LITTLE_PINCHING_BIT_FB = 0x00000020; -static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_SYSTEM_GESTURE_BIT_FB = 0x00000040; -static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_DOMINANT_HAND_BIT_FB = 0x00000080; -static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_MENU_PRESSED_BIT_FB = 0x00000100; - -// XrHandTrackingAimStateFB extends XrHandJointsLocateInfoEXT -typedef struct XrHandTrackingAimStateFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrHandTrackingAimFlagsFB status; - XrPosef aimPose; - float pinchStrengthIndex; - float pinchStrengthMiddle; - float pinchStrengthRing; - float pinchStrengthLittle; -} XrHandTrackingAimStateFB; - - - -#define XR_FB_hand_tracking_capsules 1 -#define XR_HAND_TRACKING_CAPSULE_POINT_COUNT_FB 2 -#define XR_FB_HAND_TRACKING_CAPSULE_POINT_COUNT XR_HAND_TRACKING_CAPSULE_POINT_COUNT_FB -#define XR_HAND_TRACKING_CAPSULE_COUNT_FB 19 -#define XR_FB_HAND_TRACKING_CAPSULE_COUNT XR_HAND_TRACKING_CAPSULE_COUNT_FB -#define XR_FB_hand_tracking_capsules_SPEC_VERSION 2 -#define XR_FB_HAND_TRACKING_CAPSULES_EXTENSION_NAME "XR_FB_hand_tracking_capsules" -typedef struct XrHandCapsuleFB { - XrVector3f points[XR_FB_HAND_TRACKING_CAPSULE_POINT_COUNT]; - float radius; - XrHandJointEXT joint; -} XrHandCapsuleFB; - -// XrHandTrackingCapsulesStateFB extends XrHandJointsLocateInfoEXT -typedef struct XrHandTrackingCapsulesStateFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrHandCapsuleFB capsules[XR_FB_HAND_TRACKING_CAPSULE_COUNT]; -} XrHandTrackingCapsulesStateFB; - - - -#define XR_FB_foveation 1 -XR_DEFINE_HANDLE(XrFoveationProfileFB) -#define XR_FB_foveation_SPEC_VERSION 1 -#define XR_FB_FOVEATION_EXTENSION_NAME "XR_FB_foveation" -typedef XrFlags64 XrSwapchainCreateFoveationFlagsFB; - -// Flag bits for XrSwapchainCreateFoveationFlagsFB -static const XrSwapchainCreateFoveationFlagsFB XR_SWAPCHAIN_CREATE_FOVEATION_SCALED_BIN_BIT_FB = 0x00000001; -static const XrSwapchainCreateFoveationFlagsFB XR_SWAPCHAIN_CREATE_FOVEATION_FRAGMENT_DENSITY_MAP_BIT_FB = 0x00000002; - -typedef XrFlags64 XrSwapchainStateFoveationFlagsFB; - -// Flag bits for XrSwapchainStateFoveationFlagsFB - -typedef struct XrFoveationProfileCreateInfoFB { - XrStructureType type; - void* XR_MAY_ALIAS next; -} XrFoveationProfileCreateInfoFB; - -// XrSwapchainCreateInfoFoveationFB extends XrSwapchainCreateInfo -typedef struct XrSwapchainCreateInfoFoveationFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrSwapchainCreateFoveationFlagsFB flags; -} XrSwapchainCreateInfoFoveationFB; - -typedef struct XrSwapchainStateFoveationFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrSwapchainStateFoveationFlagsFB flags; - XrFoveationProfileFB profile; -} XrSwapchainStateFoveationFB; - -typedef XrResult (XRAPI_PTR *PFN_xrCreateFoveationProfileFB)(XrSession session, const XrFoveationProfileCreateInfoFB* createInfo, XrFoveationProfileFB* profile); -typedef XrResult (XRAPI_PTR *PFN_xrDestroyFoveationProfileFB)(XrFoveationProfileFB profile); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrCreateFoveationProfileFB( - XrSession session, - const XrFoveationProfileCreateInfoFB* createInfo, - XrFoveationProfileFB* profile); - -XRAPI_ATTR XrResult XRAPI_CALL xrDestroyFoveationProfileFB( - XrFoveationProfileFB profile); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_FB_foveation_configuration 1 -#define XR_FB_foveation_configuration_SPEC_VERSION 1 -#define XR_FB_FOVEATION_CONFIGURATION_EXTENSION_NAME "XR_FB_foveation_configuration" - -typedef enum XrFoveationLevelFB { - XR_FOVEATION_LEVEL_NONE_FB = 0, - XR_FOVEATION_LEVEL_LOW_FB = 1, - XR_FOVEATION_LEVEL_MEDIUM_FB = 2, - XR_FOVEATION_LEVEL_HIGH_FB = 3, - XR_FOVEATION_LEVEL_MAX_ENUM_FB = 0x7FFFFFFF -} XrFoveationLevelFB; - -typedef enum XrFoveationDynamicFB { - XR_FOVEATION_DYNAMIC_DISABLED_FB = 0, - XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_FB = 1, - XR_FOVEATION_DYNAMIC_MAX_ENUM_FB = 0x7FFFFFFF -} XrFoveationDynamicFB; -// XrFoveationLevelProfileCreateInfoFB extends XrFoveationProfileCreateInfoFB -typedef struct XrFoveationLevelProfileCreateInfoFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrFoveationLevelFB level; - float verticalOffset; - XrFoveationDynamicFB dynamic; -} XrFoveationLevelProfileCreateInfoFB; - - - -#define XR_FB_keyboard_tracking 1 -#define XR_FB_keyboard_tracking_SPEC_VERSION 1 -#define XR_FB_KEYBOARD_TRACKING_EXTENSION_NAME "XR_FB_keyboard_tracking" -#define XR_MAX_KEYBOARD_TRACKING_NAME_SIZE_FB 128 -typedef XrFlags64 XrKeyboardTrackingFlagsFB; - -// Flag bits for XrKeyboardTrackingFlagsFB -static const XrKeyboardTrackingFlagsFB XR_KEYBOARD_TRACKING_EXISTS_BIT_FB = 0x00000001; -static const XrKeyboardTrackingFlagsFB XR_KEYBOARD_TRACKING_LOCAL_BIT_FB = 0x00000002; -static const XrKeyboardTrackingFlagsFB XR_KEYBOARD_TRACKING_REMOTE_BIT_FB = 0x00000004; -static const XrKeyboardTrackingFlagsFB XR_KEYBOARD_TRACKING_CONNECTED_BIT_FB = 0x00000008; - -typedef XrFlags64 XrKeyboardTrackingQueryFlagsFB; - -// Flag bits for XrKeyboardTrackingQueryFlagsFB -static const XrKeyboardTrackingQueryFlagsFB XR_KEYBOARD_TRACKING_QUERY_LOCAL_BIT_FB = 0x00000002; -static const XrKeyboardTrackingQueryFlagsFB XR_KEYBOARD_TRACKING_QUERY_REMOTE_BIT_FB = 0x00000004; - -// XrSystemKeyboardTrackingPropertiesFB extends XrSystemProperties -typedef struct XrSystemKeyboardTrackingPropertiesFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsKeyboardTracking; -} XrSystemKeyboardTrackingPropertiesFB; - -typedef struct XrKeyboardTrackingDescriptionFB { - uint64_t trackedKeyboardId; - XrVector3f size; - XrKeyboardTrackingFlagsFB flags; - char name[XR_MAX_KEYBOARD_TRACKING_NAME_SIZE_FB]; -} XrKeyboardTrackingDescriptionFB; - -typedef struct XrKeyboardSpaceCreateInfoFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint64_t trackedKeyboardId; -} XrKeyboardSpaceCreateInfoFB; - -typedef struct XrKeyboardTrackingQueryFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrKeyboardTrackingQueryFlagsFB flags; -} XrKeyboardTrackingQueryFB; - -typedef XrResult (XRAPI_PTR *PFN_xrQuerySystemTrackedKeyboardFB)(XrSession session, const XrKeyboardTrackingQueryFB* queryInfo, XrKeyboardTrackingDescriptionFB* keyboard); -typedef XrResult (XRAPI_PTR *PFN_xrCreateKeyboardSpaceFB)(XrSession session, const XrKeyboardSpaceCreateInfoFB* createInfo, XrSpace* keyboardSpace); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrQuerySystemTrackedKeyboardFB( - XrSession session, - const XrKeyboardTrackingQueryFB* queryInfo, - XrKeyboardTrackingDescriptionFB* keyboard); - -XRAPI_ATTR XrResult XRAPI_CALL xrCreateKeyboardSpaceFB( - XrSession session, - const XrKeyboardSpaceCreateInfoFB* createInfo, - XrSpace* keyboardSpace); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_FB_triangle_mesh 1 -XR_DEFINE_HANDLE(XrTriangleMeshFB) -#define XR_FB_triangle_mesh_SPEC_VERSION 1 -#define XR_FB_TRIANGLE_MESH_EXTENSION_NAME "XR_FB_triangle_mesh" - -typedef enum XrWindingOrderFB { - XR_WINDING_ORDER_UNKNOWN_FB = 0, - XR_WINDING_ORDER_CW_FB = 1, - XR_WINDING_ORDER_CCW_FB = 2, - XR_WINDING_ORDER_MAX_ENUM_FB = 0x7FFFFFFF -} XrWindingOrderFB; -typedef XrFlags64 XrTriangleMeshFlagsFB; - -// Flag bits for XrTriangleMeshFlagsFB -static const XrTriangleMeshFlagsFB XR_TRIANGLE_MESH_MUTABLE_BIT_FB = 0x00000001; - -typedef struct XrTriangleMeshCreateInfoFB { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrTriangleMeshFlagsFB flags; - XrWindingOrderFB windingOrder; - uint32_t vertexCount; - const XrVector3f* vertexBuffer; - uint32_t triangleCount; - const uint32_t* indexBuffer; -} XrTriangleMeshCreateInfoFB; - -typedef XrResult (XRAPI_PTR *PFN_xrCreateTriangleMeshFB)(XrSession session, const XrTriangleMeshCreateInfoFB* createInfo, XrTriangleMeshFB* outTriangleMesh); -typedef XrResult (XRAPI_PTR *PFN_xrDestroyTriangleMeshFB)(XrTriangleMeshFB mesh); -typedef XrResult (XRAPI_PTR *PFN_xrTriangleMeshGetVertexBufferFB)(XrTriangleMeshFB mesh, XrVector3f** outVertexBuffer); -typedef XrResult (XRAPI_PTR *PFN_xrTriangleMeshGetIndexBufferFB)(XrTriangleMeshFB mesh, uint32_t** outIndexBuffer); -typedef XrResult (XRAPI_PTR *PFN_xrTriangleMeshBeginUpdateFB)(XrTriangleMeshFB mesh); -typedef XrResult (XRAPI_PTR *PFN_xrTriangleMeshEndUpdateFB)(XrTriangleMeshFB mesh, uint32_t vertexCount, uint32_t triangleCount); -typedef XrResult (XRAPI_PTR *PFN_xrTriangleMeshBeginVertexBufferUpdateFB)(XrTriangleMeshFB mesh, uint32_t* outVertexCount); -typedef XrResult (XRAPI_PTR *PFN_xrTriangleMeshEndVertexBufferUpdateFB)(XrTriangleMeshFB mesh); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrCreateTriangleMeshFB( - XrSession session, - const XrTriangleMeshCreateInfoFB* createInfo, - XrTriangleMeshFB* outTriangleMesh); - -XRAPI_ATTR XrResult XRAPI_CALL xrDestroyTriangleMeshFB( - XrTriangleMeshFB mesh); - -XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshGetVertexBufferFB( - XrTriangleMeshFB mesh, - XrVector3f** outVertexBuffer); - -XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshGetIndexBufferFB( - XrTriangleMeshFB mesh, - uint32_t** outIndexBuffer); - -XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshBeginUpdateFB( - XrTriangleMeshFB mesh); - -XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshEndUpdateFB( - XrTriangleMeshFB mesh, - uint32_t vertexCount, - uint32_t triangleCount); - -XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshBeginVertexBufferUpdateFB( - XrTriangleMeshFB mesh, - uint32_t* outVertexCount); - -XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshEndVertexBufferUpdateFB( - XrTriangleMeshFB mesh); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_FB_passthrough 1 -XR_DEFINE_HANDLE(XrPassthroughFB) -XR_DEFINE_HANDLE(XrPassthroughLayerFB) -XR_DEFINE_HANDLE(XrGeometryInstanceFB) -#define XR_FB_passthrough_SPEC_VERSION 1 -#define XR_FB_PASSTHROUGH_EXTENSION_NAME "XR_FB_passthrough" -#define XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB 256 - -typedef enum XrPassthroughLayerPurposeFB { - XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB = 0, - XR_PASSTHROUGH_LAYER_PURPOSE_PROJECTED_FB = 1, - XR_PASSTHROUGH_LAYER_PURPOSE_TRACKED_KEYBOARD_HANDS_FB = 1000203001, - XR_PASSTHROUGH_LAYER_PURPOSE_MAX_ENUM_FB = 0x7FFFFFFF -} XrPassthroughLayerPurposeFB; -typedef XrFlags64 XrPassthroughFlagsFB; - -// Flag bits for XrPassthroughFlagsFB -static const XrPassthroughFlagsFB XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB = 0x00000001; - -typedef XrFlags64 XrPassthroughStateChangedFlagsFB; - -// Flag bits for XrPassthroughStateChangedFlagsFB -static const XrPassthroughStateChangedFlagsFB XR_PASSTHROUGH_STATE_CHANGED_REINIT_REQUIRED_BIT_FB = 0x00000001; -static const XrPassthroughStateChangedFlagsFB XR_PASSTHROUGH_STATE_CHANGED_NON_RECOVERABLE_ERROR_BIT_FB = 0x00000002; -static const XrPassthroughStateChangedFlagsFB XR_PASSTHROUGH_STATE_CHANGED_RECOVERABLE_ERROR_BIT_FB = 0x00000004; -static const XrPassthroughStateChangedFlagsFB XR_PASSTHROUGH_STATE_CHANGED_RESTORED_ERROR_BIT_FB = 0x00000008; - -// XrSystemPassthroughPropertiesFB extends XrSystemProperties -typedef struct XrSystemPassthroughPropertiesFB { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrBool32 supportsPassthrough; -} XrSystemPassthroughPropertiesFB; - -typedef struct XrPassthroughCreateInfoFB { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrPassthroughFlagsFB flags; -} XrPassthroughCreateInfoFB; - -typedef struct XrPassthroughLayerCreateInfoFB { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrPassthroughFB passthrough; - XrPassthroughFlagsFB flags; - XrPassthroughLayerPurposeFB purpose; -} XrPassthroughLayerCreateInfoFB; - -// XrCompositionLayerPassthroughFB extends XrCompositionLayerBaseHeader -typedef struct XrCompositionLayerPassthroughFB { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrCompositionLayerFlags flags; - XrSpace space; - XrPassthroughLayerFB layerHandle; -} XrCompositionLayerPassthroughFB; - -typedef struct XrGeometryInstanceCreateInfoFB { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrPassthroughLayerFB layer; - XrTriangleMeshFB mesh; - XrSpace baseSpace; - XrPosef pose; - XrVector3f scale; -} XrGeometryInstanceCreateInfoFB; - -typedef struct XrGeometryInstanceTransformFB { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpace baseSpace; - XrTime time; - XrPosef pose; - XrVector3f scale; -} XrGeometryInstanceTransformFB; - -typedef struct XrPassthroughStyleFB { - XrStructureType type; - const void* XR_MAY_ALIAS next; - float textureOpacityFactor; - XrColor4f edgeColor; -} XrPassthroughStyleFB; - -typedef struct XrPassthroughColorMapMonoToRgbaFB { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrColor4f textureColorMap[XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB]; -} XrPassthroughColorMapMonoToRgbaFB; - -typedef struct XrPassthroughColorMapMonoToMonoFB { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint8_t textureColorMap[XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB]; -} XrPassthroughColorMapMonoToMonoFB; - -typedef struct XrEventDataPassthroughStateChangedFB { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrPassthroughStateChangedFlagsFB flags; -} XrEventDataPassthroughStateChangedFB; - -typedef XrResult (XRAPI_PTR *PFN_xrCreatePassthroughFB)(XrSession session, const XrPassthroughCreateInfoFB* createInfo, XrPassthroughFB* outPassthrough); -typedef XrResult (XRAPI_PTR *PFN_xrDestroyPassthroughFB)(XrPassthroughFB passthrough); -typedef XrResult (XRAPI_PTR *PFN_xrPassthroughStartFB)(XrPassthroughFB passthrough); -typedef XrResult (XRAPI_PTR *PFN_xrPassthroughPauseFB)(XrPassthroughFB passthrough); -typedef XrResult (XRAPI_PTR *PFN_xrCreatePassthroughLayerFB)(XrSession session, const XrPassthroughLayerCreateInfoFB* createInfo, XrPassthroughLayerFB* outLayer); -typedef XrResult (XRAPI_PTR *PFN_xrDestroyPassthroughLayerFB)(XrPassthroughLayerFB layer); -typedef XrResult (XRAPI_PTR *PFN_xrPassthroughLayerPauseFB)(XrPassthroughLayerFB layer); -typedef XrResult (XRAPI_PTR *PFN_xrPassthroughLayerResumeFB)(XrPassthroughLayerFB layer); -typedef XrResult (XRAPI_PTR *PFN_xrPassthroughLayerSetStyleFB)(XrPassthroughLayerFB layer, const XrPassthroughStyleFB* style); -typedef XrResult (XRAPI_PTR *PFN_xrCreateGeometryInstanceFB)(XrSession session, const XrGeometryInstanceCreateInfoFB* createInfo, XrGeometryInstanceFB* outGeometryInstance); -typedef XrResult (XRAPI_PTR *PFN_xrDestroyGeometryInstanceFB)(XrGeometryInstanceFB instance); -typedef XrResult (XRAPI_PTR *PFN_xrGeometryInstanceSetTransformFB)(XrGeometryInstanceFB instance, const XrGeometryInstanceTransformFB* transformation); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrCreatePassthroughFB( - XrSession session, - const XrPassthroughCreateInfoFB* createInfo, - XrPassthroughFB* outPassthrough); - -XRAPI_ATTR XrResult XRAPI_CALL xrDestroyPassthroughFB( - XrPassthroughFB passthrough); - -XRAPI_ATTR XrResult XRAPI_CALL xrPassthroughStartFB( - XrPassthroughFB passthrough); - -XRAPI_ATTR XrResult XRAPI_CALL xrPassthroughPauseFB( - XrPassthroughFB passthrough); - -XRAPI_ATTR XrResult XRAPI_CALL xrCreatePassthroughLayerFB( - XrSession session, - const XrPassthroughLayerCreateInfoFB* createInfo, - XrPassthroughLayerFB* outLayer); - -XRAPI_ATTR XrResult XRAPI_CALL xrDestroyPassthroughLayerFB( - XrPassthroughLayerFB layer); - -XRAPI_ATTR XrResult XRAPI_CALL xrPassthroughLayerPauseFB( - XrPassthroughLayerFB layer); - -XRAPI_ATTR XrResult XRAPI_CALL xrPassthroughLayerResumeFB( - XrPassthroughLayerFB layer); - -XRAPI_ATTR XrResult XRAPI_CALL xrPassthroughLayerSetStyleFB( - XrPassthroughLayerFB layer, - const XrPassthroughStyleFB* style); - -XRAPI_ATTR XrResult XRAPI_CALL xrCreateGeometryInstanceFB( - XrSession session, - const XrGeometryInstanceCreateInfoFB* createInfo, - XrGeometryInstanceFB* outGeometryInstance); - -XRAPI_ATTR XrResult XRAPI_CALL xrDestroyGeometryInstanceFB( - XrGeometryInstanceFB instance); - -XRAPI_ATTR XrResult XRAPI_CALL xrGeometryInstanceSetTransformFB( - XrGeometryInstanceFB instance, - const XrGeometryInstanceTransformFB* transformation); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_FB_render_model 1 - -#define XR_NULL_RENDER_MODEL_KEY_FB 0 - -XR_DEFINE_ATOM(XrRenderModelKeyFB) -#define XR_FB_render_model_SPEC_VERSION 1 -#define XR_FB_RENDER_MODEL_EXTENSION_NAME "XR_FB_render_model" -#define XR_MAX_RENDER_MODEL_NAME_SIZE_FB 64 -typedef XrFlags64 XrRenderModelFlagsFB; - -// Flag bits for XrRenderModelFlagsFB - -typedef struct XrRenderModelPathInfoFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrPath path; -} XrRenderModelPathInfoFB; - -typedef struct XrRenderModelPropertiesFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t vendorId; - char modelName[XR_MAX_RENDER_MODEL_NAME_SIZE_FB]; - XrRenderModelKeyFB modelKey; - uint32_t modelVersion; - XrRenderModelFlagsFB flags; -} XrRenderModelPropertiesFB; - -typedef struct XrRenderModelBufferFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t bufferCapacityInput; - uint32_t bufferCountOutput; - uint8_t* buffer; -} XrRenderModelBufferFB; - -typedef struct XrRenderModelLoadInfoFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrRenderModelKeyFB modelKey; -} XrRenderModelLoadInfoFB; - -// XrSystemRenderModelPropertiesFB extends XrSystemProperties -typedef struct XrSystemRenderModelPropertiesFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsRenderModelLoading; -} XrSystemRenderModelPropertiesFB; - -typedef XrResult (XRAPI_PTR *PFN_xrEnumerateRenderModelPathsFB)(XrSession session, uint32_t pathCapacityInput, uint32_t* pathCountOutput, XrRenderModelPathInfoFB* paths); -typedef XrResult (XRAPI_PTR *PFN_xrGetRenderModelPropertiesFB)(XrSession session, XrPath path, XrRenderModelPropertiesFB* properties); -typedef XrResult (XRAPI_PTR *PFN_xrLoadRenderModelFB)(XrSession session, const XrRenderModelLoadInfoFB* info, XrRenderModelBufferFB* buffer); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateRenderModelPathsFB( - XrSession session, - uint32_t pathCapacityInput, - uint32_t* pathCountOutput, - XrRenderModelPathInfoFB* paths); - -XRAPI_ATTR XrResult XRAPI_CALL xrGetRenderModelPropertiesFB( - XrSession session, - XrPath path, - XrRenderModelPropertiesFB* properties); - -XRAPI_ATTR XrResult XRAPI_CALL xrLoadRenderModelFB( - XrSession session, - const XrRenderModelLoadInfoFB* info, - XrRenderModelBufferFB* buffer); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_VARJO_foveated_rendering 1 -#define XR_VARJO_foveated_rendering_SPEC_VERSION 2 -#define XR_VARJO_FOVEATED_RENDERING_EXTENSION_NAME "XR_VARJO_foveated_rendering" -// XrViewLocateFoveatedRenderingVARJO extends XrViewLocateInfo -typedef struct XrViewLocateFoveatedRenderingVARJO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrBool32 foveatedRenderingActive; -} XrViewLocateFoveatedRenderingVARJO; - -// XrFoveatedViewConfigurationViewVARJO extends XrViewConfigurationView -typedef struct XrFoveatedViewConfigurationViewVARJO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 foveatedRenderingActive; -} XrFoveatedViewConfigurationViewVARJO; - -// XrSystemFoveatedRenderingPropertiesVARJO extends XrSystemProperties -typedef struct XrSystemFoveatedRenderingPropertiesVARJO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsFoveatedRendering; -} XrSystemFoveatedRenderingPropertiesVARJO; - - - -#define XR_VARJO_composition_layer_depth_test 1 -#define XR_VARJO_composition_layer_depth_test_SPEC_VERSION 2 -#define XR_VARJO_COMPOSITION_LAYER_DEPTH_TEST_EXTENSION_NAME "XR_VARJO_composition_layer_depth_test" -// XrCompositionLayerDepthTestVARJO extends XrCompositionLayerProjection -typedef struct XrCompositionLayerDepthTestVARJO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - float depthTestRangeNearZ; - float depthTestRangeFarZ; -} XrCompositionLayerDepthTestVARJO; - - - -#define XR_VARJO_environment_depth_estimation 1 -#define XR_VARJO_environment_depth_estimation_SPEC_VERSION 1 -#define XR_VARJO_ENVIRONMENT_DEPTH_ESTIMATION_EXTENSION_NAME "XR_VARJO_environment_depth_estimation" -typedef XrResult (XRAPI_PTR *PFN_xrSetEnvironmentDepthEstimationVARJO)(XrSession session, XrBool32 enabled); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrSetEnvironmentDepthEstimationVARJO( - XrSession session, - XrBool32 enabled); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_VARJO_marker_tracking 1 -#define XR_VARJO_marker_tracking_SPEC_VERSION 1 -#define XR_VARJO_MARKER_TRACKING_EXTENSION_NAME "XR_VARJO_marker_tracking" -// XrSystemMarkerTrackingPropertiesVARJO extends XrSystemProperties -typedef struct XrSystemMarkerTrackingPropertiesVARJO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsMarkerTracking; -} XrSystemMarkerTrackingPropertiesVARJO; - -typedef struct XrEventDataMarkerTrackingUpdateVARJO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint64_t markerId; - XrBool32 isActive; - XrBool32 isPredicted; - XrTime time; -} XrEventDataMarkerTrackingUpdateVARJO; - -typedef struct XrMarkerSpaceCreateInfoVARJO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint64_t markerId; - XrPosef poseInMarkerSpace; -} XrMarkerSpaceCreateInfoVARJO; - -typedef XrResult (XRAPI_PTR *PFN_xrSetMarkerTrackingVARJO)(XrSession session, XrBool32 enabled); -typedef XrResult (XRAPI_PTR *PFN_xrSetMarkerTrackingTimeoutVARJO)(XrSession session, uint64_t markerId, XrDuration timeout); -typedef XrResult (XRAPI_PTR *PFN_xrSetMarkerTrackingPredictionVARJO)(XrSession session, uint64_t markerId, XrBool32 enabled); -typedef XrResult (XRAPI_PTR *PFN_xrGetMarkerSizeVARJO)(XrSession session, uint64_t markerId, XrExtent2Df* size); -typedef XrResult (XRAPI_PTR *PFN_xrCreateMarkerSpaceVARJO)(XrSession session, const XrMarkerSpaceCreateInfoVARJO* createInfo, XrSpace* space); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrSetMarkerTrackingVARJO( - XrSession session, - XrBool32 enabled); - -XRAPI_ATTR XrResult XRAPI_CALL xrSetMarkerTrackingTimeoutVARJO( - XrSession session, - uint64_t markerId, - XrDuration timeout); - -XRAPI_ATTR XrResult XRAPI_CALL xrSetMarkerTrackingPredictionVARJO( - XrSession session, - uint64_t markerId, - XrBool32 enabled); - -XRAPI_ATTR XrResult XRAPI_CALL xrGetMarkerSizeVARJO( - XrSession session, - uint64_t markerId, - XrExtent2Df* size); - -XRAPI_ATTR XrResult XRAPI_CALL xrCreateMarkerSpaceVARJO( - XrSession session, - const XrMarkerSpaceCreateInfoVARJO* createInfo, - XrSpace* space); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_MSFT_spatial_anchor_persistence 1 -XR_DEFINE_HANDLE(XrSpatialAnchorStoreConnectionMSFT) -#define XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_MSFT 256 -#define XR_MSFT_spatial_anchor_persistence_SPEC_VERSION 2 -#define XR_MSFT_SPATIAL_ANCHOR_PERSISTENCE_EXTENSION_NAME "XR_MSFT_spatial_anchor_persistence" -typedef struct XrSpatialAnchorPersistenceNameMSFT { - char name[XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_MSFT]; -} XrSpatialAnchorPersistenceNameMSFT; - -typedef struct XrSpatialAnchorPersistenceInfoMSFT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialAnchorPersistenceNameMSFT spatialAnchorPersistenceName; - XrSpatialAnchorMSFT spatialAnchor; -} XrSpatialAnchorPersistenceInfoMSFT; - -typedef struct XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore; - XrSpatialAnchorPersistenceNameMSFT spatialAnchorPersistenceName; -} XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT; - -typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorStoreConnectionMSFT)(XrSession session, XrSpatialAnchorStoreConnectionMSFT* spatialAnchorStore); -typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialAnchorStoreConnectionMSFT)(XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore); -typedef XrResult (XRAPI_PTR *PFN_xrPersistSpatialAnchorMSFT)(XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore, const XrSpatialAnchorPersistenceInfoMSFT* spatialAnchorPersistenceInfo); -typedef XrResult (XRAPI_PTR *PFN_xrEnumeratePersistedSpatialAnchorNamesMSFT)(XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore, uint32_t spatialAnchorNamesCapacityInput, uint32_t* spatialAnchorNamesCountOutput, XrSpatialAnchorPersistenceNameMSFT* persistedAnchorNames); -typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorFromPersistedNameMSFT)(XrSession session, const XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT* spatialAnchorCreateInfo, XrSpatialAnchorMSFT* spatialAnchor); -typedef XrResult (XRAPI_PTR *PFN_xrUnpersistSpatialAnchorMSFT)(XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore, const XrSpatialAnchorPersistenceNameMSFT* spatialAnchorPersistenceName); -typedef XrResult (XRAPI_PTR *PFN_xrClearSpatialAnchorStoreMSFT)(XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorStoreConnectionMSFT( - XrSession session, - XrSpatialAnchorStoreConnectionMSFT* spatialAnchorStore); - -XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialAnchorStoreConnectionMSFT( - XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore); - -XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialAnchorMSFT( - XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore, - const XrSpatialAnchorPersistenceInfoMSFT* spatialAnchorPersistenceInfo); - -XRAPI_ATTR XrResult XRAPI_CALL xrEnumeratePersistedSpatialAnchorNamesMSFT( - XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore, - uint32_t spatialAnchorNamesCapacityInput, - uint32_t* spatialAnchorNamesCountOutput, - XrSpatialAnchorPersistenceNameMSFT* persistedAnchorNames); - -XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorFromPersistedNameMSFT( - XrSession session, - const XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT* spatialAnchorCreateInfo, - XrSpatialAnchorMSFT* spatialAnchor); - -XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialAnchorMSFT( - XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore, - const XrSpatialAnchorPersistenceNameMSFT* spatialAnchorPersistenceName); - -XRAPI_ATTR XrResult XRAPI_CALL xrClearSpatialAnchorStoreMSFT( - XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_FB_space_warp 1 -#define XR_FB_space_warp_SPEC_VERSION 1 -#define XR_FB_SPACE_WARP_EXTENSION_NAME "XR_FB_space_warp" -typedef XrFlags64 XrCompositionLayerSpaceWarpInfoFlagsFB; - -// Flag bits for XrCompositionLayerSpaceWarpInfoFlagsFB - -// XrCompositionLayerSpaceWarpInfoFB extends XrCompositionLayerProjectionView -typedef struct XrCompositionLayerSpaceWarpInfoFB { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrCompositionLayerSpaceWarpInfoFlagsFB layerFlags; - XrSwapchainSubImage motionVectorSubImage; - XrPosef appSpaceDeltaPose; - XrSwapchainSubImage depthSubImage; - float minDepth; - float maxDepth; - float nearZ; - float farZ; -} XrCompositionLayerSpaceWarpInfoFB; - -// XrSystemSpaceWarpPropertiesFB extends XrSystemProperties -typedef struct XrSystemSpaceWarpPropertiesFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t recommendedMotionVectorImageRectWidth; - uint32_t recommendedMotionVectorImageRectHeight; -} XrSystemSpaceWarpPropertiesFB; - - - -#define XR_ALMALENCE_digital_lens_control 1 -#define XR_ALMALENCE_digital_lens_control_SPEC_VERSION 1 -#define XR_ALMALENCE_DIGITAL_LENS_CONTROL_EXTENSION_NAME "XR_ALMALENCE_digital_lens_control" -typedef XrFlags64 XrDigitalLensControlFlagsALMALENCE; - -// Flag bits for XrDigitalLensControlFlagsALMALENCE -static const XrDigitalLensControlFlagsALMALENCE XR_DIGITAL_LENS_CONTROL_PROCESSING_DISABLE_BIT_ALMALENCE = 0x00000001; - -typedef struct XrDigitalLensControlALMALENCE { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrDigitalLensControlFlagsALMALENCE flags; -} XrDigitalLensControlALMALENCE; - -typedef XrResult (XRAPI_PTR *PFN_xrSetDigitalLensControlALMALENCE)(XrSession session, const XrDigitalLensControlALMALENCE* digitalLensControl); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrSetDigitalLensControlALMALENCE( - XrSession session, - const XrDigitalLensControlALMALENCE* digitalLensControl); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_FB_passthrough_keyboard_hands 1 -#define XR_FB_passthrough_keyboard_hands_SPEC_VERSION 1 -#define XR_FB_PASSTHROUGH_KEYBOARD_HANDS_EXTENSION_NAME "XR_FB_passthrough_keyboard_hands" -typedef struct XrPassthroughKeyboardHandsIntensityFB { - XrStructureType type; - const void* XR_MAY_ALIAS next; - float leftHandIntensity; - float rightHandIntensity; -} XrPassthroughKeyboardHandsIntensityFB; - -typedef XrResult (XRAPI_PTR *PFN_xrPassthroughLayerSetKeyboardHandsIntensityFB)(XrPassthroughLayerFB layer, const XrPassthroughKeyboardHandsIntensityFB* intensity); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrPassthroughLayerSetKeyboardHandsIntensityFB( - XrPassthroughLayerFB layer, - const XrPassthroughKeyboardHandsIntensityFB* intensity); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ - - -#define XR_EXT_uuid 1 -#define XR_EXT_uuid_SPEC_VERSION 1 -#define XR_EXT_UUID_EXTENSION_NAME "XR_EXT_uuid" -#define XR_UUID_SIZE_EXT 16 -typedef struct XrUuidEXT { - uint8_t data[XR_UUID_SIZE_EXT]; -} XrUuidEXT; - +#endif #ifdef __cplusplus } diff --git a/ext/openxr/openxr_platform.h b/ext/openxr/openxr_platform.h old mode 100755 new mode 100644 index eb5e5f8c4b..f7e6338064 --- a/ext/openxr/openxr_platform.h +++ b/ext/openxr/openxr_platform.h @@ -2,7 +2,7 @@ #define OPENXR_PLATFORM_H_ 1 /* -** Copyright (c) 2017-2022, The Khronos Group Inc. +** Copyright (c) 2017-2021, The Khronos Group Inc. ** ** SPDX-License-Identifier: Apache-2.0 OR MIT */ @@ -35,13 +35,11 @@ typedef enum XrAndroidThreadTypeKHR { typedef XrResult (XRAPI_PTR *PFN_xrSetAndroidApplicationThreadKHR)(XrSession session, XrAndroidThreadTypeKHR threadType, uint32_t threadId); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrSetAndroidApplicationThreadKHR( XrSession session, XrAndroidThreadTypeKHR threadType, uint32_t threadId); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #endif /* XR_USE_PLATFORM_ANDROID */ #ifdef XR_USE_PLATFORM_ANDROID @@ -52,14 +50,12 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetAndroidApplicationThreadKHR( typedef XrResult (XRAPI_PTR *PFN_xrCreateSwapchainAndroidSurfaceKHR)(XrSession session, const XrSwapchainCreateInfo* info, XrSwapchain* swapchain, jobject* surface); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrCreateSwapchainAndroidSurfaceKHR( XrSession session, const XrSwapchainCreateInfo* info, XrSwapchain* swapchain, jobject* surface); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #endif /* XR_USE_PLATFORM_ANDROID */ #ifdef XR_USE_PLATFORM_ANDROID @@ -67,7 +63,6 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateSwapchainAndroidSurfaceKHR( #define XR_KHR_android_create_instance 1 #define XR_KHR_android_create_instance_SPEC_VERSION 3 #define XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME "XR_KHR_android_create_instance" -// XrInstanceCreateInfoAndroidKHR extends XrInstanceCreateInfo typedef struct XrInstanceCreateInfoAndroidKHR { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -80,7 +75,7 @@ typedef struct XrInstanceCreateInfoAndroidKHR { #ifdef XR_USE_GRAPHICS_API_VULKAN #define XR_KHR_vulkan_swapchain_format_list 1 -#define XR_KHR_vulkan_swapchain_format_list_SPEC_VERSION 4 +#define XR_KHR_vulkan_swapchain_format_list_SPEC_VERSION 3 #define XR_KHR_VULKAN_SWAPCHAIN_FORMAT_LIST_EXTENSION_NAME "XR_KHR_vulkan_swapchain_format_list" typedef struct XrVulkanSwapchainFormatListCreateInfoKHR { XrStructureType type; @@ -94,10 +89,9 @@ typedef struct XrVulkanSwapchainFormatListCreateInfoKHR { #ifdef XR_USE_GRAPHICS_API_OPENGL #define XR_KHR_opengl_enable 1 -#define XR_KHR_opengl_enable_SPEC_VERSION 10 +#define XR_KHR_opengl_enable_SPEC_VERSION 9 #define XR_KHR_OPENGL_ENABLE_EXTENSION_NAME "XR_KHR_opengl_enable" #ifdef XR_USE_PLATFORM_WIN32 -// XrGraphicsBindingOpenGLWin32KHR extends XrSessionCreateInfo typedef struct XrGraphicsBindingOpenGLWin32KHR { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -107,7 +101,6 @@ typedef struct XrGraphicsBindingOpenGLWin32KHR { #endif // XR_USE_PLATFORM_WIN32 #ifdef XR_USE_PLATFORM_XLIB -// XrGraphicsBindingOpenGLXlibKHR extends XrSessionCreateInfo typedef struct XrGraphicsBindingOpenGLXlibKHR { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -120,7 +113,6 @@ typedef struct XrGraphicsBindingOpenGLXlibKHR { #endif // XR_USE_PLATFORM_XLIB #ifdef XR_USE_PLATFORM_XCB -// XrGraphicsBindingOpenGLXcbKHR extends XrSessionCreateInfo typedef struct XrGraphicsBindingOpenGLXcbKHR { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -134,7 +126,6 @@ typedef struct XrGraphicsBindingOpenGLXcbKHR { #endif // XR_USE_PLATFORM_XCB #ifdef XR_USE_PLATFORM_WAYLAND -// XrGraphicsBindingOpenGLWaylandKHR extends XrSessionCreateInfo typedef struct XrGraphicsBindingOpenGLWaylandKHR { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -158,22 +149,19 @@ typedef struct XrGraphicsRequirementsOpenGLKHR { typedef XrResult (XRAPI_PTR *PFN_xrGetOpenGLGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLKHR* graphicsRequirements); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLGraphicsRequirementsKHR( XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLKHR* graphicsRequirements); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #endif /* XR_USE_GRAPHICS_API_OPENGL */ #ifdef XR_USE_GRAPHICS_API_OPENGL_ES #define XR_KHR_opengl_es_enable 1 -#define XR_KHR_opengl_es_enable_SPEC_VERSION 8 +#define XR_KHR_opengl_es_enable_SPEC_VERSION 7 #define XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME "XR_KHR_opengl_es_enable" #ifdef XR_USE_PLATFORM_ANDROID -// XrGraphicsBindingOpenGLESAndroidKHR extends XrSessionCreateInfo typedef struct XrGraphicsBindingOpenGLESAndroidKHR { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -199,21 +187,18 @@ typedef struct XrGraphicsRequirementsOpenGLESKHR { typedef XrResult (XRAPI_PTR *PFN_xrGetOpenGLESGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLESKHR* graphicsRequirements); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLESGraphicsRequirementsKHR( XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLESKHR* graphicsRequirements); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #endif /* XR_USE_GRAPHICS_API_OPENGL_ES */ #ifdef XR_USE_GRAPHICS_API_VULKAN #define XR_KHR_vulkan_enable 1 -#define XR_KHR_vulkan_enable_SPEC_VERSION 8 +#define XR_KHR_vulkan_enable_SPEC_VERSION 7 #define XR_KHR_VULKAN_ENABLE_EXTENSION_NAME "XR_KHR_vulkan_enable" -// XrGraphicsBindingVulkanKHR extends XrSessionCreateInfo typedef struct XrGraphicsBindingVulkanKHR { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -243,7 +228,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsDeviceKHR)(XrInstance instan typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsVulkanKHR* graphicsRequirements); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanInstanceExtensionsKHR( XrInstance instance, XrSystemId systemId, @@ -268,16 +252,14 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirementsKHR( XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsVulkanKHR* graphicsRequirements); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #endif /* XR_USE_GRAPHICS_API_VULKAN */ #ifdef XR_USE_GRAPHICS_API_D3D11 #define XR_KHR_D3D11_enable 1 -#define XR_KHR_D3D11_enable_SPEC_VERSION 8 +#define XR_KHR_D3D11_enable_SPEC_VERSION 5 #define XR_KHR_D3D11_ENABLE_EXTENSION_NAME "XR_KHR_D3D11_enable" -// XrGraphicsBindingD3D11KHR extends XrSessionCreateInfo typedef struct XrGraphicsBindingD3D11KHR { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -300,21 +282,18 @@ typedef struct XrGraphicsRequirementsD3D11KHR { typedef XrResult (XRAPI_PTR *PFN_xrGetD3D11GraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D11KHR* graphicsRequirements); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D11GraphicsRequirementsKHR( XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D11KHR* graphicsRequirements); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #endif /* XR_USE_GRAPHICS_API_D3D11 */ #ifdef XR_USE_GRAPHICS_API_D3D12 #define XR_KHR_D3D12_enable 1 -#define XR_KHR_D3D12_enable_SPEC_VERSION 8 +#define XR_KHR_D3D12_enable_SPEC_VERSION 7 #define XR_KHR_D3D12_ENABLE_EXTENSION_NAME "XR_KHR_D3D12_enable" -// XrGraphicsBindingD3D12KHR extends XrSessionCreateInfo typedef struct XrGraphicsBindingD3D12KHR { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -338,13 +317,11 @@ typedef struct XrGraphicsRequirementsD3D12KHR { typedef XrResult (XRAPI_PTR *PFN_xrGetD3D12GraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D12KHR* graphicsRequirements); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D12GraphicsRequirementsKHR( XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D12KHR* graphicsRequirements); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #endif /* XR_USE_GRAPHICS_API_D3D12 */ #ifdef XR_USE_PLATFORM_WIN32 @@ -356,7 +333,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrConvertWin32PerformanceCounterToTimeKHR)(XrIn typedef XrResult (XRAPI_PTR *PFN_xrConvertTimeToWin32PerformanceCounterKHR)(XrInstance instance, XrTime time, LARGE_INTEGER* performanceCounter); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrConvertWin32PerformanceCounterToTimeKHR( XrInstance instance, const LARGE_INTEGER* performanceCounter, @@ -366,8 +342,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimeToWin32PerformanceCounterKHR( XrInstance instance, XrTime time, LARGE_INTEGER* performanceCounter); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #endif /* XR_USE_PLATFORM_WIN32 */ #ifdef XR_USE_TIMESPEC @@ -379,7 +354,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrConvertTimespecTimeToTimeKHR)(XrInstance inst typedef XrResult (XRAPI_PTR *PFN_xrConvertTimeToTimespecTimeKHR)(XrInstance instance, XrTime time, struct timespec* timespecTime); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimespecTimeToTimeKHR( XrInstance instance, const struct timespec* timespecTime, @@ -389,8 +363,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimeToTimespecTimeKHR( XrInstance instance, XrTime time, struct timespec* timespecTime); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #endif /* XR_USE_TIMESPEC */ #ifdef XR_USE_PLATFORM_ANDROID @@ -410,7 +383,7 @@ typedef struct XrLoaderInitInfoAndroidKHR { #ifdef XR_USE_GRAPHICS_API_VULKAN #define XR_KHR_vulkan_enable2 1 -#define XR_KHR_vulkan_enable2_SPEC_VERSION 2 +#define XR_KHR_vulkan_enable2_SPEC_VERSION 1 #define XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME "XR_KHR_vulkan_enable2" typedef XrFlags64 XrVulkanInstanceCreateFlagsKHR; @@ -454,13 +427,12 @@ typedef XrSwapchainImageVulkanKHR XrSwapchainImageVulkan2KHR; typedef XrGraphicsRequirementsVulkanKHR XrGraphicsRequirementsVulkan2KHR; -typedef XrResult (XRAPI_PTR *PFN_xrCreateVulkanInstanceKHR)(XrInstance instance, const XrVulkanInstanceCreateInfoKHR* createInfo, VkInstance* vulkanInstance, VkResult* vulkanResult); +typedef XrResult (XRAPI_PTR *PFN_xrCreateVulkanInstanceKHR)(XrInstance instance, const XrVulkanInstanceCreateInfoKHR*createInfo, VkInstance* vulkanInstance, VkResult* vulkanResult); typedef XrResult (XRAPI_PTR *PFN_xrCreateVulkanDeviceKHR)(XrInstance instance, const XrVulkanDeviceCreateInfoKHR* createInfo, VkDevice* vulkanDevice, VkResult* vulkanResult); typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsDevice2KHR)(XrInstance instance, const XrVulkanGraphicsDeviceGetInfoKHR* getInfo, VkPhysicalDevice* vulkanPhysicalDevice); typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsRequirements2KHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsVulkanKHR* graphicsRequirements); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrCreateVulkanInstanceKHR( XrInstance instance, const XrVulkanInstanceCreateInfoKHR* createInfo, @@ -482,8 +454,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirements2KHR( XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsVulkanKHR* graphicsRequirements); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #endif /* XR_USE_GRAPHICS_API_VULKAN */ #ifdef XR_USE_PLATFORM_EGL @@ -491,7 +462,6 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirements2KHR( #define XR_MNDX_egl_enable 1 #define XR_MNDX_egl_enable_SPEC_VERSION 1 #define XR_MNDX_EGL_ENABLE_EXTENSION_NAME "XR_MNDX_egl_enable" -// XrGraphicsBindingEGLMNDX extends XrSessionCreateInfo typedef struct XrGraphicsBindingEGLMNDX { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -512,7 +482,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorFromPerceptionAnchorMSFT)( typedef XrResult (XRAPI_PTR *PFN_xrTryGetPerceptionAnchorFromSpatialAnchorMSFT)(XrSession session, XrSpatialAnchorMSFT anchor, IUnknown** perceptionAnchor); #ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorFromPerceptionAnchorMSFT( XrSession session, IUnknown* perceptionAnchor, @@ -522,8 +491,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrTryGetPerceptionAnchorFromSpatialAnchorMSFT( XrSession session, XrSpatialAnchorMSFT anchor, IUnknown** perceptionAnchor); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ +#endif #endif /* XR_USE_PLATFORM_WIN32 */ #ifdef XR_USE_PLATFORM_WIN32 @@ -532,7 +500,6 @@ XRAPI_ATTR XrResult XRAPI_CALL xrTryGetPerceptionAnchorFromSpatialAnchorMSFT( #define XR_MSFT_holographic_window_attachment_SPEC_VERSION 1 #define XR_MSFT_HOLOGRAPHIC_WINDOW_ATTACHMENT_EXTENSION_NAME "XR_MSFT_holographic_window_attachment" #ifdef XR_USE_PLATFORM_WIN32 -// XrHolographicWindowAttachmentMSFT extends XrSessionCreateInfo typedef struct XrHolographicWindowAttachmentMSFT { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -555,7 +522,6 @@ static const XrAndroidSurfaceSwapchainFlagsFB XR_ANDROID_SURFACE_SWAPCHAIN_SYNCH static const XrAndroidSurfaceSwapchainFlagsFB XR_ANDROID_SURFACE_SWAPCHAIN_USE_TIMESTAMPS_BIT_FB = 0x00000002; #ifdef XR_USE_PLATFORM_ANDROID -// XrAndroidSurfaceSwapchainCreateInfoFB extends XrSwapchainCreateInfo typedef struct XrAndroidSurfaceSwapchainCreateInfoFB { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -565,109 +531,6 @@ typedef struct XrAndroidSurfaceSwapchainCreateInfoFB { #endif /* XR_USE_PLATFORM_ANDROID */ -#ifdef XR_USE_PLATFORM_WIN32 - -#define XR_OCULUS_audio_device_guid 1 -#define XR_OCULUS_audio_device_guid_SPEC_VERSION 1 -#define XR_OCULUS_AUDIO_DEVICE_GUID_EXTENSION_NAME "XR_OCULUS_audio_device_guid" -#define XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS 128 -typedef XrResult (XRAPI_PTR *PFN_xrGetAudioOutputDeviceGuidOculus)(XrInstance instance, wchar_t buffer[XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS]); -typedef XrResult (XRAPI_PTR *PFN_xrGetAudioInputDeviceGuidOculus)(XrInstance instance, wchar_t buffer[XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS]); - -#ifndef XR_NO_PROTOTYPES -#ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrGetAudioOutputDeviceGuidOculus( - XrInstance instance, - wchar_t buffer[XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS]); - -XRAPI_ATTR XrResult XRAPI_CALL xrGetAudioInputDeviceGuidOculus( - XrInstance instance, - wchar_t buffer[XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS]); -#endif /* XR_EXTENSION_PROTOTYPES */ -#endif /* !XR_NO_PROTOTYPES */ -#endif /* XR_USE_PLATFORM_WIN32 */ - -#ifdef XR_USE_GRAPHICS_API_VULKAN - -#define XR_FB_foveation_vulkan 1 -#define XR_FB_foveation_vulkan_SPEC_VERSION 1 -#define XR_FB_FOVEATION_VULKAN_EXTENSION_NAME "XR_FB_foveation_vulkan" -// XrSwapchainImageFoveationVulkanFB extends XrSwapchainImageVulkanKHR -typedef struct XrSwapchainImageFoveationVulkanFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - VkImage image; - uint32_t width; - uint32_t height; -} XrSwapchainImageFoveationVulkanFB; - -#endif /* XR_USE_GRAPHICS_API_VULKAN */ - -#ifdef XR_USE_PLATFORM_ANDROID - -#define XR_FB_swapchain_update_state_android_surface 1 -#define XR_FB_swapchain_update_state_android_surface_SPEC_VERSION 1 -#define XR_FB_SWAPCHAIN_UPDATE_STATE_ANDROID_SURFACE_EXTENSION_NAME "XR_FB_swapchain_update_state_android_surface" -#ifdef XR_USE_PLATFORM_ANDROID -typedef struct XrSwapchainStateAndroidSurfaceDimensionsFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t width; - uint32_t height; -} XrSwapchainStateAndroidSurfaceDimensionsFB; -#endif // XR_USE_PLATFORM_ANDROID - -#endif /* XR_USE_PLATFORM_ANDROID */ - -#ifdef XR_USE_GRAPHICS_API_OPENGL_ES - -#define XR_FB_swapchain_update_state_opengl_es 1 -#define XR_FB_swapchain_update_state_opengl_es_SPEC_VERSION 1 -#define XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME "XR_FB_swapchain_update_state_opengl_es" -#ifdef XR_USE_GRAPHICS_API_OPENGL_ES -typedef struct XrSwapchainStateSamplerOpenGLESFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - EGLenum minFilter; - EGLenum magFilter; - EGLenum wrapModeS; - EGLenum wrapModeT; - EGLenum swizzleRed; - EGLenum swizzleGreen; - EGLenum swizzleBlue; - EGLenum swizzleAlpha; - float maxAnisotropy; - XrColor4f borderColor; -} XrSwapchainStateSamplerOpenGLESFB; -#endif // XR_USE_GRAPHICS_API_OPENGL_ES - -#endif /* XR_USE_GRAPHICS_API_OPENGL_ES */ - -#ifdef XR_USE_GRAPHICS_API_VULKAN - -#define XR_FB_swapchain_update_state_vulkan 1 -#define XR_FB_swapchain_update_state_vulkan_SPEC_VERSION 1 -#define XR_FB_SWAPCHAIN_UPDATE_STATE_VULKAN_EXTENSION_NAME "XR_FB_swapchain_update_state_vulkan" -#ifdef XR_USE_GRAPHICS_API_VULKAN -typedef struct XrSwapchainStateSamplerVulkanFB { - XrStructureType type; - void* XR_MAY_ALIAS next; - VkFilter minFilter; - VkFilter magFilter; - VkSamplerMipmapMode mipmapMode; - VkSamplerAddressMode wrapModeS; - VkSamplerAddressMode wrapModeT; - VkComponentSwizzle swizzleRed; - VkComponentSwizzle swizzleGreen; - VkComponentSwizzle swizzleBlue; - VkComponentSwizzle swizzleAlpha; - float maxAnisotropy; - XrColor4f borderColor; -} XrSwapchainStateSamplerVulkanFB; -#endif // XR_USE_GRAPHICS_API_VULKAN - -#endif /* XR_USE_GRAPHICS_API_VULKAN */ - #ifdef __cplusplus } #endif diff --git a/ext/openxr/openxr_platform_defines.h b/ext/openxr/openxr_platform_defines.h old mode 100755 new mode 100644 index 31fa05a0c8..a7ffcb459e --- a/ext/openxr/openxr_platform_defines.h +++ b/ext/openxr/openxr_platform_defines.h @@ -1,5 +1,5 @@ /* -** Copyright (c) 2017-2022, The Khronos Group Inc. +** Copyright (c) 2017-2021, The Khronos Group Inc. ** ** SPDX-License-Identifier: Apache-2.0 OR MIT */ diff --git a/ext/openxr/openxr_reflection.h b/ext/openxr/openxr_reflection.h old mode 100755 new mode 100644 index 8d8e36377d..5597c94a6e --- a/ext/openxr/openxr_reflection.h +++ b/ext/openxr/openxr_reflection.h @@ -2,7 +2,7 @@ #define OPENXR_REFLECTION_H_ 1 /* -** Copyright (c) 2017-2022, The Khronos Group Inc. +** Copyright (c) 2017-2021, The Khronos Group Inc. ** ** SPDX-License-Identifier: Apache-2.0 OR MIT */ @@ -85,33 +85,13 @@ XR_ENUM_STR(XrResult); _(XR_ERROR_LOCALIZED_NAME_DUPLICATED, -48) \ _(XR_ERROR_LOCALIZED_NAME_INVALID, -49) \ _(XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING, -50) \ - _(XR_ERROR_RUNTIME_UNAVAILABLE, -51) \ _(XR_ERROR_ANDROID_THREAD_SETTINGS_ID_INVALID_KHR, -1000003000) \ _(XR_ERROR_ANDROID_THREAD_SETTINGS_FAILURE_KHR, -1000003001) \ _(XR_ERROR_CREATE_SPATIAL_ANCHOR_FAILED_MSFT, -1000039001) \ _(XR_ERROR_SECONDARY_VIEW_CONFIGURATION_TYPE_NOT_ENABLED_MSFT, -1000053000) \ _(XR_ERROR_CONTROLLER_MODEL_KEY_INVALID_MSFT, -1000055000) \ - _(XR_ERROR_REPROJECTION_MODE_UNSUPPORTED_MSFT, -1000066000) \ - _(XR_ERROR_COMPUTE_NEW_SCENE_NOT_COMPLETED_MSFT, -1000097000) \ - _(XR_ERROR_SCENE_COMPONENT_ID_INVALID_MSFT, -1000097001) \ - _(XR_ERROR_SCENE_COMPONENT_TYPE_MISMATCH_MSFT, -1000097002) \ - _(XR_ERROR_SCENE_MESH_BUFFER_ID_INVALID_MSFT, -1000097003) \ - _(XR_ERROR_SCENE_COMPUTE_FEATURE_INCOMPATIBLE_MSFT, -1000097004) \ - _(XR_ERROR_SCENE_COMPUTE_CONSISTENCY_MISMATCH_MSFT, -1000097005) \ _(XR_ERROR_DISPLAY_REFRESH_RATE_UNSUPPORTED_FB, -1000101000) \ _(XR_ERROR_COLOR_SPACE_UNSUPPORTED_FB, -1000108000) \ - _(XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB, -1000118000) \ - _(XR_ERROR_FEATURE_ALREADY_CREATED_PASSTHROUGH_FB, -1000118001) \ - _(XR_ERROR_FEATURE_REQUIRED_PASSTHROUGH_FB, -1000118002) \ - _(XR_ERROR_NOT_PERMITTED_PASSTHROUGH_FB, -1000118003) \ - _(XR_ERROR_INSUFFICIENT_RESOURCES_PASSTHROUGH_FB, -1000118004) \ - _(XR_ERROR_UNKNOWN_PASSTHROUGH_FB, -1000118050) \ - _(XR_ERROR_RENDER_MODEL_KEY_INVALID_FB, -1000119000) \ - _(XR_RENDER_MODEL_UNAVAILABLE_FB, 1000119020) \ - _(XR_ERROR_MARKER_NOT_TRACKED_VARJO, -1000124000) \ - _(XR_ERROR_MARKER_ID_INVALID_VARJO, -1000124001) \ - _(XR_ERROR_SPATIAL_ANCHOR_NAME_NOT_FOUND_MSFT, -1000142001) \ - _(XR_ERROR_SPATIAL_ANCHOR_NAME_INVALID_MSFT, -1000142002) \ _(XR_RESULT_MAX_ENUM, 0x7FFFFFFF) #define XR_LIST_ENUM_XrStructureType(_) \ @@ -203,8 +183,6 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_COMPOSITION_LAYER_COLOR_SCALE_BIAS_KHR, 1000034000) \ _(XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_MSFT, 1000039000) \ _(XR_TYPE_SPATIAL_ANCHOR_SPACE_CREATE_INFO_MSFT, 1000039001) \ - _(XR_TYPE_COMPOSITION_LAYER_IMAGE_LAYOUT_FB, 1000040000) \ - _(XR_TYPE_COMPOSITION_LAYER_ALPHA_BLEND_FB, 1000041001) \ _(XR_TYPE_VIEW_CONFIGURATION_DEPTH_RANGE_EXT, 1000046000) \ _(XR_TYPE_GRAPHICS_BINDING_EGL_MNDX, 1000048004) \ _(XR_TYPE_SPATIAL_GRAPH_NODE_SPACE_CREATE_INFO_MSFT, 1000049000) \ @@ -231,90 +209,16 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_CONTROLLER_MODEL_STATE_MSFT, 1000055004) \ _(XR_TYPE_VIEW_CONFIGURATION_VIEW_FOV_EPIC, 1000059000) \ _(XR_TYPE_HOLOGRAPHIC_WINDOW_ATTACHMENT_MSFT, 1000063000) \ - _(XR_TYPE_COMPOSITION_LAYER_REPROJECTION_INFO_MSFT, 1000066000) \ - _(XR_TYPE_COMPOSITION_LAYER_REPROJECTION_PLANE_OVERRIDE_MSFT, 1000066001) \ _(XR_TYPE_ANDROID_SURFACE_SWAPCHAIN_CREATE_INFO_FB, 1000070000) \ - _(XR_TYPE_COMPOSITION_LAYER_SECURE_CONTENT_FB, 1000072000) \ _(XR_TYPE_INTERACTION_PROFILE_ANALOG_THRESHOLD_VALVE, 1000079000) \ - _(XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT, 1000080000) \ _(XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR, 1000089000) \ _(XR_TYPE_VULKAN_INSTANCE_CREATE_INFO_KHR, 1000090000) \ _(XR_TYPE_VULKAN_DEVICE_CREATE_INFO_KHR, 1000090001) \ _(XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR, 1000090003) \ _(XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR, 1000091000) \ - _(XR_TYPE_SCENE_OBSERVER_CREATE_INFO_MSFT, 1000097000) \ - _(XR_TYPE_SCENE_CREATE_INFO_MSFT, 1000097001) \ - _(XR_TYPE_NEW_SCENE_COMPUTE_INFO_MSFT, 1000097002) \ - _(XR_TYPE_VISUAL_MESH_COMPUTE_LOD_INFO_MSFT, 1000097003) \ - _(XR_TYPE_SCENE_COMPONENTS_MSFT, 1000097004) \ - _(XR_TYPE_SCENE_COMPONENTS_GET_INFO_MSFT, 1000097005) \ - _(XR_TYPE_SCENE_COMPONENT_LOCATIONS_MSFT, 1000097006) \ - _(XR_TYPE_SCENE_COMPONENTS_LOCATE_INFO_MSFT, 1000097007) \ - _(XR_TYPE_SCENE_OBJECTS_MSFT, 1000097008) \ - _(XR_TYPE_SCENE_COMPONENT_PARENT_FILTER_INFO_MSFT, 1000097009) \ - _(XR_TYPE_SCENE_OBJECT_TYPES_FILTER_INFO_MSFT, 1000097010) \ - _(XR_TYPE_SCENE_PLANES_MSFT, 1000097011) \ - _(XR_TYPE_SCENE_PLANE_ALIGNMENT_FILTER_INFO_MSFT, 1000097012) \ - _(XR_TYPE_SCENE_MESHES_MSFT, 1000097013) \ - _(XR_TYPE_SCENE_MESH_BUFFERS_GET_INFO_MSFT, 1000097014) \ - _(XR_TYPE_SCENE_MESH_BUFFERS_MSFT, 1000097015) \ - _(XR_TYPE_SCENE_MESH_VERTEX_BUFFER_MSFT, 1000097016) \ - _(XR_TYPE_SCENE_MESH_INDICES_UINT32_MSFT, 1000097017) \ - _(XR_TYPE_SCENE_MESH_INDICES_UINT16_MSFT, 1000097018) \ - _(XR_TYPE_SERIALIZED_SCENE_FRAGMENT_DATA_GET_INFO_MSFT, 1000098000) \ - _(XR_TYPE_SCENE_DESERIALIZE_INFO_MSFT, 1000098001) \ _(XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB, 1000101000) \ - _(XR_TYPE_VIVE_TRACKER_PATHS_HTCX, 1000103000) \ - _(XR_TYPE_EVENT_DATA_VIVE_TRACKER_CONNECTED_HTCX, 1000103001) \ - _(XR_TYPE_SYSTEM_FACIAL_TRACKING_PROPERTIES_HTC, 1000104000) \ - _(XR_TYPE_FACIAL_TRACKER_CREATE_INFO_HTC, 1000104001) \ - _(XR_TYPE_FACIAL_EXPRESSIONS_HTC, 1000104002) \ _(XR_TYPE_SYSTEM_COLOR_SPACE_PROPERTIES_FB, 1000108000) \ - _(XR_TYPE_HAND_TRACKING_MESH_FB, 1000110001) \ - _(XR_TYPE_HAND_TRACKING_SCALE_FB, 1000110003) \ - _(XR_TYPE_HAND_TRACKING_AIM_STATE_FB, 1000111001) \ - _(XR_TYPE_HAND_TRACKING_CAPSULES_STATE_FB, 1000112000) \ - _(XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB, 1000114000) \ - _(XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB, 1000114001) \ - _(XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB, 1000114002) \ - _(XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB, 1000115000) \ - _(XR_TYPE_KEYBOARD_SPACE_CREATE_INFO_FB, 1000116009) \ - _(XR_TYPE_KEYBOARD_TRACKING_QUERY_FB, 1000116004) \ - _(XR_TYPE_SYSTEM_KEYBOARD_TRACKING_PROPERTIES_FB, 1000116002) \ - _(XR_TYPE_TRIANGLE_MESH_CREATE_INFO_FB, 1000117001) \ - _(XR_TYPE_SYSTEM_PASSTHROUGH_PROPERTIES_FB, 1000118000) \ - _(XR_TYPE_PASSTHROUGH_CREATE_INFO_FB, 1000118001) \ - _(XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB, 1000118002) \ - _(XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB, 1000118003) \ - _(XR_TYPE_GEOMETRY_INSTANCE_CREATE_INFO_FB, 1000118004) \ - _(XR_TYPE_GEOMETRY_INSTANCE_TRANSFORM_FB, 1000118005) \ - _(XR_TYPE_PASSTHROUGH_STYLE_FB, 1000118020) \ - _(XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_RGBA_FB, 1000118021) \ - _(XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_MONO_FB, 1000118022) \ - _(XR_TYPE_EVENT_DATA_PASSTHROUGH_STATE_CHANGED_FB, 1000118030) \ - _(XR_TYPE_RENDER_MODEL_PATH_INFO_FB, 1000119000) \ - _(XR_TYPE_RENDER_MODEL_PROPERTIES_FB, 1000119001) \ - _(XR_TYPE_RENDER_MODEL_BUFFER_FB, 1000119002) \ - _(XR_TYPE_RENDER_MODEL_LOAD_INFO_FB, 1000119003) \ - _(XR_TYPE_SYSTEM_RENDER_MODEL_PROPERTIES_FB, 1000119004) \ _(XR_TYPE_BINDING_MODIFICATIONS_KHR, 1000120000) \ - _(XR_TYPE_VIEW_LOCATE_FOVEATED_RENDERING_VARJO, 1000121000) \ - _(XR_TYPE_FOVEATED_VIEW_CONFIGURATION_VIEW_VARJO, 1000121001) \ - _(XR_TYPE_SYSTEM_FOVEATED_RENDERING_PROPERTIES_VARJO, 1000121002) \ - _(XR_TYPE_COMPOSITION_LAYER_DEPTH_TEST_VARJO, 1000122000) \ - _(XR_TYPE_SYSTEM_MARKER_TRACKING_PROPERTIES_VARJO, 1000124000) \ - _(XR_TYPE_EVENT_DATA_MARKER_TRACKING_UPDATE_VARJO, 1000124001) \ - _(XR_TYPE_MARKER_SPACE_CREATE_INFO_VARJO, 1000124002) \ - _(XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT, 1000142000) \ - _(XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT, 1000142001) \ - _(XR_TYPE_SWAPCHAIN_IMAGE_FOVEATION_VULKAN_FB, 1000160000) \ - _(XR_TYPE_SWAPCHAIN_STATE_ANDROID_SURFACE_DIMENSIONS_FB, 1000161000) \ - _(XR_TYPE_SWAPCHAIN_STATE_SAMPLER_OPENGL_ES_FB, 1000162000) \ - _(XR_TYPE_SWAPCHAIN_STATE_SAMPLER_VULKAN_FB, 1000163000) \ - _(XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB, 1000171000) \ - _(XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB, 1000171001) \ - _(XR_TYPE_DIGITAL_LENS_CONTROL_ALMALENCE, 1000196000) \ - _(XR_TYPE_PASSTHROUGH_KEYBOARD_HANDS_INTENSITY_FB, 1000203002) \ _(XR_STRUCTURE_TYPE_MAX_ENUM, 0x7FFFFFFF) #define XR_LIST_ENUM_XrFormFactor(_) \ @@ -340,7 +244,6 @@ XR_ENUM_STR(XrResult); _(XR_REFERENCE_SPACE_TYPE_LOCAL, 2) \ _(XR_REFERENCE_SPACE_TYPE_STAGE, 3) \ _(XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT, 1000038000) \ - _(XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO, 1000121000) \ _(XR_REFERENCE_SPACE_TYPE_MAX_ENUM, 0x7FFFFFFF) #define XR_LIST_ENUM_XrActionType(_) \ @@ -380,15 +283,6 @@ XR_ENUM_STR(XrResult); _(XR_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT, 1000019000) \ _(XR_OBJECT_TYPE_SPATIAL_ANCHOR_MSFT, 1000039000) \ _(XR_OBJECT_TYPE_HAND_TRACKER_EXT, 1000051000) \ - _(XR_OBJECT_TYPE_SCENE_OBSERVER_MSFT, 1000097000) \ - _(XR_OBJECT_TYPE_SCENE_MSFT, 1000097001) \ - _(XR_OBJECT_TYPE_FACIAL_TRACKER_HTC, 1000104000) \ - _(XR_OBJECT_TYPE_FOVEATION_PROFILE_FB, 1000114000) \ - _(XR_OBJECT_TYPE_TRIANGLE_MESH_FB, 1000117000) \ - _(XR_OBJECT_TYPE_PASSTHROUGH_FB, 1000118000) \ - _(XR_OBJECT_TYPE_PASSTHROUGH_LAYER_FB, 1000118002) \ - _(XR_OBJECT_TYPE_GEOMETRY_INSTANCE_FB, 1000118004) \ - _(XR_OBJECT_TYPE_SPATIAL_ANCHOR_STORE_CONNECTION_MSFT, 1000142000) \ _(XR_OBJECT_TYPE_MAX_ENUM, 0x7FFFFFFF) #define XR_LIST_ENUM_XrAndroidThreadTypeKHR(_) \ @@ -428,15 +322,6 @@ XR_ENUM_STR(XrResult); _(XR_PERF_SETTINGS_NOTIF_LEVEL_IMPAIRED_EXT, 75) \ _(XR_PERF_SETTINGS_NOTIFICATION_LEVEL_MAX_ENUM_EXT, 0x7FFFFFFF) -#define XR_LIST_ENUM_XrBlendFactorFB(_) \ - _(XR_BLEND_FACTOR_ZERO_FB, 0) \ - _(XR_BLEND_FACTOR_ONE_FB, 1) \ - _(XR_BLEND_FACTOR_SRC_ALPHA_FB, 2) \ - _(XR_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA_FB, 3) \ - _(XR_BLEND_FACTOR_DST_ALPHA_FB, 4) \ - _(XR_BLEND_FACTOR_ONE_MINUS_DST_ALPHA_FB, 5) \ - _(XR_BLEND_FACTOR_MAX_ENUM_FB, 0x7FFFFFFF) - #define XR_LIST_ENUM_XrSpatialGraphNodeTypeMSFT(_) \ _(XR_SPATIAL_GRAPH_NODE_TYPE_STATIC_MSFT, 1) \ _(XR_SPATIAL_GRAPH_NODE_TYPE_DYNAMIC_MSFT, 2) \ @@ -485,133 +370,6 @@ XR_ENUM_STR(XrResult); _(XR_HAND_POSE_TYPE_REFERENCE_OPEN_PALM_MSFT, 1) \ _(XR_HAND_POSE_TYPE_MAX_ENUM_MSFT, 0x7FFFFFFF) -#define XR_LIST_ENUM_XrReprojectionModeMSFT(_) \ - _(XR_REPROJECTION_MODE_DEPTH_MSFT, 1) \ - _(XR_REPROJECTION_MODE_PLANAR_FROM_DEPTH_MSFT, 2) \ - _(XR_REPROJECTION_MODE_PLANAR_MANUAL_MSFT, 3) \ - _(XR_REPROJECTION_MODE_ORIENTATION_ONLY_MSFT, 4) \ - _(XR_REPROJECTION_MODE_MAX_ENUM_MSFT, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrHandJointsMotionRangeEXT(_) \ - _(XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT, 1) \ - _(XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT, 2) \ - _(XR_HAND_JOINTS_MOTION_RANGE_MAX_ENUM_EXT, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSceneComputeFeatureMSFT(_) \ - _(XR_SCENE_COMPUTE_FEATURE_PLANE_MSFT, 1) \ - _(XR_SCENE_COMPUTE_FEATURE_PLANE_MESH_MSFT, 2) \ - _(XR_SCENE_COMPUTE_FEATURE_VISUAL_MESH_MSFT, 3) \ - _(XR_SCENE_COMPUTE_FEATURE_COLLIDER_MESH_MSFT, 4) \ - _(XR_SCENE_COMPUTE_FEATURE_SERIALIZE_SCENE_MSFT, 1000098000) \ - _(XR_SCENE_COMPUTE_FEATURE_MAX_ENUM_MSFT, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSceneComputeConsistencyMSFT(_) \ - _(XR_SCENE_COMPUTE_CONSISTENCY_SNAPSHOT_COMPLETE_MSFT, 1) \ - _(XR_SCENE_COMPUTE_CONSISTENCY_SNAPSHOT_INCOMPLETE_FAST_MSFT, 2) \ - _(XR_SCENE_COMPUTE_CONSISTENCY_OCCLUSION_OPTIMIZED_MSFT, 3) \ - _(XR_SCENE_COMPUTE_CONSISTENCY_MAX_ENUM_MSFT, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrMeshComputeLodMSFT(_) \ - _(XR_MESH_COMPUTE_LOD_COARSE_MSFT, 1) \ - _(XR_MESH_COMPUTE_LOD_MEDIUM_MSFT, 2) \ - _(XR_MESH_COMPUTE_LOD_FINE_MSFT, 3) \ - _(XR_MESH_COMPUTE_LOD_UNLIMITED_MSFT, 4) \ - _(XR_MESH_COMPUTE_LOD_MAX_ENUM_MSFT, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSceneComponentTypeMSFT(_) \ - _(XR_SCENE_COMPONENT_TYPE_INVALID_MSFT, -1) \ - _(XR_SCENE_COMPONENT_TYPE_OBJECT_MSFT, 1) \ - _(XR_SCENE_COMPONENT_TYPE_PLANE_MSFT, 2) \ - _(XR_SCENE_COMPONENT_TYPE_VISUAL_MESH_MSFT, 3) \ - _(XR_SCENE_COMPONENT_TYPE_COLLIDER_MESH_MSFT, 4) \ - _(XR_SCENE_COMPONENT_TYPE_SERIALIZED_SCENE_FRAGMENT_MSFT, 1000098000) \ - _(XR_SCENE_COMPONENT_TYPE_MAX_ENUM_MSFT, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSceneObjectTypeMSFT(_) \ - _(XR_SCENE_OBJECT_TYPE_UNCATEGORIZED_MSFT, -1) \ - _(XR_SCENE_OBJECT_TYPE_BACKGROUND_MSFT, 1) \ - _(XR_SCENE_OBJECT_TYPE_WALL_MSFT, 2) \ - _(XR_SCENE_OBJECT_TYPE_FLOOR_MSFT, 3) \ - _(XR_SCENE_OBJECT_TYPE_CEILING_MSFT, 4) \ - _(XR_SCENE_OBJECT_TYPE_PLATFORM_MSFT, 5) \ - _(XR_SCENE_OBJECT_TYPE_INFERRED_MSFT, 6) \ - _(XR_SCENE_OBJECT_TYPE_MAX_ENUM_MSFT, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrScenePlaneAlignmentTypeMSFT(_) \ - _(XR_SCENE_PLANE_ALIGNMENT_TYPE_NON_ORTHOGONAL_MSFT, 0) \ - _(XR_SCENE_PLANE_ALIGNMENT_TYPE_HORIZONTAL_MSFT, 1) \ - _(XR_SCENE_PLANE_ALIGNMENT_TYPE_VERTICAL_MSFT, 2) \ - _(XR_SCENE_PLANE_ALIGNMENT_TYPE_MAX_ENUM_MSFT, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSceneComputeStateMSFT(_) \ - _(XR_SCENE_COMPUTE_STATE_NONE_MSFT, 0) \ - _(XR_SCENE_COMPUTE_STATE_UPDATING_MSFT, 1) \ - _(XR_SCENE_COMPUTE_STATE_COMPLETED_MSFT, 2) \ - _(XR_SCENE_COMPUTE_STATE_COMPLETED_WITH_ERROR_MSFT, 3) \ - _(XR_SCENE_COMPUTE_STATE_MAX_ENUM_MSFT, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrEyeExpressionHTC(_) \ - _(XR_EYE_EXPRESSION_LEFT_BLINK_HTC, 0) \ - _(XR_EYE_EXPRESSION_LEFT_WIDE_HTC, 1) \ - _(XR_EYE_EXPRESSION_RIGHT_BLINK_HTC, 2) \ - _(XR_EYE_EXPRESSION_RIGHT_WIDE_HTC, 3) \ - _(XR_EYE_EXPRESSION_LEFT_SQUEEZE_HTC, 4) \ - _(XR_EYE_EXPRESSION_RIGHT_SQUEEZE_HTC, 5) \ - _(XR_EYE_EXPRESSION_LEFT_DOWN_HTC, 6) \ - _(XR_EYE_EXPRESSION_RIGHT_DOWN_HTC, 7) \ - _(XR_EYE_EXPRESSION_LEFT_OUT_HTC, 8) \ - _(XR_EYE_EXPRESSION_RIGHT_IN_HTC, 9) \ - _(XR_EYE_EXPRESSION_LEFT_IN_HTC, 10) \ - _(XR_EYE_EXPRESSION_RIGHT_OUT_HTC, 11) \ - _(XR_EYE_EXPRESSION_LEFT_UP_HTC, 12) \ - _(XR_EYE_EXPRESSION_RIGHT_UP_HTC, 13) \ - _(XR_EYE_EXPRESSION_MAX_ENUM_HTC, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrLipExpressionHTC(_) \ - _(XR_LIP_EXPRESSION_JAW_RIGHT_HTC, 0) \ - _(XR_LIP_EXPRESSION_JAW_LEFT_HTC, 1) \ - _(XR_LIP_EXPRESSION_JAW_FORWARD_HTC, 2) \ - _(XR_LIP_EXPRESSION_JAW_OPEN_HTC, 3) \ - _(XR_LIP_EXPRESSION_MOUTH_APE_SHAPE_HTC, 4) \ - _(XR_LIP_EXPRESSION_MOUTH_UPPER_RIGHT_HTC, 5) \ - _(XR_LIP_EXPRESSION_MOUTH_UPPER_LEFT_HTC, 6) \ - _(XR_LIP_EXPRESSION_MOUTH_LOWER_RIGHT_HTC, 7) \ - _(XR_LIP_EXPRESSION_MOUTH_LOWER_LEFT_HTC, 8) \ - _(XR_LIP_EXPRESSION_MOUTH_UPPER_OVERTURN_HTC, 9) \ - _(XR_LIP_EXPRESSION_MOUTH_LOWER_OVERTURN_HTC, 10) \ - _(XR_LIP_EXPRESSION_MOUTH_POUT_HTC, 11) \ - _(XR_LIP_EXPRESSION_MOUTH_SMILE_RIGHT_HTC, 12) \ - _(XR_LIP_EXPRESSION_MOUTH_SMILE_LEFT_HTC, 13) \ - _(XR_LIP_EXPRESSION_MOUTH_SAD_RIGHT_HTC, 14) \ - _(XR_LIP_EXPRESSION_MOUTH_SAD_LEFT_HTC, 15) \ - _(XR_LIP_EXPRESSION_CHEEK_PUFF_RIGHT_HTC, 16) \ - _(XR_LIP_EXPRESSION_CHEEK_PUFF_LEFT_HTC, 17) \ - _(XR_LIP_EXPRESSION_CHEEK_SUCK_HTC, 18) \ - _(XR_LIP_EXPRESSION_MOUTH_UPPER_UPRIGHT_HTC, 19) \ - _(XR_LIP_EXPRESSION_MOUTH_UPPER_UPLEFT_HTC, 20) \ - _(XR_LIP_EXPRESSION_MOUTH_LOWER_DOWNRIGHT_HTC, 21) \ - _(XR_LIP_EXPRESSION_MOUTH_LOWER_DOWNLEFT_HTC, 22) \ - _(XR_LIP_EXPRESSION_MOUTH_UPPER_INSIDE_HTC, 23) \ - _(XR_LIP_EXPRESSION_MOUTH_LOWER_INSIDE_HTC, 24) \ - _(XR_LIP_EXPRESSION_MOUTH_LOWER_OVERLAY_HTC, 25) \ - _(XR_LIP_EXPRESSION_TONGUE_LONGSTEP1_HTC, 26) \ - _(XR_LIP_EXPRESSION_TONGUE_LEFT_HTC, 27) \ - _(XR_LIP_EXPRESSION_TONGUE_RIGHT_HTC, 28) \ - _(XR_LIP_EXPRESSION_TONGUE_UP_HTC, 29) \ - _(XR_LIP_EXPRESSION_TONGUE_DOWN_HTC, 30) \ - _(XR_LIP_EXPRESSION_TONGUE_ROLL_HTC, 31) \ - _(XR_LIP_EXPRESSION_TONGUE_LONGSTEP2_HTC, 32) \ - _(XR_LIP_EXPRESSION_TONGUE_UPRIGHT_MORPH_HTC, 33) \ - _(XR_LIP_EXPRESSION_TONGUE_UPLEFT_MORPH_HTC, 34) \ - _(XR_LIP_EXPRESSION_TONGUE_DOWNRIGHT_MORPH_HTC, 35) \ - _(XR_LIP_EXPRESSION_TONGUE_DOWNLEFT_MORPH_HTC, 36) \ - _(XR_LIP_EXPRESSION_MAX_ENUM_HTC, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrFacialTrackingTypeHTC(_) \ - _(XR_FACIAL_TRACKING_TYPE_EYE_DEFAULT_HTC, 1) \ - _(XR_FACIAL_TRACKING_TYPE_LIP_DEFAULT_HTC, 2) \ - _(XR_FACIAL_TRACKING_TYPE_MAX_ENUM_HTC, 0x7FFFFFFF) - #define XR_LIST_ENUM_XrColorSpaceFB(_) \ _(XR_COLOR_SPACE_UNMANAGED_FB, 0) \ _(XR_COLOR_SPACE_REC2020_FB, 1) \ @@ -623,30 +381,6 @@ XR_ENUM_STR(XrResult); _(XR_COLOR_SPACE_ADOBE_RGB_FB, 7) \ _(XR_COLOR_SPACE_MAX_ENUM_FB, 0x7FFFFFFF) -#define XR_LIST_ENUM_XrFoveationLevelFB(_) \ - _(XR_FOVEATION_LEVEL_NONE_FB, 0) \ - _(XR_FOVEATION_LEVEL_LOW_FB, 1) \ - _(XR_FOVEATION_LEVEL_MEDIUM_FB, 2) \ - _(XR_FOVEATION_LEVEL_HIGH_FB, 3) \ - _(XR_FOVEATION_LEVEL_MAX_ENUM_FB, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrFoveationDynamicFB(_) \ - _(XR_FOVEATION_DYNAMIC_DISABLED_FB, 0) \ - _(XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_FB, 1) \ - _(XR_FOVEATION_DYNAMIC_MAX_ENUM_FB, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrWindingOrderFB(_) \ - _(XR_WINDING_ORDER_UNKNOWN_FB, 0) \ - _(XR_WINDING_ORDER_CW_FB, 1) \ - _(XR_WINDING_ORDER_CCW_FB, 2) \ - _(XR_WINDING_ORDER_MAX_ENUM_FB, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrPassthroughLayerPurposeFB(_) \ - _(XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB, 0) \ - _(XR_PASSTHROUGH_LAYER_PURPOSE_PROJECTED_FB, 1) \ - _(XR_PASSTHROUGH_LAYER_PURPOSE_TRACKED_KEYBOARD_HANDS_FB, 1000203001) \ - _(XR_PASSTHROUGH_LAYER_PURPOSE_MAX_ENUM_FB, 0x7FFFFFFF) - #define XR_LIST_BITS_XrInstanceCreateFlags(_) #define XR_LIST_BITS_XrSessionCreateFlags(_) @@ -674,7 +408,6 @@ XR_ENUM_STR(XrResult); _(XR_SWAPCHAIN_USAGE_SAMPLED_BIT, 0x00000020) \ _(XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, 0x00000040) \ _(XR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_MND, 0x00000080) \ - _(XR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_KHR, XR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_MND) \ #define XR_LIST_BITS_XrCompositionLayerFlags(_) \ _(XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT, 0x00000001) \ @@ -708,68 +441,16 @@ XR_ENUM_STR(XrResult); _(XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, 0x00000004) \ _(XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT, 0x00000008) \ -#define XR_LIST_BITS_XrOverlaySessionCreateFlagsEXTX(_) +#define XR_LIST_BITS_XrOverlaySessionCreateFlagsEXTX(_) \ + _(XR_OVERLAY_SESSION_CREATE_RELAXED_DISPLAY_TIME_BIT_EXTX, 0x00000001) \ #define XR_LIST_BITS_XrOverlayMainSessionFlagsEXTX(_) \ _(XR_OVERLAY_MAIN_SESSION_ENABLED_COMPOSITION_LAYER_INFO_DEPTH_BIT_EXTX, 0x00000001) \ -#define XR_LIST_BITS_XrCompositionLayerImageLayoutFlagsFB(_) \ - _(XR_COMPOSITION_LAYER_IMAGE_LAYOUT_VERTICAL_FLIP_BIT_FB, 0x00000001) \ - #define XR_LIST_BITS_XrAndroidSurfaceSwapchainFlagsFB(_) \ _(XR_ANDROID_SURFACE_SWAPCHAIN_SYNCHRONOUS_BIT_FB, 0x00000001) \ _(XR_ANDROID_SURFACE_SWAPCHAIN_USE_TIMESTAMPS_BIT_FB, 0x00000002) \ -#define XR_LIST_BITS_XrCompositionLayerSecureContentFlagsFB(_) \ - _(XR_COMPOSITION_LAYER_SECURE_CONTENT_EXCLUDE_LAYER_BIT_FB, 0x00000001) \ - _(XR_COMPOSITION_LAYER_SECURE_CONTENT_REPLACE_LAYER_BIT_FB, 0x00000002) \ - -#define XR_LIST_BITS_XrHandTrackingAimFlagsFB(_) \ - _(XR_HAND_TRACKING_AIM_COMPUTED_BIT_FB, 0x00000001) \ - _(XR_HAND_TRACKING_AIM_VALID_BIT_FB, 0x00000002) \ - _(XR_HAND_TRACKING_AIM_INDEX_PINCHING_BIT_FB, 0x00000004) \ - _(XR_HAND_TRACKING_AIM_MIDDLE_PINCHING_BIT_FB, 0x00000008) \ - _(XR_HAND_TRACKING_AIM_RING_PINCHING_BIT_FB, 0x00000010) \ - _(XR_HAND_TRACKING_AIM_LITTLE_PINCHING_BIT_FB, 0x00000020) \ - _(XR_HAND_TRACKING_AIM_SYSTEM_GESTURE_BIT_FB, 0x00000040) \ - _(XR_HAND_TRACKING_AIM_DOMINANT_HAND_BIT_FB, 0x00000080) \ - _(XR_HAND_TRACKING_AIM_MENU_PRESSED_BIT_FB, 0x00000100) \ - -#define XR_LIST_BITS_XrSwapchainCreateFoveationFlagsFB(_) \ - _(XR_SWAPCHAIN_CREATE_FOVEATION_SCALED_BIN_BIT_FB, 0x00000001) \ - _(XR_SWAPCHAIN_CREATE_FOVEATION_FRAGMENT_DENSITY_MAP_BIT_FB, 0x00000002) \ - -#define XR_LIST_BITS_XrSwapchainStateFoveationFlagsFB(_) - -#define XR_LIST_BITS_XrKeyboardTrackingFlagsFB(_) \ - _(XR_KEYBOARD_TRACKING_EXISTS_BIT_FB, 0x00000001) \ - _(XR_KEYBOARD_TRACKING_LOCAL_BIT_FB, 0x00000002) \ - _(XR_KEYBOARD_TRACKING_REMOTE_BIT_FB, 0x00000004) \ - _(XR_KEYBOARD_TRACKING_CONNECTED_BIT_FB, 0x00000008) \ - -#define XR_LIST_BITS_XrKeyboardTrackingQueryFlagsFB(_) \ - _(XR_KEYBOARD_TRACKING_QUERY_LOCAL_BIT_FB, 0x00000002) \ - _(XR_KEYBOARD_TRACKING_QUERY_REMOTE_BIT_FB, 0x00000004) \ - -#define XR_LIST_BITS_XrTriangleMeshFlagsFB(_) \ - _(XR_TRIANGLE_MESH_MUTABLE_BIT_FB, 0x00000001) \ - -#define XR_LIST_BITS_XrPassthroughFlagsFB(_) \ - _(XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB, 0x00000001) \ - -#define XR_LIST_BITS_XrPassthroughStateChangedFlagsFB(_) \ - _(XR_PASSTHROUGH_STATE_CHANGED_REINIT_REQUIRED_BIT_FB, 0x00000001) \ - _(XR_PASSTHROUGH_STATE_CHANGED_NON_RECOVERABLE_ERROR_BIT_FB, 0x00000002) \ - _(XR_PASSTHROUGH_STATE_CHANGED_RECOVERABLE_ERROR_BIT_FB, 0x00000004) \ - _(XR_PASSTHROUGH_STATE_CHANGED_RESTORED_ERROR_BIT_FB, 0x00000008) \ - -#define XR_LIST_BITS_XrRenderModelFlagsFB(_) - -#define XR_LIST_BITS_XrCompositionLayerSpaceWarpInfoFlagsFB(_) - -#define XR_LIST_BITS_XrDigitalLensControlFlagsALMALENCE(_) \ - _(XR_DIGITAL_LENS_CONTROL_PROCESSING_DISABLE_BIT_ALMALENCE, 0x00000001) \ - #define XR_LIST_STRUCT_XrApiLayerProperties(_) \ _(type) \ _(next) \ @@ -1532,19 +1213,6 @@ XR_ENUM_STR(XrResult); _(anchor) \ _(poseInAnchorSpace) \ -#define XR_LIST_STRUCT_XrCompositionLayerImageLayoutFB(_) \ - _(type) \ - _(next) \ - _(flags) \ - -#define XR_LIST_STRUCT_XrCompositionLayerAlphaBlendFB(_) \ - _(type) \ - _(next) \ - _(srcFactorColor) \ - _(dstFactorColor) \ - _(srcFactorAlpha) \ - _(dstFactorAlpha) \ - #define XR_LIST_STRUCT_XrViewConfigurationDepthRangeEXT(_) \ _(type) \ _(next) \ @@ -1736,32 +1404,11 @@ XR_ENUM_STR(XrResult); _(holographicSpace) \ _(coreWindow) \ -#define XR_LIST_STRUCT_XrCompositionLayerReprojectionInfoMSFT(_) \ - _(type) \ - _(next) \ - _(reprojectionMode) \ - -#define XR_LIST_STRUCT_XrCompositionLayerReprojectionPlaneOverrideMSFT(_) \ - _(type) \ - _(next) \ - _(position) \ - _(normal) \ - _(velocity) \ - #define XR_LIST_STRUCT_XrAndroidSurfaceSwapchainCreateInfoFB(_) \ _(type) \ _(next) \ _(createFlags) \ -#define XR_LIST_STRUCT_XrSwapchainStateBaseHeaderFB(_) \ - _(type) \ - _(next) \ - -#define XR_LIST_STRUCT_XrCompositionLayerSecureContentFB(_) \ - _(type) \ - _(next) \ - _(flags) \ - #define XR_LIST_STRUCT_XrInteractionProfileAnalogThresholdVALVE(_) \ _(type) \ _(next) \ @@ -1772,556 +1419,17 @@ XR_ENUM_STR(XrResult); _(onHaptic) \ _(offHaptic) \ -#define XR_LIST_STRUCT_XrHandJointsMotionRangeInfoEXT(_) \ - _(type) \ - _(next) \ - _(handJointsMotionRange) \ - -#define XR_LIST_STRUCT_XrUuidMSFT(_) \ - _(bytes) \ - -#define XR_LIST_STRUCT_XrSceneObserverCreateInfoMSFT(_) \ - _(type) \ - _(next) \ - -#define XR_LIST_STRUCT_XrSceneCreateInfoMSFT(_) \ - _(type) \ - _(next) \ - -#define XR_LIST_STRUCT_XrSceneSphereBoundMSFT(_) \ - _(center) \ - _(radius) \ - -#define XR_LIST_STRUCT_XrSceneOrientedBoxBoundMSFT(_) \ - _(pose) \ - _(extents) \ - -#define XR_LIST_STRUCT_XrSceneFrustumBoundMSFT(_) \ - _(pose) \ - _(fov) \ - _(farDistance) \ - -#define XR_LIST_STRUCT_XrSceneBoundsMSFT(_) \ - _(space) \ - _(time) \ - _(sphereCount) \ - _(spheres) \ - _(boxCount) \ - _(boxes) \ - _(frustumCount) \ - _(frustums) \ - -#define XR_LIST_STRUCT_XrNewSceneComputeInfoMSFT(_) \ - _(type) \ - _(next) \ - _(requestedFeatureCount) \ - _(requestedFeatures) \ - _(consistency) \ - _(bounds) \ - -#define XR_LIST_STRUCT_XrVisualMeshComputeLodInfoMSFT(_) \ - _(type) \ - _(next) \ - _(lod) \ - -#define XR_LIST_STRUCT_XrSceneComponentMSFT(_) \ - _(componentType) \ - _(id) \ - _(parentId) \ - _(updateTime) \ - -#define XR_LIST_STRUCT_XrSceneComponentsMSFT(_) \ - _(type) \ - _(next) \ - _(componentCapacityInput) \ - _(componentCountOutput) \ - _(components) \ - -#define XR_LIST_STRUCT_XrSceneComponentsGetInfoMSFT(_) \ - _(type) \ - _(next) \ - _(componentType) \ - -#define XR_LIST_STRUCT_XrSceneComponentLocationMSFT(_) \ - _(flags) \ - _(pose) \ - -#define XR_LIST_STRUCT_XrSceneComponentLocationsMSFT(_) \ - _(type) \ - _(next) \ - _(locationCount) \ - _(locations) \ - -#define XR_LIST_STRUCT_XrSceneComponentsLocateInfoMSFT(_) \ - _(type) \ - _(next) \ - _(baseSpace) \ - _(time) \ - _(componentIdCount) \ - _(componentIds) \ - -#define XR_LIST_STRUCT_XrSceneObjectMSFT(_) \ - _(objectType) \ - -#define XR_LIST_STRUCT_XrSceneObjectsMSFT(_) \ - _(type) \ - _(next) \ - _(sceneObjectCount) \ - _(sceneObjects) \ - -#define XR_LIST_STRUCT_XrSceneComponentParentFilterInfoMSFT(_) \ - _(type) \ - _(next) \ - _(parentId) \ - -#define XR_LIST_STRUCT_XrSceneObjectTypesFilterInfoMSFT(_) \ - _(type) \ - _(next) \ - _(objectTypeCount) \ - _(objectTypes) \ - -#define XR_LIST_STRUCT_XrScenePlaneMSFT(_) \ - _(alignment) \ - _(size) \ - _(meshBufferId) \ - _(supportsIndicesUint16) \ - -#define XR_LIST_STRUCT_XrScenePlanesMSFT(_) \ - _(type) \ - _(next) \ - _(scenePlaneCount) \ - _(scenePlanes) \ - -#define XR_LIST_STRUCT_XrScenePlaneAlignmentFilterInfoMSFT(_) \ - _(type) \ - _(next) \ - _(alignmentCount) \ - _(alignments) \ - -#define XR_LIST_STRUCT_XrSceneMeshMSFT(_) \ - _(meshBufferId) \ - _(supportsIndicesUint16) \ - -#define XR_LIST_STRUCT_XrSceneMeshesMSFT(_) \ - _(type) \ - _(next) \ - _(sceneMeshCount) \ - _(sceneMeshes) \ - -#define XR_LIST_STRUCT_XrSceneMeshBuffersGetInfoMSFT(_) \ - _(type) \ - _(next) \ - _(meshBufferId) \ - -#define XR_LIST_STRUCT_XrSceneMeshBuffersMSFT(_) \ - _(type) \ - _(next) \ - -#define XR_LIST_STRUCT_XrSceneMeshVertexBufferMSFT(_) \ - _(type) \ - _(next) \ - _(vertexCapacityInput) \ - _(vertexCountOutput) \ - _(vertices) \ - -#define XR_LIST_STRUCT_XrSceneMeshIndicesUint32MSFT(_) \ - _(type) \ - _(next) \ - _(indexCapacityInput) \ - _(indexCountOutput) \ - _(indices) \ - -#define XR_LIST_STRUCT_XrSceneMeshIndicesUint16MSFT(_) \ - _(type) \ - _(next) \ - _(indexCapacityInput) \ - _(indexCountOutput) \ - _(indices) \ - -#define XR_LIST_STRUCT_XrSerializedSceneFragmentDataGetInfoMSFT(_) \ - _(type) \ - _(next) \ - _(sceneFragmentId) \ - -#define XR_LIST_STRUCT_XrDeserializeSceneFragmentMSFT(_) \ - _(bufferSize) \ - _(buffer) \ - -#define XR_LIST_STRUCT_XrSceneDeserializeInfoMSFT(_) \ - _(type) \ - _(next) \ - _(fragmentCount) \ - _(fragments) \ - #define XR_LIST_STRUCT_XrEventDataDisplayRefreshRateChangedFB(_) \ _(type) \ _(next) \ _(fromDisplayRefreshRate) \ _(toDisplayRefreshRate) \ -#define XR_LIST_STRUCT_XrViveTrackerPathsHTCX(_) \ - _(type) \ - _(next) \ - _(persistentPath) \ - _(rolePath) \ - -#define XR_LIST_STRUCT_XrEventDataViveTrackerConnectedHTCX(_) \ - _(type) \ - _(next) \ - _(paths) \ - -#define XR_LIST_STRUCT_XrSystemFacialTrackingPropertiesHTC(_) \ - _(type) \ - _(next) \ - _(supportEyeFacialTracking) \ - _(supportLipFacialTracking) \ - -#define XR_LIST_STRUCT_XrFacialExpressionsHTC(_) \ - _(type) \ - _(next) \ - _(isActive) \ - _(sampleTime) \ - _(expressionCount) \ - _(expressionWeightings) \ - -#define XR_LIST_STRUCT_XrFacialTrackerCreateInfoHTC(_) \ - _(type) \ - _(next) \ - _(facialTrackingType) \ - #define XR_LIST_STRUCT_XrSystemColorSpacePropertiesFB(_) \ _(type) \ _(next) \ _(colorSpace) \ -#define XR_LIST_STRUCT_XrVector4sFB(_) \ - _(x) \ - _(y) \ - _(z) \ - _(w) \ - -#define XR_LIST_STRUCT_XrHandTrackingMeshFB(_) \ - _(type) \ - _(next) \ - _(jointCapacityInput) \ - _(jointCountOutput) \ - _(jointBindPoses) \ - _(jointRadii) \ - _(jointParents) \ - _(vertexCapacityInput) \ - _(vertexCountOutput) \ - _(vertexPositions) \ - _(vertexNormals) \ - _(vertexUVs) \ - _(vertexBlendIndices) \ - _(vertexBlendWeights) \ - _(indexCapacityInput) \ - _(indexCountOutput) \ - _(indices) \ - -#define XR_LIST_STRUCT_XrHandTrackingScaleFB(_) \ - _(type) \ - _(next) \ - _(sensorOutput) \ - _(currentOutput) \ - _(overrideHandScale) \ - _(overrideValueInput) \ - -#define XR_LIST_STRUCT_XrHandTrackingAimStateFB(_) \ - _(type) \ - _(next) \ - _(status) \ - _(aimPose) \ - _(pinchStrengthIndex) \ - _(pinchStrengthMiddle) \ - _(pinchStrengthRing) \ - _(pinchStrengthLittle) \ - -#define XR_LIST_STRUCT_XrHandCapsuleFB(_) \ - _(points) \ - _(radius) \ - _(joint) \ - -#define XR_LIST_STRUCT_XrHandTrackingCapsulesStateFB(_) \ - _(type) \ - _(next) \ - _(capsules) \ - -#define XR_LIST_STRUCT_XrFoveationProfileCreateInfoFB(_) \ - _(type) \ - _(next) \ - -#define XR_LIST_STRUCT_XrSwapchainCreateInfoFoveationFB(_) \ - _(type) \ - _(next) \ - _(flags) \ - -#define XR_LIST_STRUCT_XrSwapchainStateFoveationFB(_) \ - _(type) \ - _(next) \ - _(flags) \ - _(profile) \ - -#define XR_LIST_STRUCT_XrFoveationLevelProfileCreateInfoFB(_) \ - _(type) \ - _(next) \ - _(level) \ - _(verticalOffset) \ - _(dynamic) \ - -#define XR_LIST_STRUCT_XrSystemKeyboardTrackingPropertiesFB(_) \ - _(type) \ - _(next) \ - _(supportsKeyboardTracking) \ - -#define XR_LIST_STRUCT_XrKeyboardTrackingDescriptionFB(_) \ - _(trackedKeyboardId) \ - _(size) \ - _(flags) \ - _(name) \ - -#define XR_LIST_STRUCT_XrKeyboardSpaceCreateInfoFB(_) \ - _(type) \ - _(next) \ - _(trackedKeyboardId) \ - -#define XR_LIST_STRUCT_XrKeyboardTrackingQueryFB(_) \ - _(type) \ - _(next) \ - _(flags) \ - -#define XR_LIST_STRUCT_XrTriangleMeshCreateInfoFB(_) \ - _(type) \ - _(next) \ - _(flags) \ - _(windingOrder) \ - _(vertexCount) \ - _(vertexBuffer) \ - _(triangleCount) \ - _(indexBuffer) \ - -#define XR_LIST_STRUCT_XrSystemPassthroughPropertiesFB(_) \ - _(type) \ - _(next) \ - _(supportsPassthrough) \ - -#define XR_LIST_STRUCT_XrPassthroughCreateInfoFB(_) \ - _(type) \ - _(next) \ - _(flags) \ - -#define XR_LIST_STRUCT_XrPassthroughLayerCreateInfoFB(_) \ - _(type) \ - _(next) \ - _(passthrough) \ - _(flags) \ - _(purpose) \ - -#define XR_LIST_STRUCT_XrCompositionLayerPassthroughFB(_) \ - _(type) \ - _(next) \ - _(flags) \ - _(space) \ - _(layerHandle) \ - -#define XR_LIST_STRUCT_XrGeometryInstanceCreateInfoFB(_) \ - _(type) \ - _(next) \ - _(layer) \ - _(mesh) \ - _(baseSpace) \ - _(pose) \ - _(scale) \ - -#define XR_LIST_STRUCT_XrGeometryInstanceTransformFB(_) \ - _(type) \ - _(next) \ - _(baseSpace) \ - _(time) \ - _(pose) \ - _(scale) \ - -#define XR_LIST_STRUCT_XrPassthroughStyleFB(_) \ - _(type) \ - _(next) \ - _(textureOpacityFactor) \ - _(edgeColor) \ - -#define XR_LIST_STRUCT_XrPassthroughColorMapMonoToRgbaFB(_) \ - _(type) \ - _(next) \ - _(textureColorMap) \ - -#define XR_LIST_STRUCT_XrPassthroughColorMapMonoToMonoFB(_) \ - _(type) \ - _(next) \ - _(textureColorMap) \ - -#define XR_LIST_STRUCT_XrEventDataPassthroughStateChangedFB(_) \ - _(type) \ - _(next) \ - _(flags) \ - -#define XR_LIST_STRUCT_XrRenderModelPathInfoFB(_) \ - _(type) \ - _(next) \ - _(path) \ - -#define XR_LIST_STRUCT_XrRenderModelPropertiesFB(_) \ - _(type) \ - _(next) \ - _(vendorId) \ - _(modelName) \ - _(modelKey) \ - _(modelVersion) \ - _(flags) \ - -#define XR_LIST_STRUCT_XrRenderModelBufferFB(_) \ - _(type) \ - _(next) \ - _(bufferCapacityInput) \ - _(bufferCountOutput) \ - _(buffer) \ - -#define XR_LIST_STRUCT_XrRenderModelLoadInfoFB(_) \ - _(type) \ - _(next) \ - _(modelKey) \ - -#define XR_LIST_STRUCT_XrSystemRenderModelPropertiesFB(_) \ - _(type) \ - _(next) \ - _(supportsRenderModelLoading) \ - -#define XR_LIST_STRUCT_XrViewLocateFoveatedRenderingVARJO(_) \ - _(type) \ - _(next) \ - _(foveatedRenderingActive) \ - -#define XR_LIST_STRUCT_XrFoveatedViewConfigurationViewVARJO(_) \ - _(type) \ - _(next) \ - _(foveatedRenderingActive) \ - -#define XR_LIST_STRUCT_XrSystemFoveatedRenderingPropertiesVARJO(_) \ - _(type) \ - _(next) \ - _(supportsFoveatedRendering) \ - -#define XR_LIST_STRUCT_XrCompositionLayerDepthTestVARJO(_) \ - _(type) \ - _(next) \ - _(depthTestRangeNearZ) \ - _(depthTestRangeFarZ) \ - -#define XR_LIST_STRUCT_XrSystemMarkerTrackingPropertiesVARJO(_) \ - _(type) \ - _(next) \ - _(supportsMarkerTracking) \ - -#define XR_LIST_STRUCT_XrEventDataMarkerTrackingUpdateVARJO(_) \ - _(type) \ - _(next) \ - _(markerId) \ - _(isActive) \ - _(isPredicted) \ - _(time) \ - -#define XR_LIST_STRUCT_XrMarkerSpaceCreateInfoVARJO(_) \ - _(type) \ - _(next) \ - _(markerId) \ - _(poseInMarkerSpace) \ - -#define XR_LIST_STRUCT_XrSpatialAnchorPersistenceNameMSFT(_) \ - _(name) \ - -#define XR_LIST_STRUCT_XrSpatialAnchorPersistenceInfoMSFT(_) \ - _(type) \ - _(next) \ - _(spatialAnchorPersistenceName) \ - _(spatialAnchor) \ - -#define XR_LIST_STRUCT_XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT(_) \ - _(type) \ - _(next) \ - _(spatialAnchorStore) \ - _(spatialAnchorPersistenceName) \ - -#define XR_LIST_STRUCT_XrSwapchainImageFoveationVulkanFB(_) \ - _(type) \ - _(next) \ - _(image) \ - _(width) \ - _(height) \ - -#define XR_LIST_STRUCT_XrSwapchainStateAndroidSurfaceDimensionsFB(_) \ - _(type) \ - _(next) \ - _(width) \ - _(height) \ - -#define XR_LIST_STRUCT_XrSwapchainStateSamplerOpenGLESFB(_) \ - _(type) \ - _(next) \ - _(minFilter) \ - _(magFilter) \ - _(wrapModeS) \ - _(wrapModeT) \ - _(swizzleRed) \ - _(swizzleGreen) \ - _(swizzleBlue) \ - _(swizzleAlpha) \ - _(maxAnisotropy) \ - _(borderColor) \ - -#define XR_LIST_STRUCT_XrSwapchainStateSamplerVulkanFB(_) \ - _(type) \ - _(next) \ - _(minFilter) \ - _(magFilter) \ - _(mipmapMode) \ - _(wrapModeS) \ - _(wrapModeT) \ - _(swizzleRed) \ - _(swizzleGreen) \ - _(swizzleBlue) \ - _(swizzleAlpha) \ - _(maxAnisotropy) \ - _(borderColor) \ - -#define XR_LIST_STRUCT_XrCompositionLayerSpaceWarpInfoFB(_) \ - _(type) \ - _(next) \ - _(layerFlags) \ - _(motionVectorSubImage) \ - _(appSpaceDeltaPose) \ - _(depthSubImage) \ - _(minDepth) \ - _(maxDepth) \ - _(nearZ) \ - _(farZ) \ - -#define XR_LIST_STRUCT_XrSystemSpaceWarpPropertiesFB(_) \ - _(type) \ - _(next) \ - _(recommendedMotionVectorImageRectWidth) \ - _(recommendedMotionVectorImageRectHeight) \ - -#define XR_LIST_STRUCT_XrDigitalLensControlALMALENCE(_) \ - _(type) \ - _(next) \ - _(flags) \ - -#define XR_LIST_STRUCT_XrPassthroughKeyboardHandsIntensityFB(_) \ - _(type) \ - _(next) \ - _(leftHandIntensity) \ - _(rightHandIntensity) \ - -#define XR_LIST_STRUCT_XrUuidEXT(_) \ - _(data) \ - #define XR_LIST_STRUCTURE_TYPES_CORE(_) \ @@ -2394,8 +1502,6 @@ XR_ENUM_STR(XrResult); _(XrEventDataMainSessionVisibilityChangedEXTX, XR_TYPE_EVENT_DATA_MAIN_SESSION_VISIBILITY_CHANGED_EXTX) \ _(XrSpatialAnchorCreateInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_MSFT) \ _(XrSpatialAnchorSpaceCreateInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_SPACE_CREATE_INFO_MSFT) \ - _(XrCompositionLayerImageLayoutFB, XR_TYPE_COMPOSITION_LAYER_IMAGE_LAYOUT_FB) \ - _(XrCompositionLayerAlphaBlendFB, XR_TYPE_COMPOSITION_LAYER_ALPHA_BLEND_FB) \ _(XrViewConfigurationDepthRangeEXT, XR_TYPE_VIEW_CONFIGURATION_DEPTH_RANGE_EXT) \ _(XrSpatialGraphNodeSpaceCreateInfoMSFT, XR_TYPE_SPATIAL_GRAPH_NODE_SPACE_CREATE_INFO_MSFT) \ _(XrSystemHandTrackingPropertiesEXT, XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT) \ @@ -2420,79 +1526,9 @@ XR_ENUM_STR(XrResult); _(XrControllerModelNodeStateMSFT, XR_TYPE_CONTROLLER_MODEL_NODE_STATE_MSFT) \ _(XrControllerModelStateMSFT, XR_TYPE_CONTROLLER_MODEL_STATE_MSFT) \ _(XrViewConfigurationViewFovEPIC, XR_TYPE_VIEW_CONFIGURATION_VIEW_FOV_EPIC) \ - _(XrCompositionLayerReprojectionInfoMSFT, XR_TYPE_COMPOSITION_LAYER_REPROJECTION_INFO_MSFT) \ - _(XrCompositionLayerReprojectionPlaneOverrideMSFT, XR_TYPE_COMPOSITION_LAYER_REPROJECTION_PLANE_OVERRIDE_MSFT) \ - _(XrCompositionLayerSecureContentFB, XR_TYPE_COMPOSITION_LAYER_SECURE_CONTENT_FB) \ _(XrInteractionProfileAnalogThresholdVALVE, XR_TYPE_INTERACTION_PROFILE_ANALOG_THRESHOLD_VALVE) \ - _(XrHandJointsMotionRangeInfoEXT, XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT) \ - _(XrSceneObserverCreateInfoMSFT, XR_TYPE_SCENE_OBSERVER_CREATE_INFO_MSFT) \ - _(XrSceneCreateInfoMSFT, XR_TYPE_SCENE_CREATE_INFO_MSFT) \ - _(XrNewSceneComputeInfoMSFT, XR_TYPE_NEW_SCENE_COMPUTE_INFO_MSFT) \ - _(XrVisualMeshComputeLodInfoMSFT, XR_TYPE_VISUAL_MESH_COMPUTE_LOD_INFO_MSFT) \ - _(XrSceneComponentsMSFT, XR_TYPE_SCENE_COMPONENTS_MSFT) \ - _(XrSceneComponentsGetInfoMSFT, XR_TYPE_SCENE_COMPONENTS_GET_INFO_MSFT) \ - _(XrSceneComponentLocationsMSFT, XR_TYPE_SCENE_COMPONENT_LOCATIONS_MSFT) \ - _(XrSceneComponentsLocateInfoMSFT, XR_TYPE_SCENE_COMPONENTS_LOCATE_INFO_MSFT) \ - _(XrSceneObjectsMSFT, XR_TYPE_SCENE_OBJECTS_MSFT) \ - _(XrSceneComponentParentFilterInfoMSFT, XR_TYPE_SCENE_COMPONENT_PARENT_FILTER_INFO_MSFT) \ - _(XrSceneObjectTypesFilterInfoMSFT, XR_TYPE_SCENE_OBJECT_TYPES_FILTER_INFO_MSFT) \ - _(XrScenePlanesMSFT, XR_TYPE_SCENE_PLANES_MSFT) \ - _(XrScenePlaneAlignmentFilterInfoMSFT, XR_TYPE_SCENE_PLANE_ALIGNMENT_FILTER_INFO_MSFT) \ - _(XrSceneMeshesMSFT, XR_TYPE_SCENE_MESHES_MSFT) \ - _(XrSceneMeshBuffersGetInfoMSFT, XR_TYPE_SCENE_MESH_BUFFERS_GET_INFO_MSFT) \ - _(XrSceneMeshBuffersMSFT, XR_TYPE_SCENE_MESH_BUFFERS_MSFT) \ - _(XrSceneMeshVertexBufferMSFT, XR_TYPE_SCENE_MESH_VERTEX_BUFFER_MSFT) \ - _(XrSceneMeshIndicesUint32MSFT, XR_TYPE_SCENE_MESH_INDICES_UINT32_MSFT) \ - _(XrSceneMeshIndicesUint16MSFT, XR_TYPE_SCENE_MESH_INDICES_UINT16_MSFT) \ - _(XrSerializedSceneFragmentDataGetInfoMSFT, XR_TYPE_SERIALIZED_SCENE_FRAGMENT_DATA_GET_INFO_MSFT) \ - _(XrSceneDeserializeInfoMSFT, XR_TYPE_SCENE_DESERIALIZE_INFO_MSFT) \ _(XrEventDataDisplayRefreshRateChangedFB, XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB) \ - _(XrViveTrackerPathsHTCX, XR_TYPE_VIVE_TRACKER_PATHS_HTCX) \ - _(XrEventDataViveTrackerConnectedHTCX, XR_TYPE_EVENT_DATA_VIVE_TRACKER_CONNECTED_HTCX) \ - _(XrSystemFacialTrackingPropertiesHTC, XR_TYPE_SYSTEM_FACIAL_TRACKING_PROPERTIES_HTC) \ - _(XrFacialExpressionsHTC, XR_TYPE_FACIAL_EXPRESSIONS_HTC) \ - _(XrFacialTrackerCreateInfoHTC, XR_TYPE_FACIAL_TRACKER_CREATE_INFO_HTC) \ _(XrSystemColorSpacePropertiesFB, XR_TYPE_SYSTEM_COLOR_SPACE_PROPERTIES_FB) \ - _(XrHandTrackingMeshFB, XR_TYPE_HAND_TRACKING_MESH_FB) \ - _(XrHandTrackingScaleFB, XR_TYPE_HAND_TRACKING_SCALE_FB) \ - _(XrHandTrackingAimStateFB, XR_TYPE_HAND_TRACKING_AIM_STATE_FB) \ - _(XrHandTrackingCapsulesStateFB, XR_TYPE_HAND_TRACKING_CAPSULES_STATE_FB) \ - _(XrFoveationProfileCreateInfoFB, XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB) \ - _(XrSwapchainCreateInfoFoveationFB, XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB) \ - _(XrSwapchainStateFoveationFB, XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB) \ - _(XrFoveationLevelProfileCreateInfoFB, XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB) \ - _(XrSystemKeyboardTrackingPropertiesFB, XR_TYPE_SYSTEM_KEYBOARD_TRACKING_PROPERTIES_FB) \ - _(XrKeyboardSpaceCreateInfoFB, XR_TYPE_KEYBOARD_SPACE_CREATE_INFO_FB) \ - _(XrKeyboardTrackingQueryFB, XR_TYPE_KEYBOARD_TRACKING_QUERY_FB) \ - _(XrTriangleMeshCreateInfoFB, XR_TYPE_TRIANGLE_MESH_CREATE_INFO_FB) \ - _(XrSystemPassthroughPropertiesFB, XR_TYPE_SYSTEM_PASSTHROUGH_PROPERTIES_FB) \ - _(XrPassthroughCreateInfoFB, XR_TYPE_PASSTHROUGH_CREATE_INFO_FB) \ - _(XrPassthroughLayerCreateInfoFB, XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB) \ - _(XrCompositionLayerPassthroughFB, XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB) \ - _(XrGeometryInstanceCreateInfoFB, XR_TYPE_GEOMETRY_INSTANCE_CREATE_INFO_FB) \ - _(XrGeometryInstanceTransformFB, XR_TYPE_GEOMETRY_INSTANCE_TRANSFORM_FB) \ - _(XrPassthroughStyleFB, XR_TYPE_PASSTHROUGH_STYLE_FB) \ - _(XrPassthroughColorMapMonoToRgbaFB, XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_RGBA_FB) \ - _(XrPassthroughColorMapMonoToMonoFB, XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_MONO_FB) \ - _(XrEventDataPassthroughStateChangedFB, XR_TYPE_EVENT_DATA_PASSTHROUGH_STATE_CHANGED_FB) \ - _(XrRenderModelPathInfoFB, XR_TYPE_RENDER_MODEL_PATH_INFO_FB) \ - _(XrRenderModelPropertiesFB, XR_TYPE_RENDER_MODEL_PROPERTIES_FB) \ - _(XrRenderModelBufferFB, XR_TYPE_RENDER_MODEL_BUFFER_FB) \ - _(XrRenderModelLoadInfoFB, XR_TYPE_RENDER_MODEL_LOAD_INFO_FB) \ - _(XrSystemRenderModelPropertiesFB, XR_TYPE_SYSTEM_RENDER_MODEL_PROPERTIES_FB) \ - _(XrViewLocateFoveatedRenderingVARJO, XR_TYPE_VIEW_LOCATE_FOVEATED_RENDERING_VARJO) \ - _(XrFoveatedViewConfigurationViewVARJO, XR_TYPE_FOVEATED_VIEW_CONFIGURATION_VIEW_VARJO) \ - _(XrSystemFoveatedRenderingPropertiesVARJO, XR_TYPE_SYSTEM_FOVEATED_RENDERING_PROPERTIES_VARJO) \ - _(XrCompositionLayerDepthTestVARJO, XR_TYPE_COMPOSITION_LAYER_DEPTH_TEST_VARJO) \ - _(XrSystemMarkerTrackingPropertiesVARJO, XR_TYPE_SYSTEM_MARKER_TRACKING_PROPERTIES_VARJO) \ - _(XrEventDataMarkerTrackingUpdateVARJO, XR_TYPE_EVENT_DATA_MARKER_TRACKING_UPDATE_VARJO) \ - _(XrMarkerSpaceCreateInfoVARJO, XR_TYPE_MARKER_SPACE_CREATE_INFO_VARJO) \ - _(XrSpatialAnchorPersistenceInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT) \ - _(XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT) \ - _(XrCompositionLayerSpaceWarpInfoFB, XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB) \ - _(XrSystemSpaceWarpPropertiesFB, XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB) \ - _(XrDigitalLensControlALMALENCE, XR_TYPE_DIGITAL_LENS_CONTROL_ALMALENCE) \ - _(XrPassthroughKeyboardHandsIntensityFB, XR_TYPE_PASSTHROUGH_KEYBOARD_HANDS_INTENSITY_FB) \ @@ -2569,22 +1605,12 @@ XR_ENUM_STR(XrResult); #define XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL_ES(_) \ _(XrSwapchainImageOpenGLESKHR, XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR) \ _(XrGraphicsRequirementsOpenGLESKHR, XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR) \ - _(XrSwapchainStateSamplerOpenGLESFB, XR_TYPE_SWAPCHAIN_STATE_SAMPLER_OPENGL_ES_FB) \ #else #define XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL_ES(_) #endif -#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) && defined(XR_USE_PLATFORM_ANDROID) -#define XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL_ES_XR_USE_PLATFORM_ANDROID(_) \ - _(XrGraphicsBindingOpenGLESAndroidKHR, XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR) \ - - -#else -#define XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL_ES_XR_USE_PLATFORM_ANDROID(_) -#endif - #if defined(XR_USE_GRAPHICS_API_VULKAN) #define XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_VULKAN(_) \ _(XrVulkanSwapchainFormatListCreateInfoKHR, XR_TYPE_VULKAN_SWAPCHAIN_FORMAT_LIST_CREATE_INFO_KHR) \ @@ -2594,8 +1620,6 @@ XR_ENUM_STR(XrResult); _(XrVulkanInstanceCreateInfoKHR, XR_TYPE_VULKAN_INSTANCE_CREATE_INFO_KHR) \ _(XrVulkanDeviceCreateInfoKHR, XR_TYPE_VULKAN_DEVICE_CREATE_INFO_KHR) \ _(XrVulkanGraphicsDeviceGetInfoKHR, XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR) \ - _(XrSwapchainImageFoveationVulkanFB, XR_TYPE_SWAPCHAIN_IMAGE_FOVEATION_VULKAN_FB) \ - _(XrSwapchainStateSamplerVulkanFB, XR_TYPE_SWAPCHAIN_STATE_SAMPLER_VULKAN_FB) \ #else @@ -2607,13 +1631,21 @@ XR_ENUM_STR(XrResult); _(XrInstanceCreateInfoAndroidKHR, XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR) \ _(XrLoaderInitInfoAndroidKHR, XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR) \ _(XrAndroidSurfaceSwapchainCreateInfoFB, XR_TYPE_ANDROID_SURFACE_SWAPCHAIN_CREATE_INFO_FB) \ - _(XrSwapchainStateAndroidSurfaceDimensionsFB, XR_TYPE_SWAPCHAIN_STATE_ANDROID_SURFACE_DIMENSIONS_FB) \ #else #define XR_LIST_STRUCTURE_TYPES_XR_USE_PLATFORM_ANDROID(_) #endif +#if defined(XR_USE_PLATFORM_ANDROID) && defined(XR_USE_GRAPHICS_API_OPENGL_ES) +#define XR_LIST_STRUCTURE_TYPES_XR_USE_PLATFORM_ANDROID_XR_USE_GRAPHICS_API_OPENGL_ES(_) \ + _(XrGraphicsBindingOpenGLESAndroidKHR, XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR) \ + + +#else +#define XR_LIST_STRUCTURE_TYPES_XR_USE_PLATFORM_ANDROID_XR_USE_GRAPHICS_API_OPENGL_ES(_) +#endif + #if defined(XR_USE_PLATFORM_EGL) #define XR_LIST_STRUCTURE_TYPES_XR_USE_PLATFORM_EGL(_) \ _(XrGraphicsBindingEGLMNDX, XR_TYPE_GRAPHICS_BINDING_EGL_MNDX) \ @@ -2642,9 +1674,9 @@ XR_ENUM_STR(XrResult); XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL_XR_USE_PLATFORM_XCB(_) \ XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL_XR_USE_PLATFORM_XLIB(_) \ XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL_ES(_) \ - XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL_ES_XR_USE_PLATFORM_ANDROID(_) \ XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_VULKAN(_) \ XR_LIST_STRUCTURE_TYPES_XR_USE_PLATFORM_ANDROID(_) \ + XR_LIST_STRUCTURE_TYPES_XR_USE_PLATFORM_ANDROID_XR_USE_GRAPHICS_API_OPENGL_ES(_) \ XR_LIST_STRUCTURE_TYPES_XR_USE_PLATFORM_EGL(_) \ XR_LIST_STRUCTURE_TYPES_XR_USE_PLATFORM_WIN32(_) \ @@ -2675,8 +1707,6 @@ XR_ENUM_STR(XrResult); _(XR_VARJO_quad_views, 38) \ _(XR_MSFT_unbounded_reference_space, 39) \ _(XR_MSFT_spatial_anchor, 40) \ - _(XR_FB_composition_layer_image_layout, 41) \ - _(XR_FB_composition_layer_alpha_blend, 42) \ _(XR_MND_headless, 43) \ _(XR_OCULUS_android_session_state_enable, 45) \ _(XR_EXT_view_configuration_depth_range, 47) \ @@ -2693,13 +1723,9 @@ XR_ENUM_STR(XrResult); _(XR_EXT_win32_appcontainer_compatible, 58) \ _(XR_EPIC_view_configuration_fov, 60) \ _(XR_MSFT_holographic_window_attachment, 64) \ - _(XR_MSFT_composition_layer_reprojection, 67) \ _(XR_HUAWEI_controller_interaction, 70) \ _(XR_FB_android_surface_swapchain_create, 71) \ - _(XR_FB_swapchain_update_state, 72) \ - _(XR_FB_composition_layer_secure_content, 73) \ _(XR_VALVE_analog_threshold, 80) \ - _(XR_EXT_hand_joints_motion_range, 81) \ _(XR_KHR_loader_init, 89) \ _(XR_KHR_loader_init_android, 90) \ _(XR_KHR_vulkan_enable2, 91) \ @@ -2707,38 +1733,11 @@ XR_ENUM_STR(XrResult); _(XR_EXT_samsung_odyssey_controller, 95) \ _(XR_EXT_hp_mixed_reality_controller, 96) \ _(XR_MND_swapchain_usage_input_attachment_bit, 97) \ - _(XR_MSFT_scene_understanding, 98) \ - _(XR_MSFT_scene_understanding_serialization, 99) \ _(XR_FB_display_refresh_rate, 102) \ _(XR_HTC_vive_cosmos_controller_interaction, 103) \ - _(XR_HTCX_vive_tracker_interaction, 104) \ - _(XR_HTC_facial_tracking, 105) \ - _(XR_HTC_vive_focus3_controller_interaction, 106) \ _(XR_FB_color_space, 109) \ - _(XR_FB_hand_tracking_mesh, 111) \ - _(XR_FB_hand_tracking_aim, 112) \ - _(XR_FB_hand_tracking_capsules, 113) \ - _(XR_FB_foveation, 115) \ - _(XR_FB_foveation_configuration, 116) \ - _(XR_FB_keyboard_tracking, 117) \ - _(XR_FB_triangle_mesh, 118) \ - _(XR_FB_passthrough, 119) \ - _(XR_FB_render_model, 120) \ _(XR_KHR_binding_modification, 121) \ - _(XR_VARJO_foveated_rendering, 122) \ - _(XR_VARJO_composition_layer_depth_test, 123) \ - _(XR_VARJO_environment_depth_estimation, 124) \ - _(XR_VARJO_marker_tracking, 125) \ - _(XR_MSFT_spatial_anchor_persistence, 143) \ - _(XR_OCULUS_audio_device_guid, 160) \ - _(XR_FB_foveation_vulkan, 161) \ - _(XR_FB_swapchain_update_state_android_surface, 162) \ - _(XR_FB_swapchain_update_state_opengl_es, 163) \ - _(XR_FB_swapchain_update_state_vulkan, 164) \ - _(XR_KHR_swapchain_usage_input_attachment_bit, 166) \ - _(XR_FB_space_warp, 172) \ - _(XR_ALMALENCE_digital_lens_control, 197) \ - _(XR_FB_passthrough_keyboard_hands, 204) \ - _(XR_EXT_uuid, 300) \ + #endif + diff --git a/ext/vma/vk_mem_alloc.h b/ext/vma/vk_mem_alloc.h index 3dc4cc9dc8..f52dceeaf4 100644 --- a/ext/vma/vk_mem_alloc.h +++ b/ext/vma/vk_mem_alloc.h @@ -26,6 +26,18 @@ // BEGIN PPSSPP HACKS !!!!! #ifdef USE_CRT_DBG #undef new +#endif + +#if defined(__APPLE__) +#include + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && (!defined(__IPHONE_10_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0) +#define VMA_USE_STL_SHARED_MUTEX 0 +#endif +#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && (!defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12) +#define VMA_USE_STL_SHARED_MUTEX 0 +#endif + #endif // END PPSSPP HACKS diff --git a/ext/zstd b/ext/zstd index 155d6a58a2..096dccbc2d 160000 --- a/ext/zstd +++ b/ext/zstd @@ -1 +1 @@ -Subproject commit 155d6a58a2a6290710194fd6367d720fca94a923 +Subproject commit 096dccbc2d89a560db0b9892c53ea0c77eff20a1 diff --git a/ffmpeg b/ffmpeg index 3ad7ddb9eb..cea6dd17be 160000 --- a/ffmpeg +++ b/ffmpeg @@ -1 +1 @@ -Subproject commit 3ad7ddb9eb2af898dce8c4b5e9a28b77b3f7ddd7 +Subproject commit cea6dd17be4ea045946dc991ef0bca91f9005345 diff --git a/headless/Compare.cpp b/headless/Compare.cpp index f5127e5ee0..76254d005a 100644 --- a/headless/Compare.cpp +++ b/headless/Compare.cpp @@ -305,11 +305,18 @@ bool CompareOutput(const Path &bootFilename, const std::string &output, bool ver } } -inline int ComparePixel(u32 pix1, u32 pix2) { - // For now, if they're different at all except alpha, it's an error. - if ((pix1 & 0xFFFFFF) != (pix2 & 0xFFFFFF)) - return 1; - return 0; +static inline double CompareChannel(int pix1, int pix2) { + double diff = pix1 - pix2; + return diff * diff; +} + +static inline double ComparePixel(u32 pix1, u32 pix2) { + // Ignore alpha. + double r = CompareChannel(pix1 & 0xFF, pix2 & 0xFF); + double g = CompareChannel((pix1 >> 8) & 0xFF, (pix2 >> 8) & 0xFF); + double b = CompareChannel((pix1 >> 16) & 0xFF, (pix2 >> 16) & 0xFF); + + return r + g + b; } std::vector TranslateDebugBufferToCompare(const GPUDebugBuffer *buffer, u32 stride, u32 h) { @@ -338,7 +345,6 @@ std::vector TranslateDebugBufferToCompare(const GPUDebugBuffer *buffer, u32 dst += (h - safeH) * stride; } - u32 errors = 0; for (u32 y = 0; y < safeH; ++y) { switch (buffer->GetFormat()) { case GPU_DBG_FORMAT_8888: @@ -429,7 +435,7 @@ double ScreenshotComparer::Compare(const Path &screenshotFilename) { return -1.0f; } - u32 errors = 0; + double errors = 0; if (asBitmap_) { // The reference is flipped and BGRA by default for the common BMP compare case. for (u32 y = 0; y < h_; ++y) { @@ -447,7 +453,8 @@ double ScreenshotComparer::Compare(const Path &screenshotFilename) { } } - return (double) errors / (double) (w_ * h_); + // Convert to MSE, accounting for all three channels (RGB.) + return errors / (double)(w_ * h_ * 3); } bool ScreenshotComparer::SaveActualBitmap(const Path &resultFilename) { diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 0be0518e8b..dcfad07e8d 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -126,15 +126,12 @@ int printUsage(const char *progname, const char *reason) fprintf(stderr, " -m, --mount umd.cso mount iso on umd1:\n"); fprintf(stderr, " -r, --root some/path mount path on host0: (elfs must be in here)\n"); fprintf(stderr, " -l, --log full log output, not just emulated printfs\n"); - fprintf(stderr, " --debugger=PORT enable websocket debugger and break at start\n"); + fprintf(stderr, " --debugger=PORT enable websocket debugger and break at start\n"); -#if defined(HEADLESSHOST_CLASS) - { - fprintf(stderr, " --graphics=BACKEND use the full gpu backend (slower)\n"); - fprintf(stderr, " options: gles, software, directx9, etc.\n"); - fprintf(stderr, " --screenshot=FILE compare against a screenshot\n"); - } -#endif + fprintf(stderr, " --graphics=BACKEND use a different gpu backend\n"); + fprintf(stderr, " options: gles, software, directx9, etc.\n"); + fprintf(stderr, " --screenshot=FILE compare against a screenshot\n"); + fprintf(stderr, " --max-mse=NUMBER maximum allowed MSE error for screenshot\n"); fprintf(stderr, " --timeout=SECONDS abort test it if takes longer than SECONDS\n"); fprintf(stderr, " -v, --verbose show the full passed/failed result\n"); @@ -142,6 +139,7 @@ int printUsage(const char *progname, const char *reason) fprintf(stderr, " --ir use ir interpreter\n"); fprintf(stderr, " -j use jit (default)\n"); fprintf(stderr, " -c, --compare compare with output in file.expected\n"); + fprintf(stderr, " --bench run multiple times and output speed\n"); fprintf(stderr, "\nSee headless.txt for details.\n"); return 1; @@ -161,13 +159,20 @@ static HeadlessHost *getHost(GPUCore gpuCore) { } } -bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, bool autoCompare, bool verbose, double timeout) -{ +struct AutoTestOptions { + double timeout; + double maxScreenshotError; + bool compare : 1; + bool verbose : 1; + bool bench : 1; +}; + +bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const AutoTestOptions &opt) { // Kinda ugly, trying to guesstimate the test name from filename... currentTestName = GetTestName(coreParameter.fileToStart); std::string output; - if (autoCompare) + if (opt.compare || opt.bench) coreParameter.collectEmuLog = &output; std::string error_string; @@ -181,23 +186,19 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, bool TeamCityPrint("testStarted name='%s' captureStandardOutput='true'", currentTestName.c_str()); - host->BootDone(); - - if (autoCompare) - headlessHost->SetComparisonScreenshot(ExpectedScreenshotFromFilename(coreParameter.fileToStart)); + if (opt.compare) + headlessHost->SetComparisonScreenshot(ExpectedScreenshotFromFilename(coreParameter.fileToStart), opt.maxScreenshotError); while (!PSP_InitUpdate(&error_string)) sleep_ms(1); if (!PSP_IsInited()) { TeamCityPrint("testFailed name='%s' message='Startup failed'", currentTestName.c_str()); TeamCityPrint("testFinished name='%s'", currentTestName.c_str()); - GitHubActionsPrint("error", "Test timeout for %s", currentTestName.c_str()); + GitHubActionsPrint("error", "Test init failed for %s", currentTestName.c_str()); return false; } - bool passed = true; - double deadline; - deadline = time_now_d() + timeout; + host->BootDone(); Core_UpdateDebugStats(g_Config.bShowDebugStats || g_Config.bLogFrameDrops); @@ -205,6 +206,8 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, bool if (coreParameter.graphicsContext && coreParameter.graphicsContext->GetDrawContext()) coreParameter.graphicsContext->GetDrawContext()->BeginFrame(); + bool passed = true; + double deadline = time_now_d() + opt.timeout; coreState = coreParameter.startBreak ? CORE_STEPPING : CORE_RUNNING; while (coreState == CORE_RUNNING || coreState == CORE_STEPPING) { @@ -221,12 +224,15 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, bool } if (time_now_d() > deadline) { // Don't compare, print the output at least up to this point, and bail. - printf("%s", output.c_str()); - passed = false; + if (!opt.bench) { + printf("%s", output.c_str()); - host->SendDebugOutput("TIMEOUT\n"); - TeamCityPrint("testFailed name='%s' message='Test timeout'", currentTestName.c_str()); - GitHubActionsPrint("error", "Test timeout for %s", currentTestName.c_str()); + host->SendDebugOutput("TIMEOUT\n"); + TeamCityPrint("testFailed name='%s' message='Test timeout'", currentTestName.c_str()); + GitHubActionsPrint("error", "Test timeout for %s", currentTestName.c_str()); + } + + passed = false; Core_Stop(); } } @@ -237,10 +243,11 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, bool PSP_Shutdown(); - headlessHost->FlushDebugOutput(); + if (!opt.bench) + headlessHost->FlushDebugOutput(); - if (autoCompare && passed) - passed = CompareOutput(coreParameter.fileToStart, output, verbose); + if (opt.compare && passed) + passed = CompareOutput(coreParameter.fileToStart, output, opt.verbose); TeamCityPrint("testFinished name='%s'", currentTestName.c_str()); @@ -263,9 +270,9 @@ int main(int argc, const char* argv[]) _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif + AutoTestOptions testOptions{}; + testOptions.timeout = std::numeric_limits::infinity(); bool fullLog = false; - bool autoCompare = false; - bool verbose = false; const char *stateToLoad = 0; GPUCore gpuCore = GPUCORE_SOFTWARE; CPUCore cpuCore = CPUCore::JIT; @@ -275,7 +282,6 @@ int main(int argc, const char* argv[]) const char *mountIso = nullptr; const char *mountRoot = nullptr; const char *screenshotFilename = nullptr; - float timeout = std::numeric_limits::infinity(); for (int i = 1; i < argc; i++) { @@ -300,9 +306,11 @@ int main(int argc, const char* argv[]) else if (!strcmp(argv[i], "--ir")) cpuCore = CPUCore::IR_JIT; else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compare")) - autoCompare = true; + testOptions.compare = true; + else if (!strcmp(argv[i], "--bench")) + testOptions.bench = true; else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) - verbose = true; + testOptions.verbose = true; else if (!strncmp(argv[i], "--graphics=", strlen("--graphics=")) && strlen(argv[i]) > strlen("--graphics=")) { const char *gpuName = argv[i] + strlen("--graphics="); @@ -330,7 +338,9 @@ int main(int argc, const char* argv[]) } else if (!strncmp(argv[i], "--screenshot=", strlen("--screenshot=")) && strlen(argv[i]) > strlen("--screenshot=")) screenshotFilename = argv[i] + strlen("--screenshot="); else if (!strncmp(argv[i], "--timeout=", strlen("--timeout=")) && strlen(argv[i]) > strlen("--timeout=")) - timeout = (float)strtod(argv[i] + strlen("--timeout="), NULL); + testOptions.timeout = strtod(argv[i] + strlen("--timeout="), nullptr); + else if (!strncmp(argv[i], "--max-mse=", strlen("--max-mse=")) && strlen(argv[i]) > strlen("--max-mse=")) + testOptions.maxScreenshotError = strtod(argv[i] + strlen("--max-mse="), nullptr); else if (!strncmp(argv[i], "--debugger=", strlen("--debugger=")) && strlen(argv[i]) > strlen("--debugger=")) debuggerPort = (int)strtoul(argv[i] + strlen("--debugger="), NULL, 10); else if (!strcmp(argv[i], "--teamcity")) @@ -388,7 +398,7 @@ int main(int argc, const char* argv[]) coreParameter.mountIso = mountIso ? Path(std::string(mountIso)) : Path(); coreParameter.mountRoot = mountRoot ? Path(std::string(mountRoot)) : Path(); coreParameter.startBreak = false; - coreParameter.printfEmuLog = !autoCompare; + coreParameter.printfEmuLog = !testOptions.compare; coreParameter.headLess = true; coreParameter.renderScaleFactor = 1; coreParameter.renderWidth = 480; @@ -401,7 +411,7 @@ int main(int argc, const char* argv[]) g_Config.bFirstRun = false; g_Config.bIgnoreBadMemAccess = true; // Never report from tests. - g_Config.sReportHost = ""; + g_Config.sReportHost.clear(); g_Config.bAutoSaveSymbolMap = false; g_Config.iRenderingMode = FB_BUFFERED_MODE; g_Config.bHardwareTransform = true; @@ -456,8 +466,9 @@ int main(int argc, const char* argv[]) if (!File::Exists(g_Config.flash0Directory)) g_Config.flash0Directory = File::GetExeDirectory() / "assets/flash0"; - if (screenshotFilename != 0) - headlessHost->SetComparisonScreenshot(Path(std::string(screenshotFilename))); + if (screenshotFilename) + headlessHost->SetComparisonScreenshot(Path(std::string(screenshotFilename)), testOptions.maxScreenshotError); + headlessHost->SetWriteFailureScreenshot(!teamCityMode && !getenv("GITHUB_ACTIONS") && !testOptions.bench); #if PPSSPP_PLATFORM(ANDROID) // For some reason the debugger installs it with this name? @@ -487,14 +498,28 @@ int main(int argc, const char* argv[]) for (size_t i = 0; i < testFilenames.size(); ++i) { coreParameter.fileToStart = Path(testFilenames[i]); - if (autoCompare) + if (testOptions.compare) printf("%s:\n", coreParameter.fileToStart.c_str()); - bool passed = RunAutoTest(headlessHost, coreParameter, autoCompare, verbose, timeout); - if (autoCompare) - { + bool passed = RunAutoTest(headlessHost, coreParameter, testOptions); + if (testOptions.bench) { + double st = time_now_d(); + double deadline = st + testOptions.timeout; + double runs = 0.0; + for (int i = 0; i < 100; ++i) { + RunAutoTest(headlessHost, coreParameter, testOptions); + runs++; + + if (time_now_d() > deadline) + break; + } + double et = time_now_d(); + std::string testName = GetTestName(coreParameter.fileToStart); - if (passed) - { + printf(" %s - %f seconds average\n", testName.c_str(), (et - st) / runs); + } + if (testOptions.compare) { + std::string testName = GetTestName(coreParameter.fileToStart); + if (passed) { passedTests.push_back(testName); printf(" %s - passed!\n", testName.c_str()); } @@ -503,8 +528,7 @@ int main(int argc, const char* argv[]) } } - if (autoCompare) - { + if (testOptions.compare) { printf("%d tests passed, %d tests failed.\n", (int)passedTests.size(), (int)failedTests.size()); if (!failedTests.empty()) { diff --git a/headless/StubHost.cpp b/headless/StubHost.cpp index e7fbd03f9a..03a27ab1bf 100644 --- a/headless/StubHost.cpp +++ b/headless/StubHost.cpp @@ -46,7 +46,7 @@ void HeadlessHost::SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) { const static u32 FRAME_HEIGHT = 272; GPUDebugBuffer buffer; - gpuDebug->GetCurrentFramebuffer(buffer, GPU_DBG_FRAMEBUF_RENDER); + gpuDebug->GetCurrentFramebuffer(buffer, GPU_DBG_FRAMEBUF_DISPLAY); const std::vector pixels = TranslateDebugBufferToCompare(&buffer, 512, 272); ScreenshotComparer comparer(pixels, FRAME_STRIDE, FRAME_WIDTH, FRAME_HEIGHT); @@ -54,10 +54,10 @@ void HeadlessHost::SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) { if (errors < 0) SendOrCollectDebugOutput(comparer.GetError() + "\n"); - if (errors > 0) - SendOrCollectDebugOutput(StringFromFormat("Screenshot error: %f%%\n", errors * 100.0f)); + if (errors > maxScreenshotError_) + SendOrCollectDebugOutput(StringFromFormat("Screenshot MSE: %f\n", errors)); - if (errors > 0 && !teamCityMode && !getenv("GITHUB_ACTIONS")) { + if (errors > maxScreenshotError_ && writeFailureScreenshot_) { if (comparer.SaveActualBitmap(Path("__testfailure.bmp"))) SendOrCollectDebugOutput("Actual output written to: __testfailure.bmp\n"); comparer.SaveVisualComparisonPNG(Path("__testcompare.png")); diff --git a/headless/StubHost.h b/headless/StubHost.h index c914f025b2..a77a42424d 100644 --- a/headless/StubHost.h +++ b/headless/StubHost.h @@ -68,8 +68,12 @@ public: } } - virtual void SetComparisonScreenshot(const Path &filename) { + void SetComparisonScreenshot(const Path &filename, double maxError) { comparisonScreenshot_ = filename; + maxScreenshotError_ = maxError; + } + void SetWriteFailureScreenshot(bool flag) { + writeFailureScreenshot_ = flag; } void SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) override; @@ -83,7 +87,9 @@ protected: void SendOrCollectDebugOutput(const std::string &output); Path comparisonScreenshot_; + double maxScreenshotError_ = 0.0; std::string debugOutputBuffer_; GPUCore gpuCore_; GraphicsContext *gfx_ = nullptr; + bool writeFailureScreenshot_ = true; }; diff --git a/libretro/LibretroVulkanContext.cpp b/libretro/LibretroVulkanContext.cpp index 571b7bba9d..ff9180cecf 100644 --- a/libretro/LibretroVulkanContext.cpp +++ b/libretro/LibretroVulkanContext.cpp @@ -137,7 +137,7 @@ void LibretroVulkanContext::CreateDrawContext() { return; } - draw_ = Draw::T3DCreateVulkanContext(vk, false); + draw_ = Draw::T3DCreateVulkanContext(vk); ((VulkanRenderManager*)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER))->SetInflightFrames(g_Config.iInflightFrames); SetGPUBackend(GPUBackend::VULKAN); } diff --git a/libretro/Makefile b/libretro/Makefile index 245d6d2267..088ad22267 100644 --- a/libretro/Makefile +++ b/libretro/Makefile @@ -332,7 +332,7 @@ else ifneq (,$(findstring windows_msvc2017,$(platform))) endif CFLAGS += $(MSVC2017CompileFlags) -nologo - CXXFLAGS += $(MSVC2017CompileFlags) -nologo -EHsc + CXXFLAGS += $(MSVC2017CompileFlags) -nologo -EHsc -DARMIPS_USE_STD_FILESYSTEM TargetArchMoniker = $(subst $(WinPartition)_,,$(PlatformSuffix)) @@ -443,7 +443,7 @@ else ifneq (,$(findstring windows_msvc2019,$(platform))) endif CFLAGS += $(MSVC2019CompileFlags) -nologo - CXXFLAGS += $(MSVC2019CompileFlags) -nologo -EHsc + CXXFLAGS += $(MSVC2019CompileFlags) -nologo -EHsc -DARMIPS_USE_STD_FILESYSTEM TargetArchMoniker = $(subst $(WinPartition)_,,$(PlatformSuffix)) @@ -537,7 +537,7 @@ else ifneq (,$(findstring windows_msvc2019,$(platform))) else ifneq (,$(findstring win,$(platform))) TARGET := $(TARGET_NAME)_libretro.dll CFLAGS += -D_UNICODE -DUNICODE - CXXFLAGS += -fpermissive -Wno-multichar -D_UNICODE -DUNICODE + CXXFLAGS += -fpermissive -Wno-multichar -D_UNICODE -DUNICODE -DARMIPS_USE_STD_FILESYSTEM LDFLAGS += -shared -Wl,--no-undefined -static-libgcc -static-libstdc++ -Wl,--version-script=link.T -lwinmm -lgdi32 -lwsock32 -lws2_32 -ld3d9 -ld3dx9 GL_LIB := -lopengl32 PLATFORM_EXT = win32 @@ -577,7 +577,9 @@ else endif ifeq (,$(findstring msvc,$(platform))) - CXXFLAGS += -std=c++11 + CXXFLAGS += -std=c++17 +else + CXXFLAGS += -std:c++17 endif ### Finalize ### diff --git a/libretro/Makefile.common b/libretro/Makefile.common index ab2c4bfe1c..56c748b52a 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -25,6 +25,8 @@ INCFLAGS += \ $(FFMPEGINCFLAGS) \ -I$(EXTDIR)/cityhash \ -I$(EXTDIR)/armips \ + -I$(EXTDIR)/armips/ext/filesystem/include \ + -I$(EXTDIR)/armips/ext/tinyformat \ -I$(EXTDIR)/zstd/lib \ -I$(EXTDIR) \ -I$(NATIVEDIR) \ @@ -208,6 +210,9 @@ SOURCES_CXX += \ SOURCES_CXX += $(EXTDIR)/cityhash/city.cpp +SOURCES_CXX += \ + $(EXTDIR)/armips/Core/Types.cpp + SOURCES_CXX += \ $(COMMONDIR)/Crypto/md5.cpp \ $(COMMONDIR)/Crypto/sha1.cpp \ @@ -253,6 +258,7 @@ SOURCES_CXX += \ $(COMMONDIR)/GPU/Vulkan/thin3d_vulkan.cpp \ $(COMMONDIR)/GPU/Vulkan/VulkanQueueRunner.cpp \ $(COMMONDIR)/GPU/Vulkan/VulkanRenderManager.cpp \ + $(COMMONDIR)/GPU/Vulkan/VulkanFrameData.cpp \ $(COMMONDIR)/GPU/Vulkan/VulkanLoader.cpp \ $(COMMONDIR)/GPU/Vulkan/VulkanContext.cpp \ $(COMMONDIR)/GPU/Vulkan/VulkanDebug.cpp \ @@ -339,6 +345,7 @@ SOURCES_CXX += \ $(GPUDIR)/Debugger/Stepping.cpp \ $(GPUDIR)/Common/FragmentShaderGenerator.cpp \ $(GPUDIR)/Common/VertexShaderGenerator.cpp \ + $(GPUDIR)/Common/GeometryShaderGenerator.cpp \ $(GPUDIR)/Common/TextureCacheCommon.cpp \ $(GPUDIR)/Common/TextureScalerCommon.cpp \ $(GPUDIR)/Common/SoftwareTransformCommon.cpp \ diff --git a/ppsspp.desktop.in b/ppsspp.desktop.in index 05fd1e7348..fd8ffc35b8 100644 --- a/ppsspp.desktop.in +++ b/ppsspp.desktop.in @@ -4,4 +4,6 @@ Exec=@TargetBin@ Icon=ppsspp Type=Application Comment=ppsspp (fast and portable PSP emulator) -Categories=Game;Emulator +Keywords=Sony;PlayStation;Portable;PSP;handheld;console; +Categories=Game;Emulator; +MimeType=application/x-cd-image;application/x-iso9660-image;application/x-compressed-iso;application/zip; diff --git a/pspautotests b/pspautotests index 682a4303ab..dd16bcad59 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 682a4303aba63a50c91ae0fa6928c9dac8ca9b92 +Subproject commit dd16bcad59dbd098a66b87048184e70debb22d6f diff --git a/test.py b/test.py index 72c6302eb2..0a8896b2b7 100755 --- a/test.py +++ b/test.py @@ -147,7 +147,10 @@ tests_good = [ "gpu/commands/blend", "gpu/commands/blend565", "gpu/commands/blocktransfer", + "gpu/commands/cull", + "gpu/commands/fog", "gpu/commands/material", + "gpu/complex/complex", "gpu/displaylist/alignment", "gpu/dither/dither", "gpu/filtering/mipmaplinear", @@ -157,7 +160,12 @@ tests_good = [ "gpu/ge/enqueueparam", "gpu/ge/queue", "gpu/primitives/indices", + "gpu/primitives/invalidprim", + "gpu/primitives/points", + "gpu/primitives/trianglefan", + "gpu/primitives/trianglestrip", "gpu/primitives/triangles", + "gpu/rendertarget/copy", "gpu/rendertarget/depal", "gpu/signals/pause", "gpu/signals/pause2", @@ -176,6 +184,7 @@ tests_good = [ "gpu/texfunc/replace", "gpu/textures/mipmap", "gpu/textures/rotate", + "gpu/vertices/colors", "hash/hash", "hle/check_not_used_uids", "intr/intr", @@ -204,6 +213,7 @@ tests_good = [ "sysmem/freesize", "sysmem/memblock", "sysmem/sysmem", + "sysmem/volatile", "threads/alarm/alarm", "threads/alarm/cancel/cancel", "threads/alarm/refer/refer", @@ -269,6 +279,7 @@ tests_good = [ "threads/mutex/refer", "threads/mutex/try", "threads/mutex/unlock", + "threads/mutex/unlock2", "threads/semaphores/semaphores", "threads/semaphores/cancel", "threads/semaphores/create", @@ -380,9 +391,7 @@ tests_next = [ "font/shadowglyphimageclip", "font/shadowinfo", "gpu/clipping/guardband", - "gpu/commands/cull", "gpu/commands/light", - "gpu/complex/complex", "gpu/depth/precision", "gpu/displaylist/state", "gpu/filtering/linear", @@ -394,23 +403,23 @@ tests_next = [ "gpu/ge/get", "gpu/primitives/bezier", "gpu/primitives/continue", - "gpu/primitives/invalidprim", + "gpu/primitives/immediate", "gpu/primitives/lines", "gpu/primitives/linestrip", - "gpu/primitives/points", "gpu/primitives/rectangles", "gpu/primitives/spline", - "gpu/primitives/trianglefan", - "gpu/primitives/trianglestrip", "gpu/reflection/reflection", - "gpu/rendertarget/copy", "gpu/rendertarget/rendertarget", "gpu/signals/continue", "gpu/signals/jumps", "gpu/signals/simple", "gpu/simple/simple", + "gpu/texmtx/normals", + "gpu/texmtx/prims", + "gpu/texmtx/source", + "gpu/texmtx/uvs", + "gpu/textures/size", "gpu/triangle/triangle", - "gpu/vertices/colors", "gpu/vertices/texcoords", "intr/registersub", "intr/releasesub", diff --git a/unittest/JitHarness.cpp b/unittest/JitHarness.cpp index 6575a1e323..c73af9733b 100644 --- a/unittest/JitHarness.cpp +++ b/unittest/JitHarness.cpp @@ -150,7 +150,7 @@ bool TestJit() { for (size_t j = 0; j < ARRAY_SIZE(lines); ++j) { p++; if (!MIPSAsm::MipsAssembleOpcode(lines[j], currentDebugMIPS, addr)) { - printf("ERROR: %ls\n", MIPSAsm::GetAssembleError().c_str()); + printf("ERROR: %s\n", MIPSAsm::GetAssembleError().c_str()); compileSuccess = false; } addr += 4; diff --git a/unittest/TestShaderGenerators.cpp b/unittest/TestShaderGenerators.cpp index 8cf73cd8b4..fa5fd1eb7c 100644 --- a/unittest/TestShaderGenerators.cpp +++ b/unittest/TestShaderGenerators.cpp @@ -12,6 +12,7 @@ #include "GPU/Common/FragmentShaderGenerator.h" #include "GPU/Common/VertexShaderGenerator.h" +#include "GPU/Common/GeometryShaderGenerator.h" #include "GPU/Common/ReinterpretFramebuffer.h" #include "GPU/Common/StencilCommon.h" #include "GPU/Common/DepalettizeShaderCommon.h" @@ -24,6 +25,8 @@ #include "GPU/D3D9/D3D9ShaderCompiler.h" #endif +static constexpr size_t CODE_BUFFER_SIZE = 32768; + bool GenerateFShader(FShaderID id, char *buffer, ShaderLanguage lang, Draw::Bugs bugs, std::string *errorString) { uint64_t uniformMask; switch (lang) { @@ -39,7 +42,7 @@ bool GenerateFShader(FShaderID id, char *buffer, ShaderLanguage lang, Draw::Bugs } case ShaderLanguage::GLSL_3xx: { - ShaderLanguageDesc compat(ShaderLanguage::GLSL_1xx); + ShaderLanguageDesc compat(ShaderLanguage::GLSL_3xx); return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, nullptr, errorString); } case ShaderLanguage::HLSL_D3D9: @@ -73,7 +76,7 @@ bool GenerateVShader(VShaderID id, char *buffer, ShaderLanguage lang, Draw::Bugs } case ShaderLanguage::GLSL_3xx: { - ShaderLanguageDesc compat(ShaderLanguage::GLSL_1xx); + ShaderLanguageDesc compat(ShaderLanguage::GLSL_3xx); return GenerateVertexShader(id, buffer, compat, bugs, &attrMask, &uniformMask, errorString); } case ShaderLanguage::HLSL_D3D9: @@ -91,18 +94,67 @@ bool GenerateVShader(VShaderID id, char *buffer, ShaderLanguage lang, Draw::Bugs } } +bool GenerateGShader(GShaderID id, char *buffer, ShaderLanguage lang, Draw::Bugs bugs, std::string *errorString) { + errorString->clear(); + + switch (lang) { + case ShaderLanguage::GLSL_VULKAN: + { + ShaderLanguageDesc compat(ShaderLanguage::GLSL_VULKAN); + return GenerateGeometryShader(id, buffer, compat, bugs, errorString); + } + /* + case ShaderLanguage::GLSL_3xx: + { + ShaderLanguageDesc compat(ShaderLanguage::GLSL_3xx); + return GenerateGeometryShader(id, buffer, compat, bugs, errorString); + } + case ShaderLanguage::HLSL_D3D11: + { + ShaderLanguageDesc compat(ShaderLanguage::HLSL_D3D11); + return GenerateGeometryShader(id, buffer, compat, bugs, errorString); + } + */ + default: + return false; + } +} + +static VkShaderStageFlagBits StageToVulkan(ShaderStage stage) { + switch (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; + case ShaderStage::Fragment: return VK_SHADER_STAGE_FRAGMENT_BIT; + } + return VK_SHADER_STAGE_FRAGMENT_BIT; +} + bool TestCompileShader(const char *buffer, ShaderLanguage lang, ShaderStage stage, std::string *errorMessage) { std::vector spirv; switch (lang) { #if PPSSPP_PLATFORM(WINDOWS) case ShaderLanguage::HLSL_D3D11: { - auto output = CompileShaderToBytecodeD3D11(buffer, strlen(buffer), stage == ShaderStage::Vertex ? "vs_4_0" : "ps_4_0", 0); + const char *programType = nullptr; + switch (stage) { + case ShaderStage::Vertex: programType = "vs_4_0"; break; + case ShaderStage::Fragment: programType = "ps_4_0"; break; + case ShaderStage::Geometry: programType = "gs_4_0"; break; + default: return false; + } + auto output = CompileShaderToBytecodeD3D11(buffer, strlen(buffer), programType, 0); return !output.empty(); } case ShaderLanguage::HLSL_D3D9: { - LPD3DBLOB blob = CompileShaderToByteCodeD3D9(buffer, stage == ShaderStage::Vertex ? "vs_3_0" : "ps_3_0", errorMessage); + const char *programType = nullptr; + switch (stage) { + case ShaderStage::Vertex: programType = "vs_3_0"; break; + case ShaderStage::Fragment: programType = "ps_3_0"; break; + default: return false; + } + LPD3DBLOB blob = CompileShaderToByteCodeD3D9(buffer, programType, errorMessage); if (blob) { blob->Release(); return true; @@ -113,11 +165,11 @@ bool TestCompileShader(const char *buffer, ShaderLanguage lang, ShaderStage stag #endif case ShaderLanguage::GLSL_VULKAN: - return GLSLtoSPV(stage == ShaderStage::Vertex ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT, buffer, GLSLVariant::VULKAN, spirv, errorMessage); + return GLSLtoSPV(StageToVulkan(stage), buffer, GLSLVariant::VULKAN, spirv, errorMessage); case ShaderLanguage::GLSL_1xx: - return GLSLtoSPV(stage == ShaderStage::Vertex ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT, buffer, GLSLVariant::GL140, spirv, errorMessage); + return GLSLtoSPV(StageToVulkan(stage), buffer, GLSLVariant::GL140, spirv, errorMessage); case ShaderLanguage::GLSL_3xx: - return GLSLtoSPV(stage == ShaderStage::Vertex ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT, buffer, GLSLVariant::GLES300, spirv, errorMessage); + return GLSLtoSPV(StageToVulkan(stage), buffer, GLSLVariant::GLES300, spirv, errorMessage); default: return false; } @@ -193,10 +245,13 @@ bool TestReinterpretShaders() { continue; // useless shader! ShaderWriter writer(buffer, desc, ShaderStage::Fragment, nullptr, 0); GenerateReinterpretFragmentShader(writer, fmts[i], fmts[j]); + if (strlen(buffer) >= 8192) { + printf("Reinterpret fragment shader %d exceeded buffer:\n\n%s\n", (int)j, LineNumberString(buffer).c_str()); + failed = true; + } if (!TestCompileShader(buffer, languages[k], ShaderStage::Fragment, &errorMessage)) { - printf("Error compiling fragment shader %d:\n\n%s\n\n%s\n", (int)j, LineNumberString(buffer).c_str(), errorMessage.c_str()); + printf("Error compiling reinterpret fragment shader %d:\n\n%s\n\n%s\n", (int)j, LineNumberString(buffer).c_str(), errorMessage.c_str()); failed = true; - return false; } else { printf("===\n%s\n===\n", buffer); } @@ -232,19 +287,25 @@ bool TestStencilShaders() { // Generate all despite failures - it's only 6. GenerateStencilFs(buffer, desc, bugs); + if (strlen(buffer) >= 8192) { + printf("Stencil fragment shader exceeded buffer:\n\n%s\n", LineNumberString(buffer).c_str()); + failed = true; + } if (!TestCompileShader(buffer, languages[k], ShaderStage::Fragment, &errorMessage)) { printf("Error compiling stencil shader:\n\n%s\n\n%s\n", LineNumberString(buffer).c_str(), errorMessage.c_str()); failed = true; - return false; } else { printf("===\n%s\n===\n", buffer); } GenerateStencilVs(buffer, desc); + if (strlen(buffer) >= 8192) { + printf("Stencil vertex shader exceeded buffer:\n\n%s\n", LineNumberString(buffer).c_str()); + failed = true; + } if (!TestCompileShader(buffer, languages[k], ShaderStage::Vertex, &errorMessage)) { printf("Error compiling stencil shader:\n\n%s\n\n%s\n", LineNumberString(buffer).c_str(), errorMessage.c_str()); failed = true; - return false; } else { printf("===\n%s\n===\n", buffer); } @@ -269,8 +330,6 @@ bool TestDepalShaders() { char *buffer = new char[65536]; - bool failed = false; - for (int k = 0; k < ARRAY_SIZE(languages); k++) { printf("=== %s ===\n\n", ShaderLanguageToString(languages[k])); @@ -290,9 +349,14 @@ bool TestDepalShaders() { ShaderWriter writer(buffer, desc, ShaderStage::Fragment); GenerateDepalFs(writer, config); + if (strlen(buffer) >= 8192) { + printf("Depal shader exceeded buffer:\n\n%s\n", LineNumberString(buffer).c_str()); + delete[] buffer; + return false; + } if (!TestCompileShader(buffer, languages[k], ShaderStage::Fragment, &errorMessage)) { printf("Error compiling depal shader:\n\n%s\n\n%s\n", LineNumberString(buffer).c_str(), errorMessage.c_str()); - failed = true; + delete[] buffer; return false; } else { printf("===\n%s\n===\n", buffer); @@ -300,7 +364,7 @@ bool TestDepalShaders() { } delete[] buffer; - return !failed; + return true; } const ShaderLanguage languages[] = { @@ -346,6 +410,9 @@ bool TestVertexShaders() { if (!id.Bit(VS_BIT_USE_HW_TRANSFORM)) { id.SetBit(VS_BIT_ENABLE_BONES, 0); } + if (id.Bit(VS_BIT_VERTEX_RANGE_CULLING)) { + continue; + } bool generateSuccess[numLanguages]{}; std::string genErrorString[numLanguages]; @@ -357,6 +424,16 @@ bool TestVertexShaders() { } } + for (int j = 0; j < numLanguages; j++) { + if (strlen(buffer[j]) >= CODE_BUFFER_SIZE) { + printf("Vertex shader exceeded buffer:\n\n%s\n", LineNumberString(buffer[j]).c_str()); + for (int i = 0; i < numLanguages; i++) { + delete[] buffer[i]; + } + return false; + } + } + // Now that we have the strings ready for easy comparison (buffer,4 in the watch window), // let's try to compile them. for (int j = 0; j < numLanguages; j++) { @@ -364,6 +441,9 @@ bool TestVertexShaders() { std::string errorMessage; if (!TestCompileShader(buffer[j], languages[j], ShaderStage::Vertex, &errorMessage)) { printf("Error compiling vertex shader %d:\n\n%s\n\n%s\n", (int)j, LineNumberString(buffer[j]).c_str(), errorMessage.c_str()); + for (int i = 0; i < numLanguages; i++) { + delete[] buffer[i]; + } return false; } successes++; @@ -418,6 +498,16 @@ bool TestFragmentShaders() { // We ignore the contents of the error string here, not even gonna try to compile if it errors. } + for (int j = 0; j < numLanguages; j++) { + if (strlen(buffer[j]) >= CODE_BUFFER_SIZE) { + printf("Fragment shader exceeded buffer:\n\n%s\n", LineNumberString(buffer[j]).c_str()); + for (int i = 0; i < numLanguages; i++) { + delete[] buffer[i]; + } + return false; + } + } + // Now that we have the strings ready for easy comparison (buffer,4 in the watch window), // let's try to compile them. for (int j = 0; j < numLanguages; j++) { @@ -425,6 +515,9 @@ bool TestFragmentShaders() { std::string errorMessage; if (!TestCompileShader(buffer[j], languages[j], ShaderStage::Fragment, &errorMessage)) { printf("Error compiling fragment shader:\n\n%s\n\n%s\n", LineNumberString(buffer[j]).c_str(), errorMessage.c_str()); + for (int i = 0; i < numLanguages; i++) { + delete[] buffer[i]; + } return false; } successes++; @@ -440,6 +533,76 @@ bool TestFragmentShaders() { return true; } + +bool TestGeometryShaders() { + char *buffer[numLanguages]; + + for (int i = 0; i < numLanguages; i++) { + buffer[i] = new char[65536]; + } + GMRng rng; + int successes = 0; + int count = 30; + + Draw::Bugs bugs; + + // Generate a bunch of random fragment shader IDs, try to generate shader source. + // Then compile it and check that it's ok. + for (int i = 0; i < count; i++) { + uint32_t bottom = i << 1; + GShaderID id; + id.d[0] = bottom; + id.d[1] = 0; + + id.SetBit(GS_BIT_ENABLED, true); + + bool generateSuccess[numLanguages]{}; + std::string genErrorString[numLanguages]; + + for (int j = 0; j < numLanguages; j++) { + generateSuccess[j] = GenerateGShader(id, buffer[j], languages[j], bugs, &genErrorString[j]); + if (!genErrorString[j].empty()) { + printf("%s\n", genErrorString[j].c_str()); + } + // We ignore the contents of the error string here, not even gonna try to compile if it errors. + } + + for (int j = 0; j < numLanguages; j++) { + if (strlen(buffer[j]) >= CODE_BUFFER_SIZE) { + printf("Geoemtry shader exceeded buffer:\n\n%s\n", LineNumberString(buffer[j]).c_str()); + for (int i = 0; i < numLanguages; i++) { + delete[] buffer[i]; + } + return false; + } + } + + // Now that we have the strings ready for easy comparison (buffer,4 in the watch window), + // let's try to compile them. + for (int j = 0; j < numLanguages; j++) { + if (generateSuccess[j]) { + std::string errorMessage; + if (!TestCompileShader(buffer[j], languages[j], ShaderStage::Geometry, &errorMessage)) { + printf("Error compiling geometry shader:\n\n%s\n\n%s\n", LineNumberString(buffer[j]).c_str(), errorMessage.c_str()); + for (int i = 0; i < numLanguages; i++) { + delete[] buffer[i]; + } + return false; + } + successes++; + } + } + } + + printf("%d/%d geometry shaders generated (it's normal that it's not all, there are invalid bit combos)\n", successes, count * numLanguages); + + for (int i = 0; i < numLanguages; i++) { + delete[] buffer[i]; + } + return true; +} + + bool TestShaderGenerators() { #if PPSSPP_PLATFORM(WINDOWS) LoadD3D11(); @@ -453,6 +616,10 @@ bool TestShaderGenerators() { return false; } + if (!TestGeometryShaders()) { + return false; + } + if (!TestReinterpretShaders()) { return false; } diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index 4f8239a11b..bb3d9d112b 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -634,7 +634,7 @@ static bool TestPath() { Path path3 = path2 / "foo/bar"; EXPECT_EQ_STR(path3.WithExtraExtension(".txt").ToString(), std::string("/asdf/jkl/foo/bar.txt")); - EXPECT_EQ_STR(Path("foo.bar/hello").GetFileExtension(), std::string("")); + EXPECT_EQ_STR(Path("foo.bar/hello").GetFileExtension(), std::string()); EXPECT_EQ_STR(Path("foo.bar/hello.txt").WithReplacedExtension(".txt", ".html").ToString(), std::string("foo.bar/hello.html")); EXPECT_EQ_STR(Path("C:\\Yo").NavigateUp().ToString(), std::string("C:"));