Merge branch 'master' into Font-ltn12-hack

This commit is contained in:
sum2012 authored and GitHub committed 2022-12-15 20:06:33 +08:00
commit 1386aad04a
584 files changed
+24063 -11166

No files matched your search

+41 -25
View File
@@ -34,12 +34,12 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Add MSBuild to PATH
uses: microsoft/setup-msbuild@v1
uses: microsoft/setup-msbuild@v1.1
- name: Build Windows
working-directory: ${{ env.GITHUB_WORKSPACE }}
@@ -63,20 +63,20 @@ jobs:
run: cp Windows/Release/*.exe ppsspp/
- name: Upload build
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v3
with:
name: Windows ${{ matrix.platform }} build
path: ppsspp/
build-uwp:
runs-on: windows-latest
runs-on: windows-2019
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Add MSBuild to PATH
uses: microsoft/setup-msbuild@v1
uses: microsoft/setup-msbuild@v1.1
- name: Build UWP
working-directory: ${{ env.GITHUB_WORKSPACE }}
@@ -87,7 +87,7 @@ jobs:
needs: build-windows
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: false
@@ -97,7 +97,7 @@ jobs:
run: git submodule update --init pspautotests assets/lang
- name: Download build
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: Windows x64 build
path: ppsspp/
@@ -151,6 +151,18 @@ jobs:
cxx: clang++
args: cd android && ./ab.sh -j2 APP_ABI=x86_64 UNITTEST=1 HEADLESS=1
id: android-x86_64
- os: ubuntu-latest
extra: android
cc: clang
cxx: clang++
args: cd android && ./ab.sh -j2 APP_ABI=arm64-v8a OPENXR=1 OPENXR_PLATFORM_QUEST=1
id: android-vr-quest
- os: ubuntu-latest
extra: android
cc: clang
cxx: clang++
args: cd android && ./ab.sh -j2 APP_ABI=arm64-v8a OPENXR=1 OPENXR_PLATFORM_PICO=1
id: android-vr-pico
- os: ubuntu-latest
extra: android
cc: clang
@@ -168,13 +180,13 @@ jobs:
extra: libretro
cc: gcc
cxx: g++
args: ./b.sh --libretro
args: make -C libretro -f Makefile -j2
id: gcc-libretro
- os: ubuntu-latest
extra: libretro
cc: clang
cxx: clang++
args: ./b.sh --libretro
args: make -C libretro -f Makefile -j2
id: clang-libretro
- os: macos-latest
@@ -192,7 +204,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: recursive
@@ -202,19 +214,11 @@ jobs:
run: |
git fetch --deepen=15000 --no-recurse-submodules --tags || exit 0
- name: Cache Qt
uses: actions/cache@v1
if: matrix.extra == 'qt'
id: cache-qt
with:
path: ${{ runner.workspace }}/Qt
key: ${{ runner.os }}-QtCache
- name: Install Qt
uses: jurplel/install-qt-action@v2
uses: jurplel/install-qt-action@v3
if: matrix.extra == 'qt'
with:
cached: ${{ steps.cache-qt.outputs.cache-hit }}
cache: true
- uses: nttld/setup-ndk@v1
if: matrix.extra == 'android'
@@ -236,7 +240,7 @@ jobs:
echo "#define PPSSPP_GIT_VERSION_NO_UPDATE 1" >> git-version.cpp
- name: Setup ccache
uses: hendrikmuhs/ccache-action@v1
uses: hendrikmuhs/ccache-action@v1.2
with:
key: ${{ matrix.id }}
@@ -246,8 +250,20 @@ jobs:
CXX: ${{ matrix.cxx }}
NDK: ${{ steps.setup-ndk.outputs.ndk-path }}
NDK_CCACHE: ccache
USE_CCACHE: 1
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
export CCACHE_SLOPPINESS=clang_index_store,ivfsoverlay,include_file_ctime,include_file_mtime,modules,system_headers,time_macros
if [ "${{ matrix.extra }}" == "android" ]; then
export CCACHE_FILECLONE=true
export CCACHE_DEPEND=true
export CCACHE_COMPILERCHECK=content
elif [ "$RUNNER_OS" == "macOS" ]; then
export CCACHE_SLOPPINESS=pch_defines,$CCACHE_SLOPPINESS
export CCACHE_FILECLONE=true
export CCACHE_DEPEND=true
export CCACHE_COMPILERCHECK=content
fi
${{ matrix.args }}
- name: Package build
@@ -279,7 +295,7 @@ jobs:
fi
- name: Upload build
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v3
if: matrix.extra == 'test'
with:
name: ${{ matrix.os }} build
@@ -313,7 +329,7 @@ jobs:
needs: build
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: false
@@ -335,7 +351,7 @@ jobs:
git submodule update --init SDL/macOS
- name: Download build
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: ${{ matrix.os }} build
path: ppsspp/
+4 -4
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Extract DOCKER_TAG using tag name
if: startsWith(github.ref, 'refs/tags/')
@@ -25,13 +25,13 @@ jobs:
echo "DOCKER_TAG=latest" >> $GITHUB_ENV
- name: Login to Github registry
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
registry: ghcr.io
- uses: docker/build-push-action@v2
- uses: docker/build-push-action@v3
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ env.DOCKER_TAG }}
tags: ghcr.io/${{ github.repository }}:${{ env.DOCKER_TAG }}
+4 -3
View File
@@ -19,14 +19,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: recursive
- name: Setup JDK
uses: actions/setup-java@v1
uses: actions/setup-java@v2
with:
distribution: 'zulu'
java-version: '11'
#- name: Setup SDK
@@ -54,7 +55,7 @@ jobs:
fi
- name: Upload APK
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v3
with:
name: android-${{ github.event.inputs.buildVariant }} build
path: ppsspp/
+2 -2
View File
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: recursive
@@ -40,7 +40,7 @@ jobs:
sed -i "s;unknown;${VERSION};" $PKGNAME/git-version.cmake || true
TARBALL=$PKGNAME.tar.xz
tar cJf $TARBALL $PKGNAME
echo "::set-output name=tarball::$TARBALL"
echo "tarball=$TARBALL" >> $GITHUB_OUTPUT
- name: upload tarball
uses: softprops/action-gh-release@v1
+37 -27
View File
@@ -119,7 +119,6 @@ add_definitions(-DASSETS_DIR="${CMAKE_INSTALL_FULL_DATADIR}/ppsspp/assets/")
if(OPENXR)
add_definitions(-DOPENXR)
add_library(openxr SHARED IMPORTED)
include_directories(ext/openxr)
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")
@@ -507,6 +506,24 @@ if(WIN32)
)
endif()
set(CommonVR
Common/VR/OpenXRLoader.cpp
Common/VR/OpenXRLoader.h
Common/VR/PPSSPPVR.cpp
Common/VR/PPSSPPVR.h
Common/VR/VRBase.cpp
Common/VR/VRBase.h
Common/VR/VRFramebuffer.cpp
Common/VR/VRFramebuffer.h
Common/VR/VRInput.cpp
Common/VR/VRInput.h
Common/VR/VRMath.cpp
Common/VR/VRMath.h
Common/VR/VRRenderer.cpp
Common/VR/VRRenderer.h
)
include_directories(ext/openxr)
add_library(Common STATIC
${CommonX86}
${CommonARM}
@@ -514,6 +531,7 @@ add_library(Common STATIC
${CommonMIPS}
${CommonRISCV64}
${CommonD3D}
${CommonVR}
Common/Serialize/Serializer.cpp
Common/Serialize/Serializer.h
Common/Serialize/SerializeDeque.h
@@ -588,6 +606,7 @@ add_library(Common STATIC
Common/File/FileDescriptor.cpp
Common/File/FileDescriptor.h
Common/GPU/DataFormat.h
Common/GPU/MiscTypes.h
Common/GPU/thin3d.cpp
Common/GPU/thin3d.h
Common/GPU/thin3d_create.h
@@ -619,6 +638,8 @@ add_library(Common STATIC
Common/GPU/Vulkan/VulkanDebug.h
Common/GPU/Vulkan/VulkanContext.cpp
Common/GPU/Vulkan/VulkanContext.h
Common/GPU/Vulkan/VulkanFramebuffer.cpp
Common/GPU/Vulkan/VulkanFramebuffer.h
Common/GPU/Vulkan/VulkanImage.cpp
Common/GPU/Vulkan/VulkanImage.h
Common/GPU/Vulkan/VulkanLoader.cpp
@@ -674,6 +695,8 @@ add_library(Common STATIC
Common/Render/TextureAtlas.h
Common/Render/DrawBuffer.cpp
Common/Render/DrawBuffer.h
Common/Render/ManagedTexture.cpp
Common/Render/ManagedTexture.h
Common/Render/Text/draw_text.cpp
Common/Render/Text/draw_text.h
Common/Render/Text/draw_text_android.cpp
@@ -692,6 +715,8 @@ add_library(Common STATIC
Common/Thread/ThreadUtil.h
Common/Thread/ThreadManager.cpp
Common/Thread/ThreadManager.h
Common/UI/AsyncImageFileView.cpp
Common/UI/AsyncImageFileView.h
Common/UI/Root.cpp
Common/UI/Root.h
Common/UI/Screen.cpp
@@ -859,7 +884,7 @@ add_library(cityhash STATIC
ext/cityhash/city.h
ext/cityhash/citycrc.h
)
include_directories(ext/cityhash)
target_include_directories(cityhash PRIVATE ext/cityhash)
if(NOT MSVC)
# These can be fast even for debug.
@@ -1091,24 +1116,7 @@ if(ANDROID)
android/jni/OpenSLContext.cpp
android/jni/OpenSLContext.h
)
if (OPENXR)
set(nativeExtra ${nativeExtra}
Common/VR/PPSSPPVR.cpp
Common/VR/PPSSPPVR.h
Common/VR/VRBase.cpp
Common/VR/VRBase.h
Common/VR/VRFramebuffer.cpp
Common/VR/VRFramebuffer.h
Common/VR/VRInput.cpp
Common/VR/VRInput.h
Common/VR/VRMath.cpp
Common/VR/VRMath.h
Common/VR/VRRenderer.cpp
Common/VR/VRRenderer.h
Common/VR/VRTweaks.cpp
Common/VR/VRTweaks.h
)
set(nativeExtraLibs ${nativeExtraLibs} openxr)
endif()
# No target
@@ -1242,8 +1250,6 @@ list(APPEND NativeAppSource
UI/ChatScreen.cpp
UI/DevScreens.cpp
UI/DevScreens.h
UI/DisplayLayoutEditor.cpp
UI/DisplayLayoutEditor.h
UI/DisplayLayoutScreen.cpp
UI/DisplayLayoutScreen.h
UI/EmuScreen.h
@@ -1292,8 +1298,6 @@ list(APPEND NativeAppSource
UI/MemStickScreen.cpp
UI/ProfilerDraw.h
UI/ProfilerDraw.cpp
UI/TextureUtil.h
UI/TextureUtil.cpp
UI/ComboKeyMappingScreen.h
UI/ComboKeyMappingScreen.cpp
UI/Theme.h
@@ -1312,6 +1316,10 @@ if(ANDROID)
endif()
endif()
if (IOS)
set(nativeExtra ${nativeExtra} ${NativeAppSource})
endif()
add_library(native STATIC
${nativeExtra}
Common/Render/Text/draw_text_qt.cpp
@@ -1356,7 +1364,7 @@ add_library(kirk STATIC
ext/libkirk/kirk_engine.c
ext/libkirk/kirk_engine.h
)
include_directories(ext/libkirk)
target_include_directories(kirk PRIVATE ext/libkirk)
add_library(sfmt19937 STATIC
ext/sfmt19937/SFMT.c
@@ -1365,19 +1373,20 @@ add_library(sfmt19937 STATIC
ext/sfmt19937/SFMT-params.h
ext/sfmt19937/SFMT-params19937.h
)
include_directories(ext/sfmt19937)
target_compile_definitions(sfmt19937 PRIVATE SFMT_MEXP=19937)
target_include_directories(sfmt19937 PRIVATE ext/sfmt19937)
add_library(xbrz STATIC
ext/xbrz/xbrz.cpp
ext/xbrz/xbrz.h
)
include_directories(ext/xbrz)
target_include_directories(xbrz PRIVATE ext/xbrz)
add_library(xxhash STATIC
ext/xxhash.c
ext/xxhash.h
)
include_directories(ext/xxhash)
target_include_directories(xxhash PRIVATE ext/xxhash)
set(CoreExtra)
set(CoreExtraLibs)
@@ -2063,6 +2072,7 @@ if(USE_SYSTEM_ZSTD)
target_link_libraries(${CoreLibName} ${ZSTD_LIBRARY})
else()
set(ZSTD_BUILD_PROGRAMS OFF CACHE BOOL "we don't need zstd programs" FORCE)
set(ZSTD_LEGACY_SUPPORT OFF CACHE BOOL "we don't use any old zstd files" FORCE)
add_subdirectory(ext/zstd/build/cmake EXCLUDE_FROM_ALL)
set(CoreExtraLibs ${CoreExtraLibs} libzstd_static)
include_directories(ext/zstd/lib)
+1 -1
View File
@@ -253,7 +253,7 @@ public:
private:
ARM64Reg m_destReg;
WidthSpecifier m_width;
ExtendSpecifier m_extend;
ExtendSpecifier m_extend = EXTEND_UXTB;
TypeSpecifier m_type;
ShiftType m_shifttype;
u32 m_shift;
+13 -16
View File
@@ -112,32 +112,29 @@ private:
OpType Type;
// IMM types
u8 Rotation; // Only for u8 values
u8 Rotation = 0; // Only for u8 values
// Register types
u8 IndexOrShift;
ShiftType Shift;
u8 IndexOrShift = 0;
ShiftType Shift = ST_LSL;
public:
OpType GetType() const
{
OpType GetType() const {
return Type;
}
Operand2() {}
Operand2(u32 imm, OpType type = TYPE_IMM)
{
Type = type;
Value = imm;
Rotation = 0;
Operand2() {
Type = TYPE_IMM;
Value = 0;
}
Operand2(u32 imm, OpType type = TYPE_IMM) {
Type = type;
Value = imm;
}
Operand2(ARMReg Reg)
{
Operand2(ARMReg Reg) {
Type = TYPE_REG;
Value = Reg;
Rotation = 0;
}
Operand2(u8 imm, u8 rotation)
{
Operand2(u8 imm, u8 rotation) {
Type = TYPE_IMM;
Value = imm;
Rotation = rotation;
+3 -1
View File
@@ -67,7 +67,9 @@ int Buffer::TakeLineCRLF(std::string *dest) {
if (after_next_line < 0) {
return after_next_line;
} else {
Take(after_next_line - 2, dest);
_dbg_assert_(after_next_line >= 2);
if (after_next_line != 2)
Take((size_t)after_next_line - 2, dest);
Skip(2); // Skip the CRLF
return after_next_line - 2;
}
+4 -1
View File
@@ -321,7 +321,10 @@ void CPUInfo::Detect() {
#if PPSSPP_PLATFORM(WINDOWS)
#if !PPSSPP_PLATFORM(UWP)
typedef BOOL (WINAPI *getLogicalProcessorInformationEx_f)(LOGICAL_PROCESSOR_RELATIONSHIP RelationshipType, PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer, PDWORD ReturnedLength);
auto getLogicalProcessorInformationEx = (getLogicalProcessorInformationEx_f)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "GetLogicalProcessorInformationEx");
getLogicalProcessorInformationEx_f getLogicalProcessorInformationEx = nullptr;
HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
if (kernel32)
getLogicalProcessorInformationEx = (getLogicalProcessorInformationEx_f)GetProcAddress(kernel32, "GetLogicalProcessorInformationEx");
#else
void *getLogicalProcessorInformationEx = nullptr;
#endif
+29 -3
View File
@@ -57,7 +57,10 @@ private:
public:
CodeBlock() {}
virtual ~CodeBlock() { if (region) FreeCodeSpace(); }
~CodeBlock() {
if (region)
FreeCodeSpace();
}
// Call this before you generate any code.
void AllocCodeSpace(int size) {
@@ -80,7 +83,7 @@ public:
// If not WX Exclusive, no need to call ProtectMemoryPages because we never change the protection from RWX.
PoisonMemory(offset);
ResetCodePtr(offset);
if (PlatformIsWXExclusive()) {
if (PlatformIsWXExclusive() && offset != 0) {
// Need to re-protect the part we didn't clear.
ProtectMemoryPages(region, offset, MEM_PROT_READ | MEM_PROT_EXEC);
}
@@ -95,15 +98,37 @@ public:
// In case the last block made the current page exec/no-write, let's fix that.
if (PlatformIsWXExclusive()) {
writeStart_ = GetCodePtr();
if (writeStart_ + sizeEstimate - region > (ptrdiff_t)region_size)
sizeEstimate = region_size - (writeStart_ - region);
writeEstimated_ = sizeEstimate;
ProtectMemoryPages(writeStart_, sizeEstimate, MEM_PROT_READ | MEM_PROT_WRITE);
}
}
// In case you now know your original estimate is wrong.
void ContinueWrite(size_t sizeEstimate = 1) {
_dbg_assert_msg_(writeStart_, "Must have already called BeginWrite()");
if (PlatformIsWXExclusive()) {
const uint8_t *pos = GetCodePtr();
if (pos + sizeEstimate - region > (ptrdiff_t)region_size)
sizeEstimate = region_size - (pos - region);
writeEstimated_ = pos - writeStart_ + sizeEstimate;
ProtectMemoryPages(pos, sizeEstimate, MEM_PROT_READ | MEM_PROT_WRITE);
}
}
void EndWrite() {
// OK, we're done. Re-protect the memory we touched.
if (PlatformIsWXExclusive() && writeStart_ != nullptr) {
const uint8_t *end = GetCodePtr();
ProtectMemoryPages(writeStart_, end - writeStart_, MEM_PROT_READ | MEM_PROT_EXEC);
size_t sz = end - writeStart_;
if (sz > writeEstimated_)
WARN_LOG(JIT, "EndWrite(): Estimated %d bytes, wrote %d", (int)writeEstimated_, (int)sz);
// If we protected and wrote less, we may need to unprotect.
// Especially if we're linking blocks or similar.
if (sz < writeEstimated_)
sz = writeEstimated_;
ProtectMemoryPages(writeStart_, sz, MEM_PROT_READ | MEM_PROT_EXEC);
writeStart_ = nullptr;
}
}
@@ -150,5 +175,6 @@ private:
// Note: this is a readable pointer.
const uint8_t *writeStart_ = nullptr;
uint8_t *writableRegion = nullptr;
size_t writeEstimated_ = 0;
};
+29 -8
View File
@@ -136,7 +136,7 @@
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ForcedIncludeFiles>Common/DbgNew.h</ForcedIncludeFiles>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalIncludeDirectories>../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../ext/openxr;../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib</AdditionalIncludeDirectories>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -162,7 +162,7 @@
<ForcedIncludeFiles>Common/DbgNew.h</ForcedIncludeFiles>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../ext/openxr;../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib</AdditionalIncludeDirectories>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<OmitFramePointers>false</OmitFramePointers>
@@ -189,7 +189,7 @@
<ForcedIncludeFiles>Common/DbgNew.h</ForcedIncludeFiles>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../ext/openxr;../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib</AdditionalIncludeDirectories>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<OmitFramePointers>false</OmitFramePointers>
@@ -217,7 +217,7 @@
</ForcedIncludeFiles>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../ext/openxr;../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib</AdditionalIncludeDirectories>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<OmitFramePointers>false</OmitFramePointers>
@@ -250,7 +250,7 @@
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<AdditionalIncludeDirectories>../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../ext/openxr;../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib</AdditionalIncludeDirectories>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
@@ -280,7 +280,7 @@
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_M_X64=1;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ForcedIncludeFiles>
</ForcedIncludeFiles>
<AdditionalIncludeDirectories>../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../ext/openxr;../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
@@ -315,7 +315,7 @@
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ForcedIncludeFiles>
</ForcedIncludeFiles>
<AdditionalIncludeDirectories>../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../ext/openxr;../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
@@ -350,7 +350,7 @@
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ForcedIncludeFiles>
</ForcedIncludeFiles>
<AdditionalIncludeDirectories>../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../ext/openxr;../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
@@ -432,6 +432,7 @@
<ClInclude Include="GPU\D3D9\D3D9ShaderCompiler.h" />
<ClInclude Include="GPU\D3D9\D3D9StateCache.h" />
<ClInclude Include="GPU\DataFormat.h" />
<ClInclude Include="GPU\MiscTypes.h" />
<ClInclude Include="GPU\OpenGL\DataFormatGL.h" />
<ClInclude Include="GPU\OpenGL\gl3stub.h" />
<ClInclude Include="GPU\OpenGL\GLFeatures.h" />
@@ -449,6 +450,7 @@
<ClInclude Include="GPU\Vulkan\VulkanBarrier.h" />
<ClInclude Include="GPU\Vulkan\VulkanContext.h" />
<ClInclude Include="GPU\Vulkan\VulkanDebug.h" />
<ClInclude Include="GPU\Vulkan\VulkanFramebuffer.h" />
<ClInclude Include="GPU\Vulkan\VulkanFrameData.h" />
<ClInclude Include="GPU\Vulkan\VulkanImage.h" />
<ClInclude Include="GPU\Vulkan\VulkanLoader.h" />
@@ -477,6 +479,7 @@
<ClInclude Include="Net\WebsocketServer.h" />
<ClInclude Include="Profiler\Profiler.h" />
<ClInclude Include="Render\DrawBuffer.h" />
<ClInclude Include="Render\ManagedTexture.h" />
<ClInclude Include="Render\TextureAtlas.h" />
<ClInclude Include="Render\Text\draw_text.h" />
<ClInclude Include="Render\Text\draw_text_android.h" />
@@ -549,6 +552,7 @@
<ClInclude Include="Thread\ThreadUtil.h" />
<ClInclude Include="Thunk.h" />
<ClInclude Include="TimeUtil.h" />
<ClInclude Include="UI\AsyncImageFileView.h" />
<ClInclude Include="UI\Context.h" />
<ClInclude Include="UI\Root.h" />
<ClInclude Include="UI\Screen.h" />
@@ -557,6 +561,13 @@
<ClInclude Include="UI\UIScreen.h" />
<ClInclude Include="UI\View.h" />
<ClInclude Include="UI\ViewGroup.h" />
<ClInclude Include="VR\OpenXRLoader.h" />
<ClInclude Include="VR\PPSSPPVR.h" />
<ClInclude Include="VR\VRBase.h" />
<ClInclude Include="VR\VRFramebuffer.h" />
<ClInclude Include="VR\VRInput.h" />
<ClInclude Include="VR\VRMath.h" />
<ClInclude Include="VR\VRRenderer.h" />
<ClInclude Include="x64Analyzer.h" />
<ClInclude Include="x64Emitter.h" />
</ItemGroup>
@@ -871,6 +882,7 @@
<ClCompile Include="GPU\Vulkan\VulkanBarrier.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanContext.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanDebug.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanFramebuffer.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanFrameData.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanImage.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanLoader.cpp" />
@@ -898,6 +910,7 @@
<ClCompile Include="Net\WebsocketServer.cpp" />
<ClCompile Include="Profiler\Profiler.cpp" />
<ClCompile Include="Render\DrawBuffer.cpp" />
<ClCompile Include="Render\ManagedTexture.cpp" />
<ClCompile Include="Render\TextureAtlas.cpp" />
<ClCompile Include="Render\Text\draw_text.cpp" />
<ClCompile Include="Render\Text\draw_text_android.cpp" />
@@ -981,6 +994,7 @@
<ClCompile Include="Thread\ThreadUtil.cpp" />
<ClCompile Include="Thunk.cpp" />
<ClCompile Include="TimeUtil.cpp" />
<ClCompile Include="UI\AsyncImageFileView.cpp" />
<ClCompile Include="UI\Context.cpp" />
<ClCompile Include="UI\Root.cpp" />
<ClCompile Include="UI\Screen.cpp" />
@@ -989,6 +1003,13 @@
<ClCompile Include="UI\UIScreen.cpp" />
<ClCompile Include="UI\View.cpp" />
<ClCompile Include="UI\ViewGroup.cpp" />
<ClCompile Include="VR\OpenXRLoader.cpp" />
<ClCompile Include="VR\PPSSPPVR.cpp" />
<ClCompile Include="VR\VRBase.cpp" />
<ClCompile Include="VR\VRFramebuffer.cpp" />
<ClCompile Include="VR\VRInput.cpp" />
<ClCompile Include="VR\VRMath.cpp" />
<ClCompile Include="VR\VRRenderer.cpp" />
<ClCompile Include="x64Analyzer.cpp" />
<ClCompile Include="x64Emitter.cpp" />
</ItemGroup>
+66
View File
@@ -425,6 +425,39 @@
<ClInclude Include="Math\Statistics.h">
<Filter>Math</Filter>
</ClInclude>
<ClInclude Include="VR\PPSSPPVR.h">
<Filter>VR</Filter>
</ClInclude>
<ClInclude Include="VR\VRBase.h">
<Filter>VR</Filter>
</ClInclude>
<ClInclude Include="VR\VRFramebuffer.h">
<Filter>VR</Filter>
</ClInclude>
<ClInclude Include="VR\VRInput.h">
<Filter>VR</Filter>
</ClInclude>
<ClInclude Include="VR\VRMath.h">
<Filter>VR</Filter>
</ClInclude>
<ClInclude Include="VR\VRRenderer.h">
<Filter>VR</Filter>
</ClInclude>
<ClInclude Include="VR\OpenXRLoader.h">
<Filter>VR</Filter>
</ClInclude>
<ClInclude Include="UI\AsyncImageFileView.h">
<Filter>UI</Filter>
</ClInclude>
<ClInclude Include="Render\ManagedTexture.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="GPU\MiscTypes.h">
<Filter>GPU</Filter>
</ClInclude>
<ClInclude Include="GPU\Vulkan\VulkanFramebuffer.h">
<Filter>GPU\Vulkan</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ABI.cpp" />
@@ -803,6 +836,36 @@
<ClCompile Include="Math\Statistics.cpp">
<Filter>Math</Filter>
</ClCompile>
<ClCompile Include="VR\PPSSPPVR.cpp">
<Filter>VR</Filter>
</ClCompile>
<ClCompile Include="VR\VRBase.cpp">
<Filter>VR</Filter>
</ClCompile>
<ClCompile Include="VR\VRFramebuffer.cpp">
<Filter>VR</Filter>
</ClCompile>
<ClCompile Include="VR\VRInput.cpp">
<Filter>VR</Filter>
</ClCompile>
<ClCompile Include="VR\VRMath.cpp">
<Filter>VR</Filter>
</ClCompile>
<ClCompile Include="VR\VRRenderer.cpp">
<Filter>VR</Filter>
</ClCompile>
<ClCompile Include="VR\OpenXRLoader.cpp">
<Filter>VR</Filter>
</ClCompile>
<ClCompile Include="UI\AsyncImageFileView.cpp">
<Filter>UI</Filter>
</ClCompile>
<ClCompile Include="Render\ManagedTexture.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="GPU\Vulkan\VulkanFramebuffer.cpp">
<Filter>GPU\Vulkan</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="Crypto">
@@ -907,6 +970,9 @@
<Filter Include="ext\vma">
<UniqueIdentifier>{7b17065c-729c-47c3-a02d-66dc383529dd}</UniqueIdentifier>
</Filter>
<Filter Include="VR">
<UniqueIdentifier>{9d1c29fd-8ac7-4475-8ea6-c8c759b695fe}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Text Include="..\ext\libpng17\CMakeLists.txt">
+3 -3
View File
@@ -41,7 +41,7 @@ public:
#if defined(USING_WIN_UI)
COORD GetCoordinates(int BytesRead, int BufferWidth);
#endif
void Log(const LogMessage &message);
void Log(const LogMessage &message) override;
void ClearScreen(bool Cursor = true);
void Show(bool bShow);
@@ -66,8 +66,8 @@ private:
static std::atomic<uint32_t> logPendingReadPos;
static std::atomic<uint32_t> logPendingWritePos;
int openWidth_;
int openHeight_;
int openWidth_ = 0;
int openHeight_ = 0;
std::wstring title_;
#endif
bool bHidden;
+6
View File
@@ -16,3 +16,9 @@ typedef unsigned int Color;
inline Color darkenColor(Color color) {
return (color & 0xFF000000) | ((color >> 1) & 0x7F7F7F);
}
inline Color lightenColor(Color color) {
color = ~color;
color = (color & 0xFF000000) | ((color >> 1) & 0x7F7F7F);
return ~color;
}
-82
View File
@@ -34,88 +34,6 @@
#endif
#endif
// convert 4444 image to 8888, parallelizable
void convert4444_gl(u16* data, u32* out, int width, int l, int u) {
for (int y = l; y < u; ++y) {
for (int x = 0; x < width; ++x) {
u32 val = data[y*width + x];
u32 r = ((val >> 12) & 0xF) * 17;
u32 g = ((val >> 8) & 0xF) * 17;
u32 b = ((val >> 4) & 0xF) * 17;
u32 a = ((val >> 0) & 0xF) * 17;
out[y*width + x] = (a << 24) | (b << 16) | (g << 8) | r;
}
}
}
// convert 565 image to 8888, parallelizable
void convert565_gl(u16* data, u32* out, int width, int l, int u) {
for (int y = l; y < u; ++y) {
for (int x = 0; x < width; ++x) {
u32 val = data[y*width + x];
u32 r = Convert5To8((val >> 11) & 0x1F);
u32 g = Convert6To8((val >> 5) & 0x3F);
u32 b = Convert5To8((val)& 0x1F);
out[y*width + x] = (0xFF << 24) | (b << 16) | (g << 8) | r;
}
}
}
// convert 5551 image to 8888, parallelizable
void convert5551_gl(u16* data, u32* out, int width, int l, int u) {
for (int y = l; y < u; ++y) {
for (int x = 0; x < width; ++x) {
u32 val = data[y*width + x];
u32 r = Convert5To8((val >> 11) & 0x1F);
u32 g = Convert5To8((val >> 6) & 0x1F);
u32 b = Convert5To8((val >> 1) & 0x1F);
u32 a = (val & 0x1) * 255;
out[y*width + x] = (a << 24) | (b << 16) | (g << 8) | r;
}
}
}
// convert 4444 image to 8888, parallelizable
void convert4444_dx9(u16* data, u32* out, int width, int l, int u) {
for (int y = l; y < u; ++y) {
for (int x = 0; x < width; ++x) {
u32 val = data[y*width + x];
u32 r = ((val >> 0) & 0xF) * 17;
u32 g = ((val >> 4) & 0xF) * 17;
u32 b = ((val >> 8) & 0xF) * 17;
u32 a = ((val >> 12) & 0xF) * 17;
out[y*width + x] = (a << 24) | (b << 16) | (g << 8) | r;
}
}
}
// convert 565 image to 8888, parallelizable
void convert565_dx9(u16* data, u32* out, int width, int l, int u) {
for (int y = l; y < u; ++y) {
for (int x = 0; x < width; ++x) {
u32 val = data[y*width + x];
u32 r = Convert5To8((val)& 0x1F);
u32 g = Convert6To8((val >> 5) & 0x3F);
u32 b = Convert5To8((val >> 11) & 0x1F);
out[y*width + x] = (0xFF << 24) | (b << 16) | (g << 8) | r;
}
}
}
// convert 5551 image to 8888, parallelizable
void convert5551_dx9(u16* data, u32* out, int width, int l, int u) {
for (int y = l; y < u; ++y) {
for (int x = 0; x < width; ++x) {
u32 val = data[y*width + x];
u32 r = Convert5To8((val >> 0) & 0x1F);
u32 g = Convert5To8((val >> 5) & 0x1F);
u32 b = Convert5To8((val >> 10) & 0x1F);
u32 a = ((val >> 15) & 0x1) * 255;
out[y*width + x] = (a << 24) | (b << 16) | (g << 8) | r;
}
}
}
void ConvertBGRA8888ToRGBA8888(u32 *dst, const u32 *src, u32 numPixels) {
#ifdef _M_SSE
const __m128i maskGA = _mm_set1_epi32(0xFF00FF00);
+15 -8
View File
@@ -101,6 +101,14 @@ inline u16 RGBA8888ToRGBA5551(u32 value) {
return (u16)(r | g | b | a);
}
// Used in fast sprite path.
inline u16 RGBA8888ToRGBA555X(u32 value) {
u32 r = (value >> 3) & 0x1F;
u32 g = (value >> 6) & (0x1F << 5);
u32 b = (value >> 9) & (0x1F << 10);
return (u16)(r | g | b);
}
inline u16 RGBA8888ToRGBA4444(u32 value) {
const u32 c = value >> 4;
const u16 r = (c >> 0) & 0x000F;
@@ -110,14 +118,13 @@ inline u16 RGBA8888ToRGBA4444(u32 value) {
return r | g | b | a;
}
// convert image to 8888, parallelizable
// TODO: Implement these in terms of the conversion functions below.
void convert4444_gl(u16* data, u32* out, int width, int l, int u);
void convert565_gl(u16* data, u32* out, int width, int l, int u);
void convert5551_gl(u16* data, u32* out, int width, int l, int u);
void convert4444_dx9(u16* data, u32* out, int width, int l, int u);
void convert565_dx9(u16* data, u32* out, int width, int l, int u);
void convert5551_dx9(u16* data, u32* out, int width, int l, int u);
inline u16 RGBA8888ToRGBA444X(u32 value) {
const u32 c = value >> 4;
const u16 r = (c >> 0) & 0x000F;
const u16 g = (c >> 4) & 0x00F0;
const u16 b = (c >> 8) & 0x0F00;
return r | g | b;
}
// "Complete" set of color conversion functions between the usual formats.
+1
View File
@@ -1,3 +1,4 @@
#include "Common/Data/Convert/SmallDataConvert.h"
alignas(16) const float one_over_255_x4[4] = { 1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f, };
alignas(16) const float exactly_255_x4[4] = { 255.0f, 255.0f, 255.0f, 255.0f, };
+39 -2
View File
@@ -18,12 +18,12 @@
#endif
extern const float one_over_255_x4[4];
extern const float exactly_255_x4[4];
// Utilities useful for filling in std140-layout uniform buffers, and similar.
// NEON intrinsics: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491f/BABDCGGF.html
// NEON intrinsics: https://developer.arm.com/documentation/den0018/a/NEON-Intrinsics?lang=en
// LSBs in f[0], etc.
// Could be SSE optimized.
inline void Uint8x4ToFloat4(float f[4], uint32_t u) {
#ifdef _M_SSE
__m128i zero = _mm_setzero_si128();
@@ -47,6 +47,19 @@ inline void Uint8x4ToFloat4(float f[4], uint32_t u) {
// Could be SSE optimized.
inline uint32_t Float4ToUint8x4(const float f[4]) {
#ifdef _M_SSE
__m128i zero = _mm_setzero_si128();
__m128 value = _mm_mul_ps(_mm_loadu_ps(f), _mm_load_ps(exactly_255_x4));
__m128i ivalue = _mm_packus_epi16(_mm_packs_epi32(_mm_cvtps_epi32(value), zero), zero);
return _mm_cvtsi128_si32(ivalue);
#elif PPSSPP_ARCH(ARM_NEON)
const float32x4_t value = vmulq_f32(vld1q_f32(f), vdupq_n_f32(255.0f));
uint32x4_t ivalue32 = vcvtq_u32_f32(value);
uint16x4_t ivalue16 = vqmovn_u32(ivalue32);
uint8x8_t ivalue8 = vqmovn_u16(vcombine_u16(ivalue16, ivalue16)); // Is there no way to avoid the combine here?
uint32x2_t outValue32 = vreinterpret_u32_u8(ivalue8);
return vget_lane_u32(outValue32, 0);
#else
int i4[4];
for (int i = 0; i < 4; i++) {
if (f[i] > 1.0f) {
@@ -58,6 +71,30 @@ inline uint32_t Float4ToUint8x4(const float f[4]) {
}
}
return i4[0] | (i4[1] << 8) | (i4[2] << 16) | (i4[3] << 24);
#endif
}
inline uint32_t Float4ToUint8x4_NoClamp(const float f[4]) {
#ifdef _M_SSE
// Does actually clamp, no way to avoid it with the pack ops!
__m128i zero = _mm_setzero_si128();
__m128 value = _mm_mul_ps(_mm_loadu_ps(f), _mm_load_ps(exactly_255_x4));
__m128i ivalue = _mm_packus_epi16(_mm_packs_epi32(_mm_cvtps_epi32(value), zero), zero);
return _mm_cvtsi128_si32(ivalue);
#elif PPSSPP_ARCH(ARM_NEON)
const float32x4_t value = vmulq_f32(vld1q_f32(f), vdupq_n_f32(255.0f));
uint32x4_t ivalue32 = vcvtq_u32_f32(value);
uint16x4_t ivalue16 = vqmovn_u32(ivalue32);
uint8x8_t ivalue8 = vqmovn_u16(vcombine_u16(ivalue16, ivalue16)); // Is there no way to avoid the combine here?
uint32x2_t outValue32 = vreinterpret_u32_u8(ivalue8);
return vget_lane_u32(outValue32, 0);
#else
u32 i4[4];
for (int i = 0; i < 4; i++) {
i4[i] = (int)(f[i] * 255.0f);
}
return i4[0] | (i4[1] << 8) | (i4[2] << 16) | (i4[3] << 24);
#endif
}
inline void Uint8x3ToFloat4_AlphaUint8(float f[4], uint32_t u, uint8_t alpha) {
+9 -14
View File
@@ -112,7 +112,7 @@ int u8_toucs(uint32_t *dest, int sz, const char *src, int srcsz)
the NUL as well.
the destination string will never be bigger than the source string.
*/
int u8_toutf8(char *dest, int sz, uint32_t *src, int srcsz)
int u8_toutf8(char *dest, int sz, const uint32_t *src, int srcsz)
{
uint32_t ch;
int i = 0;
@@ -219,19 +219,14 @@ int u8_strlen(const char *s)
}
/* reads the next utf-8 sequence out of a string, updating an index */
uint32_t u8_nextchar(const char *s, int *i)
{
uint32_t ch = 0;
int sz = 0;
do {
ch <<= 6;
ch += (unsigned char)s[(*i)++];
sz++;
} while (s[*i] && !isutf(s[*i]));
ch -= offsetsFromUTF8[sz-1];
return ch;
uint32_t u8_nextchar(const char *s, int *i) {
uint32_t ch = 0;
int sz = 0;
do {
ch = (ch << 6) + (unsigned char)s[(*i)++];
sz++;
} while (s[*i] && ((s[*i]) & 0xC0) == 0x80);
return ch - offsetsFromUTF8[sz - 1];
}
uint32_t u8_nextchar_unsafe(const char *s, int *i) {
+21 -8
View File
@@ -174,6 +174,19 @@ std::string* Section::GetLine(const char* key, std::string* valueOut, std::strin
return 0;
}
const std::string* Section::GetLine(const char* key, std::string* valueOut, std::string* commentOut) const
{
for (std::vector<std::string>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter)
{
const std::string& line = *iter;
std::string lineKey;
ParseLine(line, &lineKey, valueOut, commentOut);
if (!strcasecmp(lineKey.c_str(), key))
return &line;
}
return 0;
}
void Section::Set(const char* key, uint32_t newValue) {
Set(key, StringFromFormat("0x%08x", newValue).c_str());
}
@@ -218,7 +231,7 @@ void Section::Set(const char* key, const std::string& newValue, const std::strin
Delete(key);
}
bool Section::Get(const char* key, std::string* value, const char* defaultValue)
bool Section::Get(const char* key, std::string* value, const char* defaultValue) const
{
const std::string* line = GetLine(key, value, 0);
if (!line)
@@ -275,7 +288,7 @@ void Section::AddComment(const std::string &comment) {
lines.emplace_back("# " + comment);
}
bool Section::Get(const char* key, std::vector<std::string>& values)
bool Section::Get(const char* key, std::vector<std::string>& values) const
{
std::string temp;
bool retval = Get(key, &temp, 0);
@@ -303,7 +316,7 @@ bool Section::Get(const char* key, std::vector<std::string>& values)
return true;
}
bool Section::Get(const char* key, int* value, int defaultValue)
bool Section::Get(const char* key, int* value, int defaultValue) const
{
std::string temp;
bool retval = Get(key, &temp, 0);
@@ -313,7 +326,7 @@ bool Section::Get(const char* key, int* value, int defaultValue)
return false;
}
bool Section::Get(const char* key, uint32_t* value, uint32_t defaultValue)
bool Section::Get(const char* key, uint32_t* value, uint32_t defaultValue) const
{
std::string temp;
bool retval = Get(key, &temp, 0);
@@ -323,7 +336,7 @@ bool Section::Get(const char* key, uint32_t* value, uint32_t defaultValue)
return false;
}
bool Section::Get(const char* key, uint64_t* value, uint64_t defaultValue)
bool Section::Get(const char* key, uint64_t* value, uint64_t defaultValue) const
{
std::string temp;
bool retval = Get(key, &temp, 0);
@@ -333,7 +346,7 @@ bool Section::Get(const char* key, uint64_t* value, uint64_t defaultValue)
return false;
}
bool Section::Get(const char* key, bool* value, bool defaultValue)
bool Section::Get(const char* key, bool* value, bool defaultValue) const
{
std::string temp;
bool retval = Get(key, &temp, 0);
@@ -343,7 +356,7 @@ bool Section::Get(const char* key, bool* value, bool defaultValue)
return false;
}
bool Section::Get(const char* key, float* value, float defaultValue)
bool Section::Get(const char* key, float* value, float defaultValue) const
{
std::string temp;
bool retval = Get(key, &temp, 0);
@@ -353,7 +366,7 @@ bool Section::Get(const char* key, float* value, float defaultValue)
return false;
}
bool Section::Get(const char* key, double* value, double defaultValue)
bool Section::Get(const char* key, double* value, double defaultValue) const
{
std::string temp;
bool retval = Get(key, &temp, 0);
+11 -9
View File
@@ -25,14 +25,16 @@ public:
std::map<std::string, std::string> ToMap() const;
std::string* GetLine(const char* key, std::string* valueOut, std::string* commentOut);
std::string *GetLine(const char* key, std::string* valueOut, std::string* commentOut);
const std::string *GetLine(const char* key, std::string* valueOut, std::string* commentOut) const;
void Set(const char* key, const char* newValue);
void Set(const char* key, const std::string& newValue, const std::string& defaultValue);
void Set(const std::string &key, const std::string &value) {
Set(key.c_str(), value.c_str());
}
bool Get(const char* key, std::string* value, const char* defaultValue);
bool Get(const char* key, std::string* value, const char* defaultValue) const;
void Set(const char* key, uint32_t newValue);
void Set(const char* key, uint64_t newValue);
@@ -57,13 +59,13 @@ public:
void AddComment(const std::string &comment);
bool Get(const char* key, int* value, int defaultValue = 0);
bool Get(const char* key, uint32_t* value, uint32_t defaultValue = 0);
bool Get(const char* key, uint64_t* value, uint64_t defaultValue = 0);
bool Get(const char* key, bool* value, bool defaultValue = false);
bool Get(const char* key, float* value, float defaultValue = false);
bool Get(const char* key, double* value, double defaultValue = false);
bool Get(const char* key, std::vector<std::string>& values);
bool Get(const char* key, int* value, int defaultValue = 0) const;
bool Get(const char* key, uint32_t* value, uint32_t defaultValue = 0) const;
bool Get(const char* key, uint64_t* value, uint64_t defaultValue = 0) const;
bool Get(const char* key, bool* value, bool defaultValue = false) const;
bool Get(const char* key, float* value, float defaultValue = false) const;
bool Get(const char* key, double* value, double defaultValue = false) const;
bool Get(const char* key, std::vector<std::string>& values) const;
bool operator < (const Section& other) const {
return name_ < other.name_;
+5 -3
View File
@@ -47,9 +47,11 @@ public:
JsonReader(const std::string &filename);
JsonReader(const void *data, size_t size) {
buffer_ = (char *)malloc(size + 1);
memcpy(buffer_, data, size);
buffer_[size] = 0;
parse();
if (buffer_) {
memcpy(buffer_, data, size);
buffer_[size] = 0;
parse();
}
}
JsonReader(const JsonNode *node) {
ok_ = true;
+1
View File
@@ -7,6 +7,7 @@ public:
WordWrapper(const char *str, float maxW, int flags)
: str_(str), maxW_(maxW), flags_(flags) {
}
virtual ~WordWrapper() {}
std::string Wrapped();
+1 -1
View File
@@ -127,7 +127,7 @@ static void ExceptionThread(mach_port_t port) {
#pragma pack()
memset(&msg_in, 0xee, sizeof(msg_in));
memset(&msg_out, 0xee, sizeof(msg_out));
mach_msg_header_t* send_msg = nullptr;
mach_msg_header_t* send_msg = &msg_out.Head;
mach_msg_size_t send_size = 0;
mach_msg_option_t option = MACH_RCV_MSG;
while (true) {
+1 -1
View File
@@ -317,7 +317,7 @@ std::vector<std::string> GetWindowsDrives()
{
if (logicaldrives & (1 << i))
{
CHAR driveName[] = { TEXT('A') + i, TEXT(':'), TEXT('\\'), TEXT('\0') };
CHAR driveName[] = { (CHAR)(TEXT('A') + i), TEXT(':'), TEXT('\\'), TEXT('\0') };
std::string str(driveName);
drives.push_back(driveName);
}
+3 -2
View File
@@ -141,7 +141,7 @@ FILE *OpenCFile(const Path &path, const char *mode) {
return nullptr;
}
FILE *f = fdopen(descriptor, "wb");
if (!strcmp(mode, "at") || !strcmp(mode, "a")) {
if (f && (!strcmp(mode, "at") || !strcmp(mode, "a"))) {
// Append mode.
fseek(f, 0, SEEK_END);
}
@@ -250,7 +250,8 @@ static bool ResolvePathVista(const std::wstring &path, wchar_t *buf, DWORD bufSi
#else
if (!getFinalPathNameByHandleW) {
HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
getFinalPathNameByHandleW = (getFinalPathNameByHandleW_f)GetProcAddress(kernel32, "GetFinalPathNameByHandleW");
if (kernel32)
getFinalPathNameByHandleW = (getFinalPathNameByHandleW_f)GetProcAddress(kernel32, "GetFinalPathNameByHandleW");
}
#endif
+3
View File
@@ -118,6 +118,9 @@ public:
bool operator <(const Path &other) const {
return path_ < other.path_;
}
bool operator >(const Path &other) const {
return path_ > other.path_;
}
private:
// The internal representation is currently always the plain string.
+4 -4
View File
@@ -47,10 +47,10 @@ class DirectoryAssetReader : public AssetReader {
public:
explicit DirectoryAssetReader(const Path &path);
// use delete[]
virtual uint8_t *ReadAsset(const char *path, size_t *size);
virtual bool GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter);
virtual bool GetFileInfo(const char *path, File::FileInfo *info);
virtual std::string toString() const {
uint8_t *ReadAsset(const char *path, size_t *size) override;
bool GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter) override;
bool GetFileInfo(const char *path, File::FileInfo *info) override;
std::string toString() const override {
return path_.ToString();
}
+77 -60
View File
@@ -95,16 +95,13 @@ public:
// These functions should be self explanatory.
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
Framebuffer *GetCurrentRenderTarget() override {
return curRenderTarget_;
}
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override;
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) override;
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
void InvalidateCachedState() override;
void Invalidate(InvalidationFlags flags) override;
void BindTextures(int start, int count, Texture **textures) override;
void BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) 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;
@@ -166,21 +163,20 @@ public:
void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override;
int GetCurrentStepId() const override {
return stepId_;
void SetInvalidationCallback(InvalidationCallback callback) override {
invalidationCallback_ = callback;
}
private:
void ApplyCurrentState();
ID3D11DepthStencilState *GetCachedDepthStencilState(D3D11DepthStencilState *state, uint8_t stencilWriteMask, uint8_t stencilCompareMask);
ID3D11DepthStencilState *GetCachedDepthStencilState(const D3D11DepthStencilState *state, uint8_t stencilWriteMask, uint8_t stencilCompareMask);
HWND hWnd_;
ID3D11Device *device_;
ID3D11DeviceContext *context_;
ID3D11Device1 *device1_;
ID3D11DeviceContext1 *context1_;
int stepId_ = -1;
ID3D11Texture2D *bbRenderTargetTex_ = nullptr; // NOT OWNED
ID3D11RenderTargetView *bbRenderTargetView_ = nullptr;
@@ -219,6 +215,8 @@ private:
ID3D11Buffer *nextIndexBuffer_ = nullptr;
int nextIndexBufferOffset_ = 0;
InvalidationCallback invalidationCallback_;
// Dynamic state
float blendFactor_[4]{};
bool blendFactorDirty_ = false;
@@ -264,13 +262,16 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de
caps_.framebufferStencilBlitSupported = false;
caps_.framebufferDepthCopySupported = true;
caps_.framebufferSeparateDepthCopySupported = false; // Though could be emulated with a draw.
caps_.preferredDepthBufferFormat = DataFormat::D24_S8;
caps_.textureDepthSupported = true;
caps_.texture3DSupported = true;
caps_.fragmentShaderInt32Supported = true;
caps_.anisoSupported = true;
caps_.textureNPOTFullySupported = true;
caps_.fragmentShaderDepthWriteSupported = true;
caps_.fragmentShaderStencilWriteSupported = false;
caps_.blendMinMaxSupported = true;
caps_.multiSampleLevelsMask = 1; // More could be supported with some work.
D3D11_FEATURE_DATA_D3D11_OPTIONS options{};
HRESULT result = device_->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options, sizeof(options));
@@ -309,6 +310,14 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de
dxgiDevice->Release();
}
caps_.isTilingGPU = false;
// Hide D3D9 when we know it likely won't work well.
caps_.supportsD3D9 = true;
if (!strcmp(adapterDesc_.c_str(), "Intel(R) Iris(R) Xe Graphics")) {
caps_.supportsD3D9 = false;
}
// Temp texture for read-back of small images. Custom textures are created on demand for larger ones.
// TODO: Should really benchmark if this extra complexity has any benefit.
D3D11_TEXTURE2D_DESC packDesc{};
@@ -401,7 +410,6 @@ void D3D11DrawContext::HandleEvent(Event ev, int width, int height, void *param1
// Make sure that we don't eliminate the next time the render target is set.
curRenderTargetView_ = nullptr;
curDepthStencilView_ = nullptr;
stepId_ = 0;
break;
}
}
@@ -548,7 +556,7 @@ public:
float blendFactor[4];
};
ID3D11DepthStencilState *D3D11DrawContext::GetCachedDepthStencilState(D3D11DepthStencilState *state, uint8_t stencilWriteMask, uint8_t stencilCompareMask) {
ID3D11DepthStencilState *D3D11DrawContext::GetCachedDepthStencilState(const D3D11DepthStencilState *state, uint8_t stencilWriteMask, uint8_t stencilCompareMask) {
D3D11DepthStencilKey key;
key.desc = state->desc;
key.writeMask = stencilWriteMask;
@@ -1081,17 +1089,19 @@ void D3D11DrawContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {
context_->Unmap(curPipeline_->dynamicUniforms, 0);
}
void D3D11DrawContext::InvalidateCachedState() {
// This is a signal to forget all our state caching.
curBlend_ = nullptr;
curDepthStencil_ = nullptr;
curRaster_ = nullptr;
curPS_ = nullptr;
curVS_ = nullptr;
curGS_ = nullptr;
curInputLayout_ = nullptr;
curTopology_ = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
curPipeline_ = nullptr;
void D3D11DrawContext::Invalidate(InvalidationFlags flags) {
if (flags & InvalidationFlags::CACHED_RENDER_STATE) {
// This is a signal to forget all our state caching.
curBlend_ = nullptr;
curDepthStencil_ = nullptr;
curRaster_ = nullptr;
curPS_ = nullptr;
curVS_ = nullptr;
curGS_ = nullptr;
curInputLayout_ = nullptr;
curTopology_ = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
curPipeline_ = nullptr;
}
}
void D3D11DrawContext::BindPipeline(Pipeline *pipeline) {
@@ -1306,35 +1316,38 @@ public:
Framebuffer *D3D11DrawContext::CreateFramebuffer(const FramebufferDesc &desc) {
HRESULT hr;
D3D11Framebuffer *fb = new D3D11Framebuffer(desc.width, desc.height);
if (desc.numColorAttachments) {
fb->colorFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
D3D11_TEXTURE2D_DESC descColor{};
descColor.Width = desc.width;
descColor.Height = desc.height;
descColor.MipLevels = 1;
descColor.ArraySize = 1;
descColor.Format = fb->colorFormat;
descColor.SampleDesc.Count = 1;
descColor.SampleDesc.Quality = 0;
descColor.Usage = D3D11_USAGE_DEFAULT;
descColor.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
descColor.CPUAccessFlags = 0;
descColor.MiscFlags = 0;
hr = device_->CreateTexture2D(&descColor, nullptr, &fb->colorTex);
if (FAILED(hr)) {
delete fb;
return nullptr;
}
hr = device_->CreateRenderTargetView(fb->colorTex, nullptr, &fb->colorRTView);
if (FAILED(hr)) {
delete fb;
return nullptr;
}
hr = device_->CreateShaderResourceView(fb->colorTex, nullptr, &fb->colorSRView);
if (FAILED(hr)) {
delete fb;
return nullptr;
}
// We don't (yet?) support multiview for D3D11. Not sure if there's a way to do it.
// Texture arrays are supported but we don't have any other use cases yet.
_dbg_assert_(desc.numLayers == 1);
fb->colorFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
D3D11_TEXTURE2D_DESC descColor{};
descColor.Width = desc.width;
descColor.Height = desc.height;
descColor.MipLevels = 1;
descColor.ArraySize = 1;
descColor.Format = fb->colorFormat;
descColor.SampleDesc.Count = 1;
descColor.SampleDesc.Quality = 0;
descColor.Usage = D3D11_USAGE_DEFAULT;
descColor.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
descColor.CPUAccessFlags = 0;
descColor.MiscFlags = 0;
hr = device_->CreateTexture2D(&descColor, nullptr, &fb->colorTex);
if (FAILED(hr)) {
delete fb;
return nullptr;
}
hr = device_->CreateRenderTargetView(fb->colorTex, nullptr, &fb->colorRTView);
if (FAILED(hr)) {
delete fb;
return nullptr;
}
hr = device_->CreateShaderResourceView(fb->colorTex, nullptr, &fb->colorSRView);
if (FAILED(hr)) {
delete fb;
return nullptr;
}
if (desc.z_stencil) {
@@ -1381,7 +1394,7 @@ Framebuffer *D3D11DrawContext::CreateFramebuffer(const FramebufferDesc &desc) {
return fb;
}
void D3D11DrawContext::BindTextures(int start, int count, Texture **textures) {
void D3D11DrawContext::BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) {
// Collect the resource views from the textures.
ID3D11ShaderResourceView *views[MAX_BOUND_TEXTURES];
_assert_(start + count <= ARRAY_SIZE(views));
@@ -1469,6 +1482,7 @@ void D3D11DrawContext::CopyFramebufferImage(Framebuffer *srcfb, int level, int x
dstTex = dst->depthStencilTex;
break;
}
_assert_(srcTex && dstTex);
// TODO: Check for level too!
if (width == src->Width() && width == dst->Width() && height == src->Height() && height == dst->Height() && x == 0 && y == 0 && z == 0 && dstX == 0 && dstY == 0 && dstZ == 0) {
@@ -1499,13 +1513,11 @@ void D3D11DrawContext::CopyFramebufferImage(Framebuffer *srcfb, int level, int x
D3D11_BOX srcBox{ (UINT)x, (UINT)y, (UINT)z, (UINT)(x + width), (UINT)(y + height), (UINT)(z + depth) };
context_->CopySubresourceRegion(dstTex, dstLevel, dstX, dstY, dstZ, srcTex, level, &srcBox);
}
stepId_++;
}
bool D3D11DrawContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dstfb, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) {
// Unfortunately D3D11 has no equivalent to this, gotta render a quad. Well, in some cases we can issue a copy instead.
Crash();
stepId_++;
return false;
}
@@ -1529,7 +1541,7 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel
bool useGlobalPacktex = (bx + bw <= 512 && by + bh <= 512) && channelBits == FB_COLOR_BIT;
ID3D11Texture2D *packTex;
ID3D11Texture2D *packTex = nullptr;
if (!useGlobalPacktex) {
D3D11_TEXTURE2D_DESC packDesc{};
packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
@@ -1568,6 +1580,9 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel
packTex = packTexture_;
}
if (!packTex)
return false;
D3D11_BOX srcBox{ (UINT)bx, (UINT)by, 0, (UINT)(bx + bw), (UINT)(by + bh), 1 };
DataFormat srcFormat = DataFormat::UNDEFINED;
switch (channelBits) {
@@ -1651,7 +1666,6 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel
if (!useGlobalPacktex) {
packTex->Release();
}
stepId_++;
return true;
}
@@ -1698,11 +1712,14 @@ void D3D11DrawContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const Ren
context_->ClearDepthStencilView(curDepthStencilView_, mask, rp.clearDepth, rp.clearStencil);
}
stepId_++;
if (invalidationCallback_) {
invalidationCallback_(InvalidationCallbackFlags::RENDER_PASS_STATE);
}
}
void D3D11DrawContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) {
_assert_(binding < MAX_BOUND_TEXTURES);
void D3D11DrawContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) {
_dbg_assert_(binding < MAX_BOUND_TEXTURES);
_dbg_assert_(layer == ALL_LAYERS || layer == 0); // No multiple layer support on D3D
D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo;
switch (channelBit) {
case FBChannel::FB_COLOR_BIT:
+51 -32
View File
@@ -291,7 +291,7 @@ public:
D3D9Texture(LPDIRECT3DDEVICE9 device, LPDIRECT3DDEVICE9EX deviceEx, const TextureDesc &desc);
~D3D9Texture();
void SetToSampler(LPDIRECT3DDEVICE9 device, int sampler);
LPDIRECT3DBASETEXTURE9 Texture() const {
LPDIRECT3DBASETEXTURE9 TexturePtr() const {
// TODO: Cleanup
if (tex_) {
return tex_;
@@ -532,16 +532,13 @@ public:
// These functions should be self explanatory.
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
Framebuffer *GetCurrentRenderTarget() override {
return curRenderTarget_;
}
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override;
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) override;
uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) override;
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
void BindTextures(int start, int count, Texture **textures) override;
void BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) override;
void BindNativeTexture(int index, void *nativeTexture) override;
void BindSamplerStates(int start, int count, SamplerState **states) override {
@@ -593,7 +590,7 @@ public:
case NativeObject::DEVICE_EX:
return (uint64_t)(uintptr_t)deviceEx_;
case NativeObject::TEXTURE_VIEW:
return (uint64_t)(((D3D9Texture *)srcObject)->Texture());
return (uint64_t)(((D3D9Texture *)srcObject)->TexturePtr());
default:
return 0;
}
@@ -613,18 +610,17 @@ public:
void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override;
int GetCurrentStepId() const override {
return stepId_;
}
void Invalidate(InvalidationFlags flags) override;
void InvalidateCachedState() override;
void SetInvalidationCallback(InvalidationCallback callback) override {
invalidationCallback_ = callback;
}
private:
LPDIRECT3D9 d3d_;
LPDIRECT3D9EX d3dEx_;
LPDIRECT3DDEVICE9 device_;
LPDIRECT3DDEVICE9EX deviceEx_;
int stepId_ = -1;
int adapterId_ = -1;
D3DADAPTER_IDENTIFIER9 identifier_{};
D3DCAPS9 d3dCaps_;
@@ -650,10 +646,14 @@ private:
// Dynamic state
uint8_t stencilRef_ = 0;
InvalidationCallback invalidationCallback_;
};
void D3D9Context::InvalidateCachedState() {
curPipeline_ = nullptr;
void D3D9Context::Invalidate(InvalidationFlags flags) {
if (flags & InvalidationFlags::CACHED_RENDER_STATE) {
curPipeline_ = nullptr;
}
}
// TODO: Move this detection elsewhere when it's needed elsewhere, not before. It's ugly.
@@ -749,7 +749,6 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID
}
caps_.deviceID = identifier_.DeviceId;
caps_.multiViewport = false;
caps_.depthRangeMinusOneToOne = false;
caps_.preferredDepthBufferFormat = DataFormat::D24_S8;
caps_.dualSourceBlend = false;
@@ -762,7 +761,10 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID
caps_.framebufferSeparateDepthCopySupported = false;
caps_.texture3DSupported = true;
caps_.fragmentShaderDepthWriteSupported = true;
caps_.fragmentShaderStencilWriteSupported = false;
caps_.blendMinMaxSupported = true;
caps_.isTilingGPU = false;
caps_.multiSampleLevelsMask = 1; // More could be supported with some work.
if ((caps.RasterCaps & D3DPRASTERCAPS_ANISOTROPY) != 0 && caps.MaxAnisotropy > 1) {
caps_.anisoSupported = true;
@@ -771,6 +773,11 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID
caps_.textureNPOTFullySupported = true;
}
caps_.supportsD3D9 = true;
if (!strcmp(identifier_.Description, "Intel(R) Iris(R) Xe Graphics")) {
caps_.supportsD3D9 = false;
}
// VS range culling (killing triangles in the vertex shader using NaN) causes problems on Intel.
// Also causes problems on old NVIDIA.
switch (caps_.vendor) {
@@ -915,7 +922,7 @@ Texture *D3D9Context::CreateTexture(const TextureDesc &desc) {
return tex;
}
void D3D9Context::BindTextures(int start, int count, Texture **textures) {
void D3D9Context::BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) {
_assert_(start + count <= MAX_BOUND_TEXTURES);
for (int i = start; i < start + count; i++) {
D3D9Texture *tex = static_cast<D3D9Texture *>(textures[i - start]);
@@ -1005,7 +1012,7 @@ public:
device->CreateVertexBuffer((UINT)size, usage, 0, D3DPOOL_DEFAULT, &vbuffer_, NULL);
}
}
virtual ~D3D9Buffer() override {
~D3D9Buffer() {
if (ibuffer_) {
ibuffer_->Release();
}
@@ -1070,16 +1077,16 @@ void D3D9Context::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offse
return;
}
if (buf->vbuffer_) {
void *ptr;
void *ptr = nullptr;
HRESULT res = buf->vbuffer_->Lock((UINT)offset, (UINT)size, &ptr, (flags & UPDATE_DISCARD) ? D3DLOCK_DISCARD : 0);
if (!FAILED(res)) {
if (!FAILED(res) && ptr) {
memcpy(ptr, data, size);
buf->vbuffer_->Unlock();
}
} else if (buf->ibuffer_) {
void *ptr;
void *ptr = nullptr;
HRESULT res = buf->ibuffer_->Lock((UINT)offset, (UINT)size, &ptr, (flags & UPDATE_DISCARD) ? D3DLOCK_DISCARD : 0);
if (!FAILED(res)) {
if (!FAILED(res) && ptr) {
memcpy(ptr, data, size);
buf->ibuffer_->Unlock();
}
@@ -1244,15 +1251,18 @@ public:
};
Framebuffer *D3D9Context::CreateFramebuffer(const FramebufferDesc &desc) {
// Don't think D3D9 does array layers.
_dbg_assert_(desc.numLayers == 1);
static uint32_t id = 0;
D3D9Framebuffer *fbo = new D3D9Framebuffer(desc.width, desc.height);
fbo->depthstenciltex = nullptr;
HRESULT rtResult = device_->CreateTexture(desc.width, desc.height, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &fbo->tex, NULL);
HRESULT rtResult = device_->CreateTexture(desc.width, desc.height, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &fbo->tex, nullptr);
if (FAILED(rtResult)) {
ERROR_LOG(G3D, "Failed to create render target");
delete fbo;
fbo->Release();
return NULL;
}
fbo->tex->GetSurfaceLevel(0, &fbo->surf);
@@ -1281,9 +1291,15 @@ Framebuffer *D3D9Context::CreateFramebuffer(const FramebufferDesc &desc) {
}
D3D9Framebuffer::~D3D9Framebuffer() {
tex->Release();
surf->Release();
depthstencil->Release();
if (tex) {
tex->Release();
}
if (surf) {
surf->Release();
}
if (depthstencil) {
depthstencil->Release();
}
if (depthstenciltex) {
depthstenciltex->Release();
}
@@ -1320,7 +1336,10 @@ void D3D9Context::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPa
dxstate.scissorRect.restore();
dxstate.scissorTest.restore();
dxstate.viewport.restore();
stepId_++;
if (invalidationCallback_) {
invalidationCallback_(InvalidationCallbackFlags::RENDER_PASS_STATE);
}
}
uintptr_t D3D9Context::GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) {
@@ -1348,8 +1367,9 @@ uintptr_t D3D9Context::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit
}
}
void D3D9Context::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int color) {
_assert_(binding < MAX_BOUND_TEXTURES);
void D3D9Context::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) {
_dbg_assert_(binding < MAX_BOUND_TEXTURES);
_dbg_assert_(layer == ALL_LAYERS || layer == 0); // No stereo support
D3D9Framebuffer *fb = (D3D9Framebuffer *)fbo;
switch (channelBit) {
case FB_DEPTH_BIT:
@@ -1398,7 +1418,6 @@ bool D3D9Context::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int
} else {
return false;
}
stepId_++;
return SUCCEEDED(device_->StretchRect(srcSurf, &srcRect, dstSurf, &dstRect, (filter == FB_BLIT_LINEAR && channelBits == FB_COLOR_BIT) ? D3DTEXF_LINEAR : D3DTEXF_POINT));
}
@@ -1430,6 +1449,7 @@ bool D3D9Context::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits,
LPDIRECT3DSURFACE9 offscreen = nullptr;
HRESULT hr = E_UNEXPECTED;
_assert_(fb != nullptr);
if (channelBits == FB_COLOR_BIT) {
fb->tex->GetLevelDesc(0, &desc);
@@ -1519,7 +1539,6 @@ void D3D9Context::HandleEvent(Event ev, int width, int height, void *param1, voi
device_->GetDepthStencilSurface(&deviceDSsurf);
break;
case Event::PRESENTED:
stepId_ = 0;
break;
}
}
+4
View File
@@ -65,6 +65,7 @@ enum class DataFormat : uint8_t {
S8,
D16,
D16_S8,
D24_S8,
D32F,
D32F_S8,
@@ -76,6 +77,9 @@ inline bool DataFormatIsColor(DataFormat fmt) {
return !DataFormatIsDepthStencil(fmt);
}
// Limited format support for now.
const char *DataFormatToString(DataFormat fmt);
void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format);
void ConvertFromBGRA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format);
void ConvertToD32F(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format);
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include "Common/Common.h"
enum class InvalidationFlags {
CACHED_RENDER_STATE = 1,
};
ENUM_CLASS_BITOPS(InvalidationFlags);
enum class InvalidationCallbackFlags {
RENDER_PASS_STATE = 1,
COMMAND_BUFFER_STATE = 2,
};
ENUM_CLASS_BITOPS(InvalidationCallbackFlags);
typedef std::function<void(InvalidationCallbackFlags)> InvalidationCallback;
+1
View File
@@ -371,6 +371,7 @@ void CheckGLExtensions() {
gl_extensions.ARB_uniform_buffer_object = g_set_gl_extensions.count("GL_ARB_uniform_buffer_object") != 0;
gl_extensions.ARB_explicit_attrib_location = g_set_gl_extensions.count("GL_ARB_explicit_attrib_location") != 0;
gl_extensions.ARB_texture_non_power_of_two = g_set_gl_extensions.count("GL_ARB_texture_non_power_of_two") != 0;
gl_extensions.ARB_shader_stencil_export = g_set_gl_extensions.count("GL_ARB_shader_stencil_export") != 0;
if (gl_extensions.IsGLES) {
gl_extensions.EXT_blend_func_extended = g_set_gl_extensions.count("GL_EXT_blend_func_extended") != 0;
gl_extensions.OES_texture_npot = g_set_gl_extensions.count("GL_OES_texture_npot") != 0;
+1
View File
@@ -72,6 +72,7 @@ struct GLExtensions {
bool ARB_uniform_buffer_object;
bool ARB_texture_non_power_of_two;
bool ARB_stencil_texturing;
bool ARB_shader_stencil_export;
// EXT
bool EXT_swap_control_tear;
+6 -7
View File
@@ -280,7 +280,7 @@ void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool ski
_dbg_assert_(query.name);
int location = -1;
if (IsVRBuild() && IsMultiviewSupported()) {
if (IsVREnabled() && IsMultiviewSupported()) {
int index = GetStereoBufferIndex(query.name);
if (index >= 0) {
std::string layout = GetStereoBufferLayout(query.name);
@@ -648,7 +648,7 @@ retry_depth:
currentReadHandle_ = fbo->handle;
}
void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCalls, bool keepSteps) {
void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCalls, bool keepSteps, bool useVR) {
if (skipGLCalls) {
if (keepSteps) {
return;
@@ -707,7 +707,7 @@ void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCal
switch (step.stepType) {
case GLRStepType::RENDER:
renderCount++;
if (IsVRBuild()) {
if (IsVREnabled()) {
GLRStep vrStep = step;
PreprocessStepVR(&vrStep);
PerformRenderPass(vrStep, renderCount == 1, renderCount == totalRenderCount);
@@ -1603,6 +1603,9 @@ void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) {
fbo_bind_fb_target(false, curFB_->handle);
} else {
fbo_unbind();
if (IsVREnabled()) {
BindVRFramebuffer();
}
// Backbuffer is now bound.
}
CHECK_GL_ERROR_IF_DEBUG();
@@ -1755,10 +1758,6 @@ void GLQueueRunner::fbo_unbind() {
bindDefaultFBO();
#endif
if (IsVRBuild()) {
BindVRFramebuffer();
}
currentDrawHandle_ = 0;
currentReadHandle_ = 0;
CHECK_GL_ERROR_IF_DEBUG();
+1 -1
View File
@@ -363,7 +363,7 @@ public:
void RunInitSteps(const std::vector<GLRInitStep> &steps, bool skipGLCalls);
void RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCalls, bool keepSteps = false);
void RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCalls, bool keepSteps, bool useVR);
void LogSteps(const std::vector<GLRStep *> &steps);
void CreateDeviceObjects();
+9 -14
View File
@@ -5,8 +5,6 @@
#include "Common/Thread/ThreadUtil.h"
#include "Common/VR/PPSSPPVR.h"
#include "Core/Config.h"
#include "Common/Log.h"
#include "Common/MemoryUtil.h"
#include "Common/Math/math_util.h"
@@ -356,9 +354,13 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende
step->dependencies.insert(fb);
}
}
if (invalidationCallback_) {
invalidationCallback_(InvalidationCallbackFlags::RENDER_PASS_STATE);
}
}
void GLRenderManager::BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit, int attachment) {
void GLRenderManager::BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
_dbg_assert_(binding < MAX_GL_TEXTURE_SLOTS);
GLRRenderData data{ GLRRenderCommand::BIND_FB_TEXTURE };
@@ -482,7 +484,6 @@ void GLRenderManager::BeginFrame() {
// In GL, we have to do deletes on the submission thread.
insideFrame_ = true;
renderStepOffset_ = 0;
}
void GLRenderManager::Finish() {
@@ -579,18 +580,15 @@ void GLRenderManager::Run(int frame) {
}
}
if (IsVRBuild()) {
int passes = 1;
if (!IsMultiviewSupported() && g_Config.bEnableStereo) {
passes = 2;
}
if (IsVREnabled()) {
int passes = GetVRPassesCount();
for (int i = 0; i < passes; i++) {
PreVRFrameRender(i);
queueRunner_.RunSteps(stepsOnThread, skipGLCalls_, i < passes - 1);
queueRunner_.RunSteps(stepsOnThread, skipGLCalls_, i < passes - 1, true);
PostVRFrameRender();
}
} else {
queueRunner_.RunSteps(stepsOnThread, skipGLCalls_);
queueRunner_.RunSteps(stepsOnThread, skipGLCalls_, false, false);
}
stepsOnThread.clear();
@@ -617,9 +615,6 @@ void GLRenderManager::Run(int frame) {
}
void GLRenderManager::FlushSync() {
// TODO: Reset curRenderStep_?
renderStepOffset_ += (int)steps_.size();
int curFrame = curFrame_;
FrameData &frameData = frameData_[curFrame];
{
+25 -11
View File
@@ -10,6 +10,7 @@
#include <condition_variable>
#include "Common/GPU/OpenGL/GLCommon.h"
#include "Common/GPU/MiscTypes.h"
#include "Common/Data/Convert/SmallDataConvert.h"
#include "Common/Log.h"
#include "GLQueueRunner.h"
@@ -98,12 +99,23 @@ struct GLRProgramFlags {
bool useClipDistance2 : 1;
};
// Unless you manage lifetimes in some smart way,
// your loc data for uniforms and samplers need to be in a struct
// derived from this, and passed into CreateProgram.
class GLRProgramLocData {
public:
virtual ~GLRProgramLocData() {}
};
class GLRProgram {
public:
~GLRProgram() {
if (program) {
glDeleteProgram(program);
}
if (locData_) {
delete locData_;
}
}
struct Semantic {
int location;
@@ -126,6 +138,8 @@ public:
std::vector<Semantic> semantics_;
std::vector<UniformLocQuery> queries_;
std::vector<Initializer> initialize_;
GLRProgramLocData *locData_;
bool use_clip_distance[8]{};
struct UniformInfo {
@@ -147,6 +161,7 @@ public:
}
return loc;
}
std::unordered_map<std::string, UniformInfo> uniformCache_;
};
@@ -368,6 +383,9 @@ public:
GLRenderManager() {}
~GLRenderManager();
void SetInvalidationCallback(InvalidationCallback callback) {
invalidationCallback_ = callback;
}
void SetErrorCallback(ErrorCallbackFn callback, void *userdata) {
queueRunner_.SetErrorCallback(callback, userdata);
}
@@ -434,13 +452,14 @@ public:
// not be an active render pass.
GLRProgram *CreateProgram(
std::vector<GLRShader *> shaders, std::vector<GLRProgram::Semantic> semantics, std::vector<GLRProgram::UniformLocQuery> queries,
std::vector<GLRProgram::Initializer> initializers, const GLRProgramFlags &flags) {
std::vector<GLRProgram::Initializer> initializers, GLRProgramLocData *locData, 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->locData_ = locData;
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;
@@ -462,7 +481,7 @@ public:
return step.create_program.program;
}
GLRInputLayout *CreateInputLayout(std::vector<GLRInputLayout::Entry> &entries) {
GLRInputLayout *CreateInputLayout(const std::vector<GLRInputLayout::Entry> &entries) {
GLRInitStep step{ GLRInitStepType::CREATE_INPUT_LAYOUT };
step.create_input_layout.inputLayout = new GLRInputLayout();
step.create_input_layout.inputLayout->entries = entries;
@@ -526,7 +545,7 @@ public:
void BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, GLRRenderPassAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag);
// Binds a framebuffer as a texture, for the following draws.
void BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit, int attachment);
void BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit);
bool CopyFramebufferToMemorySync(GLRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag);
void CopyImageToMemorySync(GLRTexture *texture, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag);
@@ -980,12 +999,6 @@ public:
skipGLCalls_ = true;
}
// Gets a frame-unique ID of the current step being recorded. Can be used to figure out
// when the current step has changed, which means the caller will need to re-record its state.
int GetCurrentStepId() const {
return renderStepOffset_ + (int)steps_.size();
}
private:
void BeginSubmitFrame(int frame);
void EndSubmitFrame(int frame);
@@ -1033,8 +1046,7 @@ private:
// Submission time state
bool insideFrame_ = false;
// This is the offset within this frame, in case of a mid-frame sync.
int renderStepOffset_ = 0;
GLRStep *curRenderStep_ = nullptr;
std::vector<GLRStep *> steps_;
std::vector<GLRInitStep> initSteps_;
@@ -1074,4 +1086,6 @@ private:
GLRProgram *curProgram_ = nullptr;
#endif
Draw::DeviceCaps caps_{};
InvalidationCallback invalidationCallback_;
};
+95 -42
View File
@@ -10,6 +10,8 @@
#include "Common/Data/Convert/SmallDataConvert.h"
#include "Common/Math/math_util.h"
#include "Common/Math/lin/matrix4x4.h"
#include "Common/System/System.h"
#include "Common/Log.h"
#include "Common/GPU/thin3d.h"
#include "Common/GPU/Shader.h"
#include "Common/GPU/OpenGL/DataFormatGL.h"
@@ -249,12 +251,21 @@ bool OpenGLShaderModule::Compile(GLRenderManager *render, ShaderLanguage languag
if (source_.find("#version") == source_.npos) {
source_ = ApplyGLSLPrelude(source_, glstage_);
}
} else {
// Unsupported shader type
return false;
}
shader_ = render->CreateShader(glstage_, source_, tag_);
_assert_(shader_ != nullptr); // normally can't fail since we defer creation, unless there's a memory error or similar.
return true;
}
struct PipelineLocData : GLRProgramLocData {
GLint samplerLocs_[MAX_TEXTURE_SLOTS]{};
std::vector<GLint> dynamicUniformLocs_;
};
class OpenGLInputLayout : public InputLayout {
public:
OpenGLInputLayout(GLRenderManager *render) : render_(render) {}
@@ -276,10 +287,13 @@ public:
for (auto &iter : shaders) {
iter->Release();
}
if (program_) render_->DeleteProgram(program_);
if (program_) {
render_->DeleteProgram(program_);
}
// DO NOT delete locs_ here, it's deleted by the render manager.
}
bool LinkShaders();
bool LinkShaders(const PipelineDesc &desc);
GLuint prim = 0;
std::vector<OpenGLShaderModule *> shaders;
@@ -288,12 +302,14 @@ public:
AutoRef<OpenGLBlendState> blend;
AutoRef<OpenGLRasterState> raster;
// Not owned!
PipelineLocData *locs_ = nullptr;
// TODO: Optimize by getting the locations first and putting in a custom struct
UniformBufferDesc dynamicUniforms;
GLint samplerLocs_[MAX_TEXTURE_SLOTS]{};
std::vector<GLint> dynamicUniformLocs_;
GLRProgram *program_ = nullptr;
// Allow using other sampler names than sampler0, sampler1 etc in shaders.
// If not set, will default to those, though.
Slice<SamplerDef> samplers_;
@@ -308,7 +324,7 @@ class OpenGLTexture;
class OpenGLContext : public DrawContext {
public:
OpenGLContext();
virtual ~OpenGLContext();
~OpenGLContext();
void SetTargetSize(int w, int h) override {
DrawContext::SetTargetSize(w, h);
@@ -355,10 +371,7 @@ public:
// These functions should be self explanatory.
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
Framebuffer *GetCurrentRenderTarget() override {
return curRenderTarget_;
}
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override;
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) override;
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
@@ -400,7 +413,7 @@ public:
curPipeline_->depthStencil->stencilPass);
}
void BindTextures(int start, int count, Texture **textures) override;
void BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) override;
void BindNativeTexture(int sampler, void *nativeTexture) override;
void BindPipeline(Pipeline *pipeline) override;
@@ -462,11 +475,11 @@ public:
void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override {}
int GetCurrentStepId() const override {
return renderManager_.GetCurrentStepId();
}
void Invalidate(InvalidationFlags flags) override;
void InvalidateCachedState() override;
void SetInvalidationCallback(InvalidationCallback callback) override {
renderManager_.SetInvalidationCallback(callback);
}
private:
void ApplySamplers();
@@ -504,7 +517,7 @@ static constexpr int MakeIntelSimpleVer(int v1, int v2, int v3) {
return (v1 << 16) | (v2 << 8) | v3;
}
static bool HasIntelDualSrcBug(int versions[4]) {
static bool HasIntelDualSrcBug(const int versions[4]) {
// Intel uses a confusing set of at least 3 version numbering schemes. This is the one given to OpenGL.
switch (MakeIntelSimpleVer(versions[0], versions[1], versions[2])) {
case MakeIntelSimpleVer(9, 17, 10):
@@ -521,7 +534,6 @@ static bool HasIntelDualSrcBug(int versions[4]) {
}
OpenGLContext::OpenGLContext() {
// TODO: Detect more caps
if (gl_extensions.IsGLES) {
if (gl_extensions.OES_packed_depth_stencil || gl_extensions.OES_depth24) {
caps_.preferredDepthBufferFormat = DataFormat::D24_S8;
@@ -553,6 +565,7 @@ OpenGLContext::OpenGLContext() {
caps_.framebufferStencilBlitSupported = caps_.framebufferBlitSupported;
caps_.depthClampSupported = gl_extensions.ARB_depth_clamp || gl_extensions.EXT_depth_clamp;
caps_.blendMinMaxSupported = gl_extensions.EXT_blend_minmax;
caps_.multiSampleLevelsMask = 1; // More could be supported with some work.
if (gl_extensions.IsGLES) {
caps_.clipDistanceSupported = gl_extensions.EXT_clip_cull_distance || gl_extensions.APPLE_clip_distance;
@@ -572,6 +585,7 @@ OpenGLContext::OpenGLContext() {
} else {
caps_.fragmentShaderDepthWriteSupported = true;
}
caps_.fragmentShaderStencilWriteSupported = gl_extensions.ARB_shader_stencil_export;
// GLES has no support for logic framebuffer operations. There doesn't even seem to exist any such extensions.
caps_.logicOpSupported = !gl_extensions.IsGLES;
@@ -596,6 +610,18 @@ OpenGLContext::OpenGLContext() {
caps_.vendor = GPUVendor::VENDOR_UNKNOWN;
break;
}
// Hide D3D9 when we know it likely won't work well.
#if PPSSPP_PLATFORM(WINDOWS)
caps_.supportsD3D9 = true;
if (!strcmp(gl_extensions.model, "Intel(R) Iris(R) Xe Graphics")) {
caps_.supportsD3D9 = false;
}
#endif
// Very rough heuristic!
caps_.isTilingGPU = gl_extensions.IsGLES && caps_.vendor != GPUVendor::VENDOR_NVIDIA && caps_.vendor != GPUVendor::VENDOR_INTEL;
for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) {
frameData_[i].push = renderManager_.CreatePushBuffer(i, GL_ARRAY_BUFFER, 64 * 1024);
}
@@ -642,6 +668,15 @@ OpenGLContext::OpenGLContext() {
bugs_.Infest(Bugs::PVR_GENMIPMAP_HEIGHT_GREATER);
}
if (caps_.vendor == GPUVendor::VENDOR_QUALCOMM) {
#if PPSSPP_PLATFORM(ANDROID)
// The bug seems to affect Adreno 3xx and 5xx, and appeared in Android 8.0 Oreo, so API 26.
// See https://github.com/hrydgard/ppsspp/issues/16015#issuecomment-1328316080.
if (gl_extensions.modelNumber < 600 && System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 26)
bugs_.Infest(Bugs::ADRENO_RESOURCE_DEADLOCK);
#endif
}
#if PPSSPP_PLATFORM(IOS)
// For some reason, this bug does not appear on M1.
if (caps_.vendor == GPUVendor::VENDOR_APPLE) {
@@ -747,18 +782,20 @@ void OpenGLContext::EndFrame() {
renderManager_.EndPushBuffer(frameData.push); // upload the data!
renderManager_.Finish();
InvalidateCachedState();
Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
}
void OpenGLContext::InvalidateCachedState() {
// Unbind stuff.
for (auto &texture : boundTextures_) {
texture = nullptr;
void OpenGLContext::Invalidate(InvalidationFlags flags) {
if (flags & InvalidationFlags::CACHED_RENDER_STATE) {
// Unbind stuff.
for (auto &texture : boundTextures_) {
texture = nullptr;
}
for (auto &sampler : boundSamplers_) {
sampler = nullptr;
}
curPipeline_ = nullptr;
}
for (auto &sampler : boundSamplers_) {
sampler = nullptr;
}
curPipeline_ = nullptr;
}
InputLayout *OpenGLContext::CreateInputLayout(const InputLayoutDesc &desc) {
@@ -1055,7 +1092,7 @@ public:
buffer_ = render->CreateBuffer(target_, size, usage_);
totalSize_ = size;
}
~OpenGLBuffer() override {
~OpenGLBuffer() {
render_->DeleteBuffer(buffer_);
}
@@ -1112,10 +1149,10 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc, const
}
if (desc.uniformDesc) {
pipeline->dynamicUniforms = *desc.uniformDesc;
pipeline->dynamicUniformLocs_.resize(desc.uniformDesc->uniforms.size());
}
pipeline->samplers_ = desc.samplers;
if (pipeline->LinkShaders()) {
if (pipeline->LinkShaders(desc)) {
// Build the rest of the virtual pipeline object.
pipeline->prim = primToGL[(int)desc.prim];
pipeline->depthStencil = (OpenGLDepthStencilState *)desc.depthStencil;
@@ -1130,7 +1167,7 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc, const
}
}
void OpenGLContext::BindTextures(int start, int count, Texture **textures) {
void OpenGLContext::BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) {
_assert_(start + count <= MAX_TEXTURE_SLOTS);
for (int i = start; i < start + count; i++) {
OpenGLTexture *glTex = static_cast<OpenGLTexture *>(textures[i - start]);
@@ -1185,7 +1222,7 @@ ShaderModule *OpenGLContext::CreateShaderModule(ShaderStage stage, ShaderLanguag
}
}
bool OpenGLPipeline::LinkShaders() {
bool OpenGLPipeline::LinkShaders(const PipelineDesc &desc) {
std::vector<GLRShader *> linkShaders;
for (auto shaderModule : shaders) {
if (shaderModule) {
@@ -1216,32 +1253,38 @@ bool OpenGLPipeline::LinkShaders() {
semantics.push_back({ SEM_POSITION, "a_position" });
semantics.push_back({ SEM_TEXCOORD0, "a_texcoord0" });
locs_ = new PipelineLocData();
locs_->dynamicUniformLocs_.resize(desc.uniformDesc->uniforms.size());
std::vector<GLRProgram::UniformLocQuery> queries;
int samplersToCheck;
if (!samplers_.is_empty()) {
for (int i = 0; i < (int)std::min((const uint32_t)samplers_.size(), MAX_TEXTURE_SLOTS); i++) {
queries.push_back({ &samplerLocs_[i], samplers_[i].name, true });
queries.push_back({ &locs_->samplerLocs_[i], samplers_[i].name, true });
}
samplersToCheck = (int)samplers_.size();
} else {
queries.push_back({ &samplerLocs_[0], "sampler0" });
queries.push_back({ &samplerLocs_[1], "sampler1" });
queries.push_back({ &samplerLocs_[2], "sampler2" });
queries.push_back({ &locs_->samplerLocs_[0], "sampler0" });
queries.push_back({ &locs_->samplerLocs_[1], "sampler1" });
queries.push_back({ &locs_->samplerLocs_[2], "sampler2" });
samplersToCheck = 3;
}
_assert_(queries.size() <= MAX_TEXTURE_SLOTS);
for (size_t i = 0; i < dynamicUniforms.uniforms.size(); ++i) {
queries.push_back({ &dynamicUniformLocs_[i], dynamicUniforms.uniforms[i].name });
queries.push_back({ &locs_->dynamicUniformLocs_[i], dynamicUniforms.uniforms[i].name });
}
std::vector<GLRProgram::Initializer> initialize;
for (int i = 0; i < MAX_TEXTURE_SLOTS; ++i) {
if (i < (int)samplers_.size()) {
initialize.push_back({ &samplerLocs_[i], 0, i });
if (i < samplersToCheck) {
initialize.push_back({ &locs_->samplerLocs_[i], 0, i });
} else {
samplerLocs_[i] = -1;
locs_->samplerLocs_[i] = -1;
}
}
GLRProgramFlags flags{};
program_ = render_->CreateProgram(linkShaders, semantics, queries, initialize, flags);
program_ = render_->CreateProgram(linkShaders, semantics, queries, initialize, locs_, flags);
return true;
}
@@ -1263,7 +1306,7 @@ void OpenGLContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {
for (size_t i = 0; i < curPipeline_->dynamicUniforms.uniforms.size(); ++i) {
const auto &uniform = curPipeline_->dynamicUniforms.uniforms[i];
const GLint &loc = curPipeline_->dynamicUniformLocs_[i];
const GLint &loc = curPipeline_->locs_->dynamicUniformLocs_[i];
const float *data = (const float *)((uint8_t *)ub + uniform.offset);
switch (uniform.type) {
case UniformType::FLOAT1:
@@ -1390,6 +1433,9 @@ void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) {
Framebuffer *OpenGLContext::CreateFramebuffer(const FramebufferDesc &desc) {
CheckGLExtensions();
// TODO: Support multiview later. (It's our only use case for multi layers).
_dbg_assert_(desc.numLayers == 1);
GLRFramebuffer *framebuffer = renderManager_.CreateFramebuffer(desc.width, desc.height, desc.z_stencil);
OpenGLFramebuffer *fbo = new OpenGLFramebuffer(&renderManager_, framebuffer);
return fbo;
@@ -1436,7 +1482,7 @@ bool OpenGLContext::BlitFramebuffer(Framebuffer *fbsrc, int srcX1, int srcY1, in
return true;
}
void OpenGLContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int color) {
void OpenGLContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) {
OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo;
_assert_(binding < MAX_TEXTURE_SLOTS);
@@ -1453,7 +1499,7 @@ void OpenGLContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBCh
aspect |= GL_STENCIL_BUFFER_BIT;
boundTextures_[binding] = &fb->framebuffer_->z_stencil_texture;
}
renderManager_.BindFramebufferAsTexture(fb->framebuffer_, binding, aspect, color);
renderManager_.BindFramebufferAsTexture(fb->framebuffer_, binding, aspect);
}
void OpenGLContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) {
@@ -1499,6 +1545,13 @@ uint32_t OpenGLContext::GetDataFormatSupport(DataFormat fmt) const {
case DataFormat::R8_UNORM:
return FMT_TEXTURE;
case DataFormat::R16_UNORM:
if (!gl_extensions.IsGLES) {
return FMT_TEXTURE;
} else {
return 0;
}
break;
case DataFormat::BC1_RGBA_UNORM_BLOCK:
case DataFormat::BC2_UNORM_BLOCK:
+10 -2
View File
@@ -4,9 +4,12 @@
#include <cstdint>
#include <cstddef> // for size_t
#include "Common/Common.h"
// GLSL_1xx and GLSL_3xx each cover a lot of sub variants. All the little quirks
// that differ are covered in ShaderLanguageDesc.
// Defined as a bitmask so stuff like GetSupportedShaderLanguages can return combinations.
// TODO: We can probably move away from this distinction soon, now that we mostly generate/translate shaders.
enum ShaderLanguage {
GLSL_1xx = 1,
GLSL_3xx = 2,
@@ -30,7 +33,6 @@ enum class ShaderStage {
const char *ShaderStageAsString(ShaderStage lang);
struct ShaderLanguageDesc {
ShaderLanguageDesc() {}
explicit ShaderLanguageDesc(ShaderLanguage lang);
@@ -91,12 +93,18 @@ struct UniformDef {
int index;
};
enum class SamplerFlags {
ARRAY_ON_VULKAN = 1,
};
ENUM_CLASS_BITOPS(SamplerFlags);
struct SamplerDef {
int binding; // Might only be used by some backends.
const char *name;
SamplerFlags flags;
// TODO: Might need unsigned samplers, 3d samplers, or other types in the future.
};
// For passing error messages from shader compilation (and other critical issues) back to the host.
// This can run on any thread - be aware!
// TODO: See if we can find a less generic name for this.
+4 -4
View File
@@ -98,7 +98,7 @@ R"(#version 450
)";
static const char *vulkanUboDecl = R"(
layout (std140, set = 0, binding = 0) uniform Data {
layout (std140, set = 1, binding = 0) uniform Data {
vec2 u_texelDelta;
vec2 u_pixelDelta;
vec4 u_time;
@@ -187,11 +187,11 @@ bool ConvertToVulkanGLSL(std::string *dest, TranslatedShaderMetadata *destMetada
continue;
} else if (line.find("uniform sampler2D") == 0) {
if (sscanf(line.c_str(), "uniform sampler2D sampler%d", &num) == 1)
line = StringFromFormat("layout(set = 0, binding = %d) ", num + 1) + line;
line = StringFromFormat("layout(set = 1, binding = %d) ", num + 1) + line;
else if (line.find("sampler0") != line.npos)
line = "layout(set = 0, binding = 1) " + line;
line = "layout(set = 1, binding = 1) " + line;
else
line = "layout(set = 0, binding = 2) " + line;
line = "layout(set = 1, binding = 2) " + line;
} else if (line.find("uniform ") != std::string::npos) {
continue;
} else if (2 == sscanf(line.c_str(), "varying vec%d v_texcoord%d;", &vecSize, &num)) {
+71 -28
View File
@@ -7,7 +7,6 @@
#include "Common/Log.h"
const char * const vulkan_glsl_preamble_fs =
"#version 450\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"#extension GL_ARB_shading_language_420pack : enable\n"
"#extension GL_ARB_conservative_depth : enable\n"
@@ -45,7 +44,6 @@ static const char * const hlsl_d3d9_preamble_fs =
"#define DISCARD_BELOW(x) clip(x)\n";
static const char * const vulkan_glsl_preamble_vs =
"#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"
@@ -69,7 +67,6 @@ static const char * const hlsl_preamble_gs =
"\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"
@@ -113,9 +110,17 @@ ShaderWriter & ShaderWriter::F(const char *format, ...) {
return *this;
}
void ShaderWriter::Preamble(const char **gl_extensions, size_t num_gl_extensions) {
void ShaderWriter::Preamble(Slice<const char *> extensions) {
switch (lang_.shaderLanguage) {
case GLSL_VULKAN:
C("#version 450\n");
if (flags_ & ShaderWriterFlags::FS_AUTO_STEREO) {
C("#extension GL_EXT_multiview : enable\n");
}
// IMPORTANT! Extensions must be the first thing after #version.
for (size_t i = 0; i < extensions.size(); i++) {
F("%s\n", extensions[i]);
}
switch (stage_) {
case ShaderStage::Vertex:
W(vulkan_glsl_preamble_vs);
@@ -154,8 +159,8 @@ void ShaderWriter::Preamble(const char **gl_extensions, size_t num_gl_extensions
default: // OpenGL
F("#version %d%s\n", lang_.glslVersionNumber, lang_.gles && lang_.glslES30 ? " es" : "");
// IMPORTANT! Extensions must be the first thing after #version.
for (size_t i = 0; i < num_gl_extensions; i++) {
F("%s\n", gl_extensions[i]);
for (size_t i = 0; i < extensions.size(); i++) {
F("%s\n", extensions[i]);
}
// Print some system info - useful to gather information directly from screenshots.
if (strlen(lang_.driverInfo) != 0) {
@@ -250,7 +255,7 @@ void ShaderWriter::BeginVSMain(Slice<InputDef> inputs, Slice<UniformDef> uniform
}
}
void ShaderWriter::BeginFSMain(Slice<UniformDef> uniforms, Slice<VaryingDef> varyings, FSFlags flags) {
void ShaderWriter::BeginFSMain(Slice<UniformDef> uniforms, Slice<VaryingDef> varyings) {
_assert_(this->stage_ == ShaderStage::Fragment);
switch (lang_.shaderLanguage) {
case HLSL_D3D11:
@@ -264,13 +269,13 @@ void ShaderWriter::BeginFSMain(Slice<UniformDef> uniforms, Slice<VaryingDef> var
C("};\n");
}
if (flags & FSFLAG_WRITEDEPTH) {
if (flags_ & ShaderWriterFlags::FS_WRITE_DEPTH) {
C("float gl_FragDepth;\n");
}
C("struct PS_OUT {\n");
C(" vec4 target : SV_Target0;\n");
if (flags & FSFLAG_WRITEDEPTH) {
if (flags_ & ShaderWriterFlags::FS_WRITE_DEPTH) {
C(" float depth : SV_Depth;\n");
}
C("};\n");
@@ -285,14 +290,14 @@ void ShaderWriter::BeginFSMain(Slice<UniformDef> uniforms, Slice<VaryingDef> var
F(") {\n");
C(" PS_OUT ps_out;\n");
if (flags & FSFLAG_WRITEDEPTH) {
if (flags_ & ShaderWriterFlags::FS_WRITE_DEPTH) {
C(" float gl_FragDepth;\n");
}
break;
case HLSL_D3D9:
C("struct PS_OUT {\n");
C(" vec4 target : SV_Target0;\n");
if (flags & FSFLAG_WRITEDEPTH) {
if (flags_ & ShaderWriterFlags::FS_WRITE_DEPTH) {
C(" float depth : DEPTH;\n");
}
C("};\n");
@@ -310,7 +315,7 @@ void ShaderWriter::BeginFSMain(Slice<UniformDef> uniforms, Slice<VaryingDef> var
F(") {\n");
C(" PS_OUT ps_out;\n");
if (flags & FSFLAG_WRITEDEPTH) {
if (flags_ & ShaderWriterFlags::FS_WRITE_DEPTH) {
C(" float gl_FragDepth;\n");
}
@@ -321,7 +326,7 @@ void ShaderWriter::BeginFSMain(Slice<UniformDef> uniforms, Slice<VaryingDef> var
}
C("layout(location = 0, index = 0) out vec4 fragColor0;\n");
if (!uniforms.is_empty()) {
C("layout(std140, set = 0, binding = 0) uniform bufferVals {\n");
C("layout(std140, set = 1, binding = 0) uniform bufferVals {\n");
for (auto &uniform : uniforms) {
F("%s %s;\n", uniform.type, uniform.name);
}
@@ -404,13 +409,13 @@ void ShaderWriter::EndVSMain(Slice<VaryingDef> varyings) {
C("}\n");
}
void ShaderWriter::EndFSMain(const char *vec4_color_variable, FSFlags flags) {
void ShaderWriter::EndFSMain(const char *vec4_color_variable) {
_assert_(this->stage_ == ShaderStage::Fragment);
switch (lang_.shaderLanguage) {
case HLSL_D3D11:
case HLSL_D3D9:
F(" ps_out.target = %s;\n", vec4_color_variable);
if (flags & FSFLAG_WRITEDEPTH) {
if (flags_ & ShaderWriterFlags::FS_WRITE_DEPTH) {
C(" ps_out.depth = gl_FragDepth;\n");
}
C(" return ps_out;\n");
@@ -454,34 +459,43 @@ void ShaderWriter::ConstFloat(const char *name, float value) {
void ShaderWriter::DeclareSamplers(Slice<SamplerDef> samplers) {
for (int i = 0; i < (int)samplers.size(); i++) {
DeclareTexture2D(samplers[i].name, i);
DeclareSampler2D(samplers[i].name, i);
DeclareTexture2D(samplers[i]);
DeclareSampler2D(samplers[i]);
}
samplerDefs_ = samplers;
}
void ShaderWriter::DeclareTexture2D(const char *name, int binding) {
void ShaderWriter::ApplySamplerMetadata(Slice<SamplerDef> samplers) {
samplerDefs_ = samplers;
}
void ShaderWriter::DeclareTexture2D(const SamplerDef &def) {
switch (lang_.shaderLanguage) {
case HLSL_D3D11:
F("Texture2D<float4> %s : register(t%d);\n", name, binding);
F("Texture2D<float4> %s : register(t%d);\n", def.name, def.binding);
break;
case HLSL_D3D9:
F("sampler %s: register(s%d);\n", name, binding);
F("sampler %s: register(s%d);\n", def.name, def.binding);
break;
case GLSL_VULKAN:
// In the thin3d descriptor set layout, textures start at 1 in set 0. Hence the +1.
F("layout(set = 0, binding = %d) uniform sampler2D %s;\n", binding + 1, name);
// texBindingBase_ is used for the thin3d descriptor set layout, where they start at 1.
if (def.flags & SamplerFlags::ARRAY_ON_VULKAN) {
F("layout(set = 1, binding = %d) uniform sampler2DArray %s;\n", def.binding + texBindingBase_, def.name);
} else {
F("layout(set = 1, binding = %d) uniform sampler2D %s;\n", def.binding + texBindingBase_, def.name);
}
break;
default:
F("uniform sampler2D %s;\n", name);
F("uniform sampler2D %s;\n", def.name);
break;
}
}
void ShaderWriter::DeclareSampler2D(const char *name, int binding) {
void ShaderWriter::DeclareSampler2D(const SamplerDef &def) {
// We only use separate samplers in HLSL D3D11, where we have no choice.
switch (lang_.shaderLanguage) {
case HLSL_D3D11:
F("SamplerState %sSamp : register(s%d);\n", name, binding);
F("SamplerState %sSamp : register(s%d);\n", def.name, def.binding);
break;
default:
break;
@@ -489,6 +503,7 @@ void ShaderWriter::DeclareSampler2D(const char *name, int binding) {
}
ShaderWriter &ShaderWriter::SampleTexture2D(const char *sampName, const char *uv) {
const SamplerDef *samp = GetSamplerDef(sampName);
switch (lang_.shaderLanguage) {
case HLSL_D3D11:
F("%s.Sample(%sSamp, %s)", sampName, sampName, uv);
@@ -498,13 +513,20 @@ ShaderWriter &ShaderWriter::SampleTexture2D(const char *sampName, const char *uv
break;
default:
// Note: we ignore the sampler. make sure you bound samplers to the textures correctly.
F("%s(%s, %s)", lang_.texture, sampName, uv);
if (samp && (samp->flags & SamplerFlags::ARRAY_ON_VULKAN) && lang_.shaderLanguage == GLSL_VULKAN) {
const char *index = (flags_ & ShaderWriterFlags::FS_AUTO_STEREO) ? "float(gl_ViewIndex)" : "0.0";
F("%s(%s, vec3(%s, %s))", lang_.texture, sampName, uv, index);
} else {
F("%s(%s, %s)", lang_.texture, sampName, uv);
}
break;
}
return *this;
}
ShaderWriter &ShaderWriter::SampleTexture2DOffset(const char *sampName, const char *uv, int offX, int offY) {
const SamplerDef *samp = GetSamplerDef(sampName);
switch (lang_.shaderLanguage) {
case HLSL_D3D11:
F("%s.Sample(%sSamp, %s, int2(%d, %d))", sampName, sampName, uv, offX, offY);
@@ -515,13 +537,20 @@ ShaderWriter &ShaderWriter::SampleTexture2DOffset(const char *sampName, const ch
break;
default:
// Note: we ignore the sampler. make sure you bound samplers to the textures correctly.
F("%sOffset(%s, %s, ivec2(%d, %d))", lang_.texture, sampName, uv, offX, offY);
if (samp && (samp->flags & SamplerFlags::ARRAY_ON_VULKAN) && lang_.shaderLanguage == GLSL_VULKAN) {
const char *index = (flags_ & ShaderWriterFlags::FS_AUTO_STEREO) ? "float(gl_ViewIndex)" : "0.0";
F("%sOffset(%s, vec3(%s, %s), ivec2(%d, %d))", lang_.texture, sampName, uv, index, offX, offY);
} else {
F("%sOffset(%s, %s, ivec2(%d, %d))", lang_.texture, sampName, uv, offX, offY);
}
break;
}
return *this;
}
ShaderWriter &ShaderWriter::LoadTexture2D(const char *sampName, const char *uv, int level) {
const SamplerDef *samp = GetSamplerDef(sampName);
switch (lang_.shaderLanguage) {
case HLSL_D3D11:
F("%s.Load(ivec3(%s, %d))", sampName, uv, level);
@@ -532,7 +561,12 @@ ShaderWriter &ShaderWriter::LoadTexture2D(const char *sampName, const char *uv,
break;
default:
// Note: we ignore the sampler. make sure you bound samplers to the textures correctly.
F("texelFetch(%s, %s, %d)", sampName, uv, level);
if (samp && (samp->flags & SamplerFlags::ARRAY_ON_VULKAN) && lang_.shaderLanguage == GLSL_VULKAN) {
const char *index = (flags_ & ShaderWriterFlags::FS_AUTO_STEREO) ? "gl_ViewIndex" : "0";
F("texelFetch(%s, vec3(%s, %s), %d)", sampName, uv, index, level);
} else {
F("texelFetch(%s, %s, %d)", sampName, uv, level);
}
break;
}
return *this;
@@ -553,3 +587,12 @@ ShaderWriter &ShaderWriter::GetTextureSize(const char *szVariable, const char *t
}
return *this;
}
const SamplerDef *ShaderWriter::GetSamplerDef(const char *name) const {
for (int i = 0; i < (int)samplerDefs_.size(); i++) {
if (!strcmp(samplerDefs_[i].name, name)) {
return &samplerDefs_[i];
}
}
return nullptr;
}
+26 -10
View File
@@ -30,15 +30,19 @@ struct VaryingDef {
const char *precision;
};
enum FSFlags {
FSFLAG_NONE = 0,
FSFLAG_WRITEDEPTH = 1,
enum class ShaderWriterFlags {
NONE = 0,
FS_WRITE_DEPTH = 1,
FS_AUTO_STEREO = 2, // Automatically indexes makes samplers tagged with `array` by gl_ViewIndex. Useful for stereo rendering.
};
ENUM_CLASS_BITOPS(ShaderWriterFlags);
class ShaderWriter {
public:
ShaderWriter(char *buffer, const ShaderLanguageDesc &lang, ShaderStage stage, const char **gl_extensions = nullptr, size_t num_gl_extensions = 0) : p_(buffer), lang_(lang), stage_(stage) {
Preamble(gl_extensions, num_gl_extensions);
// Extensions are supported for both OpenGL ES and Vulkan (though of course, they're different).
ShaderWriter(char *buffer, const ShaderLanguageDesc &lang, ShaderStage stage, Slice<const char *> extensions = Slice<const char *>(), ShaderWriterFlags flags = ShaderWriterFlags::NONE) : p_(buffer), lang_(lang), stage_(stage), flags_(flags) {
buffer[0] = '\0';
Preamble(extensions);
}
ShaderWriter(const ShaderWriter &) = delete;
@@ -73,9 +77,16 @@ public:
void HighPrecisionFloat();
void LowPrecisionFloat();
// NOTE: samplers must live for the rest of ShaderWriter's lifetime. No way to express that in C++ though :(
void DeclareSamplers(Slice<SamplerDef> samplers);
// Same as DeclareSamplers, but doesn't actually declare them.
// This is currently only required by FragmentShaderGenerator.
void ApplySamplerMetadata(Slice<SamplerDef> samplers);
void ConstFloat(const char *name, float value);
void SetFlags(ShaderWriterFlags flags) { flags_ |= flags; }
void SetTexBindingBase(int base) { texBindingBase_ = base; }
ShaderWriter &SampleTexture2D(const char *texName, const char *uv);
ShaderWriter &SampleTexture2DOffset(const char *texName, const char *uv, int offX, int offY);
@@ -84,12 +95,12 @@ public:
// Simple shaders with no special tricks.
void BeginVSMain(Slice<InputDef> inputs, Slice<UniformDef> uniforms, Slice<VaryingDef> varyings);
void BeginFSMain(Slice<UniformDef> uniforms, Slice<VaryingDef> varyings, FSFlags flags);
void BeginFSMain(Slice<UniformDef> uniforms, Slice<VaryingDef> varyings);
void BeginGSMain(Slice<VaryingDef> varyings, Slice<VaryingDef> outVaryings);
// For simple shaders that output a single color, we can deal with this generically.
void EndVSMain(Slice<VaryingDef> varyings);
void EndFSMain(const char *vec4_color_variable, FSFlags flags);
void EndFSMain(const char *vec4_color_variable);
void EndGSMain();
const ShaderLanguageDesc &Lang() const {
@@ -107,12 +118,17 @@ public:
private:
// Several of the shader languages ignore samplers, beware of that.
void DeclareSampler2D(const char *name, int binding);
void DeclareTexture2D(const char *name, int binding);
void DeclareSampler2D(const SamplerDef &def);
void DeclareTexture2D(const SamplerDef &def);
const SamplerDef *GetSamplerDef(const char *name) const;
void Preamble(const char **gl_extensions, size_t num_gl_extensions);
void Preamble(Slice<const char *> extensions);
char *p_;
const ShaderLanguageDesc &lang_;
const ShaderStage stage_;
Slice<SamplerDef> samplers_;
ShaderWriterFlags flags_ = ShaderWriterFlags::NONE;
Slice<SamplerDef> samplerDefs_;
int texBindingBase_ = 1;
};
+1
View File
@@ -9,4 +9,5 @@ void VulkanBarrier::Flush(VkCommandBuffer cmd) {
imageBarriers_.clear();
srcStageMask_ = 0;
dstStageMask_ = 0;
dependencyFlags_ = 0;
}
+4 -4
View File
@@ -14,7 +14,7 @@ class VulkanContext;
class VulkanBarrier {
public:
void TransitionImage(
VkImage image, int baseMip, int numMipLevels, VkImageAspectFlags aspectMask,
VkImage image, int baseMip, int numMipLevels, int numLayers, VkImageAspectFlags aspectMask,
VkImageLayout oldImageLayout, VkImageLayout newImageLayout,
VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask,
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask
@@ -36,7 +36,7 @@ public:
imageBarrier.subresourceRange.aspectMask = aspectMask;
imageBarrier.subresourceRange.baseMipLevel = baseMip;
imageBarrier.subresourceRange.levelCount = numMipLevels;
imageBarrier.subresourceRange.layerCount = 1; // We never use more than one layer, and old Mali drivers have problems with VK_REMAINING_ARRAY_LAYERS/VK_REMAINING_MIP_LEVELS.
imageBarrier.subresourceRange.layerCount = numLayers; // NOTE: We could usually use VK_REMAINING_ARRAY_LAYERS/VK_REMAINING_MIP_LEVELS, but really old Mali drivers have problems with those.
imageBarrier.subresourceRange.baseArrayLayer = 0;
imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
@@ -46,7 +46,7 @@ public:
// Automatically determines access and stage masks from layouts.
// Not universally usable, but works for PPSSPP's use.
void TransitionImageAuto(
VkImage image, int baseMip, int numMipLevels, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout
VkImage image, int baseMip, int numMipLevels, int numLayers, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout
) {
_dbg_assert_(image != VK_NULL_HANDLE);
@@ -104,7 +104,7 @@ public:
imageBarrier.subresourceRange.aspectMask = aspectMask;
imageBarrier.subresourceRange.baseMipLevel = baseMip;
imageBarrier.subresourceRange.levelCount = numMipLevels;
imageBarrier.subresourceRange.layerCount = 1; // We never use more than one layer, and old Mali drivers have problems with VK_REMAINING_ARRAY_LAYERS/VK_REMAINING_MIP_LEVELS.
imageBarrier.subresourceRange.layerCount = numLayers; // NOTE: We could usually use VK_REMAINING_ARRAY_LAYERS/VK_REMAINING_MIP_LEVELS, but really old Mali drivers have problems with those.
imageBarrier.subresourceRange.baseArrayLayer = 0;
imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+71 -63
View File
@@ -57,7 +57,7 @@ std::string VulkanVendorString(uint32_t vendorId) {
case VULKAN_VENDOR_ARM: return "ARM";
case VULKAN_VENDOR_QUALCOMM: return "Qualcomm";
case VULKAN_VENDOR_IMGTEC: return "Imagination";
case VULKAN_VENDOR_APPLE: return "Apple";
default:
return StringFromFormat("%08x", vendorId);
}
@@ -253,15 +253,21 @@ VkResult VulkanContext::CreateInstance(const CreateInfo &info) {
VkPhysicalDeviceProperties2 props2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2};
VkPhysicalDevicePushDescriptorPropertiesKHR pushProps{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR};
VkPhysicalDeviceExternalMemoryHostPropertiesEXT extHostMemProps{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT};
VkPhysicalDeviceDepthStencilResolveProperties depthStencilResolveProps{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES};
props2.pNext = &pushProps;
pushProps.pNext = &extHostMemProps;
extHostMemProps.pNext = &depthStencilResolveProps;
vkGetPhysicalDeviceProperties2KHR(physical_devices_[i], &props2);
// Don't want bad pointers sitting around.
props2.pNext = nullptr;
pushProps.pNext = nullptr;
extHostMemProps.pNext = nullptr;
depthStencilResolveProps.pNext = nullptr;
physicalDeviceProperties_[i].properties = props2.properties;
physicalDeviceProperties_[i].pushDescriptorProperties = pushProps;
physicalDeviceProperties_[i].externalMemoryHostProperties = extHostMemProps;
physicalDeviceProperties_[i].depthStencilResolve = depthStencilResolveProps;
}
} else {
for (uint32_t i = 0; i < gpu_count; i++) {
@@ -329,7 +335,7 @@ bool VulkanContext::MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirem
for (uint32_t i = 0; i < 32; i++) {
if ((typeBits & 1) == 1) {
// Type is available, does it match user properties?
if ((memory_properties.memoryTypes[i].propertyFlags & requirements_mask) == requirements_mask) {
if ((memory_properties_.memoryTypes[i].propertyFlags & requirements_mask) == requirements_mask) {
*typeIndex = i;
return true;
}
@@ -569,38 +575,48 @@ void VulkanContext::ChooseDevice(int physical_device) {
}
// This is as good a place as any to do this.
vkGetPhysicalDeviceMemoryProperties(physical_devices_[physical_device_], &memory_properties);
INFO_LOG(G3D, "Memory Types (%d):", memory_properties.memoryTypeCount);
for (int i = 0; i < (int)memory_properties.memoryTypeCount; i++) {
vkGetPhysicalDeviceMemoryProperties(physical_devices_[physical_device_], &memory_properties_);
INFO_LOG(G3D, "Memory Types (%d):", memory_properties_.memoryTypeCount);
for (int i = 0; i < (int)memory_properties_.memoryTypeCount; i++) {
// Don't bother printing dummy memory types.
if (!memory_properties.memoryTypes[i].propertyFlags)
if (!memory_properties_.memoryTypes[i].propertyFlags)
continue;
INFO_LOG(G3D, " %d: Heap %d; Flags: %s%s%s%s ", i, memory_properties.memoryTypes[i].heapIndex,
(memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) ? "DEVICE_LOCAL " : "",
(memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) ? "HOST_VISIBLE " : "",
(memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) ? "HOST_CACHED " : "",
(memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) ? "HOST_COHERENT " : "");
INFO_LOG(G3D, " %d: Heap %d; Flags: %s%s%s%s ", i, memory_properties_.memoryTypes[i].heapIndex,
(memory_properties_.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) ? "DEVICE_LOCAL " : "",
(memory_properties_.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) ? "HOST_VISIBLE " : "",
(memory_properties_.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) ? "HOST_CACHED " : "",
(memory_properties_.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) ? "HOST_COHERENT " : "");
}
// Optional features
if (extensionsLookup_.KHR_get_physical_device_properties2) {
VkPhysicalDeviceFeatures2 features2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR};
// Add to chain even if not supported, GetPhysicalDeviceFeatures is supposed to ignore unknown structs.
VkPhysicalDeviceMultiviewFeatures multiViewFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES };
features2.pNext = &multiViewFeatures;
vkGetPhysicalDeviceFeatures2KHR(physical_devices_[physical_device_], &features2);
deviceFeatures_.available = features2.features;
deviceFeatures_.available.standard = features2.features;
deviceFeatures_.available.multiview = multiViewFeatures;
} else {
vkGetPhysicalDeviceFeatures(physical_devices_[physical_device_], &deviceFeatures_.available);
vkGetPhysicalDeviceFeatures(physical_devices_[physical_device_], &deviceFeatures_.available.standard);
deviceFeatures_.available.multiview = {};
}
deviceFeatures_.enabled = {};
// Enable a few safe ones if they are available.
deviceFeatures_.enabled.dualSrcBlend = deviceFeatures_.available.dualSrcBlend;
deviceFeatures_.enabled.logicOp = deviceFeatures_.available.logicOp;
deviceFeatures_.enabled.depthClamp = deviceFeatures_.available.depthClamp;
deviceFeatures_.enabled.depthBounds = deviceFeatures_.available.depthBounds;
deviceFeatures_.enabled.samplerAnisotropy = deviceFeatures_.available.samplerAnisotropy;
deviceFeatures_.enabled.shaderClipDistance = deviceFeatures_.available.shaderClipDistance;
deviceFeatures_.enabled.shaderCullDistance = deviceFeatures_.available.shaderCullDistance;
deviceFeatures_.enabled.geometryShader = deviceFeatures_.available.geometryShader;
deviceFeatures_.enabled.standard.dualSrcBlend = deviceFeatures_.available.standard.dualSrcBlend;
deviceFeatures_.enabled.standard.logicOp = deviceFeatures_.available.standard.logicOp;
deviceFeatures_.enabled.standard.depthClamp = deviceFeatures_.available.standard.depthClamp;
deviceFeatures_.enabled.standard.depthBounds = deviceFeatures_.available.standard.depthBounds;
deviceFeatures_.enabled.standard.samplerAnisotropy = deviceFeatures_.available.standard.samplerAnisotropy;
deviceFeatures_.enabled.standard.shaderClipDistance = deviceFeatures_.available.standard.shaderClipDistance;
deviceFeatures_.enabled.standard.shaderCullDistance = deviceFeatures_.available.standard.shaderCullDistance;
deviceFeatures_.enabled.standard.geometryShader = deviceFeatures_.available.standard.geometryShader;
deviceFeatures_.enabled.standard.sampleRateShading = deviceFeatures_.available.standard.sampleRateShading;
deviceFeatures_.enabled.multiview = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES };
deviceFeatures_.enabled.multiview.multiview = deviceFeatures_.available.multiview.multiview;
// deviceFeatures_.enabled.multiview.multiviewGeometryShader = deviceFeatures_.available.multiview.multiviewGeometryShader;
GetDeviceLayerExtensionList(nullptr, device_extension_properties_);
@@ -671,6 +687,8 @@ VkResult VulkanContext::CreateDevice() {
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);
VkPhysicalDeviceFeatures2 features2{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 };
VkDeviceCreateInfo device_info{ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
device_info.queueCreateInfoCount = 1;
device_info.pQueueCreateInfos = &queue_info;
@@ -678,7 +696,14 @@ VkResult VulkanContext::CreateDevice() {
device_info.ppEnabledLayerNames = device_info.enabledLayerCount ? device_layer_names_.data() : nullptr;
device_info.enabledExtensionCount = (uint32_t)device_extensions_enabled_.size();
device_info.ppEnabledExtensionNames = device_info.enabledExtensionCount ? device_extensions_enabled_.data() : nullptr;
device_info.pEnabledFeatures = &deviceFeatures_.enabled;
if (extensionsLookup_.KHR_get_physical_device_properties2) {
device_info.pNext = &features2;
features2.features = deviceFeatures_.enabled.standard;
features2.pNext = &deviceFeatures_.enabled.multiview;
} else {
device_info.pEnabledFeatures = &deviceFeatures_.enabled.standard;
}
VkResult res = vkCreateDevice(physical_devices_[physical_device_], &device_info, nullptr, &device_);
if (res != VK_SUCCESS) {
@@ -1084,9 +1109,14 @@ bool VulkanContext::InitSwapchain() {
std::string currentTransform = surface_transforms_to_string(surfCapabilities_.currentTransform);
g_display_rotation = DisplayRotation::ROTATE_0;
g_display_rot_matrix.setIdentity();
uint32_t allowedRotations = VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR;
// Hack: Don't allow 270 degrees pretransform (inverse landscape), it creates bizarre issues on some devices (see #15773).
allowedRotations &= ~VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR;
if (surfCapabilities_.currentTransform & (VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR | VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR)) {
preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
} else if (surfCapabilities_.currentTransform & (VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR)) {
} else if (surfCapabilities_.currentTransform & allowedRotations) {
// Normal, sensible rotations. Let's handle it.
preTransform = surfCapabilities_.currentTransform;
g_display_rot_matrix.setIdentity();
@@ -1109,20 +1139,29 @@ bool VulkanContext::InitSwapchain() {
_dbg_assert_(false);
}
} else {
// Let the OS rotate the image (potentially slow on many Android devices)
// Let the OS rotate the image (potentially slower on many Android devices)
preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
}
std::string preTransformStr = surface_transforms_to_string(preTransform);
INFO_LOG(G3D, "Transform supported: %s current: %s chosen: %s", supportedTransforms.c_str(), currentTransform.c_str(), preTransformStr.c_str());
if (physicalDeviceProperties_[physical_device_].properties.vendorID == VULKAN_VENDOR_IMGTEC) {
INFO_LOG(G3D, "Applying PowerVR hack (rounding off the width!)");
// Swap chain width hack to avoid issue #11743 (PowerVR driver bug).
// To keep the size consistent even with pretransform, do this after the swap. Should be fine.
// This is fixed in newer PowerVR drivers but I don't know the cutoff.
swapChainExtent_.width &= ~31;
u32 driverVersion = physicalDeviceProperties_[physical_device_].properties.driverVersion;
// Cutoff the hack at driver version 1.386.1368 (0x00582558, see issue #15773).
if (driverVersion < 0x00582558) {
INFO_LOG(G3D, "Applying PowerVR hack (rounding off the width!) driverVersion=%08x", driverVersion);
// Swap chain width hack to avoid issue #11743 (PowerVR driver bug).
// To keep the size consistent even with pretransform, do this after the swap. Should be fine.
// This is fixed in newer PowerVR drivers but I don't know the cutoff.
swapChainExtent_.width &= ~31;
// TODO: Also modify display_xres/display_yres appropriately for scissors to match.
// This will get a bit messy. Ideally we should remove that logic from app-android.cpp
// and move it here, but the OpenGL code still needs it.
} else {
INFO_LOG(G3D, "PowerVR driver version new enough (%08x), not applying swapchain width hack", driverVersion);
}
}
VkSwapchainCreateInfoKHR swap_chain_info{ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
@@ -1227,7 +1266,7 @@ bool VulkanContext::CreateShaderModule(const std::vector<uint32_t> &spirv, VkSha
}
}
void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, int baseMip, int numMipLevels, VkImageAspectFlags aspectMask,
void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, int baseMip, int numMipLevels, int numLayers, VkImageAspectFlags aspectMask,
VkImageLayout oldImageLayout, VkImageLayout newImageLayout,
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask) {
@@ -1240,7 +1279,7 @@ void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, int baseMip, int
image_memory_barrier.subresourceRange.aspectMask = aspectMask;
image_memory_barrier.subresourceRange.baseMipLevel = baseMip;
image_memory_barrier.subresourceRange.levelCount = numMipLevels;
image_memory_barrier.subresourceRange.layerCount = 1; // We never use more than one layer, and old Mali drivers have problems with VK_REMAINING_ARRAY_LAYERS/VK_REMAINING_MIP_LEVELS.
image_memory_barrier.subresourceRange.layerCount = numLayers; // We never use more than one layer, and old Mali drivers have problems with VK_REMAINING_ARRAY_LAYERS/VK_REMAINING_MIP_LEVELS.
image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
vkCmdPipelineBarrier(cmd, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
@@ -1354,37 +1393,6 @@ void finalize_glslang() {
glslang::FinalizeProcess();
}
const char *VulkanResultToString(VkResult res) {
switch (res) {
case VK_NOT_READY: return "VK_NOT_READY";
case VK_TIMEOUT: return "VK_TIMEOUT";
case VK_EVENT_SET: return "VK_EVENT_SET";
case VK_EVENT_RESET: return "VK_EVENT_RESET";
case VK_INCOMPLETE: return "VK_INCOMPLETE";
case VK_ERROR_OUT_OF_HOST_MEMORY: return "VK_ERROR_OUT_OF_HOST_MEMORY";
case VK_ERROR_OUT_OF_DEVICE_MEMORY: return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
case VK_ERROR_INITIALIZATION_FAILED: return "VK_ERROR_INITIALIZATION_FAILED";
case VK_ERROR_DEVICE_LOST: return "VK_ERROR_DEVICE_LOST";
case VK_ERROR_MEMORY_MAP_FAILED: return "VK_ERROR_MEMORY_MAP_FAILED";
case VK_ERROR_LAYER_NOT_PRESENT: return "VK_ERROR_LAYER_NOT_PRESENT";
case VK_ERROR_EXTENSION_NOT_PRESENT: return "VK_ERROR_EXTENSION_NOT_PRESENT";
case VK_ERROR_FEATURE_NOT_PRESENT: return "VK_ERROR_FEATURE_NOT_PRESENT";
case VK_ERROR_INCOMPATIBLE_DRIVER: return "VK_ERROR_INCOMPATIBLE_DRIVER";
case VK_ERROR_TOO_MANY_OBJECTS: return "VK_ERROR_TOO_MANY_OBJECTS";
case VK_ERROR_FORMAT_NOT_SUPPORTED: return "VK_ERROR_FORMAT_NOT_SUPPORTED";
case VK_ERROR_SURFACE_LOST_KHR: return "VK_ERROR_SURFACE_LOST_KHR";
case VK_SUBOPTIMAL_KHR: return "VK_SUBOPTIMAL_KHR";
case VK_ERROR_OUT_OF_DATE_KHR: return "VK_ERROR_OUT_OF_DATE_KHR";
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
case VK_ERROR_OUT_OF_POOL_MEMORY_KHR: return "VK_ERROR_OUT_OF_POOL_MEMORY_KHR";
case VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR: return "VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR";
default:
return "VK_ERROR_...(unknown)";
}
}
void VulkanDeleteList::Take(VulkanDeleteList &del) {
_dbg_assert_(cmdPools_.empty());
_dbg_assert_(descPools_.empty());
+19 -5
View File
@@ -35,6 +35,7 @@ enum {
VULKAN_VENDOR_ARM = 0x000013B5, // Mali
VULKAN_VENDOR_QUALCOMM = 0x00005143,
VULKAN_VENDOR_IMGTEC = 0x00001010, // PowerVR
VULKAN_VENDOR_APPLE = 0x0000106b, // Apple through MoltenVK
};
VK_DEFINE_HANDLE(VmaAllocator);
@@ -222,6 +223,9 @@ public:
SetDebugNameImpl((uint64_t)handle, type, name);
}
}
bool DebugLayerEnabled() const {
return extensionsLookup_.EXT_debug_utils;
}
bool MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex);
@@ -250,6 +254,12 @@ public:
VkPhysicalDeviceProperties properties;
VkPhysicalDevicePushDescriptorPropertiesKHR pushDescriptorProperties;
VkPhysicalDeviceExternalMemoryHostPropertiesEXT externalMemoryHostProperties;
VkPhysicalDeviceDepthStencilResolveProperties depthStencilResolve;
};
struct AllPhysicalDeviceFeatures {
VkPhysicalDeviceFeatures standard;
VkPhysicalDeviceMultiviewFeatures multiview;
};
const PhysicalDeviceProps &GetPhysicalDeviceProperties(int i = -1) const {
@@ -275,9 +285,13 @@ public:
return device_extensions_enabled_;
}
const VkPhysicalDeviceMemoryProperties &GetMemoryProperties() const {
return memory_properties_;
}
struct PhysicalDeviceFeatures {
VkPhysicalDeviceFeatures available{};
VkPhysicalDeviceFeatures enabled{};
AllPhysicalDeviceFeatures available{};
AllPhysicalDeviceFeatures enabled{};
};
const PhysicalDeviceFeatures &GetDeviceFeatures() const { return deviceFeatures_; }
@@ -393,7 +407,8 @@ private:
uint32_t graphics_queue_family_index_ = -1;
std::vector<PhysicalDeviceProps> physicalDeviceProperties_;
std::vector<VkQueueFamilyProperties> queueFamilyProperties_;
VkPhysicalDeviceMemoryProperties memory_properties{};
VkPhysicalDeviceMemoryProperties memory_properties_{};
// Custom collection of things that are good to know
VulkanPhysicalDeviceInfo deviceInfo_{};
@@ -436,7 +451,7 @@ private:
};
// Detailed control.
void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, int baseMip, int mipLevels, VkImageAspectFlags aspectMask,
void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, int baseMip, int mipLevels, int numLayers, VkImageAspectFlags aspectMask,
VkImageLayout oldImageLayout, VkImageLayout newImageLayout,
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask);
@@ -453,7 +468,6 @@ enum class GLSLVariant {
bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *sourceCode, GLSLVariant variant, std::vector<uint32_t> &spirv, std::string *errorMessage);
const char *VulkanResultToString(VkResult res);
const char *VulkanColorSpaceToString(VkColorSpaceKHR colorSpace);
const char *VulkanFormatToString(VkFormat format);
+7
View File
@@ -42,6 +42,12 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback(
// UNASSIGNED
return false;
}
if (messageCode == 606910136 || messageCode == -392708513 || messageCode == -384083808) {
// VUID-vkCmdDraw-None-02686
// Kinda false positive, or at least very unnecessary, now that I solved the real issue.
// See https://github.com/hrydgard/ppsspp/pull/16354
return false;
}
if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
message << "ERROR(";
@@ -86,6 +92,7 @@ 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).
+1 -1
View File
@@ -121,7 +121,7 @@ VkCommandBuffer FrameData::GetInitCmd(VulkanContext *vulkan) {
}
void FrameData::SubmitPending(VulkanContext *vulkan, FrameSubmitType type, FrameDataShared &sharedData) {
VkCommandBuffer cmdBufs[2];
VkCommandBuffer cmdBufs[3];
int numCmdBufs = 0;
VkFence fenceToTrigger = VK_NULL_HANDLE;
+536
View File
@@ -0,0 +1,536 @@
#include "Common/StringUtils.h"
#include "Common/GPU/Vulkan/VulkanFramebuffer.h"
#include "Common/GPU/Vulkan/VulkanQueueRunner.h"
VkSampleCountFlagBits MultiSampleLevelToFlagBits(int count) {
// TODO: Check hardware support here, or elsewhere?
// Some hardware only supports 4x.
switch (count) {
case 0: return VK_SAMPLE_COUNT_1_BIT;
case 1: return VK_SAMPLE_COUNT_2_BIT;
case 2: return VK_SAMPLE_COUNT_4_BIT; // The only non-1 level supported on some mobile chips.
case 3: return VK_SAMPLE_COUNT_8_BIT;
case 4: return VK_SAMPLE_COUNT_16_BIT; // rare but exists, on Intel for example
default:
_assert_(false);
return VK_SAMPLE_COUNT_1_BIT;
}
}
void VKRImage::Delete(VulkanContext *vulkan) {
// Get rid of the views first, feels cleaner (but in reality doesn't matter).
if (rtView)
vulkan->Delete().QueueDeleteImageView(rtView);
if (texAllLayersView)
vulkan->Delete().QueueDeleteImageView(texAllLayersView);
for (int i = 0; i < 2; i++) {
if (texLayerViews[i]) {
vulkan->Delete().QueueDeleteImageView(texLayerViews[i]);
}
}
if (image) {
_dbg_assert_(alloc);
vulkan->Delete().QueueDeleteImageAllocation(image, alloc);
}
}
VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, int _numLayers, int _multiSampleLevel, bool createDepthStencilBuffer, const char *tag)
: vulkan_(vk), tag_(tag), width(_width), height(_height), numLayers(_numLayers) {
_dbg_assert_(tag);
CreateImage(vulkan_, initCmd, color, width, height, numLayers, VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, tag);
if (createDepthStencilBuffer) {
CreateImage(vulkan_, initCmd, depth, width, height, numLayers, VK_SAMPLE_COUNT_1_BIT, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, tag);
}
if (_multiSampleLevel > 0) {
sampleCount = MultiSampleLevelToFlagBits(_multiSampleLevel);
// TODO: Create a different tag for these?
CreateImage(vulkan_, initCmd, msaaColor, width, height, numLayers, sampleCount, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, tag);
if (createDepthStencilBuffer) {
CreateImage(vulkan_, initCmd, msaaDepth, width, height, numLayers, sampleCount, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, tag);
}
} else {
sampleCount = VK_SAMPLE_COUNT_1_BIT;
}
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.
}
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.rtView, 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.rtView, VK_OBJECT_TYPE_IMAGE_VIEW, name);
}
for (size_t rpType = 0; rpType < (size_t)RenderPassType::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) {
bool multiview = RenderPassTypeHasMultiView(rpType);
if (framebuf[(int)rpType]) {
return framebuf[(int)rpType];
}
VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
VkImageView views[4]{};
bool hasDepth = RenderPassTypeHasDepth(rpType);
int attachmentCount = 0;
views[attachmentCount++] = color.rtView; // 2D array texture if multilayered.
if (hasDepth) {
if (!depth.rtView) {
WARN_LOG(G3D, "depth render type to non-depth fb: %p %p fmt=%d (%s %dx%d)", depth.image, depth.texAllLayersView, depth.format, tag_.c_str(), width, height);
// Will probably crash, depending on driver.
}
views[attachmentCount++] = depth.rtView;
}
if (rpType & RenderPassType::MULTISAMPLE) {
views[attachmentCount++] = msaaColor.rtView;
if (hasDepth) {
views[attachmentCount++] = msaaDepth.rtView;
}
}
fbci.renderPass = compatibleRenderPass->Get(vulkan_, rpType, sampleCount);
fbci.attachmentCount = attachmentCount;
fbci.pAttachments = views;
fbci.width = width;
fbci.height = height;
fbci.layers = 1; // With multiview, this should be set as 1.
VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf[(int)rpType]);
_assert_(res == VK_SUCCESS);
if (!tag_.empty() && vulkan_->Extensions().EXT_debug_utils) {
vulkan_->SetDebugName(framebuf[(int)rpType], VK_OBJECT_TYPE_FRAMEBUFFER, StringFromFormat("fb_%s", tag_.c_str()).c_str());
}
return framebuf[(int)rpType];
}
VKRFramebuffer::~VKRFramebuffer() {
color.Delete(vulkan_);
depth.Delete(vulkan_);
msaaColor.Delete(vulkan_);
msaaDepth.Delete(vulkan_);
for (auto &fb : framebuf) {
if (fb) {
vulkan_->Delete().QueueDeleteFramebuffer(fb);
}
}
}
// NOTE: If numLayers > 1, it will create an array texture, rather than a normal 2D texture.
// This requires a different sampling path!
void VKRFramebuffer::CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, int numLayers, VkSampleCountFlagBits sampleCount, VkFormat format, VkImageLayout initialLayout, bool color, const char *tag) {
// We don't support more exotic layer setups for now. Mono or stereo.
_dbg_assert_(numLayers == 1 || numLayers == 2);
VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
ici.arrayLayers = numLayers;
ici.mipLevels = 1;
ici.extent.width = width;
ici.extent.height = height;
ici.extent.depth = 1;
ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
ici.imageType = VK_IMAGE_TYPE_2D;
ici.samples = sampleCount;
ici.tiling = VK_IMAGE_TILING_OPTIMAL;
ici.format = format;
ici.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
if (sampleCount == VK_SAMPLE_COUNT_1_BIT) {
ici.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
}
if (color) {
ici.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
if (sampleCount == VK_SAMPLE_COUNT_1_BIT) {
ici.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
}
} else {
ici.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
}
VmaAllocationCreateInfo allocCreateInfo{};
allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
VmaAllocationInfo allocInfo{};
VkResult res = vmaCreateImage(vulkan->Allocator(), &ici, &allocCreateInfo, &img.image, &img.alloc, &allocInfo);
_dbg_assert_(res == VK_SUCCESS);
VkImageAspectFlags aspects = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
VkImageViewCreateInfo ivci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
ivci.components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };
ivci.format = ici.format;
ivci.image = img.image;
ivci.viewType = numLayers == 1 ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY;
ivci.subresourceRange.aspectMask = aspects;
ivci.subresourceRange.layerCount = numLayers;
ivci.subresourceRange.levelCount = 1;
res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.rtView);
vulkan->SetDebugName(img.rtView, VK_OBJECT_TYPE_IMAGE_VIEW, tag);
_dbg_assert_(res == VK_SUCCESS);
// Separate view for texture sampling all layers together.
if (!color) {
ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
}
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; // layered for consistency, even if single image.
res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.texAllLayersView);
vulkan->SetDebugName(img.texAllLayersView, VK_OBJECT_TYPE_IMAGE_VIEW, tag);
// Create 2D views for both layers.
// Useful when multipassing shaders that don't yet exist in a single-pass-stereo version.
for (int i = 0; i < numLayers; i++) {
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
ivci.subresourceRange.layerCount = 1;
ivci.subresourceRange.baseArrayLayer = i;
res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.texLayerViews[i]);
if (vulkan->DebugLayerEnabled()) {
char temp[128];
snprintf(temp, sizeof(temp), "%s_layer%d", tag, i);
vulkan->SetDebugName(img.texLayerViews[i], VK_OBJECT_TYPE_IMAGE_VIEW, temp);
}
_dbg_assert_(res == VK_SUCCESS);
}
VkPipelineStageFlags dstStage;
VkAccessFlagBits dstAccessMask;
switch (initialLayout) {
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
break;
default:
Crash();
return;
}
TransitionImageLayout2(cmd, img.image, 0, 1, numLayers, aspects,
VK_IMAGE_LAYOUT_UNDEFINED, initialLayout,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, dstStage,
0, dstAccessMask);
img.layout = initialLayout;
img.format = format;
img.sampleCount = sampleCount;
img.tag = tag ? tag : "N/A";
img.numLayers = numLayers;
}
static VkAttachmentLoadOp ConvertLoadAction(VKRRenderPassLoadAction action) {
switch (action) {
case VKRRenderPassLoadAction::CLEAR: return VK_ATTACHMENT_LOAD_OP_CLEAR;
case VKRRenderPassLoadAction::KEEP: return VK_ATTACHMENT_LOAD_OP_LOAD;
case VKRRenderPassLoadAction::DONT_CARE: return VK_ATTACHMENT_LOAD_OP_DONT_CARE;
}
return VK_ATTACHMENT_LOAD_OP_DONT_CARE; // avoid compiler warning
}
static VkAttachmentStoreOp ConvertStoreAction(VKRRenderPassStoreAction action) {
switch (action) {
case VKRRenderPassStoreAction::STORE: return VK_ATTACHMENT_STORE_OP_STORE;
case VKRRenderPassStoreAction::DONT_CARE: return VK_ATTACHMENT_STORE_OP_DONT_CARE;
}
return VK_ATTACHMENT_STORE_OP_DONT_CARE; // avoid compiler warning
}
// 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, VkSampleCountFlagBits sampleCount) {
bool selfDependency = RenderPassTypeHasInput(rpType);
bool isBackbuffer = rpType == RenderPassType::BACKBUFFER;
bool hasDepth = RenderPassTypeHasDepth(rpType);
bool multiview = RenderPassTypeHasMultiView(rpType);
bool multisample = RenderPassTypeHasMultisample(rpType);
if (multiview) {
// TODO: Assert that the device has multiview support enabled.
}
int colorAttachmentIndex = 0;
int depthAttachmentIndex = 1;
int attachmentCount = 0;
VkAttachmentDescription attachments[4]{};
attachments[attachmentCount].format = isBackbuffer ? vulkan->GetSwapchainFormat() : VK_FORMAT_R8G8B8A8_UNORM;
attachments[attachmentCount].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[attachmentCount].loadOp = multisample ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : ConvertLoadAction(key.colorLoadAction);
attachments[attachmentCount].storeOp = ConvertStoreAction(key.colorStoreAction);
attachments[attachmentCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[attachmentCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[attachmentCount].initialLayout = isBackbuffer ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[attachmentCount].finalLayout = isBackbuffer ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachmentCount++;
if (hasDepth) {
attachments[attachmentCount].format = vulkan->GetDeviceInfo().preferredDepthStencilFormat;
attachments[attachmentCount].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[attachmentCount].loadOp = multisample ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : ConvertLoadAction(key.depthLoadAction);
attachments[attachmentCount].storeOp = ConvertStoreAction(key.depthStoreAction);
attachments[attachmentCount].stencilLoadOp = multisample ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : ConvertLoadAction(key.stencilLoadAction);
attachments[attachmentCount].stencilStoreOp = ConvertStoreAction(key.stencilStoreAction);
attachments[attachmentCount].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[attachmentCount].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachmentCount++;
}
if (multisample) {
colorAttachmentIndex = attachmentCount;
attachments[attachmentCount].format = isBackbuffer ? vulkan->GetSwapchainFormat() : VK_FORMAT_R8G8B8A8_UNORM;
attachments[attachmentCount].samples = sampleCount;
attachments[attachmentCount].loadOp = ConvertLoadAction(key.colorLoadAction);
attachments[attachmentCount].storeOp = ConvertStoreAction(key.colorStoreAction);
attachments[attachmentCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[attachmentCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[attachmentCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[attachmentCount].finalLayout = isBackbuffer ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachmentCount++;
if (hasDepth) {
depthAttachmentIndex = attachmentCount;
attachments[attachmentCount].format = vulkan->GetDeviceInfo().preferredDepthStencilFormat;
attachments[attachmentCount].samples = sampleCount;
attachments[attachmentCount].loadOp = ConvertLoadAction(key.depthLoadAction);
attachments[attachmentCount].storeOp = ConvertStoreAction(key.depthStoreAction);
attachments[attachmentCount].stencilLoadOp = ConvertLoadAction(key.stencilLoadAction);
attachments[attachmentCount].stencilStoreOp = ConvertStoreAction(key.stencilStoreAction);
attachments[attachmentCount].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[attachmentCount].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachmentCount++;
}
}
VkAttachmentReference colorReference{};
colorReference.attachment = colorAttachmentIndex;
colorReference.layout = selfDependency ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference depthReference{};
depthReference.attachment = depthAttachmentIndex;
depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.flags = 0;
if (selfDependency) {
subpass.inputAttachmentCount = 1;
subpass.pInputAttachments = &colorReference;
} else {
subpass.inputAttachmentCount = 0;
subpass.pInputAttachments = nullptr;
}
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorReference;
VkAttachmentReference colorResolveReference;
if (multisample) {
colorResolveReference.attachment = 0; // the non-msaa color buffer.
colorResolveReference.layout = selfDependency ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
subpass.pResolveAttachments = &colorResolveReference;
} else {
subpass.pResolveAttachments = nullptr;
}
if (hasDepth) {
subpass.pDepthStencilAttachment = &depthReference;
}
subpass.preserveAttachmentCount = 0;
subpass.pPreserveAttachments = nullptr;
// Not sure if this is really necessary.
VkSubpassDependency deps[2]{};
size_t numDeps = 0;
VkRenderPassCreateInfo rp{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
rp.attachmentCount = attachmentCount;
rp.pAttachments = attachments;
rp.subpassCount = 1;
rp.pSubpasses = &subpass;
VkRenderPassMultiviewCreateInfoKHR mv{ VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHR };
uint32_t viewMask = 0x3; // Must be outside the 'if (multiview)' scope!
int viewOffset = 0;
if (multiview) {
rp.pNext = &mv;
mv.subpassCount = 1;
mv.pViewMasks = &viewMask;
mv.dependencyCount = 0;
mv.pCorrelationMasks = &viewMask; // same masks
mv.correlationMaskCount = 1;
mv.pViewOffsets = &viewOffset;
}
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;
VkResult res;
// We could always use renderpass2, but I think it'll get both paths better tested if we
// only use it with multisample enabled.
// if (vulkan->Extensions().KHR_create_renderpass2) {
if (multisample) {
// It's a bit unfortunate that we can't rely on vkCreateRenderPass2, because here we now have
// to do a bunch of struct conversion, just to not have to repeat the logic from above.
VkAttachmentDescription2KHR attachments2[4]{};
for (int i = 0; i < attachmentCount; i++) {
attachments2[i].sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR;
attachments2[i].format = attachments[i].format;
attachments2[i].samples = attachments[i].samples;
attachments2[i].loadOp = attachments[i].loadOp;
attachments2[i].storeOp = attachments[i].storeOp;
attachments2[i].stencilLoadOp = attachments[i].stencilLoadOp;
attachments2[i].stencilStoreOp = attachments[i].stencilStoreOp;
attachments2[i].initialLayout = attachments[i].initialLayout;
attachments2[i].finalLayout = attachments[i].finalLayout;
}
VkAttachmentReference2KHR colorReference2{ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR };
colorReference2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
colorReference2.attachment = colorReference.attachment;
colorReference2.layout = colorReference.layout;
VkAttachmentReference2KHR depthReference2{ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR };
depthReference2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
depthReference2.attachment = depthReference.attachment;
depthReference2.layout = depthReference.layout;
VkSubpassDependency2KHR deps2[2]{};
for (int i = 0; i < numDeps; i++) {
deps2[i].sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2_KHR;
deps2[i].dependencyFlags = deps[i].dependencyFlags;
deps2[i].srcAccessMask = deps[i].srcAccessMask;
deps2[i].dstAccessMask = deps[i].dstAccessMask;
deps2[i].srcStageMask = deps[i].srcStageMask;
deps2[i].dstStageMask = deps[i].dstStageMask;
deps2[i].srcSubpass = deps[i].srcSubpass;
deps2[i].dstSubpass = deps[i].dstSubpass;
deps2[i].dependencyFlags = deps[i].dependencyFlags;
deps2[i].viewOffset = 0;
}
VkAttachmentReference2KHR colorResolveReference2{ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR };
VkSubpassDescription2KHR subpass2{ VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR };
subpass2.colorAttachmentCount = subpass.colorAttachmentCount;
subpass2.flags = subpass.flags;
if (selfDependency) {
subpass2.inputAttachmentCount = subpass.inputAttachmentCount;
subpass2.pInputAttachments = &colorReference2;
}
subpass2.pColorAttachments = &colorReference2;
if (hasDepth) {
subpass2.pDepthStencilAttachment = &depthReference2;
}
subpass2.pipelineBindPoint = subpass.pipelineBindPoint;
subpass2.viewMask = multiview ? viewMask : 0;
if (multisample) {
colorResolveReference2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
colorResolveReference2.attachment = colorResolveReference.attachment; // the non-msaa color buffer.
colorResolveReference2.layout = selfDependency ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
subpass2.pResolveAttachments = &colorResolveReference2;
} else {
subpass2.pResolveAttachments = nullptr;
}
VkAttachmentReference2KHR depthResolveReference2{ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR };
VkSubpassDescriptionDepthStencilResolveKHR depthStencilResolve{ VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR };
if (hasDepth && multisample) {
subpass2.pNext = &depthStencilResolve;
depthResolveReference2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
depthResolveReference2.attachment = 1;
depthResolveReference2.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
// TODO: Some games might benefit from the other depth resolve modes when depth texturing.
depthStencilResolve.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT_KHR;
depthStencilResolve.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT_KHR;
depthStencilResolve.pDepthStencilResolveAttachment = &depthResolveReference2;
}
VkRenderPassCreateInfo2KHR rp2{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR };
rp2.pAttachments = attachments2;
rp2.pDependencies = deps2;
rp2.attachmentCount = rp.attachmentCount;
rp2.dependencyCount = rp.dependencyCount;
rp2.correlatedViewMaskCount = multiview ? 1 : 0;
rp2.pCorrelatedViewMasks = multiview ? &viewMask : nullptr;
rp2.pSubpasses = &subpass2;
rp2.subpassCount = 1;
res = vkCreateRenderPass2KHR(vulkan->GetDevice(), &rp2, nullptr, &pass);
} else {
res = vkCreateRenderPass(vulkan->GetDevice(), &rp, nullptr, &pass);
}
_assert_(res == VK_SUCCESS);
_assert_(pass != VK_NULL_HANDLE);
return pass;
}
VkRenderPass VKRRenderPass::Get(VulkanContext *vulkan, RenderPassType rpType, VkSampleCountFlagBits sampleCount) {
// When we create a render pass, we create all "types" of it immediately,
// practical later when referring to it. Could change to on-demand if it feels motivated
// but I think the render pass objects are cheap.
// WARNING: We don't include sampleCount in the key, there's only the distinction multisampled or not
// which comes from the rpType.
// So you CAN NOT mix and match different non-one sample counts.
_dbg_assert_(!((rpType & RenderPassType::MULTISAMPLE) && sampleCount == VK_SAMPLE_COUNT_1_BIT));
if (!pass[(int)rpType] || sampleCounts[(int)rpType] != sampleCount) {
if (pass[(int)rpType]) {
vulkan->Delete().QueueDeleteRenderPass(pass[(int)rpType]);
}
pass[(int)rpType] = CreateRenderPass(vulkan, key_, (RenderPassType)rpType, sampleCount);
sampleCounts[(int)rpType] = sampleCount;
}
return pass[(int)rpType];
}
+164
View File
@@ -0,0 +1,164 @@
#pragma once
#include "Common/Common.h"
#include "Common/GPU/Vulkan/VulkanContext.h"
class VKRRenderPass;
// Pipelines need to be created for the right type of render pass.
// TODO: Rename to RenderPassFlags?
// When you add more flags, don't forget to update rpTypeDebugNames[].
enum class RenderPassType {
DEFAULT = 0,
// These eight are organized so that bit 0 is DEPTH and bit 1 is INPUT and bit 2 is MULTIVIEW, so
// they can be OR-ed together in MergeRPTypes.
HAS_DEPTH = 1,
COLOR_INPUT = 2, // input attachment
MULTIVIEW = 4,
MULTISAMPLE = 8,
// This is the odd one out, and gets special handling in MergeRPTypes.
// If this flag is set, none of the other flags can be set.
// For the backbuffer we can always use CLEAR/DONT_CARE, so bandwidth cost for a depth channel is negligible
// so we don't bother with a non-depth version.
BACKBUFFER = 16,
TYPE_COUNT = BACKBUFFER + 1,
};
ENUM_CLASS_BITOPS(RenderPassType);
// Simple independent framebuffer image.
struct VKRImage {
// These four are "immutable".
VkImage image;
VkImageView rtView; // Used for rendering to, and readbacks of stencil. 2D if single layer, 2D_ARRAY if multiple. Includes both depth and stencil if depth/stencil.
// This is for texturing all layers at once. If aspect is depth/stencil, does not include stencil.
VkImageView texAllLayersView;
// If it's a layered image (for stereo), this is two 2D views of it, to make it compatible with shaders that don't yet support stereo.
// If there's only one layer, layerViews[0] only is initialized.
VkImageView texLayerViews[2]{};
VmaAllocation alloc;
VkFormat format;
VkSampleCountFlagBits sampleCount;
// This one is used by QueueRunner's Perform functions to keep track. CANNOT be used anywhere else due to sync issues.
VkImageLayout layout;
int numLayers;
// For debugging.
std::string tag;
void Delete(VulkanContext *vulkan);
};
class VKRFramebuffer {
public:
VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, int _numLayers, int _multiSampleLevel, bool createDepthStencilBuffer, const char *tag);
~VKRFramebuffer();
VkFramebuffer Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType);
int width = 0;
int height = 0;
int numLayers = 0;
VkSampleCountFlagBits sampleCount;
VKRImage color{}; // color.image is always there.
VKRImage depth{}; // depth.image is allowed to be VK_NULL_HANDLE.
// These are only initialized and used if numSamples > 1.
VKRImage msaaColor{};
VKRImage msaaDepth{};
const char *Tag() const {
return tag_.c_str();
}
void UpdateTag(const char *newTag);
bool HasDepth() const {
return depth.image != VK_NULL_HANDLE;
}
VkImageView GetRTView() {
if (sampleCount == VK_SAMPLE_COUNT_1_BIT) {
return color.rtView;
} else {
return msaaColor.rtView;
}
}
VulkanContext *Vulkan() const { return vulkan_; }
private:
static void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, int numLayers, VkSampleCountFlagBits sampleCount, VkFormat format, VkImageLayout initialLayout, bool color, const char *tag);
VkFramebuffer framebuf[(size_t)RenderPassType::TYPE_COUNT]{};
VulkanContext *vulkan_;
std::string tag_;
};
inline bool RenderPassTypeHasDepth(RenderPassType type) {
return (type & RenderPassType::HAS_DEPTH) || type == RenderPassType::BACKBUFFER;
}
inline bool RenderPassTypeHasInput(RenderPassType type) {
return (type & RenderPassType::COLOR_INPUT) != 0;
}
inline bool RenderPassTypeHasMultiView(RenderPassType type) {
return (type & RenderPassType::MULTIVIEW) != 0;
}
inline bool RenderPassTypeHasMultisample(RenderPassType type) {
return (type & RenderPassType::MULTISAMPLE) != 0;
}
VkSampleCountFlagBits MultiSampleLevelToFlagBits(int count);
// Must be the same order as Draw::RPAction
enum class VKRRenderPassLoadAction : uint8_t {
KEEP, // default. avoid when possible.
CLEAR,
DONT_CARE,
};
enum class VKRRenderPassStoreAction : uint8_t {
STORE, // default. avoid when possible.
DONT_CARE,
};
struct RPKey {
// Only render-pass-compatibility-volatile things can be here.
VKRRenderPassLoadAction colorLoadAction;
VKRRenderPassLoadAction depthLoadAction;
VKRRenderPassLoadAction stencilLoadAction;
VKRRenderPassStoreAction colorStoreAction;
VKRRenderPassStoreAction depthStoreAction;
VKRRenderPassStoreAction stencilStoreAction;
};
class VKRRenderPass {
public:
VKRRenderPass(const RPKey &key) : key_(key) {}
VkRenderPass Get(VulkanContext *vulkan, RenderPassType rpType, VkSampleCountFlagBits sampleCount);
void Destroy(VulkanContext *vulkan) {
for (size_t i = 0; i < (size_t)RenderPassType::TYPE_COUNT; i++) {
if (pass[i]) {
vulkan->Delete().QueueDeleteRenderPass(pass[i]);
}
}
}
private:
// TODO: Might be better off with a hashmap once the render pass type count grows really large..
VkRenderPass pass[(size_t)RenderPassType::TYPE_COUNT]{};
VkSampleCountFlagBits sampleCounts[(size_t)RenderPassType::TYPE_COUNT];
RPKey key_;
};
+26 -6
View File
@@ -5,9 +5,15 @@
#include "Common/GPU/Vulkan/VulkanAlloc.h"
#include "Common/GPU/Vulkan/VulkanImage.h"
#include "Common/GPU/Vulkan/VulkanMemory.h"
#include "Common/StringUtils.h"
using namespace PPSSPP_VK;
VulkanTexture::VulkanTexture(VulkanContext *vulkan, const char *tag)
: vulkan_(vulkan) {
truncate_cpy(tag_, tag);
}
void VulkanTexture::Wipe() {
if (view_ != VK_NULL_HANDLE) {
vulkan_->Delete().QueueDeleteImageView(view_);
@@ -80,14 +86,14 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int depth, i
VkResult res = vmaCreateImage(vulkan_->Allocator(), &image_create_info, &allocCreateInfo, &image_, &allocation_, &allocInfo);
// Apply the tag
vulkan_->SetDebugName(image_, VK_OBJECT_TYPE_IMAGE, tag_.c_str());
vulkan_->SetDebugName(image_, VK_OBJECT_TYPE_IMAGE, tag_);
// Write a command to transition the image to the requested layout, if it's not already that layout.
if (initialLayout != VK_IMAGE_LAYOUT_UNDEFINED && initialLayout != VK_IMAGE_LAYOUT_PREINITIALIZED) {
switch (initialLayout) {
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
case VK_IMAGE_LAYOUT_GENERAL:
TransitionImageLayout2(cmd, image_, 0, numMips, VK_IMAGE_ASPECT_COLOR_BIT,
TransitionImageLayout2(cmd, image_, 0, numMips, 1, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, initialLayout,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, VK_ACCESS_TRANSFER_WRITE_BIT);
@@ -123,6 +129,16 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int depth, i
_assert_(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS);
return false;
}
vulkan_->SetDebugName(view_, VK_OBJECT_TYPE_IMAGE_VIEW, tag_);
// Additionally, create an array view, but only if it's a 2D texture.
if (view_info.viewType == VK_IMAGE_VIEW_TYPE_2D) {
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &arrayView_);
_assert_(res == VK_SUCCESS);
vulkan_->SetDebugName(arrayView_, VK_OBJECT_TYPE_IMAGE_VIEW, tag_);
}
return true;
}
@@ -164,7 +180,7 @@ void VulkanTexture::GenerateMips(VkCommandBuffer cmd, int firstMipToGenerate, bo
_assert_msg_(firstMipToGenerate < numMips_, "Can't generate levels beyond storage");
// Transition the pre-set levels to GENERAL.
TransitionImageLayout2(cmd, image_, 0, firstMipToGenerate, VK_IMAGE_ASPECT_COLOR_BIT,
TransitionImageLayout2(cmd, image_, 0, firstMipToGenerate, 1, VK_IMAGE_ASPECT_COLOR_BIT,
fromCompute ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_GENERAL,
fromCompute ? VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT : VK_PIPELINE_STAGE_TRANSFER_BIT,
@@ -173,7 +189,7 @@ void VulkanTexture::GenerateMips(VkCommandBuffer cmd, int firstMipToGenerate, bo
VK_ACCESS_TRANSFER_READ_BIT);
// Do the same with the uninitialized levels.
TransitionImageLayout2(cmd, image_, firstMipToGenerate, numMips_ - firstMipToGenerate,
TransitionImageLayout2(cmd, image_, firstMipToGenerate, numMips_ - firstMipToGenerate, 1,
VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_GENERAL,
@@ -206,7 +222,7 @@ void VulkanTexture::GenerateMips(VkCommandBuffer cmd, int firstMipToGenerate, bo
vkCmdBlitImage(cmd, image_, VK_IMAGE_LAYOUT_GENERAL, image_, VK_IMAGE_LAYOUT_GENERAL, 1, &blit, VK_FILTER_LINEAR);
TransitionImageLayout2(cmd, image_, mip, 1, VK_IMAGE_ASPECT_COLOR_BIT,
TransitionImageLayout2(cmd, image_, mip, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
@@ -214,7 +230,7 @@ void VulkanTexture::GenerateMips(VkCommandBuffer cmd, int firstMipToGenerate, bo
}
void VulkanTexture::EndCreate(VkCommandBuffer cmd, bool vertexTexture, VkPipelineStageFlags prevStage, VkImageLayout layout) {
TransitionImageLayout2(cmd, image_, 0, numMips_,
TransitionImageLayout2(cmd, image_, 0, numMips_, 1,
VK_IMAGE_ASPECT_COLOR_BIT,
layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
prevStage, vertexTexture ? VK_PIPELINE_STAGE_VERTEX_SHADER_BIT : VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
@@ -237,6 +253,7 @@ VkImageView VulkanTexture::CreateViewForMip(int mip) {
view_info.subresourceRange.layerCount = 1;
VkImageView view;
VkResult res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view);
vulkan_->SetDebugName(view, VK_OBJECT_TYPE_IMAGE_VIEW, "mipview");
_assert_(res == VK_SUCCESS);
return view;
}
@@ -245,6 +262,9 @@ void VulkanTexture::Destroy() {
if (view_ != VK_NULL_HANDLE) {
vulkan_->Delete().QueueDeleteImageView(view_);
}
if (arrayView_ != VK_NULL_HANDLE) {
vulkan_->Delete().QueueDeleteImageView(arrayView_);
}
if (image_ != VK_NULL_HANDLE) {
_dbg_assert_(allocation_ != VK_NULL_HANDLE);
vulkan_->Delete().QueueDeleteImageAllocation(image_, allocation_);
+7 -10
View File
@@ -1,6 +1,5 @@
#pragma once
#include <string>
#include "VulkanLoader.h"
class VulkanContext;
@@ -12,9 +11,7 @@ VK_DEFINE_HANDLE(VmaAllocation);
// ALWAYS use an allocator when calling CreateDirect.
class VulkanTexture {
public:
VulkanTexture(VulkanContext *vulkan)
: vulkan_(vulkan) {
}
VulkanTexture(VulkanContext *vulkan, const char *tag);
~VulkanTexture() {
Destroy();
}
@@ -37,13 +34,9 @@ public:
void Destroy();
void SetTag(const char *tag) {
tag_ = tag;
}
const std::string &Tag() const {
const char *Tag() const {
return tag_;
}
void Touch() {}
// Used in image copies, etc.
VkImage GetImage() const { return image_; }
@@ -51,6 +44,9 @@ public:
// Used for sampling, generally.
VkImageView GetImageView() const { return view_; }
// For use with some shaders, we might want to view it as a single entry array for convenience.
VkImageView GetImageArrayView() const { return arrayView_; }
int32_t GetWidth() const { return width_; }
int32_t GetHeight() const { return height_; }
int32_t GetNumMips() const { return numMips_; }
@@ -62,6 +58,7 @@ private:
VulkanContext *vulkan_;
VkImage image_ = VK_NULL_HANDLE;
VkImageView view_ = VK_NULL_HANDLE;
VkImageView arrayView_ = VK_NULL_HANDLE;
VmaAllocation allocation_ = VK_NULL_HANDLE;
int16_t width_ = 0;
@@ -70,5 +67,5 @@ private:
int16_t depth_ = 1;
VkFormat format_ = VK_FORMAT_UNDEFINED;
std::string tag_;
char tag_[64];
};
+55 -4
View File
@@ -223,6 +223,7 @@ PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR;
PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR;
PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR;
PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR;
PFN_vkCreateRenderPass2KHR vkCreateRenderPass2KHR;
} // namespace PPSSPP_VK
using namespace PPSSPP_VK;
@@ -239,7 +240,6 @@ static VulkanLibraryHandle vulkanLibrary;
typedef void *VulkanLibraryHandle;
static VulkanLibraryHandle vulkanLibrary;
#endif
const char *VulkanResultToString(VkResult res);
bool g_vulkanAvailabilityChecked = false;
bool g_vulkanMayBeAvailable = false;
@@ -274,6 +274,8 @@ static VulkanLibraryHandle VulkanLoadLibrary(const char *logname) {
#if PPSSPP_PLATFORM(SWITCH)
// Always unavailable, for now.
return nullptr;
#elif PPSSPP_PLATFORM(UWP)
return nullptr;
#elif PPSSPP_PLATFORM(WINDOWS)
return LoadLibrary(L"vulkan-1.dll");
#else
@@ -308,9 +310,8 @@ void VulkanSetAvailable(bool available) {
}
bool VulkanMayBeAvailable() {
//unsupported in VR at the moment
if (IsVRBuild()) {
// Unsupported in VR at the moment
if (IsVREnabled()) {
return false;
}
@@ -720,8 +721,58 @@ void VulkanLoadDeviceFunctions(VkDevice device, const VulkanExtensions &enabledE
LOAD_DEVICE_FUNC(device, vkGetBufferMemoryRequirements2KHR);
LOAD_DEVICE_FUNC(device, vkGetImageMemoryRequirements2KHR);
}
if (enabledExtensions.KHR_create_renderpass2) {
LOAD_DEVICE_FUNC(device, vkCreateRenderPass2KHR);
}
}
void VulkanFree() {
VulkanFreeLibrary(vulkanLibrary);
}
const char *VulkanResultToString(VkResult res) {
static char temp[128]{};
switch (res) {
case VK_NOT_READY: return "VK_NOT_READY";
case VK_TIMEOUT: return "VK_TIMEOUT";
case VK_EVENT_SET: return "VK_EVENT_SET";
case VK_EVENT_RESET: return "VK_EVENT_RESET";
case VK_INCOMPLETE: return "VK_INCOMPLETE";
case VK_ERROR_OUT_OF_HOST_MEMORY: return "VK_ERROR_OUT_OF_HOST_MEMORY";
case VK_ERROR_OUT_OF_DEVICE_MEMORY: return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
case VK_ERROR_INITIALIZATION_FAILED: return "VK_ERROR_INITIALIZATION_FAILED";
case VK_ERROR_DEVICE_LOST: return "VK_ERROR_DEVICE_LOST";
case VK_ERROR_MEMORY_MAP_FAILED: return "VK_ERROR_MEMORY_MAP_FAILED";
case VK_ERROR_LAYER_NOT_PRESENT: return "VK_ERROR_LAYER_NOT_PRESENT";
case VK_ERROR_EXTENSION_NOT_PRESENT: return "VK_ERROR_EXTENSION_NOT_PRESENT";
case VK_ERROR_FEATURE_NOT_PRESENT: return "VK_ERROR_FEATURE_NOT_PRESENT";
case VK_ERROR_INCOMPATIBLE_DRIVER: return "VK_ERROR_INCOMPATIBLE_DRIVER";
case VK_ERROR_TOO_MANY_OBJECTS: return "VK_ERROR_TOO_MANY_OBJECTS";
case VK_ERROR_FORMAT_NOT_SUPPORTED: return "VK_ERROR_FORMAT_NOT_SUPPORTED";
case VK_ERROR_SURFACE_LOST_KHR: return "VK_ERROR_SURFACE_LOST_KHR";
case VK_SUBOPTIMAL_KHR: return "VK_SUBOPTIMAL_KHR";
case VK_ERROR_OUT_OF_DATE_KHR: return "VK_ERROR_OUT_OF_DATE_KHR";
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
case VK_ERROR_OUT_OF_POOL_MEMORY: return "VK_ERROR_OUT_OF_POOL_MEMORY_KHR";
case VK_ERROR_INVALID_EXTERNAL_HANDLE: return "VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR";
case VK_ERROR_FRAGMENTED_POOL: return "VK_ERROR_FRAGMENTED_POOL";
case VK_ERROR_UNKNOWN: return "VK_ERROR_UNKNOWN (-13)";
case VK_ERROR_FRAGMENTATION: return "VK_ERROR_FRAGMENTATION";
case VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS: return "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS";
case VK_PIPELINE_COMPILE_REQUIRED: return "VK_PIPELINE_COMPILE_REQUIRED";
case VK_ERROR_VALIDATION_FAILED_EXT: return "VK_ERROR_VALIDATION_FAILED_EXT";
case VK_ERROR_INVALID_SHADER_NV: return "VK_ERROR_INVALID_SHADER_NV";
case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
case VK_ERROR_NOT_PERMITTED_KHR: return "VK_ERROR_NOT_PERMITTED_KHR";
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
case VK_THREAD_IDLE_KHR: return "VK_THREAD_IDLE_KHR";
case VK_THREAD_DONE_KHR: return "VK_THREAD_DONE_KHR";
case VK_OPERATION_DEFERRED_KHR: return "VK_OPERATION_DEFERRED_KHR";
case VK_OPERATION_NOT_DEFERRED_KHR: return "VK_OPERATION_NOT_DEFERRED_KHR";
default:
// This isn't thread safe, but this should be rare, and at worst, we'll get a jumble of two messages.
snprintf(temp, sizeof(temp), "VK_ERROR_???: 0x%08x", (u32)res);
return temp;
}
}
+3
View File
@@ -224,6 +224,7 @@ extern PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR;
extern PFN_vkGetMemoryHostPointerPropertiesEXT vkGetMemoryHostPointerPropertiesEXT;
extern PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR;
extern PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR;
extern PFN_vkCreateRenderPass2KHR vkCreateRenderPass2KHR;
} // namespace PPSSPP_VK
// For fast extension-enabled checks.
@@ -254,3 +255,5 @@ bool VulkanLoad();
void VulkanLoadInstanceFunctions(VkInstance instance, const VulkanExtensions &enabledExtensions);
void VulkanLoadDeviceFunctions(VkDevice device, const VulkanExtensions &enabledExtensions);
void VulkanFree();
const char *VulkanResultToString(VkResult res);
+7 -4
View File
@@ -153,7 +153,7 @@ void VulkanDescSetPool::Create(VulkanContext *vulkan, const VkDescriptorPoolCrea
_assert_msg_(res == VK_SUCCESS, "Could not create VulkanDescSetPool %s", tag_);
}
VkDescriptorSet VulkanDescSetPool::Allocate(int n, const VkDescriptorSetLayout *layouts) {
VkDescriptorSet VulkanDescSetPool::Allocate(int n, const VkDescriptorSetLayout *layouts, const char *tag) {
if (descPool_ == VK_NULL_HANDLE || usage_ + n >= info_.maxSets) {
// Missing or out of space, need to recreate.
VkResult res = Recreate(grow_);
@@ -180,9 +180,12 @@ VkDescriptorSet VulkanDescSetPool::Allocate(int n, const VkDescriptorSetLayout *
_assert_msg_(result == VK_SUCCESS, "Ran out of descriptor space (frag?) and failed to allocate after recreating a descriptor pool. res=%d", (int)result);
}
if (result == VK_SUCCESS)
return desc;
return VK_NULL_HANDLE;
if (result != VK_SUCCESS) {
return VK_NULL_HANDLE;
}
vulkan_->SetDebugName(desc, VK_OBJECT_TYPE_DESCRIPTOR_SET, tag);
return desc;
}
void VulkanDescSetPool::Reset() {
+11 -1
View File
@@ -1,6 +1,7 @@
#pragma once
#include <cstdint>
#include <cstring>
#include <functional>
#include <vector>
@@ -121,6 +122,15 @@ public:
return writePtr_ + off;
}
template<class T>
void PushUBOData(const T &data, VkDescriptorBufferInfo *info) {
uint32_t bindOffset;
void *ptr = PushAligned(sizeof(T), &bindOffset, &info->buffer, vulkan_->GetPhysicalDeviceProperties().properties.limits.minUniformBufferOffsetAlignment);
memcpy(ptr, &data, sizeof(T));
info->offset = bindOffset;
info->range = sizeof(T);
}
size_t GetTotalSize() const;
private:
@@ -153,7 +163,7 @@ public:
void Create(VulkanContext *vulkan, const VkDescriptorPoolCreateInfo &info, const std::vector<VkDescriptorPoolSize> &sizes);
// Allocate a new set, which may resize and empty the current sets.
// Use only for the current frame, unless in a cache cleared by clear_.
VkDescriptorSet Allocate(int n, const VkDescriptorSetLayout *layouts);
VkDescriptorSet Allocate(int n, const VkDescriptorSetLayout *layouts, const char *tag);
void Reset();
void Destroy();
+4 -7
View File
@@ -40,7 +40,7 @@ void VulkanProfiler::BeginFrame(VulkanContext *vulkan, VkCommandBuffer firstComm
// Log it all out.
for (auto &scope : scopes_) {
if (scope.endQueryId == -1) {
WARN_LOG(G3D, "Unclosed scope: %s", scope.name.c_str());
WARN_LOG(G3D, "Unclosed scope: %s", scope.name);
continue;
}
uint64_t startTime = results[scope.startQueryId];
@@ -50,7 +50,7 @@ void VulkanProfiler::BeginFrame(VulkanContext *vulkan, VkCommandBuffer firstComm
double milliseconds = (double)delta * timestampConversionFactor;
INFO_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, milliseconds);
}
scopes_.clear();
@@ -73,14 +73,11 @@ void VulkanProfiler::Begin(VkCommandBuffer cmdBuf, VkPipelineStageFlagBits stage
return;
}
ProfilerScope scope;
va_list args;
va_start(args, fmt);
char temp[512];
vsnprintf(temp, sizeof(temp), fmt, args);
vsnprintf(scope.name, sizeof(scope.name), fmt, args);
va_end(args);
ProfilerScope scope;
scope.name = temp;
scope.startQueryId = numQueries_;
scope.endQueryId = -1;
scope.level = (int)scopeStack_.size();
+1 -1
View File
@@ -13,7 +13,7 @@
// Put the whole thing in a FrameData to allow for overlap.
struct ProfilerScope {
std::string name;
char name[52]; // to make a struct size of 64, just because
int startQueryId;
int endQueryId;
int level;
+291 -247
View File
@@ -3,6 +3,7 @@
#include "Common/GPU/DataFormat.h"
#include "Common/GPU/Vulkan/VulkanQueueRunner.h"
#include "Common/GPU/Vulkan/VulkanRenderManager.h"
#include "Common/VR/PPSSPPVR.h"
#include "Common/Log.h"
#include "Common/TimeUtil.h"
@@ -10,7 +11,7 @@ using namespace PPSSPP_VK;
// Debug help: adb logcat -s DEBUG PPSSPPNativeActivity PPSSPP NativeGLView NativeRenderer NativeSurfaceView PowerSaveModeReceiver InputDeviceState
static void MergeRenderAreaRectInto(VkRect2D *dest, VkRect2D &src) {
static void MergeRenderAreaRectInto(VkRect2D *dest, const VkRect2D &src) {
if (dest->offset.x > src.offset.x) {
dest->extent.width += (dest->offset.x - src.offset.x);
dest->offset.x = src.offset.x;
@@ -31,11 +32,13 @@ static void MergeRenderAreaRectInto(VkRect2D *dest, VkRect2D &src) {
RenderPassType MergeRPTypes(RenderPassType a, RenderPassType b) {
// Either both are backbuffer type, or neither are.
// These can't merge with other renderpasses
if (a == RP_TYPE_BACKBUFFER || b == RP_TYPE_BACKBUFFER) {
if (a == RenderPassType::BACKBUFFER || b == RenderPassType::BACKBUFFER) {
_dbg_assert_(a == b);
return a;
}
_dbg_assert_((a & RenderPassType::MULTIVIEW) == (b & RenderPassType::MULTIVIEW));
// The rest we can just OR together to get the maximum feature set.
return (RenderPassType)((u32)a | (u32)b);
}
@@ -165,7 +168,7 @@ bool VulkanQueueRunner::CreateSwapchain(VkCommandBuffer cmdInit) {
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.subresourceRange.layerCount = 1; // TODO: Investigate hw-assisted stereo.
color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D;
color_image_view.flags = 0;
color_image_view.image = sc_buffer.image;
@@ -175,6 +178,7 @@ bool VulkanQueueRunner::CreateSwapchain(VkCommandBuffer cmdInit) {
// 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);
vulkan_->SetDebugName(sc_buffer.view, VK_OBJECT_TYPE_IMAGE_VIEW, "swapchain_view");
swapchainImages_.push_back(sc_buffer);
_dbg_assert_(res == VK_SUCCESS);
}
@@ -194,7 +198,7 @@ bool VulkanQueueRunner::InitBackbufferFramebuffers(int width, int height) {
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.renderPass = GetCompatibleRenderPass()->Get(vulkan_, RenderPassType::BACKBUFFER, VK_SAMPLE_COUNT_1_BIT);
fb_info.attachmentCount = 2;
fb_info.pAttachments = attachments;
fb_info.width = width;
@@ -248,7 +252,7 @@ bool VulkanQueueRunner::InitDepthStencilBuffer(VkCommandBuffer cmd) {
vulkan_->SetDebugName(depth_.image, VK_OBJECT_TYPE_IMAGE, "BackbufferDepth");
TransitionImageLayout2(cmd, depth_.image, 0, 1,
TransitionImageLayout2(cmd, depth_.image, 0, 1, 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,
@@ -273,6 +277,7 @@ bool VulkanQueueRunner::InitDepthStencilBuffer(VkCommandBuffer cmd) {
VkDevice device = vulkan_->GetDevice();
res = vkCreateImageView(device, &depth_view_info, NULL, &depth_.view);
vulkan_->SetDebugName(depth_.view, VK_OBJECT_TYPE_IMAGE_VIEW, "depth_stencil_backbuffer");
_dbg_assert_(res == VK_SUCCESS);
if (res != VK_SUCCESS)
return false;
@@ -304,133 +309,6 @@ void VulkanQueueRunner::DestroyBackBuffers() {
INFO_LOG(G3D, "Backbuffers destroyed");
}
static VkAttachmentLoadOp ConvertLoadAction(VKRRenderPassLoadAction action) {
switch (action) {
case VKRRenderPassLoadAction::CLEAR: return VK_ATTACHMENT_LOAD_OP_CLEAR;
case VKRRenderPassLoadAction::KEEP: return VK_ATTACHMENT_LOAD_OP_LOAD;
case VKRRenderPassLoadAction::DONT_CARE: return VK_ATTACHMENT_LOAD_OP_DONT_CARE;
}
return VK_ATTACHMENT_LOAD_OP_DONT_CARE; // avoid compiler warning
}
static VkAttachmentStoreOp ConvertStoreAction(VKRRenderPassStoreAction action) {
switch (action) {
case VKRRenderPassStoreAction::STORE: return VK_ATTACHMENT_STORE_OP_STORE;
case VKRRenderPassStoreAction::DONT_CARE: return VK_ATTACHMENT_STORE_OP_DONT_CARE;
}
return VK_ATTACHMENT_STORE_OP_DONT_CARE; // avoid compiler warning
}
// 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 = 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 = 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;
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 = selfDependency ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference depth_reference{};
depth_reference.attachment = 1;
depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.flags = 0;
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;
if (hasDepth) {
subpass.pDepthStencilAttachment = &depth_reference;
}
subpass.preserveAttachmentCount = 0;
subpass.pPreserveAttachments = nullptr;
// Not sure if this is really necessary.
VkSubpassDependency deps[2]{};
size_t numDeps = 0;
VkRenderPassCreateInfo rp{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
rp.attachmentCount = hasDepth ? 2 : 1;
rp.pAttachments = attachments;
rp.subpassCount = 1;
rp.pSubpasses = &subpass;
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;
VkResult res = vkCreateRenderPass(vulkan->GetDevice(), &rp, nullptr, &pass);
_assert_(res == VK_SUCCESS);
_assert_(pass != VK_NULL_HANDLE);
return pass;
}
VkRenderPass VKRRenderPass::Get(VulkanContext *vulkan, RenderPassType rpType) {
// When we create a render pass, we create all "types" of it immediately,
// 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] = CreateRenderPass(vulkan, key_, (RenderPassType)rpType);
}
return pass[(int)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
@@ -456,6 +334,7 @@ void VulkanQueueRunner::SelfDependencyBarrier(VKRImage &img, VkImageAspectFlags
img.image,
0,
1,
img.numLayers,
aspect,
VK_IMAGE_LAYOUT_GENERAL,
VK_IMAGE_LAYOUT_GENERAL,
@@ -546,7 +425,7 @@ void VulkanQueueRunner::PreprocessSteps(std::vector<VKRStep *> &steps) {
}
}
void VulkanQueueRunner::RunSteps(std::vector<VKRStep *> &steps, FrameData &frameData, FrameDataShared &frameDataShared) {
void VulkanQueueRunner::RunSteps(std::vector<VKRStep *> &steps, FrameData &frameData, FrameDataShared &frameDataShared, bool keepSteps) {
QueueProfileContext *profile = frameData.profilingEnabled_ ? &frameData.profile : nullptr;
if (profile)
@@ -624,12 +503,13 @@ void VulkanQueueRunner::RunSteps(std::vector<VKRStep *> &steps, FrameData &frame
// 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 (auto step : steps) {
delete step;
if (!keepSteps) {
for (auto step : steps) {
delete step;
}
steps.clear();
}
steps.clear();
if (profile)
profile->cpuEndTime = time_now_d();
}
@@ -873,6 +753,26 @@ const char *AspectToString(VkImageAspectFlags aspect) {
}
}
static const char *rpTypeDebugNames[] = {
"RENDER",
"RENDER_DEPTH",
"RENDER_INPUT",
"RENDER_DEPTH_INPUT",
"MV_RENDER",
"MV_RENDER_DEPTH",
"MV_RENDER_INPUT",
"MV_RENDER_DEPTH_INPUT",
"MS_RENDER",
"MS_RENDER_DEPTH",
"MS_RENDER_INPUT",
"MS_RENDER_DEPTH_INPUT",
"MS_MV_RENDER",
"MS_MV_RENDER_DEPTH",
"MS_MV_RENDER_INPUT",
"MS_MV_RENDER_DEPTH_INPUT",
"BACKBUF",
};
std::string VulkanQueueRunner::StepToString(const VKRStep &step) const {
char buffer[256];
switch (step.stepType) {
@@ -882,15 +782,7 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const {
int h = step.render.framebuffer ? step.render.framebuffer->height : vulkan_->GetBackbufferHeight();
int actual_w = step.render.renderArea.extent.width;
int actual_h = step.render.renderArea.extent.height;
const char *renderCmd;
switch (step.render.renderPassType) {
case RP_TYPE_BACKBUFFER: renderCmd = "BACKBUF"; 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";
}
const char *renderCmd = rpTypeDebugNames[(size_t)step.render.renderPassType];
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;
}
@@ -1113,6 +1005,12 @@ void VulkanQueueRunner::LogRenderPass(const VKRStep &pass, bool verbose) {
case VKRRenderCommand::PUSH_CONSTANTS:
INFO_LOG(G3D, " PushConstants(%d)", cmd.push.size);
break;
case VKRRenderCommand::DEBUG_ANNOTATION:
INFO_LOG(G3D, " DebugAnnotation(%s)", cmd.debugAnnotation.annotation);
break;
case VKRRenderCommand::BIND_DESCRIPTOR_SET:
INFO_LOG(G3D, " BindDescSet(%d)", cmd.bindDescSet.setNumber);
break;
case VKRRenderCommand::NUM_RENDER_COMMANDS:
break;
@@ -1140,7 +1038,7 @@ void VulkanQueueRunner::LogReadbackImage(const VKRStep &step) {
INFO_LOG(G3D, "%s", StepToString(step).c_str());
}
void TransitionToOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayout colorLayout, VkImage depthStencilImage, VkImageLayout depthStencilLayout, VulkanBarrier *recordBarrier) {
void TransitionToOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayout colorLayout, VkImage depthStencilImage, VkImageLayout depthStencilLayout, int numLayers, VulkanBarrier *recordBarrier) {
if (colorLayout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
VkPipelineStageFlags srcStageMask = 0;
VkAccessFlags srcAccessMask = 0;
@@ -1173,7 +1071,7 @@ void TransitionToOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayout
break;
}
recordBarrier->TransitionImage(
colorImage, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT,
colorImage, 0, 1, numLayers, VK_IMAGE_ASPECT_COLOR_BIT,
colorLayout,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
srcAccessMask,
@@ -1209,7 +1107,7 @@ void TransitionToOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayout
break;
}
recordBarrier->TransitionImage(
depthStencilImage, 0, 1, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
depthStencilImage, 0, 1, numLayers, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
depthStencilLayout,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
srcAccessMask,
@@ -1219,7 +1117,7 @@ void TransitionToOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayout
}
}
void TransitionFromOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayout colorLayout, VkImage depthStencilImage, VkImageLayout depthStencilLayout) {
void TransitionFromOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayout colorLayout, VkImage depthStencilImage, int numLayers, VkImageLayout depthStencilLayout) {
VkPipelineStageFlags srcStageMask = 0;
VkPipelineStageFlags dstStageMask = 0;
@@ -1261,7 +1159,7 @@ void TransitionFromOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayou
barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier[0].subresourceRange.baseMipLevel = 0;
barrier[0].subresourceRange.levelCount = 1;
barrier[0].subresourceRange.layerCount = 1;
barrier[0].subresourceRange.layerCount = numLayers;
barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrierCount++;
@@ -1300,7 +1198,7 @@ void TransitionFromOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayou
barrier[barrierCount].subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
barrier[barrierCount].subresourceRange.baseMipLevel = 0;
barrier[barrierCount].subresourceRange.levelCount = 1;
barrier[barrierCount].subresourceRange.layerCount = 1;
barrier[barrierCount].subresourceRange.layerCount = numLayers;
barrier[barrierCount].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier[barrierCount].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrierCount++;
@@ -1318,6 +1216,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
iter.fb->color.image,
0,
1,
iter.fb->numLayers,
VK_IMAGE_ASPECT_COLOR_BIT,
iter.fb->color.layout,
iter.targetLayout
@@ -1328,6 +1227,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
iter.fb->depth.image,
0,
1,
iter.fb->numLayers,
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
iter.fb->depth.layout,
iter.targetLayout
@@ -1357,6 +1257,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
step.render.framebuffer->color.image,
0,
1,
step.render.framebuffer->numLayers,
VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
@@ -1371,6 +1272,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
step.render.framebuffer->depth.image,
0,
1,
step.render.framebuffer->numLayers,
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
@@ -1404,13 +1306,16 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
VkPipeline lastPipeline = VK_NULL_HANDLE;
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
bool pipelineOK = false;
int lastStencilWriteMask = -1;
int lastStencilCompareMask = -1;
int lastStencilReference = -1;
const RenderPassType rpType = step.render.renderPassType;
for (const auto &c : commands) {
for (size_t i = 0; i < commands.size(); i++) {
const VkRenderData &c = commands[i];
switch (c.cmd) {
case VKRRenderCommand::REMOVED:
break;
@@ -1419,26 +1324,39 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
{
VKRGraphicsPipeline *graphicsPipeline = c.graphics_pipeline.pipeline;
if (graphicsPipeline != lastGraphicsPipeline) {
if (!graphicsPipeline->pipeline[rpType]) {
VkSampleCountFlagBits fbSampleCount = fb ? fb->sampleCount : VK_SAMPLE_COUNT_1_BIT;
if (RenderPassTypeHasMultisample(rpType) && fbSampleCount != graphicsPipeline->SampleCount()) {
// should have been invalidated.
_assert_msg_(graphicsPipeline->SampleCount() == VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM,
"expected %d sample count, got %d", fbSampleCount, graphicsPipeline->SampleCount());
}
if (!graphicsPipeline->pipeline[(size_t)rpType]) {
// NOTE: If render steps got merged, it can happen that, as they ended during recording,
// they didn't know their final render pass type so they created the wrong pipelines in EndCurRenderStep().
// Unfortunately I don't know if we can fix it in any more sensible place than here.
// Maybe a middle pass. But let's try to just block and compile here for now, this doesn't
// happen all that much.
graphicsPipeline->pipeline[rpType] = Promise<VkPipeline>::CreateEmpty();
graphicsPipeline->Create(vulkan_, renderPass->Get(vulkan_, rpType), rpType);
graphicsPipeline->pipeline[(size_t)rpType] = Promise<VkPipeline>::CreateEmpty();
graphicsPipeline->Create(vulkan_, renderPass->Get(vulkan_, rpType, fbSampleCount), rpType, fbSampleCount);
}
VkPipeline pipeline = graphicsPipeline->pipeline[rpType]->BlockUntilReady();
VkPipeline pipeline = graphicsPipeline->pipeline[(size_t)rpType]->BlockUntilReady();
if (pipeline != VK_NULL_HANDLE) {
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
pipelineLayout = c.pipeline.pipelineLayout;
lastGraphicsPipeline = graphicsPipeline;
// Reset dynamic state so it gets refreshed with the new pipeline.
lastStencilWriteMask = -1;
lastStencilCompareMask = -1;
lastStencilReference = -1;
pipelineOK = true;
} else {
pipelineOK = false;
}
// Reset dynamic state so it gets refreshed with the new pipeline.
lastStencilWriteMask = -1;
lastStencilCompareMask = -1;
lastStencilReference = -1;
}
break;
}
@@ -1503,14 +1421,22 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
case VKRRenderCommand::SELF_DEPENDENCY_BARRIER:
{
_assert_(step.render.pipelineFlags & PipelineFlags::USES_INPUT_ATTACHMENT);
_assert_(fb);
VulkanBarrier barrier;
SelfDependencyBarrier(step.render.framebuffer->color, VK_IMAGE_ASPECT_COLOR_BIT, &barrier);
if (fb->sampleCount != VK_SAMPLE_COUNT_1_BIT) {
// Rendering is happening to the multisample buffer, not the color buffer.
SelfDependencyBarrier(fb->msaaColor, VK_IMAGE_ASPECT_COLOR_BIT, &barrier);
} else {
SelfDependencyBarrier(fb->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);
if (pipelineOK) {
vkCmdPushConstants(cmd, pipelineLayout, c.push.stages, c.push.offset, c.push.size, c.push.data);
}
break;
case VKRRenderCommand::STENCIL:
@@ -1529,21 +1455,23 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
break;
case VKRRenderCommand::DRAW_INDEXED:
{
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets);
vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, (VkIndexType)c.drawIndexed.indexType);
VkDeviceSize voffset = c.drawIndexed.voffset;
vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &voffset);
vkCmdDrawIndexed(cmd, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0);
if (pipelineOK) {
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets);
vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, (VkIndexType)c.drawIndexed.indexType);
VkDeviceSize voffset = c.drawIndexed.voffset;
vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &voffset);
vkCmdDrawIndexed(cmd, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0);
}
break;
}
case VKRRenderCommand::DRAW:
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets);
if (c.draw.vbuffer) {
vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset);
if (pipelineOK) {
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets);
if (c.draw.vbuffer) {
vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset);
}
vkCmdDraw(cmd, c.draw.count, 1, c.draw.offset, 0);
}
vkCmdDraw(cmd, c.draw.count, 1, c.draw.offset, 0);
break;
case VKRRenderCommand::CLEAR:
@@ -1552,7 +1480,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
int numAttachments = 0;
VkClearRect rc{};
rc.baseArrayLayer = 0;
rc.layerCount = 1;
rc.layerCount = 1; // In multiview mode, 1 means to replicate to all the active layers.
rc.rect.extent.width = (uint32_t)curWidth;
rc.rect.extent.height = (uint32_t)curHeight;
VkClearAttachment attachments[2]{};
@@ -1588,15 +1516,20 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
}
break;
case VKRRenderCommand::BIND_DESCRIPTOR_SET:
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.bindDescSet.pipelineLayout, c.bindDescSet.setNumber, 1, &c.bindDescSet.set, 0, nullptr);
break;
default:
ERROR_LOG(G3D, "Unimpl queue command");
break;
}
}
vkCmdEndRenderPass(cmd);
if (fb) {
// If the desired final layout aren't the optimal layout for rendering, transition.
TransitionFromOptimal(cmd, fb->color.image, step.render.finalColorLayout, fb->depth.image, step.render.finalDepthStencilLayout);
TransitionFromOptimal(cmd, fb->color.image, step.render.finalColorLayout, fb->depth.image, fb->numLayers, step.render.finalDepthStencilLayout);
fb->color.layout = step.render.finalColorLayout;
fb->depth.layout = step.render.finalDepthStencilLayout;
@@ -1606,13 +1539,15 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd) {
VKRRenderPass *renderPass;
int numClearVals = 0;
VkClearValue clearVal[2]{};
VkClearValue clearVal[4]{};
VkFramebuffer framebuf;
int w;
int h;
bool hasDepth = RenderPassTypeHasDepth(step.render.renderPassType);
VkSampleCountFlagBits sampleCount;
if (step.render.framebuffer) {
_dbg_assert_(step.render.finalColorLayout != VK_IMAGE_LAYOUT_UNDEFINED);
_dbg_assert_(step.render.finalDepthStencilLayout != VK_IMAGE_LAYOUT_UNDEFINED);
@@ -1625,6 +1560,8 @@ VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKR
VKRFramebuffer *fb = step.render.framebuffer;
framebuf = fb->Get(renderPass, step.render.renderPassType);
sampleCount = fb->sampleCount;
_dbg_assert_(framebuf != VK_NULL_HANDLE);
w = fb->width;
h = fb->height;
@@ -1636,7 +1573,7 @@ VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKR
step.render.colorLoad == VKRRenderPassLoadAction::CLEAR &&
vulkan_->GetPhysicalDeviceProperties().properties.driverVersion == 0xaa9c4b29;
if (maliBugWorkaround) {
recordBarrier_.TransitionImage(step.render.framebuffer->color.image, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT,
recordBarrier_.TransitionImage(fb->color.image, 0, 1, fb->numLayers, VK_IMAGE_ASPECT_COLOR_BIT,
fb->color.layout, VK_IMAGE_LAYOUT_GENERAL,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
@@ -1644,20 +1581,27 @@ VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKR
fb->color.layout = VK_IMAGE_LAYOUT_GENERAL;
}
TransitionToOptimal(cmd, fb->color.image, fb->color.layout, fb->depth.image, fb->depth.layout, &recordBarrier_);
TransitionToOptimal(cmd, fb->color.image, fb->color.layout, fb->depth.image, fb->depth.layout, fb->numLayers, &recordBarrier_);
// The transition from the optimal format happens after EndRenderPass, now that we don't
// do it as part of the renderpass itself anymore.
if (sampleCount != VK_SAMPLE_COUNT_1_BIT) {
// We don't initialize values for these.
numClearVals = hasDepth ? 2 : 1; // Skip the resolve buffers, don't need to clear those.
}
if (step.render.colorLoad == VKRRenderPassLoadAction::CLEAR) {
Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor);
numClearVals = 1;
Uint8x4ToFloat4(clearVal[numClearVals].color.float32, step.render.clearColor);
}
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;
numClearVals++;
if (hasDepth) {
if (step.render.depthLoad == VKRRenderPassLoadAction::CLEAR || step.render.stencilLoad == VKRRenderPassLoadAction::CLEAR) {
clearVal[numClearVals].depthStencil.depth = step.render.clearDepth;
clearVal[numClearVals].depthStencil.stencil = step.render.clearStencil;
}
numClearVals++;
}
_dbg_assert_(numClearVals != 3);
} else {
RPKey key{
VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR,
@@ -1665,7 +1609,11 @@ VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKR
};
renderPass = GetRenderPass(key);
framebuf = backbuffer_;
if (IsVREnabled()) {
framebuf = (VkFramebuffer)BindVRFramebuffer();
} else {
framebuf = backbuffer_;
}
// Raw, rotated backbuffer size.
w = vulkan_->GetBackbufferWidth();
@@ -1675,10 +1623,11 @@ VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKR
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;
sampleCount = VK_SAMPLE_COUNT_1_BIT;
}
VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
rp_begin.renderPass = renderPass->Get(vulkan_, step.render.renderPassType);
rp_begin.renderPass = renderPass->Get(vulkan_, step.render.renderPassType, sampleCount);
rp_begin.framebuffer = framebuf;
VkRect2D rc = step.render.renderArea;
@@ -1710,54 +1659,79 @@ void VulkanQueueRunner::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) {
VKRFramebuffer *src = step.copy.src;
VKRFramebuffer *dst = step.copy.dst;
VkImageCopy copy{};
copy.srcOffset.x = step.copy.srcRect.offset.x;
copy.srcOffset.y = step.copy.srcRect.offset.y;
copy.srcOffset.z = 0;
copy.srcSubresource.mipLevel = 0;
copy.srcSubresource.layerCount = 1;
copy.dstOffset.x = step.copy.dstPos.x;
copy.dstOffset.y = step.copy.dstPos.y;
copy.dstOffset.z = 0;
copy.dstSubresource.mipLevel = 0;
copy.dstSubresource.layerCount = 1;
copy.extent.width = step.copy.srcRect.extent.width;
copy.extent.height = step.copy.srcRect.extent.height;
copy.extent.depth = 1;
int layerCount = std::min(step.copy.src->numLayers, step.copy.dst->numLayers);
_dbg_assert_(step.copy.src->numLayers >= step.copy.dst->numLayers);
// TODO: If dst covers exactly the whole destination, we can set up a UNDEFINED->TRANSFER_DST_OPTIMAL transition,
// which can potentially be more efficient.
// First source barriers.
if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {
if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) {
SetupTransitionToTransferSrc(src->color, VK_IMAGE_ASPECT_COLOR_BIT, &recordBarrier_);
}
if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
SetupTransitionToTransferDst(dst->color, VK_IMAGE_ASPECT_COLOR_BIT, &recordBarrier_);
}
SetupTransitionToTransferSrc(src->color, VK_IMAGE_ASPECT_COLOR_BIT, &recordBarrier_);
SetupTransitionToTransferDst(dst->color, VK_IMAGE_ASPECT_COLOR_BIT, &recordBarrier_);
}
// We can't copy only depth or only stencil unfortunately - or can we?.
if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
_dbg_assert_(src->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_);
}
SetupTransitionToTransferSrc(src->depth, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, &recordBarrier_);
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.
// Kingdom Hearts: Subsequent copies twice 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_);
}
}
recordBarrier_.Flush(cmd);
bool multisampled = src->sampleCount != VK_SAMPLE_COUNT_1_BIT && dst->sampleCount != VK_SAMPLE_COUNT_1_BIT;
if (multisampled) {
// If both the targets are multisampled, copy the msaa targets too.
// For that, we need to transition them from their normally permanent VK_*_ATTACHMENT_OPTIMAL layouts, and then back.
if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {
SetupTransitionToTransferSrc(src->msaaColor, VK_IMAGE_ASPECT_COLOR_BIT, &recordBarrier_);
recordBarrier_.Flush(cmd);
SetupTransitionToTransferDst(dst->msaaColor, VK_IMAGE_ASPECT_COLOR_BIT, &recordBarrier_);
recordBarrier_.Flush(cmd);
}
if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
// 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.
SetupTransitionToTransferSrc(src->msaaDepth, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, &recordBarrier_);
recordBarrier_.Flush(cmd);
SetupTransitionToTransferDst(dst->msaaDepth, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, &recordBarrier_);
recordBarrier_.Flush(cmd);
}
}
recordBarrier_.Flush(cmd);
VkImageCopy copy{};
copy.srcOffset.x = step.copy.srcRect.offset.x;
copy.srcOffset.y = step.copy.srcRect.offset.y;
copy.srcOffset.z = 0;
copy.srcSubresource.mipLevel = 0;
copy.srcSubresource.layerCount = layerCount;
copy.dstOffset.x = step.copy.dstPos.x;
copy.dstOffset.y = step.copy.dstPos.y;
copy.dstOffset.z = 0;
copy.dstSubresource.mipLevel = 0;
copy.dstSubresource.layerCount = layerCount;
copy.extent.width = step.copy.srcRect.extent.width;
copy.extent.height = step.copy.srcRect.extent.height;
copy.extent.depth = 1;
if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {
copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
vkCmdCopyImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, &copy);
if (multisampled) {
vkCmdCopyImage(cmd, src->msaaColor.image, src->msaaColor.layout, dst->msaaColor.image, dst->msaaColor.layout, 1, &copy);
}
}
if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
_dbg_assert_(src->depth.image != VK_NULL_HANDLE);
@@ -1765,6 +1739,75 @@ void VulkanQueueRunner::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) {
copy.srcSubresource.aspectMask = step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
copy.dstSubresource.aspectMask = step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
vkCmdCopyImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, &copy);
if (multisampled) {
vkCmdCopyImage(cmd, src->msaaDepth.image, src->msaaDepth.layout, dst->msaaDepth.image, dst->msaaDepth.layout, 1, &copy);
}
}
if (multisampled) {
// Transition the MSAA surfaces back to optimal.
if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {
recordBarrier_.TransitionImage(
src->msaaColor.image,
0,
1,
src->msaaColor.numLayers,
VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_ACCESS_TRANSFER_READ_BIT,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
);
recordBarrier_.TransitionImage(
dst->msaaColor.image,
0,
1,
dst->msaaColor.numLayers,
VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
);
src->msaaColor.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
dst->msaaColor.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
}
if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
recordBarrier_.TransitionImage(
src->msaaDepth.image,
0,
1,
src->msaaDepth.numLayers,
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
VK_ACCESS_TRANSFER_READ_BIT,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT
);
recordBarrier_.TransitionImage(
dst->msaaDepth.image,
0,
1,
dst->msaaDepth.numLayers,
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT
);
src->msaaDepth.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
dst->msaaDepth.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
}
recordBarrier_.Flush(cmd);
}
}
@@ -1772,9 +1815,29 @@ 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);
int layerCount = std::min(step.blit.src->numLayers, step.blit.dst->numLayers);
_dbg_assert_(step.blit.src->numLayers >= step.blit.dst->numLayers);
VKRFramebuffer *src = step.blit.src;
VKRFramebuffer *dst = step.blit.dst;
// First source barriers.
if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {
SetupTransitionToTransferSrc(src->color, VK_IMAGE_ASPECT_COLOR_BIT, &recordBarrier_);
SetupTransitionToTransferDst(dst->color, VK_IMAGE_ASPECT_COLOR_BIT, &recordBarrier_);
}
// 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);
SetupTransitionToTransferSrc(src->depth, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, &recordBarrier_);
SetupTransitionToTransferDst(dst->depth, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, &recordBarrier_);
}
recordBarrier_.Flush(cmd);
// If any validation needs to be performed here, it should probably have been done
// already when the blit was queued. So don't validate here.
VkImageBlit blit{};
@@ -1785,7 +1848,7 @@ void VulkanQueueRunner::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) {
blit.srcOffsets[1].y = step.blit.srcRect.offset.y + step.blit.srcRect.extent.height;
blit.srcOffsets[1].z = 1;
blit.srcSubresource.mipLevel = 0;
blit.srcSubresource.layerCount = 1;
blit.srcSubresource.layerCount = layerCount;
blit.dstOffsets[0].x = step.blit.dstRect.offset.x;
blit.dstOffsets[0].y = step.blit.dstRect.offset.y;
blit.dstOffsets[0].z = 0;
@@ -1793,38 +1856,7 @@ void VulkanQueueRunner::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) {
blit.dstOffsets[1].y = step.blit.dstRect.offset.y + step.blit.dstRect.extent.height;
blit.dstOffsets[1].z = 1;
blit.dstSubresource.mipLevel = 0;
blit.dstSubresource.layerCount = 1;
VkPipelineStageFlags srcStage = 0;
VkPipelineStageFlags dstStage = 0;
int srcCount = 0;
int dstCount = 0;
// First source barriers.
if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {
if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) {
SetupTransitionToTransferSrc(src->color, VK_IMAGE_ASPECT_COLOR_BIT, &recordBarrier_);
}
if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
SetupTransitionToTransferDst(dst->color, VK_IMAGE_ASPECT_COLOR_BIT, &recordBarrier_);
}
}
// 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_);
}
if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
SetupTransitionToTransferDst(dst->depth, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, &recordBarrier_);
}
}
recordBarrier_.Flush(cmd);
blit.dstSubresource.layerCount = layerCount;
if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
@@ -1850,6 +1882,9 @@ void VulkanQueueRunner::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) {
}
void VulkanQueueRunner::SetupTransitionToTransferSrc(VKRImage &img, VkImageAspectFlags aspect, VulkanBarrier *recordBarrier) {
if (img.layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) {
return;
}
VkImageAspectFlags imageAspect = aspect;
VkAccessFlags srcAccessMask = 0;
VkPipelineStageFlags srcStageMask = 0;
@@ -1886,6 +1921,7 @@ void VulkanQueueRunner::SetupTransitionToTransferSrc(VKRImage &img, VkImageAspec
img.image,
0,
1,
img.numLayers,
imageAspect,
img.layout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
@@ -1898,6 +1934,9 @@ void VulkanQueueRunner::SetupTransitionToTransferSrc(VKRImage &img, VkImageAspec
}
void VulkanQueueRunner::SetupTransitionToTransferDst(VKRImage &img, VkImageAspectFlags aspect, VulkanBarrier *recordBarrier) {
if (img.layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
return;
}
VkImageAspectFlags imageAspect = aspect;
VkAccessFlags srcAccessMask = 0;
VkPipelineStageFlags srcStageMask = 0;
@@ -1934,6 +1973,7 @@ void VulkanQueueRunner::SetupTransitionToTransferDst(VKRImage &img, VkImageAspec
img.image,
0,
1,
img.numLayers,
aspect,
img.layout,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
@@ -1963,6 +2003,7 @@ void VulkanQueueRunner::SetupTransferDstWriteAfterWrite(VKRImage &img, VkImageAs
img.image,
0,
1,
img.numLayers,
aspect,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
@@ -1991,7 +2032,8 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd
if (step.readback.src == nullptr) {
// We only take screenshots after the main render pass (anything else would be stupid) so we need to transition out of PRESENT,
// and then back into it.
TransitionImageLayout2(cmd, backbufferImage_, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT,
// Regarding layers, backbuffer currently only has one layer.
TransitionImageLayout2(cmd, backbufferImage_, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, VK_ACCESS_TRANSFER_READ_BIT);
@@ -2025,7 +2067,8 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd
if (step.readback.src == nullptr) {
// We only take screenshots after the main render pass (anything else would be stupid) so we need to transition out of PRESENT,
// and then back into it.
TransitionImageLayout2(cmd, backbufferImage_, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT,
// Regarding layers, backbuffer currently only has one layer.
TransitionImageLayout2(cmd, backbufferImage_, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_ACCESS_TRANSFER_READ_BIT, 0);
@@ -2038,6 +2081,7 @@ void VulkanQueueRunner::PerformReadbackImage(const VKRStep &step, VkCommandBuffe
VKRImage srcImage{};
srcImage.image = step.readback_image.image;
srcImage.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
srcImage.numLayers = 1;
SetupTransitionToTransferSrc(srcImage, VK_IMAGE_ASPECT_COLOR_BIT, &recordBarrier_);
recordBarrier_.Flush(cmd);
@@ -2056,7 +2100,7 @@ void VulkanQueueRunner::PerformReadbackImage(const VKRStep &step, VkCommandBuffe
vkCmdCopyImageToBuffer(cmd, step.readback_image.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, readbackBuffer_, 1, &region);
// Now transfer it back to a texture.
TransitionImageLayout2(cmd, step.readback_image.image, 0, 1,
TransitionImageLayout2(cmd, step.readback_image.image, 0, 1, 1, // I don't think we have any multilayer cases for regular textures. Above in PerformReadback, though..
VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
+12 -67
View File
@@ -9,6 +9,7 @@
#include "Common/GPU/Vulkan/VulkanContext.h"
#include "Common/GPU/Vulkan/VulkanBarrier.h"
#include "Common/GPU/Vulkan/VulkanFrameData.h"
#include "Common/GPU/Vulkan/VulkanFramebuffer.h"
#include "Common/Data/Convert/SmallDataConvert.h"
#include "Common/Data/Collections/TinySet.h"
#include "Common/GPU/DataFormat.h"
@@ -39,42 +40,21 @@ enum class VKRRenderCommand : uint8_t {
PUSH_CONSTANTS,
SELF_DEPENDENCY_BARRIER,
DEBUG_ANNOTATION,
BIND_DESCRIPTOR_SET,
NUM_RENDER_COMMANDS,
};
enum class PipelineFlags {
enum class PipelineFlags : u8 {
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),
USES_MULTIVIEW = (1 << 5), // Inherited from the render pass it was created with.
USES_DISCARD = (1 << 6),
};
ENUM_CLASS_BITOPS(PipelineFlags);
// Pipelines need to be created for the right type of render pass.
enum RenderPassType {
// These four are organized so that bit 0 is DEPTH and bit 1 is INPUT, so
// they can be OR-ed together in MergeRPTypes.
RP_TYPE_COLOR,
RP_TYPE_COLOR_DEPTH,
RP_TYPE_COLOR_INPUT,
RP_TYPE_COLOR_DEPTH_INPUT,
// This is the odd one out, and gets special handling in MergeRPTypes.
RP_TYPE_BACKBUFFER, // For the backbuffer we can always use CLEAR/DONT_CARE, so bandwidth cost for a depth channel is negligible.
// Later will add pure-color render passes.
RP_TYPE_COUNT,
};
inline bool RenderPassTypeHasDepth(RenderPassType type) {
return type == RP_TYPE_BACKBUFFER || type == RP_TYPE_COLOR_DEPTH || type == RP_TYPE_COLOR_DEPTH_INPUT;
}
inline bool RenderPassTypeHasInput(RenderPassType type) {
return type == RP_TYPE_COLOR_INPUT || type == RP_TYPE_COLOR_DEPTH_INPUT;
}
struct VkRenderData {
VKRRenderCommand cmd;
union {
@@ -103,7 +83,7 @@ struct VkRenderData {
VkDescriptorSet ds;
int numUboOffsets;
uint32_t uboOffsets[3];
VkBuffer vbuffer; // might need to increase at some point
VkBuffer vbuffer;
VkBuffer ibuffer;
uint32_t voffset;
uint32_t ioffset;
@@ -140,6 +120,11 @@ struct VkRenderData {
struct {
const char *annotation;
} debugAnnotation;
struct {
int setNumber;
VkDescriptorSet set;
VkPipelineLayout pipelineLayout;
} bindDescSet;
};
};
@@ -152,18 +137,6 @@ enum class VKRStepType : uint8_t {
READBACK_IMAGE,
};
// Must be the same order as Draw::RPAction
enum class VKRRenderPassLoadAction : uint8_t {
KEEP, // default. avoid when possible.
CLEAR,
DONT_CARE,
};
enum class VKRRenderPassStoreAction : uint8_t {
STORE, // default. avoid when possible.
DONT_CARE,
};
struct TransitionRequest {
VKRFramebuffer *fb;
VkImageAspectFlags aspect; // COLOR or DEPTH
@@ -236,34 +209,6 @@ struct VKRStep {
};
};
struct RPKey {
// Only render-pass-compatibility-volatile things can be here.
VKRRenderPassLoadAction colorLoadAction;
VKRRenderPassLoadAction depthLoadAction;
VKRRenderPassLoadAction stencilLoadAction;
VKRRenderPassStoreAction colorStoreAction;
VKRRenderPassStoreAction depthStoreAction;
VKRRenderPassStoreAction stencilStoreAction;
};
class VKRRenderPass {
public:
VKRRenderPass(const RPKey &key) : key_(key) {}
VkRenderPass Get(VulkanContext *vulkan, RenderPassType rpType);
void Destroy(VulkanContext *vulkan) {
for (int i = 0; i < RP_TYPE_COUNT; i++) {
if (pass[i]) {
vulkan->Delete().QueueDeleteRenderPass(pass[i]);
}
}
}
private:
VkRenderPass pass[RP_TYPE_COUNT]{};
RPKey key_;
};
// These are enqueued from the main thread,
// and the render thread pops them off
struct VKRRenderThreadTask {
@@ -282,7 +227,7 @@ public:
}
void PreprocessSteps(std::vector<VKRStep *> &steps);
void RunSteps(std::vector<VKRStep *> &steps, FrameData &frameData, FrameDataShared &frameDataShared);
void RunSteps(std::vector<VKRStep *> &steps, FrameData &frameData, FrameDataShared &frameDataShared, bool keepSteps = false);
void LogSteps(const std::vector<VKRStep *> &steps, bool verbose);
std::string StepToString(const VKRStep &step) const;
+155 -219
View File
@@ -12,6 +12,7 @@
#include "Common/GPU/Vulkan/VulkanRenderManager.h"
#include "Common/Thread/ThreadUtil.h"
#include "Common/VR/PPSSPPVR.h"
#if 0 // def _DEBUG
#define VLOG(...) NOTICE_LOG(G3D, __VA_ARGS__)
@@ -26,7 +27,16 @@
using namespace PPSSPP_VK;
// renderPass is an example of the "compatibility class" or RenderPassType type.
bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType) {
bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType, VkSampleCountFlagBits sampleCount) {
bool multisample = RenderPassTypeHasMultisample(rpType);
if (multisample) {
if (sampleCount_ != VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM) {
_assert_(sampleCount == sampleCount_);
} else {
sampleCount_ = sampleCount;
}
}
// 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();
@@ -38,6 +48,12 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleR
return false;
}
if (!compatibleRenderPass) {
ERROR_LOG(G3D, "Failed creating graphics pipeline - compatible render pass was null");
// We're kinda screwed here?
return false;
}
uint32_t stageCount = 2;
VkPipelineShaderStageCreateInfo ss[3]{};
ss[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
@@ -68,13 +84,24 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleR
pipe.pDepthStencilState = &desc->dss;
pipe.pRasterizationState = &desc->rs;
VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
ms.rasterizationSamples = multisample ? sampleCount : VK_SAMPLE_COUNT_1_BIT;
if (multisample && (flags_ & PipelineFlags::USES_DISCARD)) {
// Extreme quality
ms.sampleShadingEnable = true;
ms.minSampleShading = 1.0f;
}
VkPipelineInputAssemblyStateCreateInfo inputAssembly{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
inputAssembly.topology = desc->topology;
// We will use dynamic viewport state.
pipe.pVertexInputState = &desc->vis;
pipe.pViewportState = &desc->views;
pipe.pTessellationState = nullptr;
pipe.pDynamicState = &desc->ds;
pipe.pInputAssemblyState = &desc->inputAssembly;
pipe.pMultisampleState = &desc->ms;
pipe.pInputAssemblyState = &inputAssembly;
pipe.pMultisampleState = &ms;
pipe.layout = desc->pipelineLayout;
pipe.basePipelineHandle = VK_NULL_HANDLE;
pipe.basePipelineIndex = 0;
@@ -83,8 +110,13 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleR
double start = time_now_d();
VkPipeline vkpipeline;
VkResult result = vkCreateGraphicsPipelines(vulkan->GetDevice(), desc->pipelineCache, 1, &pipe, nullptr, &vkpipeline);
double taken_ms = (time_now_d() - start) * 1000.0;
INFO_LOG(G3D, "Pipeline creation time: %0.2f ms", (time_now_d() - start) * 1000.0);
if (taken_ms < 0.1) {
DEBUG_LOG(G3D, "Pipeline creation time: %0.2f ms (fast) rpType: %08x sampleBits: %d\n(%s)", taken_ms, (u32)rpType, (u32)sampleCount, tag_.c_str());
} else {
INFO_LOG(G3D, "Pipeline creation time: %0.2f ms rpType: %08x sampleBits: %d\n(%s)", taken_ms, (u32)rpType, (u32)sampleCount, tag_.c_str());
}
bool success = true;
if (result == VK_INCOMPLETE) {
@@ -92,30 +124,45 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleR
// Would really like to log more here, we could probably attach more info to desc.
//
// At least create a null placeholder to avoid creating over and over if something is broken.
pipeline[rpType]->Post(VK_NULL_HANDLE);
pipeline[(size_t)rpType]->Post(VK_NULL_HANDLE);
ERROR_LOG(G3D, "Failed creating graphics pipeline! VK_INCOMPLETE");
LogCreationFailure();
success = false;
} else if (result != VK_SUCCESS) {
pipeline[rpType]->Post(VK_NULL_HANDLE);
pipeline[(size_t)rpType]->Post(VK_NULL_HANDLE);
ERROR_LOG(G3D, "Failed creating graphics pipeline! result='%s'", VulkanResultToString(result));
LogCreationFailure();
success = false;
} else {
// Success!
if (!tag.empty()) {
vulkan->SetDebugName(vkpipeline, VK_OBJECT_TYPE_PIPELINE, tag.c_str());
if (!tag_.empty()) {
vulkan->SetDebugName(vkpipeline, VK_OBJECT_TYPE_PIPELINE, tag_.c_str());
}
pipeline[rpType]->Post(vkpipeline);
pipeline[(size_t)rpType]->Post(vkpipeline);
}
return success;
}
void VKRGraphicsPipeline::QueueForDeletion(VulkanContext *vulkan) {
for (int i = 0; i < RP_TYPE_COUNT; i++) {
if (!pipeline[i])
void VKRGraphicsPipeline::DestroyVariants(VulkanContext *vulkan, bool msaaOnly) {
for (size_t i = 0; i < (size_t)RenderPassType::TYPE_COUNT; i++) {
if (!this->pipeline[i])
continue;
if (msaaOnly && (i & (int)RenderPassType::MULTISAMPLE) == 0)
continue;
VkPipeline pipeline = this->pipeline[i]->BlockUntilReady();
vulkan->Delete().QueueDeletePipeline(pipeline);
// pipeline can be nullptr here, if it failed to compile before.
if (pipeline) {
vulkan->Delete().QueueDeletePipeline(pipeline);
}
this->pipeline[i] = nullptr;
}
sampleCount_ = VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM;
}
void VKRGraphicsPipeline::QueueForDeletion(VulkanContext *vulkan) {
DestroyVariants(vulkan, false);
vulkan->Delete().QueueCallback([](void *p) {
VKRGraphicsPipeline *pipeline = (VKRGraphicsPipeline *)p;
delete pipeline;
@@ -124,7 +171,7 @@ void VKRGraphicsPipeline::QueueForDeletion(VulkanContext *vulkan) {
u32 VKRGraphicsPipeline::GetVariantsBitmask() const {
u32 bitmask = 0;
for (int i = 0; i < RP_TYPE_COUNT; i++) {
for (size_t i = 0; i < (size_t)RenderPassType::TYPE_COUNT; i++) {
if (pipeline[i]) {
bitmask |= 1 << i;
}
@@ -132,6 +179,16 @@ u32 VKRGraphicsPipeline::GetVariantsBitmask() const {
return bitmask;
}
void VKRGraphicsPipeline::LogCreationFailure() const {
ERROR_LOG(G3D, "vs: %s\n[END VS]", desc->vertexShaderSource.c_str());
ERROR_LOG(G3D, "fs: %s\n[END FS]", desc->fragmentShaderSource.c_str());
if (desc->geometryShader) {
ERROR_LOG(G3D, "gs: %s\n[END GS]", desc->geometryShaderSource.c_str());
}
// TODO: Maybe log various other state?
ERROR_LOG(G3D, "======== END OF PIPELINE ==========");
}
bool VKRComputePipeline::Create(VulkanContext *vulkan) {
if (!desc) {
// Already failed to create this one.
@@ -154,173 +211,6 @@ bool VKRComputePipeline::Create(VulkanContext *vulkan) {
return success;
}
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);
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.
}
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]{};
bool hasDepth = rpType == RP_TYPE_BACKBUFFER || rpType == RP_TYPE_COLOR_DEPTH || rpType == RP_TYPE_COLOR_DEPTH_INPUT;
views[0] = color.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)rpType]);
_assert_(res == VK_SUCCESS);
if (!tag_.empty() && vulkan_->Extensions().EXT_debug_utils) {
vulkan_->SetDebugName(framebuf[(int)rpType], VK_OBJECT_TYPE_FRAMEBUFFER, StringFromFormat("fb_%s", tag_.c_str()).c_str());
}
return framebuf[(int)rpType];
}
VKRFramebuffer::~VKRFramebuffer() {
if (color.imageView)
vulkan_->Delete().QueueDeleteImageView(color.imageView);
if (depth.imageView)
vulkan_->Delete().QueueDeleteImageView(depth.imageView);
if (color.image) {
_dbg_assert_(color.alloc);
vulkan_->Delete().QueueDeleteImageAllocation(color.image, color.alloc);
}
if (depth.image) {
_dbg_assert_(depth.alloc);
vulkan_->Delete().QueueDeleteImageAllocation(depth.image, depth.alloc);
}
if (depth.depthSampleView)
vulkan_->Delete().QueueDeleteImageView(depth.depthSampleView);
for (auto &fb : framebuf) {
if (fb)
vulkan_->Delete().QueueDeleteFramebuffer(fb);
}
}
void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color, const char *tag) {
VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
ici.arrayLayers = 1;
ici.mipLevels = 1;
ici.extent.width = width;
ici.extent.height = height;
ici.extent.depth = 1;
ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
ici.imageType = VK_IMAGE_TYPE_2D;
ici.samples = VK_SAMPLE_COUNT_1_BIT;
ici.tiling = VK_IMAGE_TILING_OPTIMAL;
ici.format = format;
// 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 | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
} else {
ici.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
}
VmaAllocationCreateInfo allocCreateInfo{};
allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
VmaAllocationInfo allocInfo{};
VkResult res = vmaCreateImage(vulkan->Allocator(), &ici, &allocCreateInfo, &img.image, &img.alloc, &allocInfo);
_dbg_assert_(res == VK_SUCCESS);
VkImageAspectFlags aspects = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
VkImageViewCreateInfo ivci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
ivci.components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };
ivci.format = ici.format;
ivci.image = img.image;
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
ivci.subresourceRange.aspectMask = aspects;
ivci.subresourceRange.layerCount = 1;
ivci.subresourceRange.levelCount = 1;
res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.imageView);
_dbg_assert_(res == VK_SUCCESS);
// Separate view for texture sampling that only exposes depth.
if (!color) {
ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.depthSampleView);
_dbg_assert_(res == VK_SUCCESS);
} else {
img.depthSampleView = VK_NULL_HANDLE;
}
VkPipelineStageFlags dstStage;
VkAccessFlagBits dstAccessMask;
switch (initialLayout) {
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
break;
default:
Crash();
return;
}
TransitionImageLayout2(cmd, img.image, 0, 1, aspects,
VK_IMAGE_LAYOUT_UNDEFINED, initialLayout,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, dstStage,
0, dstAccessMask);
img.layout = initialLayout;
img.format = format;
img.tag = tag ? tag : "N/A";
}
VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan)
: vulkan_(vulkan), queueRunner_(vulkan),
initTimeMs_("initTimeMs"),
@@ -472,23 +362,26 @@ void VulkanRenderManager::CompileThreadFunc() {
break;
}
if (!toCompile.empty()) {
INFO_LOG(G3D, "Compilation thread has %d pipelines to create", (int)toCompile.size());
}
double time = time_now_d();
// TODO: Here we can sort the pending pipelines by vertex and fragment shaders,
// and split up further.
// Those with the same pairs of shaders should be on the same thread.
for (auto &entry : toCompile) {
switch (entry.type) {
case CompileQueueEntry::Type::GRAPHICS:
entry.graphics->Create(vulkan_, entry.compatibleRenderPass, entry.renderPassType);
entry.graphics->Create(vulkan_, entry.compatibleRenderPass, entry.renderPassType, entry.sampleCount);
break;
case CompileQueueEntry::Type::COMPUTE:
entry.compute->Create(vulkan_);
break;
}
}
double delta = time_now_d() - time;
if (delta > 0.005f) {
INFO_LOG(G3D, "CompileThreadFunc: Creating %d pipelines took %0.3f ms", (int)toCompile.size(), delta * 1000.0f);
}
queueRunner_.NotifyCompileDone();
}
}
@@ -611,10 +504,8 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling, bool enableLogProfile
// Must be after the fence - this performs deletes.
VLOG("PUSH: BeginFrame %d", curFrame);
vulkan_->BeginFrame(enableLogProfiler ? GetInitCmd() : VK_NULL_HANDLE);
insideFrame_ = true;
renderStepOffset_ = 0;
vulkan_->BeginFrame(enableLogProfiler ? GetInitCmd() : VK_NULL_HANDLE);
frameData.profile.timestampDescriptions.clear();
if (frameData.profilingEnabled_) {
@@ -633,12 +524,12 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() {
return frameData_[curFrame].GetInitCmd(vulkan_);
}
VKRGraphicsPipeline *VulkanRenderManager::CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, PipelineFlags pipelineFlags, uint32_t variantBitmask, const char *tag) {
VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline();
VKRGraphicsPipeline *VulkanRenderManager::CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, PipelineFlags pipelineFlags, uint32_t variantBitmask, VkSampleCountFlagBits sampleCount, const char *tag) {
VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline(pipelineFlags, tag);
_dbg_assert_(desc->vertexShader);
_dbg_assert_(desc->fragmentShader);
pipeline->desc = desc;
pipeline->tag = tag;
pipeline->desc->AddRef();
if (curRenderStep_) {
// The common case
pipelinesToCheck_.push_back(pipeline);
@@ -655,27 +546,28 @@ VKRGraphicsPipeline *VulkanRenderManager::CreateGraphicsPipeline(VKRGraphicsPipe
VKRRenderPass *compatibleRenderPass = queueRunner_.GetRenderPass(key);
compileMutex_.lock();
bool needsCompile = false;
for (int i = 0; i < RP_TYPE_COUNT; i++) {
for (size_t i = 0; i < (size_t)RenderPassType::TYPE_COUNT; i++) {
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_DEPTH_STENCIL) && !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;
}
if ((pipelineFlags & PipelineFlags::USES_INPUT_ATTACHMENT) && !RenderPassTypeHasInput(rpType)) {
WARN_LOG(G3D, "Not compiling pipeline that requires input attachment, for non input renderpass type");
continue;
}
// Shouldn't hit this, these should have been filtered elsewhere. However, still a good check to do.
if (sampleCount == VK_SAMPLE_COUNT_1_BIT && RenderPassTypeHasMultisample(rpType)) {
WARN_LOG(G3D, "Not compiling single sample pipeline for a multisampled render pass type");
continue;
}
pipeline->pipeline[rpType] = Promise<VkPipeline>::CreateEmpty();
compileQueue_.push_back(CompileQueueEntry(pipeline, compatibleRenderPass->Get(vulkan_, rpType), rpType));
pipeline->pipeline[i] = Promise<VkPipeline>::CreateEmpty();
compileQueue_.push_back(CompileQueueEntry(pipeline, compatibleRenderPass->Get(vulkan_, rpType, sampleCount), rpType, sampleCount));
needsCompile = true;
}
if (needsCompile)
@@ -707,24 +599,43 @@ void VulkanRenderManager::EndCurRenderStep() {
// 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;
RenderPassType rpType = depthStencil ? RenderPassType::HAS_DEPTH : RenderPassType::DEFAULT;
if (curRenderStep_->render.framebuffer && (rpType & RenderPassType::HAS_DEPTH) && !curRenderStep_->render.framebuffer->HasDepth()) {
WARN_LOG(G3D, "Trying to render with a depth-writing pipeline to a framebuffer without depth: %s", curRenderStep_->render.framebuffer->Tag());
rpType = RenderPassType::DEFAULT;
}
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;
rpType = RenderPassType::BACKBUFFER;
} else {
if (curPipelineFlags_ & PipelineFlags::USES_INPUT_ATTACHMENT) {
// Not allowed on backbuffers.
rpType = depthStencil ? (RenderPassType::HAS_DEPTH | RenderPassType::COLOR_INPUT) : RenderPassType::COLOR_INPUT;
}
// Framebuffers can be stereo, and if so, will control the render pass type to match.
// Pipelines can be mono and render fine to stereo etc, so not checking them here.
// Note that we don't support rendering to just one layer of a multilayer framebuffer!
if (curRenderStep_->render.framebuffer->numLayers > 1) {
rpType = (RenderPassType)(rpType | RenderPassType::MULTIVIEW);
}
if (curRenderStep_->render.framebuffer->sampleCount != VK_SAMPLE_COUNT_1_BIT) {
rpType = (RenderPassType)(rpType | RenderPassType::MULTISAMPLE);
}
}
// TODO: Also add render pass types for depth/stencil-less.
VKRRenderPass *renderPass = queueRunner_.GetRenderPass(key);
curRenderStep_->render.renderPassType = rpType;
VkSampleCountFlagBits sampleCount = curRenderStep_->render.framebuffer ? curRenderStep_->render.framebuffer->sampleCount : VK_SAMPLE_COUNT_1_BIT;
compileMutex_.lock();
bool needsCompile = false;
for (VKRGraphicsPipeline *pipeline : pipelinesToCheck_) {
if (!pipeline->pipeline[rpType]) {
pipeline->pipeline[rpType] = Promise<VkPipeline>::CreateEmpty();
compileQueue_.push_back(CompileQueueEntry(pipeline, renderPass->Get(vulkan_, rpType), rpType));
if (!pipeline->pipeline[(size_t)rpType]) {
pipeline->pipeline[(size_t)rpType] = Promise<VkPipeline>::CreateEmpty();
compileQueue_.push_back(CompileQueueEntry(pipeline, renderPass->Get(vulkan_, rpType, sampleCount), rpType, sampleCount));
needsCompile = true;
}
}
@@ -889,6 +800,10 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR
data.clear.clearMask = lateClearMask;
curRenderStep_->commands.push_back(data);
}
if (invalidationCallback_) {
invalidationCallback_(InvalidationCallbackFlags::RENDER_PASS_STATE);
}
}
bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkImageAspectFlags aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag) {
@@ -1037,6 +952,7 @@ void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearSten
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
if (!clearMask)
return;
// If this is the first drawing command or clears everything, merge it into the pass.
int allAspects = VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
if (curRenderStep_->render.numDraws == 0 || clearMask == allAspects) {
@@ -1048,7 +964,11 @@ void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearSten
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;
if (curRenderStep_->render.framebuffer && !curRenderStep_->render.framebuffer->HasDepth()) {
WARN_LOG(G3D, "Trying to clear depth/stencil on a non-depth framebuffer: %s", curRenderStep_->render.framebuffer->Tag());
} else {
curPipelineFlags_ |= PipelineFlags::USES_DEPTH_STENCIL;
}
}
// In case there were commands already.
@@ -1175,7 +1095,7 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect,
steps_.push_back(step);
}
VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBit, int attachment) {
VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBit, int layer) {
_dbg_assert_(curRenderStep_ != nullptr);
// We don't support texturing from stencil, neither do we support texturing from depth|stencil together (nonsensical).
@@ -1210,7 +1130,12 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in
// 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;
if (layer == -1) {
return aspectBit == VK_IMAGE_ASPECT_COLOR_BIT ? fb->color.texAllLayersView : fb->depth.texAllLayersView;
} else {
return aspectBit == VK_IMAGE_ASPECT_COLOR_BIT ? fb->color.texLayerViews[layer] : fb->depth.texLayerViews[layer];
}
}
// Called on main thread.
@@ -1278,7 +1203,16 @@ void VulkanRenderManager::Run(VKRRenderThreadTask &task) {
if (task.steps.empty() && !frameData.hasAcquired)
frameData.skipSwap = true;
//queueRunner_.LogSteps(stepsOnThread, false);
queueRunner_.RunSteps(task.steps, frameData, frameDataShared_);
if (IsVREnabled()) {
int passes = GetVRPassesCount();
for (int i = 0; i < passes; i++) {
PreVRFrameRender(i);
queueRunner_.RunSteps(task.steps, frameData, frameDataShared_, i < passes - 1);
PostVRFrameRender();
}
} else {
queueRunner_.RunSteps(task.steps, frameData, frameDataShared_);
}
switch (task.runType) {
case VKRRunType::PRESENT:
@@ -1328,7 +1262,9 @@ void VulkanRenderManager::Run(VKRRenderThreadTask &task) {
// Called from main thread.
void VulkanRenderManager::FlushSync() {
renderStepOffset_ += (int)steps_.size();
if (invalidationCallback_) {
invalidationCallback_(InvalidationCallbackFlags::COMMAND_BUFFER_STATE);
}
int curFrame = vulkan_->GetCurFrame();
FrameData &frameData = frameData_[curFrame];
+60 -66
View File
@@ -19,55 +19,14 @@
#include "Common/Data/Convert/SmallDataConvert.h"
#include "Common/Math/math_util.h"
#include "Common/GPU/DataFormat.h"
#include "Common/GPU/MiscTypes.h"
#include "Common/GPU/Vulkan/VulkanQueueRunner.h"
#include "Common/GPU/Vulkan/VulkanFramebuffer.h"
#include "Common/GPU/thin3d.h"
// Forward declaration
VK_DEFINE_HANDLE(VmaAllocation);
// Simple independent framebuffer image. Gets its own allocation, we don't have that many framebuffers so it's fine
// to let them have individual non-pooled allocations. Until it's not fine. We'll see.
struct VKRImage {
// These four are "immutable".
VkImage image;
VkImageView imageView;
VkImageView depthSampleView;
VmaAllocation alloc;
VkFormat format;
// This one is used by QueueRunner's Perform functions to keep track. CANNOT be used anywhere else due to sync issues.
VkImageLayout layout;
// For debugging.
std::string tag;
};
void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color, const char *tag);
class VKRFramebuffer {
public:
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{}; // 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:
VkFramebuffer framebuf[RP_TYPE_COUNT]{};
std::string tag_;
};
struct BoundingRect {
int x1;
int y1;
@@ -116,7 +75,8 @@ struct BoundingRect {
};
// All the data needed to create a graphics pipeline.
struct VKRGraphicsPipelineDesc {
// TODO: Compress this down greatly.
struct VKRGraphicsPipelineDesc : Draw::RefCountedObject {
VkPipelineCache pipelineCache = VK_NULL_HANDLE;
VkPipelineColorBlendStateCreateInfo cbs{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
VkPipelineColorBlendAttachmentState blend0{};
@@ -124,14 +84,19 @@ struct VKRGraphicsPipelineDesc {
VkDynamicState dynamicStates[6]{};
VkPipelineDynamicStateCreateInfo ds{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
// Replaced the ShaderStageInfo with promises here so we can wait for compiles to finish.
Promise<VkShaderModule> *vertexShader = nullptr;
Promise<VkShaderModule> *fragmentShader = nullptr;
Promise<VkShaderModule> *geometryShader = nullptr;
VkPipelineInputAssemblyStateCreateInfo inputAssembly{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
// These are for pipeline creation failure logging.
// TODO: Store pointers to the string instead? Feels iffy but will probably work.
std::string vertexShaderSource;
std::string fragmentShaderSource;
std::string geometryShaderSource;
VkPrimitiveTopology topology;
VkVertexInputAttributeDescription attrs[8]{};
VkVertexInputBindingDescription ibd{};
VkPipelineVertexInputStateCreateInfo vis{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
@@ -152,22 +117,34 @@ struct VKRComputePipelineDesc {
// Wrapped pipeline. Doesn't own desc.
struct VKRGraphicsPipeline {
VKRGraphicsPipeline(PipelineFlags flags, const char *tag) : flags_(flags), tag_(tag) {}
~VKRGraphicsPipeline() {
for (int i = 0; i < RP_TYPE_COUNT; i++) {
for (size_t i = 0; i < (size_t)RenderPassType::TYPE_COUNT; i++) {
delete pipeline[i];
}
if (desc)
desc->Release();
}
bool Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType);
bool Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType, VkSampleCountFlagBits sampleCount);
void DestroyVariants(VulkanContext *vulkan, bool msaaOnly);
// This deletes the whole VKRGraphicsPipeline, you must remove your last pointer to it when doing this.
void QueueForDeletion(VulkanContext *vulkan);
u32 GetVariantsBitmask() const;
VKRGraphicsPipelineDesc *desc = nullptr; // not owned!
Promise<VkPipeline> *pipeline[RP_TYPE_COUNT]{};
std::string tag;
void LogCreationFailure() const;
VKRGraphicsPipelineDesc *desc = nullptr;
Promise<VkPipeline> *pipeline[(size_t)RenderPassType::TYPE_COUNT]{};
VkSampleCountFlagBits SampleCount() const { return sampleCount_; }
private:
std::string tag_;
PipelineFlags flags_;
VkSampleCountFlagBits sampleCount_ = VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM;
};
struct VKRComputePipeline {
@@ -185,9 +162,9 @@ struct VKRComputePipeline {
};
struct CompileQueueEntry {
CompileQueueEntry(VKRGraphicsPipeline *p, VkRenderPass _compatibleRenderPass, RenderPassType _renderPassType)
: type(Type::GRAPHICS), graphics(p), compatibleRenderPass(_compatibleRenderPass), renderPassType(_renderPassType) {}
CompileQueueEntry(VKRComputePipeline *p) : type(Type::COMPUTE), compute(p), renderPassType(RP_TYPE_COLOR_DEPTH) {}
CompileQueueEntry(VKRGraphicsPipeline *p, VkRenderPass _compatibleRenderPass, RenderPassType _renderPassType, VkSampleCountFlagBits _sampleCount)
: type(Type::GRAPHICS), graphics(p), compatibleRenderPass(_compatibleRenderPass), renderPassType(_renderPassType), sampleCount(_sampleCount) {}
CompileQueueEntry(VKRComputePipeline *p) : type(Type::COMPUTE), compute(p), renderPassType(RenderPassType::HAS_DEPTH), sampleCount(VK_SAMPLE_COUNT_1_BIT) {} // renderpasstype here shouldn't matter
enum class Type {
GRAPHICS,
COMPUTE,
@@ -197,6 +174,7 @@ struct CompileQueueEntry {
RenderPassType renderPassType;
VKRGraphicsPipeline *graphics = nullptr;
VKRComputePipeline *compute = nullptr;
VkSampleCountFlagBits sampleCount;
};
class VulkanRenderManager {
@@ -211,7 +189,11 @@ public:
// 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();
// This starts a new step containing a render pass.
void SetInvalidationCallback(InvalidationCallback callback) {
invalidationCallback_ = callback;
}
// This starts a new step containing a render pass (unless it can be trivially merged into the previous one, which is pretty common).
//
// After a "CopyFramebuffer" or the other functions that start "steps", you need to call this beforce
// making any new render state changes or draw calls.
@@ -230,7 +212,9 @@ public:
// Returns an ImageView corresponding to a framebuffer. Is called BindFramebufferAsTexture to maintain a similar interface
// as the other backends, even though there's no actual binding happening here.
VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBits, int attachment);
// For layer, we use the same convention as thin3d, where layer = -1 means all layers together. For texturing, that means that you
// get an array texture view.
VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBits, int layer);
void BindCurrentFramebufferAsInputAttachment0(VkImageAspectFlags aspectBits);
@@ -245,7 +229,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, PipelineFlags pipelineFlags, uint32_t variantBitmask, const char *tag);
VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, PipelineFlags pipelineFlags, uint32_t variantBitmask, VkSampleCountFlagBits sampleCount, const char *tag);
VKRComputePipeline *CreateComputePipeline(VKRComputePipelineDesc *desc);
void NudgeCompilerThread() {
@@ -254,6 +238,16 @@ public:
compileMutex_.unlock();
}
// Mainly used to bind the frame-global desc set.
// Can be done before binding a pipeline, so not asserting on that.
void BindDescriptorSet(int setNumber, VkDescriptorSet set, VkPipelineLayout pipelineLayout) {
VkRenderData data{ VKRRenderCommand::BIND_DESCRIPTOR_SET };
data.bindDescSet.setNumber = setNumber;
data.bindDescSet.set = set;
data.bindDescSet.pipelineLayout = pipelineLayout;
curRenderStep_->commands.push_back(data);
}
void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
_dbg_assert_(pipeline != nullptr);
@@ -261,6 +255,10 @@ public:
pipelinesToCheck_.push_back(pipeline);
data.graphics_pipeline.pipeline = pipeline;
data.graphics_pipeline.pipelineLayout = pipelineLayout;
// This can be used to debug cases where depth/stencil rendering is used on color-only framebuffers.
// if ((flags & PipelineFlags::USES_DEPTH_STENCIL) && curRenderStep_->render.framebuffer && !curRenderStep_->render.framebuffer->HasDepth()) {
// DebugBreak();
// }
curPipelineFlags_ |= flags;
curRenderStep_->commands.push_back(data);
}
@@ -435,12 +433,6 @@ public:
VkCommandBuffer GetInitCmd();
// Gets a frame-unique ID of the current step being recorded. Can be used to figure out
// when the current step has changed, which means the caller will need to re-record its state.
int GetCurrentStepId() const {
return renderStepOffset_ + (int)steps_.size();
}
bool CreateBackbuffers();
void DestroyBackbuffers();
@@ -470,6 +462,8 @@ public:
return outOfDateFrames_ > VulkanContext::MAX_INFLIGHT_FRAMES;
}
void Invalidate(InvalidationFlags flags);
void ResetStats();
private:
@@ -480,7 +474,6 @@ private:
void DrainCompileQueue();
void Run(VKRRenderThreadTask &task);
void BeginSubmitFrame(int frame);
// Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot).
void FlushSync();
@@ -508,7 +501,6 @@ private:
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;
@@ -547,4 +539,6 @@ private:
SimpleStat initTimeMs_;
SimpleStat totalGPUTimeMs_;
SimpleStat renderCPUTimeMs_;
std::function<void(InvalidationCallbackFlags)> invalidationCallback_;
};
+217 -89
View File
@@ -35,15 +35,22 @@
#include "Core/Config.h"
// We use a simple descriptor set for all rendering: 1 sampler, 1 texture, 1 UBO binding point.
// binding 0 - uniform data
// binding 1 - sampler
// binding 2 - sampler
//
// Vertex data lives in a separate namespace (location = 0, 1, etc)
#include "Common/GPU/Vulkan/VulkanLoader.h"
// We support a frame-global descriptor set, which can be optionally used by other code,
// but is not directly used by thin3d. It has to be defined here though, be in set 0
// and specified in every pipeline layout, otherwise it can't sit undisturbed when other
// descriptor sets are bound on top.
// For descriptor set 1, we use a simple descriptor set for all thin3d rendering: 1 UBO binding point, 3 combined texture/samples.
//
// binding 0 - uniform buffer
// binding 1 - texture/sampler
// binding 2 - texture/sampler
// binding 3 - texture/sampler
//
// Vertex data lives in a separate namespace (location = 0, 1, etc).
using namespace PPSSPP_VK;
namespace Draw {
@@ -196,7 +203,10 @@ public:
DEBUG_LOG(G3D, "Queueing %s (shmodule %p) for release", tag_.c_str(), module_);
VkShaderModule shaderModule = module_->BlockUntilReady();
vulkan_->Delete().QueueDeleteShaderModule(shaderModule);
delete module_;
vulkan_->Delete().QueueCallback([](void *m) {
auto module = (Promise<VkShaderModule> *)m;
delete module;
}, module_);
}
}
Promise<VkShaderModule> *Get() const { return module_; }
@@ -235,7 +245,7 @@ bool VKShaderModule::Compile(VulkanContext *vulkan, ShaderLanguage language, con
#endif
VkShaderModule shaderModule = VK_NULL_HANDLE;
if (vulkan->CreateShaderModule(spirv, &shaderModule, vkstage_ == VK_SHADER_STAGE_VERTEX_BIT ? "thin3d_vs" : "thin3d_fs")) {
if (vulkan->CreateShaderModule(spirv, &shaderModule, tag_.c_str())) {
module_ = Promise<VkShaderModule>::AlreadyDone(shaderModule);
ok_ = true;
} else {
@@ -257,6 +267,7 @@ public:
VKPipeline(VulkanContext *vulkan, size_t size, PipelineFlags _flags, const char *tag) : vulkan_(vulkan), flags(_flags), tag_(tag) {
uboSize_ = (int)size;
ubo_ = new uint8_t[uboSize_];
vkrDesc = new VKRGraphicsPipelineDesc();
}
~VKPipeline() {
DEBUG_LOG(G3D, "Queueing %s (pipeline) for release", tag_.c_str());
@@ -267,9 +278,11 @@ public:
dep->Release();
}
delete[] ubo_;
vkrDesc->Release();
}
void SetDynamicUniformData(const void *data, size_t size) {
_dbg_assert_(size <= uboSize_);
memcpy(ubo_, data, size);
}
@@ -283,7 +296,7 @@ public:
}
VKRGraphicsPipeline *pipeline = nullptr;
VKRGraphicsPipelineDesc vkrDesc;
VKRGraphicsPipelineDesc *vkrDesc = nullptr;
PipelineFlags flags;
std::vector<VKShaderModule *> deps;
@@ -335,12 +348,16 @@ public:
VkImageView GetImageView() {
if (vkTex_) {
vkTex_->Touch();
return vkTex_->GetImageView();
} else {
// This would be bad.
return VK_NULL_HANDLE;
}
return VK_NULL_HANDLE; // This would be bad.
}
VkImageView GetImageArrayView() {
if (vkTex_) {
return vkTex_->GetImageArrayView();
}
return VK_NULL_HANDLE; // This would be bad.
}
private:
@@ -365,7 +382,7 @@ class VKFramebuffer;
class VKContext : public DrawContext {
public:
VKContext(VulkanContext *vulkan);
virtual ~VKContext();
~VKContext();
void DebugAnnotate(const char *annotation) override;
@@ -405,10 +422,7 @@ public:
// These functions should be self explanatory.
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
Framebuffer *GetCurrentRenderTarget() override {
return (Framebuffer *)curFramebuffer_.ptr;
}
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override;
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) override;
void BindCurrentFramebufferForColorInput() override;
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
@@ -419,7 +433,7 @@ public:
void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override;
void BindSamplerStates(int start, int count, SamplerState **state) override;
void BindTextures(int start, int count, Texture **textures) override;
void BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) override;
void BindNativeTexture(int sampler, void *nativeTexture) override;
void BindPipeline(Pipeline *pipeline) override {
@@ -487,14 +501,14 @@ public:
void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override;
int GetCurrentStepId() const override {
return renderManager_.GetCurrentStepId();
}
void InvalidateCachedState() override;
void Invalidate(InvalidationFlags flags) override;
void InvalidateFramebuffer(FBInvalidationStage stage, uint32_t channels) override;
void SetInvalidationCallback(InvalidationCallback callback) override {
renderManager_.SetInvalidationCallback(callback);
}
private:
VulkanTexture *GetNullTexture();
VulkanContext *vulkan_ = nullptr;
@@ -510,6 +524,7 @@ private:
int curIBufferOffset_ = 0;
VkDescriptorSetLayout descriptorSetLayout_ = VK_NULL_HANDLE;
VkDescriptorSetLayout frameDescSetLayout_ = VK_NULL_HANDLE;
VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE;
VkPipelineCache pipelineCache_ = VK_NULL_HANDLE;
AutoRef<VKFramebuffer> curFramebuffer_;
@@ -524,6 +539,7 @@ private:
AutoRef<VKTexture> boundTextures_[MAX_BOUND_TEXTURES];
AutoRef<VKSamplerState> boundSamplers_[MAX_BOUND_TEXTURES];
VkImageView boundImageView_[MAX_BOUND_TEXTURES]{};
TextureBindFlags boundTextureFlags_[MAX_BOUND_TEXTURES];
struct FrameData {
FrameData() : descriptorPool("VKContext", false) {
@@ -579,6 +595,8 @@ static int GetBpp(VkFormat format) {
static VkFormat DataFormatToVulkan(DataFormat format) {
switch (format) {
case DataFormat::D16: return VK_FORMAT_D16_UNORM;
case DataFormat::D16_S8: return VK_FORMAT_D16_UNORM_S8_UINT;
case DataFormat::D24_S8: return VK_FORMAT_D24_UNORM_S8_UINT;
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;
@@ -637,8 +655,7 @@ static inline VkSamplerAddressMode AddressModeToVulkan(Draw::TextureAddressMode
VulkanTexture *VKContext::GetNullTexture() {
if (!nullTexture_) {
VkCommandBuffer cmdInit = renderManager_.GetInitCmd();
nullTexture_ = new VulkanTexture(vulkan_);
nullTexture_->SetTag("Null");
nullTexture_ = new VulkanTexture(vulkan_, "Null");
int w = 8;
int h = 8;
nullTexture_->CreateDirect(cmdInit, w, h, 1, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
@@ -654,8 +671,6 @@ VulkanTexture *VKContext::GetNullTexture() {
}
nullTexture_->UploadMip(cmdInit, 0, w, h, 0, bindBuf, bindOffset, w);
nullTexture_->EndCreate(cmdInit, false, VK_PIPELINE_STAGE_TRANSFER_BIT);
} else {
nullTexture_->Touch();
}
return nullTexture_;
}
@@ -668,6 +683,7 @@ public:
s.addressModeV = AddressModeToVulkan(desc.wrapV);
s.addressModeW = AddressModeToVulkan(desc.wrapW);
s.anisotropyEnable = desc.maxAniso > 1.0f;
s.maxAnisotropy = desc.maxAniso;
s.magFilter = desc.magFilter == TextureFilter::LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
s.minFilter = desc.minFilter == TextureFilter::LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
s.mipmapMode = desc.mipFilter == TextureFilter::LINEAR ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST;
@@ -721,10 +737,7 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushBuffer *push, const Textur
width_ = desc.width;
height_ = desc.height;
depth_ = desc.depth;
vkTex_ = new VulkanTexture(vulkan_);
if (desc.tag) {
vkTex_->SetTag(desc.tag);
}
vkTex_ = new VulkanTexture(vulkan_, desc.tag);
VkFormat vulkanFormat = DataFormatToVulkan(format_);
int bpp = GetBpp(vulkanFormat);
int bytesPerPixel = bpp / 8;
@@ -771,34 +784,58 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushBuffer *push, const Textur
return true;
}
static DataFormat DataFormatFromVulkanDepth(VkFormat fmt) {
switch (fmt) {
case VK_FORMAT_D24_UNORM_S8_UINT:
return DataFormat::D24_S8;
case VK_FORMAT_D16_UNORM:
return DataFormat::D16;
case VK_FORMAT_D32_SFLOAT:
return DataFormat::D32F;
case VK_FORMAT_D32_SFLOAT_S8_UINT:
return DataFormat::D32F_S8;
case VK_FORMAT_D16_UNORM_S8_UINT:
return DataFormat::D16_S8;
default:
break;
}
return DataFormat::UNDEFINED;
}
VKContext::VKContext(VulkanContext *vulkan)
: vulkan_(vulkan), renderManager_(vulkan) {
shaderLanguageDesc_.Init(GLSL_VULKAN);
VkFormat depthStencilFormat = vulkan->GetDeviceInfo().preferredDepthStencilFormat;
caps_.anisoSupported = vulkan->GetDeviceFeatures().enabled.samplerAnisotropy != 0;
caps_.geometryShaderSupported = vulkan->GetDeviceFeatures().enabled.geometryShader != 0;
caps_.tesselationShaderSupported = vulkan->GetDeviceFeatures().enabled.tessellationShader != 0;
caps_.multiViewport = vulkan->GetDeviceFeatures().enabled.multiViewport != 0;
caps_.dualSourceBlend = vulkan->GetDeviceFeatures().enabled.dualSrcBlend != 0;
caps_.depthClampSupported = vulkan->GetDeviceFeatures().enabled.depthClamp != 0;
caps_.clipDistanceSupported = vulkan->GetDeviceFeatures().enabled.shaderClipDistance != 0;
caps_.cullDistanceSupported = vulkan->GetDeviceFeatures().enabled.shaderCullDistance != 0;
caps_.anisoSupported = vulkan->GetDeviceFeatures().enabled.standard.samplerAnisotropy != 0;
caps_.geometryShaderSupported = vulkan->GetDeviceFeatures().enabled.standard.geometryShader != 0;
caps_.tesselationShaderSupported = vulkan->GetDeviceFeatures().enabled.standard.tessellationShader != 0;
caps_.dualSourceBlend = vulkan->GetDeviceFeatures().enabled.standard.dualSrcBlend != 0;
caps_.depthClampSupported = vulkan->GetDeviceFeatures().enabled.standard.depthClamp != 0;
caps_.clipDistanceSupported = vulkan->GetDeviceFeatures().enabled.standard.shaderClipDistance != 0;
caps_.cullDistanceSupported = vulkan->GetDeviceFeatures().enabled.standard.shaderCullDistance != 0;
caps_.framebufferBlitSupported = true;
caps_.framebufferCopySupported = true;
caps_.framebufferDepthBlitSupported = vulkan->GetDeviceInfo().canBlitToPreferredDepthStencilFormat;
caps_.framebufferStencilBlitSupported = caps_.framebufferDepthBlitSupported;
caps_.framebufferDepthCopySupported = true; // Will pretty much always be the case.
caps_.framebufferSeparateDepthCopySupported = true; // Will pretty much always be the case.
caps_.preferredDepthBufferFormat = DataFormat::D24_S8; // TODO: Ask vulkan.
// This doesn't affect what depth/stencil format is actually used, see VulkanQueueRunner.
caps_.preferredDepthBufferFormat = DataFormatFromVulkanDepth(vulkan->GetDeviceInfo().preferredDepthStencilFormat);
caps_.texture3DSupported = true;
caps_.textureDepthSupported = true;
caps_.fragmentShaderInt32Supported = true;
caps_.textureNPOTFullySupported = true;
caps_.fragmentShaderDepthWriteSupported = true;
caps_.fragmentShaderStencilWriteSupported = vulkan->Extensions().EXT_shader_stencil_export;
caps_.blendMinMaxSupported = true;
caps_.logicOpSupported = vulkan->GetDeviceFeatures().enabled.logicOp != 0;
caps_.logicOpSupported = vulkan->GetDeviceFeatures().enabled.standard.logicOp != 0;
caps_.multiViewSupported = vulkan->GetDeviceFeatures().enabled.multiview.multiview != 0;
caps_.sampleRateShadingSupported = vulkan->GetDeviceFeatures().enabled.standard.sampleRateShading != 0;
const auto &limits = vulkan->GetPhysicalDeviceProperties().properties.limits;
auto deviceProps = vulkan->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDeviceIndex()).properties;
@@ -809,7 +846,47 @@ VKContext::VKContext(VulkanContext *vulkan)
case VULKAN_VENDOR_NVIDIA: caps_.vendor = GPUVendor::VENDOR_NVIDIA; break;
case VULKAN_VENDOR_QUALCOMM: caps_.vendor = GPUVendor::VENDOR_QUALCOMM; break;
case VULKAN_VENDOR_INTEL: caps_.vendor = GPUVendor::VENDOR_INTEL; break;
default: caps_.vendor = GPUVendor::VENDOR_UNKNOWN; break;
case VULKAN_VENDOR_APPLE: caps_.vendor = GPUVendor::VENDOR_APPLE; break;
default:
WARN_LOG(G3D, "Unknown vendor ID %08x", deviceProps.vendorID);
caps_.vendor = GPUVendor::VENDOR_UNKNOWN;
break;
}
switch (caps_.vendor) {
case GPUVendor::VENDOR_ARM:
case GPUVendor::VENDOR_IMGTEC:
case GPUVendor::VENDOR_QUALCOMM:
caps_.isTilingGPU = true;
break;
default:
caps_.isTilingGPU = false;
break;
}
// Hide D3D9 when we know it likely won't work well.
#if PPSSPP_PLATFORM(WINDOWS)
caps_.supportsD3D9 = true;
if (!strcmp(deviceProps.deviceName, "Intel(R) Iris(R) Xe Graphics")) {
caps_.supportsD3D9 = false;
}
#endif
// VkSampleCountFlagBits is arranged correctly for our purposes.
// Only support MSAA levels that have support for all three of color, depth, stencil.
if (!caps_.isTilingGPU) {
// Check for depth stencil resolve. Without it, depth textures won't work, and we don't want that mess
// of compatibility reports, so we'll just disable multisampling in this case for now.
// There are potential workarounds for devices that don't support it, but those are nearly non-existent now.
const auto &resolveProperties = vulkan->GetPhysicalDeviceProperties().depthStencilResolve;
if (vulkan->Extensions().KHR_depth_stencil_resolve &&
((resolveProperties.supportedDepthResolveModes & resolveProperties.supportedStencilResolveModes) & VK_RESOLVE_MODE_SAMPLE_ZERO_BIT) != 0) {
caps_.multiSampleLevelsMask = (limits.framebufferColorSampleCounts & limits.framebufferDepthSampleCounts & limits.framebufferStencilSampleCounts);
} else {
caps_.multiSampleLevelsMask = 1;
}
} else {
caps_.multiSampleLevelsMask = 1;
}
if (caps_.vendor == GPUVendor::VENDOR_QUALCOMM) {
@@ -822,7 +899,9 @@ VKContext::VKContext(VulkanContext *vulkan)
}
// 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);
// Known working on driver 0x801EA000 and Adreno 620.
if (deviceProps.driverVersion < 0x801EA000 || deviceProps.deviceID < 0x06000000)
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
@@ -916,13 +995,25 @@ VKContext::VKContext(VulkanContext *vulkan)
VkResult res = vkCreateDescriptorSetLayout(device_, &dsl, nullptr, &descriptorSetLayout_);
_assert_(VK_SUCCESS == res);
VkDescriptorSetLayoutBinding frameBindings[1]{};
frameBindings[0].descriptorCount = 1;
frameBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
frameBindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
frameBindings[0].binding = 0;
dsl.bindingCount = ARRAY_SIZE(frameBindings);
dsl.pBindings = frameBindings;
res = vkCreateDescriptorSetLayout(device_, &dsl, nullptr, &frameDescSetLayout_);
_dbg_assert_(VK_SUCCESS == res);
vulkan_->SetDebugName(descriptorSetLayout_, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, "thin3d_d_layout");
VkPipelineLayoutCreateInfo pl = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
pl.pPushConstantRanges = nullptr;
pl.pushConstantRangeCount = 0;
pl.setLayoutCount = 1;
pl.pSetLayouts = &descriptorSetLayout_;
VkDescriptorSetLayout setLayouts[2] = { frameDescSetLayout_, descriptorSetLayout_ };
pl.setLayoutCount = ARRAY_SIZE(setLayouts);
pl.pSetLayouts = setLayouts;
res = vkCreatePipelineLayout(device_, &pl, nullptr, &pipelineLayout_);
_assert_(VK_SUCCESS == res);
@@ -942,6 +1033,7 @@ VKContext::~VKContext() {
delete frame_[i].pushBuffer;
}
vulkan_->Delete().QueueDeleteDescriptorSetLayout(descriptorSetLayout_);
vulkan_->Delete().QueueDeleteDescriptorSetLayout(frameDescSetLayout_);
vulkan_->Delete().QueueDeletePipelineLayout(pipelineLayout_);
vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_);
}
@@ -969,20 +1061,22 @@ void VKContext::EndFrame() {
push_ = nullptr;
// Unbind stuff, to avoid accidentally relying on it across frames (and provide some protection against forgotten unbinds of deleted things).
InvalidateCachedState();
Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
}
void VKContext::InvalidateCachedState() {
curPipeline_ = nullptr;
void VKContext::Invalidate(InvalidationFlags flags) {
if (flags & InvalidationFlags::CACHED_RENDER_STATE) {
curPipeline_ = nullptr;
for (auto &view : boundImageView_) {
view = VK_NULL_HANDLE;
}
for (auto &sampler : boundSamplers_) {
sampler = nullptr;
}
for (auto &texture : boundTextures_) {
texture = nullptr;
for (auto &view : boundImageView_) {
view = VK_NULL_HANDLE;
}
for (auto &sampler : boundSamplers_) {
sampler = nullptr;
}
for (auto &texture : boundTextures_) {
texture = nullptr;
}
}
}
@@ -996,7 +1090,11 @@ VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) {
FrameData *frame = &frame_[vulkan_->GetCurFrame()];
for (int i = 0; i < MAX_BOUND_TEXTURES; ++i) {
key.imageViews_[i] = boundTextures_[i] ? boundTextures_[i]->GetImageView() : boundImageView_[i];
if (boundTextures_[i]) {
key.imageViews_[i] = (boundTextureFlags_[i] & TextureBindFlags::VULKAN_BIND_ARRAY) ? boundTextures_[i]->GetImageArrayView() : boundTextures_[i]->GetImageView();
} else {
key.imageViews_[i] = boundImageView_[i];
}
key.samplers_[i] = boundSamplers_[i];
}
key.buffer_ = buf;
@@ -1006,7 +1104,7 @@ VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) {
return iter->second;
}
VkDescriptorSet descSet = frame->descriptorPool.Allocate(1, &descriptorSetLayout_);
VkDescriptorSet descSet = frame->descriptorPool.Allocate(1, &descriptorSetLayout_, "thin3d_descset");
if (descSet == VK_NULL_HANDLE) {
ERROR_LOG(G3D, "GetOrCreateDescriptorSet failed");
return VK_NULL_HANDLE;
@@ -1077,7 +1175,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char
VKPipeline *pipeline = new VKPipeline(vulkan_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float), pipelineFlags, tag);
VKRGraphicsPipelineDesc &gDesc = pipeline->vkrDesc;
VKRGraphicsPipelineDesc &gDesc = *pipeline->vkrDesc;
std::vector<VkPipelineShaderStageCreateInfo> stages;
stages.resize(desc.shaders.size());
@@ -1097,16 +1195,12 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char
}
}
if (input) {
for (int i = 0; i < (int)input->bindings.size(); i++) {
pipeline->stride[i] = input->bindings[i].stride;
}
} else {
pipeline->stride[0] = 0;
}
_dbg_assert_(input->bindings.size() == 1);
_dbg_assert_(input && input->bindings.size() == 1);
_dbg_assert_((int)input->attributes.size() == (int)input->visc.vertexAttributeDescriptionCount);
for (int i = 0; i < (int)input->bindings.size(); i++) {
pipeline->stride[i] = input->bindings[i].stride;
}
gDesc.ibd = input->bindings[0];
for (size_t i = 0; i < input->attributes.size(); i++) {
gDesc.attrs[i] = input->attributes[i];
@@ -1125,7 +1219,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char
raster->ToVulkan(&gDesc.rs);
// Copy bindings from input layout.
gDesc.inputAssembly.topology = primToVK[(int)desc.prim];
gDesc.topology = primToVK[(int)desc.prim];
// We treat the three stencil states as a unit in other places, so let's do that here too.
const VkDynamicState dynamics[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK };
@@ -1135,9 +1229,6 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char
}
gDesc.ds.pDynamicStates = gDesc.dynamicStates;
gDesc.ms.pSampleMask = nullptr;
gDesc.ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
gDesc.views.viewportCount = 1;
gDesc.views.scissorCount = 1;
gDesc.views.pViewports = nullptr; // dynamic
@@ -1148,7 +1239,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, pipelineFlags, 1 << RP_TYPE_BACKBUFFER, tag ? tag : "thin3d");
pipeline->pipeline = renderManager_.CreateGraphicsPipeline(&gDesc, pipelineFlags, 1 << (size_t)RenderPassType::BACKBUFFER, VK_SAMPLE_COUNT_1_BIT, tag ? tag : "thin3d");
if (desc.uniformDesc) {
pipeline->dynamicUniformSize = (int)desc.uniformDesc->uniformBufferSize;
@@ -1278,7 +1369,7 @@ public:
VKBuffer(size_t size, uint32_t flags) : dataSize_(size) {
data_ = new uint8_t[size];
}
~VKBuffer() override {
~VKBuffer() {
delete[] data_;
}
@@ -1298,15 +1389,32 @@ void VKContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset,
memcpy(buf->data_ + offset, data, size);
}
void VKContext::BindTextures(int start, int count, Texture **textures) {
void VKContext::BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) {
_assert_(start + count <= MAX_BOUND_TEXTURES);
for (int i = start; i < start + count; i++) {
_dbg_assert_(i >= 0 && i < MAX_BOUND_TEXTURES);
boundTextures_[i] = static_cast<VKTexture *>(textures[i - start]);
boundImageView_[i] = boundTextures_[i] ? boundTextures_[i]->GetImageView() : GetNullTexture()->GetImageView();
boundTextureFlags_[i] = flags;
if (boundTextures_[i]) {
// If a texture is bound, we set these up in GetOrCreateDescriptorSet too.
// But we might need to set the view here anyway so it can be queried using GetNativeObject.
if (flags & TextureBindFlags::VULKAN_BIND_ARRAY) {
boundImageView_[i] = boundTextures_[i]->GetImageArrayView();
} else {
boundImageView_[i] = boundTextures_[i]->GetImageView();
}
} else {
if (flags & TextureBindFlags::VULKAN_BIND_ARRAY) {
boundImageView_[i] = GetNullTexture()->GetImageArrayView();
} else {
boundImageView_[i] = GetNullTexture()->GetImageView();
}
}
}
}
void VKContext::BindNativeTexture(int sampler, void *nativeTexture) {
_dbg_assert_(sampler >= 0 && sampler < MAX_BOUND_TEXTURES);
boundTextures_[sampler] = nullptr;
boundImageView_[sampler] = (VkImageView)nativeTexture;
}
@@ -1414,8 +1522,8 @@ void AddFeature(std::vector<std::string> &features, const char *name, VkBool32 a
}
std::vector<std::string> VKContext::GetFeatureList() const {
const VkPhysicalDeviceFeatures &available = vulkan_->GetDeviceFeatures().available;
const VkPhysicalDeviceFeatures &enabled = vulkan_->GetDeviceFeatures().enabled;
const VkPhysicalDeviceFeatures &available = vulkan_->GetDeviceFeatures().available.standard;
const VkPhysicalDeviceFeatures &enabled = vulkan_->GetDeviceFeatures().enabled.standard;
std::vector<std::string> features;
AddFeature(features, "dualSrcBlend", available.dualSrcBlend, enabled.dualSrcBlend);
@@ -1434,6 +1542,9 @@ std::vector<std::string> VKContext::GetFeatureList() const {
AddFeature(features, "occlusionQueryPrecise", available.occlusionQueryPrecise, enabled.occlusionQueryPrecise);
AddFeature(features, "multiDrawIndirect", available.multiDrawIndirect, enabled.multiDrawIndirect);
AddFeature(features, "multiview", vulkan_->GetDeviceFeatures().available.multiview.multiview, vulkan_->GetDeviceFeatures().enabled.multiview.multiview);
AddFeature(features, "multiviewGeometryShader", vulkan_->GetDeviceFeatures().available.multiview.multiviewGeometryShader, vulkan_->GetDeviceFeatures().enabled.multiview.multiviewGeometryShader);
features.emplace_back(std::string("Preferred depth buffer format: ") + VulkanFormatToString(vulkan_->GetDeviceInfo().preferredDepthStencilFormat));
return features;
@@ -1479,14 +1590,16 @@ uint32_t VKContext::GetDataFormatSupport(DataFormat fmt) const {
// use this frame's init command buffer.
class VKFramebuffer : public Framebuffer {
public:
VKFramebuffer(VKRFramebuffer *fb) : buf_(fb) {
VKFramebuffer(VKRFramebuffer *fb, int multiSampleLevel) : buf_(fb) {
_assert_msg_(fb, "Null fb in VKFramebuffer constructor");
width_ = fb->width;
height_ = fb->height;
layers_ = fb->numLayers;
multiSampleLevel_ = multiSampleLevel;
}
~VKFramebuffer() {
_assert_msg_(buf_, "Null buf_ in VKFramebuffer - double delete?");
buf_->vulkan_->Delete().QueueCallback([](void *fb) {
buf_->Vulkan()->Delete().QueueCallback([](void *fb) {
VKRFramebuffer *vfb = static_cast<VKRFramebuffer *>(fb);
delete vfb;
}, buf_);
@@ -1501,9 +1614,14 @@ private:
};
Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) {
_assert_(desc.multiSampleLevel >= 0);
_assert_(desc.numLayers > 0);
_assert_(desc.width > 0);
_assert_(desc.height > 0);
VkCommandBuffer cmd = renderManager_.GetInitCmd();
VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetQueueRunner()->GetCompatibleRenderPass(), desc.width, desc.height, desc.z_stencil, desc.tag);
return new VKFramebuffer(vkrfb);
VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetQueueRunner()->GetCompatibleRenderPass(), desc.width, desc.height, desc.numLayers, desc.multiSampleLevel, desc.z_stencil, desc.tag);
return new VKFramebuffer(vkrfb, desc.multiSampleLevel);
}
void VKContext::CopyFramebufferImage(Framebuffer *srcfb, int level, int x, int y, int z, Framebuffer *dstfb, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) {
@@ -1563,9 +1681,9 @@ void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPass
curFramebuffer_ = fb;
}
void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) {
void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) {
VKFramebuffer *fb = (VKFramebuffer *)fbo;
_assert_(binding < MAX_BOUND_TEXTURES);
_assert_(binding >= 0 && binding < MAX_BOUND_TEXTURES);
// TODO: There are cases where this is okay, actually. But requires layout transitions and stuff -
// we're not ready for this.
@@ -1583,8 +1701,9 @@ void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChanne
_assert_(false);
break;
}
boundTextures_[binding] = nullptr;
boundImageView_[binding] = renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, attachment);
boundTextures_[binding].clear();
boundImageView_[binding] = renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, layer);
}
void VKContext::BindCurrentFramebufferForColorInput() {
@@ -1645,10 +1764,19 @@ uint64_t VKContext::GetNativeObject(NativeObject obj, void *srcObject) {
return (uint64_t)(uintptr_t)&renderManager_;
case NativeObject::NULL_IMAGEVIEW:
return (uint64_t)GetNullTexture()->GetImageView();
case NativeObject::NULL_IMAGEVIEW_ARRAY:
return (uint64_t)GetNullTexture()->GetImageArrayView();
case NativeObject::TEXTURE_VIEW:
return (uint64_t)(((VKTexture *)srcObject)->GetImageView());
case NativeObject::BOUND_FRAMEBUFFER_COLOR_IMAGEVIEW:
return (uint64_t)curFramebuffer_->GetFB()->color.imageView;
case NativeObject::BOUND_FRAMEBUFFER_COLOR_IMAGEVIEW_ALL_LAYERS:
return (uint64_t)curFramebuffer_->GetFB()->color.texAllLayersView;
case NativeObject::BOUND_FRAMEBUFFER_COLOR_IMAGEVIEW_RT:
return (uint64_t)curFramebuffer_->GetFB()->GetRTView();
case NativeObject::FRAME_DATA_DESC_SET_LAYOUT:
return (uint64_t)frameDescSetLayout_;
case NativeObject::THIN3D_PIPELINE_LAYOUT:
return (uint64_t)pipelineLayout_;
default:
Crash();
return 0;
+30 -4
View File
@@ -46,6 +46,7 @@ size_t DataFormatSizeInBytes(DataFormat fmt) {
case DataFormat::S8: return 1;
case DataFormat::D16: return 2;
case DataFormat::D16_S8: return 3;
case DataFormat::D24_S8: return 4;
case DataFormat::D32F: return 4;
// Or maybe 8...
@@ -56,9 +57,32 @@ size_t DataFormatSizeInBytes(DataFormat fmt) {
}
}
const char *DataFormatToString(DataFormat fmt) {
switch (fmt) {
case DataFormat::R8_UNORM: return "R8_UNORM";
case DataFormat::R8G8_UNORM: return "R8G8_UNORM";
case DataFormat::R8G8B8A8_UNORM: return "R8G8B8A8_UNORM";
case DataFormat::B8G8R8A8_UNORM: return "B8G8R8A8_UNORM";
case DataFormat::R16_UNORM: return "R16_UNORM";
case DataFormat::R16_FLOAT: return "R16_FLOAT";
case DataFormat::R32_FLOAT: return "R32_FLOAT";
case DataFormat::S8: return "S8";
case DataFormat::D16: return "D16";
case DataFormat::D16_S8: return "D16_S8";
case DataFormat::D24_S8: return "D24_S8";
case DataFormat::D32F: return "D32F";
case DataFormat::D32F_S8: return "D32F_S8";
default:
return "(N/A)";
}
}
bool DataFormatIsDepthStencil(DataFormat fmt) {
switch (fmt) {
case DataFormat::D16:
case DataFormat::D16_S8:
case DataFormat::D24_S8:
case DataFormat::S8:
case DataFormat::D32F:
@@ -137,7 +161,7 @@ static const std::vector<ShaderSource> fsTexCol = {
"layout(location = 0) in vec4 oColor0;\n"
"layout(location = 1) in vec2 oTexCoord0;\n"
"layout(location = 0) out vec4 fragColor0;\n"
"layout(set = 0, binding = 1) uniform sampler2D Sampler0;\n"
"layout(set = 1, binding = 1) uniform sampler2D Sampler0;\n"
"void main() { fragColor0 = texture(Sampler0, oTexCoord0) * oColor0; }\n"
}
};
@@ -181,7 +205,7 @@ static const std::vector<ShaderSource> fsTexColRBSwizzle = {
"layout(location = 0) in vec4 oColor0;\n"
"layout(location = 1) in vec2 oTexCoord0;\n"
"layout(location = 0) out vec4 fragColor0\n;"
"layout(set = 0, binding = 1) uniform sampler2D Sampler0;\n"
"layout(set = 1, binding = 1) uniform sampler2D Sampler0;\n"
"void main() { fragColor0 = texture(Sampler0, oTexCoord0).bgra * oColor0; }\n"
}
};
@@ -270,7 +294,7 @@ static const std::vector<ShaderSource> vsCol = {
R"(#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
layout (std140, set = 0, binding = 0) uniform bufferVals {
layout (std140, set = 1, binding = 0) uniform bufferVals {
mat4 WorldViewProj;
vec2 TintSaturation;
} myBufferVals;
@@ -416,7 +440,7 @@ VS_OUTPUT main(VS_INPUT input) {
R"(#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
layout (std140, set = 0, binding = 0) uniform bufferVals {
layout (std140, set = 1, binding = 0) uniform bufferVals {
mat4 WorldViewProj;
vec2 TintSaturation;
} myBufferVals;
@@ -712,6 +736,8 @@ const char *Bugs::GetBugName(uint32_t 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";
case GEOMETRY_SHADERS_SLOW_OR_BROKEN: return "GEOMETRY_SHADERS_SLOW_OR_BROKEN";
case ADRENO_RESOURCE_DEADLOCK: return "ADRENO_RESOURCE_DEADLOCK";
default: return "(N/A)";
}
}
+53 -15
View File
@@ -13,8 +13,10 @@
#include <string>
#include <vector>
#include "Common/Common.h"
#include "Common/GPU/DataFormat.h"
#include "Common/GPU/Shader.h"
#include "Common/GPU/MiscTypes.h"
#include "Common/Data/Collections/Slice.h"
namespace Lin {
@@ -241,12 +243,16 @@ enum class NativeObject {
BACKBUFFER_DEPTH_TEX,
FEATURE_LEVEL,
INIT_COMMANDBUFFER,
BOUND_TEXTURE0_IMAGEVIEW,
BOUND_TEXTURE1_IMAGEVIEW,
BOUND_FRAMEBUFFER_COLOR_IMAGEVIEW,
BOUND_TEXTURE0_IMAGEVIEW, // Layer etc depends on how you bound it...
BOUND_TEXTURE1_IMAGEVIEW, // Layer etc depends on how you bound it...
BOUND_FRAMEBUFFER_COLOR_IMAGEVIEW_ALL_LAYERS,
BOUND_FRAMEBUFFER_COLOR_IMAGEVIEW_RT,
RENDER_MANAGER,
TEXTURE_VIEW,
NULL_IMAGEVIEW,
NULL_IMAGEVIEW_ARRAY,
FRAME_DATA_DESC_SET_LAYOUT,
THIN3D_PIPELINE_LAYOUT,
};
enum FBChannel {
@@ -293,7 +299,8 @@ struct FramebufferDesc {
int width;
int height;
int depth;
int numColorAttachments;
int numLayers;
int multiSampleLevel; // 0 = 1xaa, 1 = 2xaa, and so on.
bool z_stencil;
const char *tag; // For graphics debuggers
};
@@ -334,6 +341,7 @@ public:
MALI_CONSTANT_LOAD_BUG = 9,
SUBPASS_FEEDBACK_BROKEN = 10,
GEOMETRY_SHADERS_SLOW_OR_BROKEN = 11,
ADRENO_RESOURCE_DEADLOCK = 12,
MAX_BUG,
};
@@ -388,6 +396,9 @@ struct AutoRef {
*this = p.ptr;
return *this;
}
bool operator !=(const AutoRef<T> &p) const {
return ptr != p.ptr;
}
T *operator->() const {
return ptr;
@@ -395,6 +406,16 @@ struct AutoRef {
operator T *() {
return ptr;
}
operator bool() const {
return ptr != nullptr;
}
void clear() {
if (ptr) {
ptr->Release();
ptr = nullptr;
}
}
T *ptr = nullptr;
};
@@ -415,9 +436,12 @@ class Framebuffer : public RefCountedObject {
public:
int Width() { return width_; }
int Height() { return height_; }
int Layers() { return layers_; }
int MultiSampleLevel() { return multiSampleLevel_; }
virtual void UpdateTag(const char *tag) {}
protected:
int width_ = -1, height_ = -1;
int width_ = -1, height_ = -1, layers_ = 1, multiSampleLevel_ = 0;
};
class Buffer : public RefCountedObject {
@@ -459,10 +483,7 @@ public:
virtual ShaderStage GetStage() const = 0;
};
class Pipeline : public RefCountedObject {
public:
virtual ~Pipeline() {}
};
class Pipeline : public RefCountedObject { };
class RasterState : public RefCountedObject {};
@@ -534,7 +555,6 @@ struct DeviceCaps {
bool depthRangeMinusOneToOne; // OpenGL style depth
bool geometryShaderSupported;
bool tesselationShaderSupported;
bool multiViewport;
bool dualSourceBlend;
bool logicOpSupported;
bool depthClampSupported;
@@ -551,9 +571,17 @@ struct DeviceCaps {
bool fragmentShaderInt32Supported;
bool textureNPOTFullySupported;
bool fragmentShaderDepthWriteSupported;
bool fragmentShaderStencilWriteSupported;
bool textureDepthSupported;
bool blendMinMaxSupported;
bool multiViewSupported;
bool isTilingGPU; // This means that it benefits from correct store-ops, msaa without backing memory, etc.
bool sampleRateShadingSupported;
// From the other backends, we can detect if D3D9 support is known bad (like on Xe) and disable it.
bool supportsD3D9;
u32 multiSampleLevelsMask; // Bit n is set if (1 << n) is a valid multisample level. Bit 0 is always set.
std::string deviceName; // The device name to use when creating the thin3d context, to get the same one.
};
@@ -593,6 +621,14 @@ struct RenderPassInfo {
const char *tag;
};
const int ALL_LAYERS = -1;
enum class TextureBindFlags {
NONE = 0,
VULKAN_BIND_ARRAY = 1,
};
ENUM_CLASS_BITOPS(TextureBindFlags);
class DrawContext {
public:
virtual ~DrawContext();
@@ -654,11 +690,11 @@ public:
// These functions should be self explanatory.
// Binding a zero render target means binding the backbuffer.
// If an fbo has two layers, we bind for stereo rendering ALWAYS. There's no rendering to one layer anymore.
virtual void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) = 0;
virtual Framebuffer *GetCurrentRenderTarget() = 0;
// binding must be < MAX_TEXTURE_SLOTS (0, 1 are okay if it's 2).
virtual void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) = 0;
virtual void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) = 0;
// Framebuffer fetch / input attachment support, needs to be explicit in Vulkan.
virtual void BindCurrentFramebufferForColorInput() {}
@@ -683,7 +719,7 @@ public:
virtual void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) = 0;
virtual void BindSamplerStates(int start, int count, SamplerState **state) = 0;
virtual void BindTextures(int start, int count, Texture **textures) = 0;
virtual void BindTextures(int start, int count, Texture **textures, TextureBindFlags flags = TextureBindFlags::NONE) = 0;
virtual void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) = 0;
virtual void BindIndexBuffer(Buffer *indexBuffer, int offset) = 0;
@@ -706,7 +742,7 @@ public:
// Clear state cached within thin3d. Must be called after directly calling API functions.
// Note that framebuffer state (which framebuffer is bounds) may not be cached.
// Must not actually perform any API calls itself since this can be called when no framebuffer is bound for rendering.
virtual void InvalidateCachedState() = 0;
virtual void Invalidate(InvalidationFlags flags) = 0;
virtual void BindPipeline(Pipeline *pipeline) = 0;
@@ -740,7 +776,9 @@ public:
// This is called when we launch a new game, so any collected internal stats in the backends don't carry over.
virtual void ResetStats() {}
virtual int GetCurrentStepId() const = 0;
// Used by the DrawEngines to know when they have to re-apply some state.
// Not very elegant, but more elegant than the old passId hack.
virtual void SetInvalidationCallback(InvalidationCallback callback) = 0;
protected:
ShaderModule *vsPresets_[VS_MAX_PRESET];
+1
View File
@@ -33,6 +33,7 @@ enum {
DEVICE_ID_ACCELEROMETER = 30,
DEVICE_ID_XR_CONTROLLER_LEFT = 40,
DEVICE_ID_XR_CONTROLLER_RIGHT = 41,
DEVICE_ID_TOUCH = 42,
};
//number of contiguous generic joypad IDs
+8 -1
View File
@@ -261,7 +261,14 @@ typedef enum _keycode_t {
NKCODE_EXT_MOUSEBUTTON_4 = 1006,
NKCODE_EXT_MOUSEBUTTON_5 = 1007,
NKCODE_EXT_MOUSEWHEEL_UP = 1008,
NKCODE_EXT_MOUSEWHEEL_DOWN = 1009
NKCODE_EXT_MOUSEWHEEL_DOWN = 1009,
// Virtual reality controller motion
NKCODE_EXT_MOTION_UP = 1101,
NKCODE_EXT_MOTION_DOWN = 1102,
NKCODE_EXT_MOTION_LEFT = 1103,
NKCODE_EXT_MOTION_RIGHT = 1104,
NKCODE_EXT_MOTION_FORWARD = 1105,
} keycode_t;
enum AndroidJoystickAxis {
+2 -2
View File
@@ -56,7 +56,7 @@ void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char
if (instance) {
instance->Log(level, type, file, line, fmt, args);
} else {
// Fall back to printf if we're before the log manager has been initialized.
// Fall back to printf or direct android logger with a small buffer if the log manager hasn't been initialized yet.
#if PPSSPP_PLATFORM(ANDROID)
char temp[512];
vsnprintf(temp, sizeof(temp), fmt, args);
@@ -200,7 +200,7 @@ void LogManager::SaveConfig(Section *section) {
}
}
void LogManager::LoadConfig(Section *section, bool debugDefaults) {
void LogManager::LoadConfig(const Section *section, bool debugDefaults) {
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) {
bool enabled = false;
int level = 0;
+7 -8
View File
@@ -55,7 +55,7 @@ public:
FileLogListener(const char *filename);
~FileLogListener();
void Log(const LogMessage &msg);
void Log(const LogMessage &msg) override;
bool IsValid() { if (!fp_) return false; else return true; }
bool IsEnabled() const { return m_enable; }
@@ -71,13 +71,12 @@ private:
class OutputDebugStringLogListener : public LogListener {
public:
void Log(const LogMessage &msg);
void Log(const LogMessage &msg) override;
};
class RingbufferLogListener : public LogListener {
public:
RingbufferLogListener() : curMessage_(0), count_(0), enabled_(false) {}
void Log(const LogMessage &msg);
void Log(const LogMessage &msg) override;
bool IsEnabled() const { return enabled_; }
void SetEnabled(bool enable) { enabled_ = enable; }
@@ -89,9 +88,9 @@ public:
private:
enum { MAX_LOGS = 128 };
LogMessage messages_[MAX_LOGS];
int curMessage_;
int count_;
bool enabled_;
int curMessage_ = 0;
int count_ = 0;
bool enabled_ = false;
};
// TODO: A simple buffered log that can be used to display the log in-window
@@ -184,5 +183,5 @@ public:
void ChangeFileLog(const char *filename);
void SaveConfig(Section *section);
void LoadConfig(Section *section, bool debugDefaults);
void LoadConfig(const Section *section, bool debugDefaults);
};
+40 -31
View File
@@ -59,45 +59,54 @@ const ExpressionOpcode ExpressionOpcodes[] = {
{ "", 0, 0, 0, false } // EXOP_NONE
};
bool parseNumber(char* str, int defaultrad, int len, uint32_t& result)
{
static int radixFromZeroPrefix(char c) {
switch (tolower(c)) {
case 'b': return 2;
case 'o': return 8;
case 'x': return 16;
// Inventing a prefix since we default to hex.
case 'd': return 10;
}
return -1;
}
static int radixFromSuffix(char c, int defaultrad) {
switch (tolower(c)) {
case 'o': return 8;
case 'h': return 16;
case 'i': return 10;
case 'u': return 10;
case 'b': return defaultrad == 16 ? -1 : 2;
}
return -1;
}
bool parseNumber(char *str, int defaultrad, int len, uint32_t &result) {
int val = 0;
int r = 0;
if (len == 0) len = (int) strlen(str);
if (len == 0)
len = (int)strlen(str);
if (str[0] == '0' && tolower(str[1]) == 'x')
{
r = 16;
str+=2;
len-=2;
} else if (str[0] == '$')
{
if (str[0] == '0' && radixFromZeroPrefix(str[1]) != -1) {
r = radixFromZeroPrefix(str[1]);
str += 2;
len -= 2;
} else if (str[0] == '$') {
r = 16;
str++;
len--;
} else if (str[0] == '0' && tolower(str[1]) == 'o')
{
r = 8;
str+=2;
len-=2;
} else {
if (!(str[0] >= '0' && str[0] <= '9')) return false;
if (tolower(str[len-1]) == 'b' && defaultrad != 16)
{
r = 2;
len--;
} else if (tolower(str[len-1]) == 'o')
{
r = 8;
len--;
} else if (tolower(str[len-1]) == 'h')
{
r = 16;
} else if (str[0] >= '0' && str[0] <= '9') {
int suffix = radixFromSuffix(str[len - 1], defaultrad);
if (suffix != -1) {
r = suffix;
len--;
} else {
r = defaultrad;
}
} else {
return false;
}
switch (r)
@@ -397,8 +406,8 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs,
size_t num = 0;
uint32_t opcode;
std::vector<uint32_t> valueStack;
unsigned int arg[5];
float fArg[5];
unsigned int arg[5]{};
float fArg[5]{};
bool useFloat = false;
while (num < exp.size())
+1
View File
@@ -15,6 +15,7 @@ enum ExpressionType
class IExpressionFunctions
{
public:
virtual ~IExpressionFunctions() {}
virtual bool parseReference(char* str, uint32_t& referenceIndex) = 0;
virtual bool parseSymbol(char* str, uint32_t& symbolValue) = 0;
virtual uint32_t getReferenceValue(uint32_t referenceIndex) = 0;
+1 -1
View File
@@ -224,7 +224,7 @@ void DeChunk(Buffer *inbuffer, Buffer *outbuffer, int contentLength, float *prog
inbuffer->TakeLineCRLF(&line);
if (!line.size())
return;
unsigned int chunkSize;
unsigned int chunkSize = 0;
sscanf(line.c_str(), "%x", &chunkSize);
if (chunkSize) {
std::string data;
+5 -6
View File
@@ -15,6 +15,8 @@
struct UrlEncoder
{
virtual ~UrlEncoder() {}
UrlEncoder() : paramCount(0)
{
data.reserve(256);
@@ -129,8 +131,7 @@ struct MultipartFormDataEncoder : UrlEncoder
boundary = temp;
}
virtual void Add(const std::string &key, const std::string &value)
{
void Add(const std::string &key, const std::string &value) override {
Add(key, value, "", "");
}
@@ -158,13 +159,11 @@ struct MultipartFormDataEncoder : UrlEncoder
Add(key, std::string((const char *)&value[0], value.size()), filename, mimeType);
}
virtual void Finish()
{
void Finish() override {
data += "--" + boundary + "--";
}
virtual std::string GetMimeType() const
{
std::string GetMimeType() const override {
return "multipart/form-data; boundary=\"" + boundary + "\"";
}
+4 -2
View File
@@ -130,7 +130,7 @@ void DrawBuffer::RectOutline(float x, float y, float w, float h, uint32_t color,
vLine(x + w, y, y + h + pixel_in_dps_y, color);
}
void DrawBuffer::MultiVGradient(float x, float y, float w, float h, GradientStop *stops, int numStops) {
void DrawBuffer::MultiVGradient(float x, float y, float w, float h, const GradientStop *stops, int numStops) {
for (int i = 0; i < numStops - 1; i++) {
float t0 = stops[i].t, t1 = stops[i+1].t;
uint32_t c0 = stops[i].color, c1 = stops[i+1].color;
@@ -285,7 +285,9 @@ void DrawBuffer::DrawImageRotated(ImageID atlas_image, float x, float y, float s
{u1, image->v2},
};
for (int i = 0; i < 6; i++) {
rot(v[i], angle, x, y);
if (angle != 0.0f) {
rot(v[i], angle, x, y);
}
V(v[i][0], v[i][1], 0, color, uv[i][0], uv[i][1]);
}
}
+1 -1
View File
@@ -88,7 +88,7 @@ public:
RectVGradient(x, y, w, h, colorTop, darkenColor(colorTop));
}
void MultiVGradient(float x, float y, float w, float h, GradientStop *stops, int numStops);
void MultiVGradient(float x, float y, float w, float h, const GradientStop *stops, int numStops);
void RectCenter(float x, float y, float w, float h, uint32_t color) {
Rect(x - w/2, y - h/2, w, h, color);
@@ -14,8 +14,7 @@
#include "Common/File/VFS/VFS.h"
#include "Common/Log.h"
#include "Common/TimeUtil.h"
#include "UI/TextureUtil.h"
#include "UI/GameInfoCache.h"
#include "Common/Render/ManagedTexture.h"
static Draw::DataFormat ZimToT3DFormat(int zim) {
switch (zim) {
@@ -217,38 +216,3 @@ std::unique_ptr<ManagedTexture> CreateTextureFromFileData(Draw::DrawContext *dra
return std::unique_ptr<ManagedTexture>();
}
}
void GameIconView::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
w = textureWidth_;
h = textureHeight_;
}
void GameIconView::Draw(UIContext &dc) {
using namespace UI;
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE);
if (!info->icon.texture) {
return;
}
textureWidth_ = info->icon.texture->Width() * scale_;
textureHeight_ = info->icon.texture->Height() * scale_;
// Fade icon with the backgrounds.
double loadTime = info->icon.timeLoaded;
auto pic = info->GetBGPic();
if (pic) {
loadTime = std::max(loadTime, pic->timeLoaded);
}
uint32_t color = whiteAlpha(ease((time_now_d() - loadTime) * 3));
// Adjust size so we don't stretch the image vertically or horizontally.
// Make sure it's not wider than 144 (like Doom Legacy homebrew), ugly in the grid mode.
float nw = std::min(bounds_.h * textureWidth_ / textureHeight_, (float)bounds_.w);
dc.Flush();
dc.GetDrawContext()->BindTexture(0, info->icon.texture->GetTexture());
dc.Draw()->Rect(bounds_.x, bounds_.y, nw, bounds_.h, color);
dc.Flush();
dc.RebindTexture();
}
@@ -43,18 +43,3 @@ private:
std::unique_ptr<ManagedTexture> CreateTextureFromFile(Draw::DrawContext *draw, const char *filename, ImageFileType fileType, bool generateMips);
std::unique_ptr<ManagedTexture> CreateTextureFromFileData(Draw::DrawContext *draw, const uint8_t *data, int size, ImageFileType fileType, bool generateMips, const char *name);
class GameIconView : public UI::InertView {
public:
GameIconView(const Path &gamePath, float scale, UI::LayoutParams *layoutParams = 0)
: InertView(layoutParams), gamePath_(gamePath), scale_(scale) {}
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
void Draw(UIContext &dc) override;
std::string DescribeText() const override { return ""; }
private:
Path gamePath_;
float scale_ = 1.0f;
int textureWidth_ = 0;
int textureHeight_ = 0;
};
+1
View File
@@ -69,6 +69,7 @@ TextDrawerWin32::TextDrawerWin32(Draw::DrawContext *draw) : TextDrawer(draw), ct
bmi.bmiHeader.biBitCount = 32;
ctx_->hbmBitmap = CreateDIBSection(ctx_->hDC, &bmi, DIB_RGB_COLORS, (VOID**)&ctx_->pBitmapBits, NULL, 0);
_assert_(ctx_->hbmBitmap != nullptr);
SetMapMode(ctx_->hDC, MM_TEXT);
SelectObject(ctx_->hDC, ctx_->hbmBitmap);
+2 -2
View File
@@ -615,7 +615,7 @@ const u8 *RiscVEmitter::AlignCode16() {
const u8 *RiscVEmitter::AlignCodePage() {
int page_size = GetMemoryProtectPageSize();
int c = int((u64)code_ & (page_size - 1));
int c = int((intptr_t)code_ & ((intptr_t)page_size - 1));
if (c)
ReserveCodeSpace(page_size - c);
return code_;
@@ -747,7 +747,7 @@ void RiscVEmitter::SetRegToImmediate(RiscVReg rd, uint64_t value, RiscVReg temp)
if (SignReduce64(v, 32) == v || force) {
int32_t lower = (int32_t)SignReduce64(svalue, 12);
int32_t upper = ((v - lower) >> 12) << 12;
_assert_msg_(force || upper + lower == v, "Upper + ADDI immediate math mistake?");
_assert_msg_(force || (int64_t)upper + lower == v, "Upper + ADDI immediate math mistake?");
// Should be fused on some processors.
(this->*upperOp)(rd, upper);
+1 -1
View File
@@ -113,7 +113,7 @@ struct FixupBranch {
~FixupBranch();
const u8 *ptr = nullptr;
FixupBranchType type;
FixupBranchType type = FixupBranchType::B;
};
class RiscVEmitter {
+3 -6
View File
@@ -33,11 +33,7 @@ void Do(PointerWrap &p, tm &t);
// This makes it a compile error if you forget to define DoState() on non-POD.
// Which also can be a problem, for example struct tm is non-POD on linux, for whatever reason...
#ifdef _MSC_VER
template<typename T, bool isPOD = std::is_pod<T>::value, bool isPointer = std::is_pointer<T>::value>
#else
template<typename T, bool isPOD = __is_pod(T), bool isPointer = std::is_pointer<T>::value>
#endif
template<typename T, bool isPOD = std::is_standard_layout<T>::value && std::is_trivial<T>::value, bool isPointer = std::is_pointer<T>::value>
struct DoHelper_ {
static void DoArray(PointerWrap &p, T *x, int count) {
for (int i = 0; i < count; ++i)
@@ -89,7 +85,8 @@ template<class T>
void DoVector(PointerWrap &p, std::vector<T> &x, T &default_val) {
u32 vec_size = (u32)x.size();
Do(p, vec_size);
x.resize(vec_size, default_val);
if (vec_size != x.size())
x.resize(vec_size, default_val);
if (vec_size > 0)
DoArray(p, &x[0], vec_size);
}
+8 -4
View File
@@ -38,8 +38,8 @@ void DoMap(PointerWrap &p, M &x, typename M::mapped_type &default_val) {
x[first] = second;
--number;
}
break;
}
break;
case PointerWrap::MODE_WRITE:
case PointerWrap::MODE_MEASURE:
case PointerWrap::MODE_VERIFY:
@@ -52,8 +52,10 @@ void DoMap(PointerWrap &p, M &x, typename M::mapped_type &default_val) {
--number;
++itr;
}
break;
}
break;
case PointerWrap::MODE_NOOP:
break;
}
}
@@ -109,8 +111,8 @@ void DoMultimap(PointerWrap &p, M &x, typename M::mapped_type &default_val) {
x.insert(std::make_pair(first, second));
--number;
}
break;
}
break;
case PointerWrap::MODE_WRITE:
case PointerWrap::MODE_MEASURE:
case PointerWrap::MODE_VERIFY:
@@ -122,8 +124,10 @@ void DoMultimap(PointerWrap &p, M &x, typename M::mapped_type &default_val) {
--number;
++itr;
}
break;
}
break;
case PointerWrap::MODE_NOOP:
break;
}
}
+2 -3
View File
@@ -46,9 +46,8 @@ void DoSet(PointerWrap &p, std::set<T> &x) {
Do(p, *itr++);
}
break;
default:
ERROR_LOG(SAVESTATE, "Savestate error: invalid mode %d.", p.mode);
case PointerWrap::MODE_NOOP:
break;
}
}
+57 -6
View File
@@ -33,8 +33,27 @@ enum class SerializeCompressType {
static constexpr SerializeCompressType SAVE_TYPE = SerializeCompressType::ZSTD;
PointerWrapSection PointerWrap::Section(const char *title, int ver) {
return Section(title, ver, ver);
void PointerWrap::RewindForWrite(u8 *writePtr) {
_assert_(mode == MODE_MEASURE);
// Switch to writing mode, save the size for later checking and start again.
measuredSize_ = Offset();
mode = MODE_WRITE;
*ptr = writePtr;
ptrStart_ = writePtr;
}
bool PointerWrap::CheckAfterWrite() {
_assert_(error != ERROR_NONE || mode == MODE_WRITE);
size_t offset = Offset();
if (measuredSize_ != 0 && offset != measuredSize_) {
WARN_LOG(SAVESTATE, "CheckAfterWrite: Size mismatch! %d but expected %d", (int)offset, (int)measuredSize_);
return false;
}
if (!checkpoints_.empty() && curCheckpoint_ != checkpoints_.size()) {
WARN_LOG(SAVESTATE, "Checkpoint count mismatch!");
return false;
}
return true;
}
PointerWrapSection PointerWrap::Section(const char *title, int minVer, int ver) {
@@ -44,6 +63,32 @@ PointerWrapSection PointerWrap::Section(const char *title, int minVer, int ver)
// This is strncpy because we rely on its weird non-null-terminating zero-filling truncation behaviour.
// Can't replace it with the more sensible truncate_cpy because that would break savestates.
strncpy(marker, title, sizeof(marker));
// Compare the measure and write passes. Sanity check to catch bugs, doesn't do anything for output.
size_t offset = Offset();
if (mode == MODE_MEASURE) {
checkpoints_.emplace_back(marker, offset);
} else if (mode == MODE_WRITE) {
if (!checkpoints_.empty()) {
if (checkpoints_.size() <= curCheckpoint_) {
WARN_LOG(SAVESTATE, "Write: Not enough checkpoints from measure pass (%d). cur section: %s", (int)checkpoints_.size(), title);
SetError(ERROR_FAILURE);
return PointerWrapSection(*this, -1, title);
}
if (!checkpoints_[curCheckpoint_].Matches(marker, offset)) {
WARN_LOG(SAVESTATE, "Checkpoint mismatch during write! Section %s but expected %s, offset %d but expected %d", title, marker, (int)offset, (int)checkpoints_[curCheckpoint_].offset);
if (curCheckpoint_ > 1) {
WARN_LOG(SAVESTATE, "Previous checkpoint: %s (%d)", checkpoints_[curCheckpoint_ - 1].title, (int)checkpoints_[curCheckpoint_ - 1].offset);
}
SetError(ERROR_FAILURE);
return PointerWrapSection(*this, -1, title);
}
} else {
WARN_LOG(SAVESTATE, "Writing savestate without checkpoints. This is OK but should be fixed.");
}
curCheckpoint_++;
}
if (!ExpectVoid(marker, sizeof(marker))) {
// Might be before we added name markers for safety.
if (foundVersion == 1 && ExpectVoid(&foundVersion, sizeof(foundVersion))) {
@@ -60,8 +105,10 @@ PointerWrapSection PointerWrap::Section(const char *title, int minVer, int ver)
if (!firstBadSectionTitle_) {
firstBadSectionTitle_ = title;
}
WARN_LOG(SAVESTATE, "Savestate failure: wrong version %d found for section '%s'", foundVersion, title);
SetError(ERROR_FAILURE);
if (mode != MODE_NOOP) {
WARN_LOG(SAVESTATE, "Savestate failure: wrong version %d found for section '%s'", foundVersion, title);
SetError(ERROR_FAILURE);
}
return PointerWrapSection(*this, -1, title);
}
return PointerWrapSection(*this, foundVersion, title);
@@ -72,8 +119,9 @@ void PointerWrap::SetError(Error error_) {
error = error_;
}
if (error > ERROR_WARNING) {
// For the rest of this run, just measure.
mode = PointerWrap::MODE_MEASURE;
// For the rest of this run, do nothing, to avoid running off the end of memory or something,
// and also not logspam like MEASURE will do in an error case.
mode = PointerWrap::MODE_NOOP;
}
}
@@ -123,6 +171,7 @@ void Do(PointerWrap &p, std::string &x) {
case PointerWrap::MODE_READ: x = (char*)*p.ptr; break;
case PointerWrap::MODE_WRITE: memcpy(*p.ptr, x.c_str(), stringLen); break;
case PointerWrap::MODE_MEASURE: break;
case PointerWrap::MODE_NOOP: break;
case PointerWrap::MODE_VERIFY: _dbg_assert_msg_(!strcmp(x.c_str(), (char*)*p.ptr), "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", x.c_str(), (char *)*p.ptr, p.ptr); break;
}
(*p.ptr) += stringLen;
@@ -150,6 +199,7 @@ void Do(PointerWrap &p, std::wstring &x) {
case PointerWrap::MODE_READ: x = read(); break;
case PointerWrap::MODE_WRITE: memcpy(*p.ptr, x.c_str(), stringLen); break;
case PointerWrap::MODE_MEASURE: break;
case PointerWrap::MODE_NOOP: break;
case PointerWrap::MODE_VERIFY: _dbg_assert_msg_(x == read(), "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", x.c_str(), read().c_str(), p.ptr); break;
}
(*p.ptr) += stringLen;
@@ -177,6 +227,7 @@ void Do(PointerWrap &p, std::u16string &x) {
case PointerWrap::MODE_READ: x = read(); break;
case PointerWrap::MODE_WRITE: memcpy(*p.ptr, x.c_str(), stringLen); break;
case PointerWrap::MODE_MEASURE: break;
case PointerWrap::MODE_NOOP: break;
case PointerWrap::MODE_VERIFY: _dbg_assert_msg_(x == read(), "Savestate verification failure: (at %p).\n", p.ptr); break;
}
(*p.ptr) += stringLen;
+74 -21
View File
@@ -30,6 +30,7 @@
// - Serialization code for anything complex has to be manually written.
#include <string>
#include <cstring>
#include <vector>
#include <cstdlib>
@@ -52,8 +53,7 @@ class PointerWrap;
class PointerWrapSection
{
public:
PointerWrapSection(PointerWrap &p, int ver, const char *title) : p_(p), ver_(ver), title_(title) {
}
PointerWrapSection(PointerWrap &p, int ver, const char *title) : p_(p), ver_(ver), title_(title) {}
~PointerWrapSection();
bool operator == (const int &v) const { return ver_ == v; }
@@ -73,15 +73,32 @@ private:
const char *title_;
};
// For measure vs write detailed verification
struct SerializeCheckpoint {
char title[17]; // 16-byte section header, plus a zero terminator for debug printing.
size_t offset;
SerializeCheckpoint(char _title[16], size_t off) {
memcpy(title, _title, 16);
title[16] = 0;
offset = off;
}
bool Matches(const char *_title, size_t off) const {
return memcmp(title, _title, 16) == 0 && off == offset;
}
};
// Wrapper class
class PointerWrap
{
public:
enum Mode {
MODE_READ = 1, // load
MODE_WRITE, // save
MODE_MEASURE, // calculate size
MODE_VERIFY, // compare
MODE_WRITE, // save
MODE_MEASURE, // calculate size
MODE_VERIFY, // compare
MODE_NOOP, // don't do anything. Useful to cleanly doing stuff once we've hit an error.
};
enum Error {
@@ -94,19 +111,26 @@ public:
Mode mode;
Error error = ERROR_NONE;
PointerWrap(u8 **ptr_, Mode mode_) : ptr(ptr_), mode(mode_) {}
PointerWrap(unsigned char **ptr_, int mode_) : ptr((u8**)ptr_), mode((Mode)mode_) {}
PointerWrap(u8 **ptr_, Mode mode_) : ptr(ptr_), ptrStart_(*ptr), mode(mode_) {
if (mode == MODE_MEASURE) {
checkpoints_.reserve(750);
}
}
PointerWrapSection Section(const char *title, int ver);
void RewindForWrite(u8 *writePtr);
bool CheckAfterWrite();
// The returned object can be compared against the version that was loaded.
// This can be used to support versions as old as minVer.
// Version = 0 means the section was not found.
PointerWrapSection Section(const char *title, int minVer, int ver);
PointerWrapSection Section(const char *title, int ver) {
return Section(title, ver, ver);
}
void SetMode(Mode mode_) {mode = mode_;}
Mode GetMode() const {return mode;}
u8 **GetPPtr() {return ptr;}
void SetMode(Mode mode_) { mode = mode_; }
Mode GetMode() const { return mode; }
u8 **GetPPtr() { return ptr; }
void SetError(Error error_);
const char *GetBadSectionTitle() const {
@@ -119,8 +143,14 @@ public:
void DoMarker(const char *prevName, u32 arbitraryNumber = 0x42);
size_t Offset() const { return *ptr - ptrStart_; }
private:
const char *firstBadSectionTitle_ = nullptr;
u8 *ptrStart_;
std::vector<SerializeCheckpoint> checkpoints_;
size_t curCheckpoint_ = 0;
size_t measuredSize_ = 0;
};
class CChunkFileReader
@@ -152,7 +182,7 @@ public:
template<class T>
static size_t MeasurePtr(T &_class)
{
u8 *ptr = 0;
u8 *ptr = nullptr;
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
_class.DoState(p);
return (size_t)ptr;
@@ -166,13 +196,39 @@ public:
PointerWrap p(&ptr, PointerWrap::MODE_WRITE);
_class.DoState(p);
if (p.error != p.ERROR_FAILURE && (expected_end == ptr || expected_size == 0)) {
if (p.error != PointerWrap::ERROR_FAILURE && (expected_end == ptr || expected_size == 0)) {
return ERROR_NONE;
} else {
return ERROR_BROKEN_STATE;
}
}
template<class T>
static Error MeasureAndSavePtr(T &_class, u8 **saved, size_t *savedSize)
{
u8 *ptr = nullptr;
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
_class.DoState(p);
_assert_(p.error == PointerWrap::ERROR_NONE);
size_t measuredSize = p.Offset();
u8 *data = (u8 *)malloc(measuredSize);
if (!data)
return ERROR_BAD_ALLOC;
p.RewindForWrite(data);
_class.DoState(p);
if (p.CheckAfterWrite()) {
*saved = data;
*savedSize = measuredSize;
return ERROR_NONE;
} else {
free(data);
return ERROR_BROKEN_STATE;
}
}
// Load file template
template<class T>
static Error Load(const Path &filename, std::string *gitVersion, T& _class, std::string *failureReason)
@@ -197,19 +253,16 @@ public:
template<class T>
static Error Save(const Path &filename, const std::string &title, const char *gitVersion, T& _class)
{
// Get data
size_t const sz = MeasurePtr(_class);
u8 *buffer = (u8 *)malloc(sz);
if (!buffer)
return ERROR_BAD_ALLOC;
Error error = SavePtr(buffer, _class, sz);
u8 *buffer;
size_t sz;
Error error = MeasureAndSavePtr(_class, &buffer, &sz);
// SaveFile takes ownership of buffer
// SaveFile takes ownership of buffer (malloc/free)
if (error == ERROR_NONE)
error = SaveFile(filename, title, gitVersion, buffer, sz);
return error;
}
template <class T>
static Error Verify(T& _class)
{
+20 -1
View File
@@ -286,6 +286,25 @@ void SplitString(const std::string& str, const char delim, std::vector<std::stri
}
}
static std::string ApplyHtmlEscapes(std::string str) {
struct Repl {
const char *a;
const char *b;
};
static const Repl replacements[] = {
{ "&amp;", "&" },
// Easy to add more cases.
};
for (const Repl &r : replacements) {
str = ReplaceAll(str, r.a, r.b);
}
return str;
}
// Meant for HTML listings and similar, so supports some HTML escapes.
void GetQuotedStrings(const std::string& str, std::vector<std::string>& output)
{
size_t next = 0;
@@ -294,7 +313,7 @@ void GetQuotedStrings(const std::string& str, std::vector<std::string>& output)
if (str[pos] == '\"' || str[pos] == '\'') {
if (even) {
//quoted text
output.emplace_back(str.substr(next, pos - next));
output.emplace_back(ApplyHtmlEscapes(str.substr(next, pos - next)));
even = 0;
} else {
//non quoted text
+5 -3
View File
@@ -20,10 +20,11 @@ struct Mailbox {
std::mutex mutex_;
std::condition_variable condvar_;
T data_{};
bool dataReceived_ = false;
T Wait() {
std::unique_lock<std::mutex> lock(mutex_);
while (!data_) {
while (!dataReceived_) {
condvar_.wait(lock);
}
return data_;
@@ -31,7 +32,7 @@ struct Mailbox {
bool Poll(T *data) {
std::unique_lock<std::mutex> lock(mutex_);
if (data_) {
if (dataReceived_) {
*data = data_;
return true;
} else {
@@ -41,8 +42,9 @@ struct Mailbox {
bool Send(T data) {
std::unique_lock<std::mutex> lock(mutex_);
if (!data_) {
if (!dataReceived_) {
data_ = data;
dataReceived_ = true;
condvar_.notify_all();
return true;
} else {
+1 -1
View File
@@ -107,7 +107,7 @@ public:
}
}
// For outside injection of data, when not using Spawn
// For outside injection of data, when not using Spawn.
void Post(T data) {
rx_->Send(data);
}
+8 -3
View File
@@ -113,7 +113,7 @@ bool ThreadManager::TeardownTask(Task *task, bool enqueue) {
if (task->Type() == TaskType::CPU_COMPUTE) {
global_->compute_queue.push_back(task);
global_->compute_queue_size++;
} else if (task->Type() == TaskType::CPU_COMPUTE) {
} else if (task->Type() == TaskType::IO_BLOCKING) {
global_->io_queue.push_back(task);
global_->io_queue_size++;
} else {
@@ -125,7 +125,12 @@ bool ThreadManager::TeardownTask(Task *task, bool enqueue) {
static void WorkerThreadFunc(GlobalThreadContext *global, ThreadContext *thread) {
char threadName[16];
snprintf(threadName, sizeof(threadName), "PoolWorker %d", thread->index);
if (thread->type == TaskType::CPU_COMPUTE) {
snprintf(threadName, sizeof(threadName), "PoolWorker %d", thread->index);
} else {
_assert_(thread->type == TaskType::IO_BLOCKING);
snprintf(threadName, sizeof(threadName), "PoolWorkerIO %d", thread->index);
}
SetCurrentThreadName(threadName);
const bool isCompute = thread->type == TaskType::CPU_COMPUTE;
@@ -199,8 +204,8 @@ void ThreadManager::Init(int numRealCores, int numLogicalCoresPerCpu) {
thread->cancelled.store(false);
thread->private_single.store(nullptr);
thread->type = i < numComputeThreads_ ? TaskType::CPU_COMPUTE : TaskType::IO_BLOCKING;
thread->thread = std::thread(&WorkerThreadFunc, global_, thread);
thread->index = i;
thread->thread = std::thread(&WorkerThreadFunc, global_, thread);
global_->threads_.push_back(thread);
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
// The new threadpool.
// To help future smart scheduling.
// To help smart scheduling.
enum class TaskType {
CPU_COMPUTE,
IO_BLOCKING,
+2 -2
View File
@@ -43,7 +43,7 @@ void ThunkManager::Init()
#endif
AllocCodeSpace(THUNK_ARENA_SIZE);
BeginWrite();
BeginWrite(512);
save_regs = GetCodePtr();
#if PPSSPP_ARCH(AMD64)
for (int i = 2; i < ABI_GetNumXMMRegs(); i++)
@@ -151,7 +151,7 @@ const void *ThunkManager::ProtectFunction(const void *function, int num_params)
_assert_msg_(region != nullptr, "Can't protect functions before the emu is started.");
BeginWrite();
BeginWrite(128);
const u8 *call_point = GetCodePtr();
Enter(this, true);
-3
View File
@@ -21,8 +21,6 @@
// TODO: https://github.com/floooh/sokol/blob/9a6237fcdf213e6da48e4f9201f144bcb2dcb46f/sokol_time.h#L229-L248
static double curtime = 0;
#ifdef _WIN32
static LARGE_INTEGER frequency;
@@ -33,7 +31,6 @@ double time_now_d() {
if (frequency.QuadPart == 0) {
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&startTime);
curtime = 0.0;
frequencyMult = 1.0 / static_cast<double>(frequency.QuadPart);
}
LARGE_INTEGER time;
+113
View File
@@ -0,0 +1,113 @@
#include "Common/UI/View.h"
#include "Common/UI/AsyncImageFileView.h"
#include "Common/UI/Context.h"
#include "Common/Render/DrawBuffer.h"
AsyncImageFileView::AsyncImageFileView(const Path &filename, UI::ImageSizeMode sizeMode, UI::LayoutParams *layoutParams)
: UI::Clickable(layoutParams), canFocus_(true), filename_(filename), color_(0xFFFFFFFF), sizeMode_(sizeMode), textureFailed_(false), fixedSizeW_(0.0f), fixedSizeH_(0.0f) {}
AsyncImageFileView::~AsyncImageFileView() {}
static float DesiredSize(float sz, float contentSize, UI::MeasureSpec spec) {
float measured;
UI::MeasureBySpec(sz, contentSize, spec, &measured);
return measured;
}
void AsyncImageFileView::GetContentDimensionsBySpec(const UIContext &dc, UI::MeasureSpec horiz, UI::MeasureSpec vert, float &w, float &h) const {
if (texture_ && texture_->GetTexture()) {
float texw = (float)texture_->Width();
float texh = (float)texture_->Height();
float desiredW = DesiredSize(layoutParams_->width, w, horiz);
float desiredH = DesiredSize(layoutParams_->height, h, vert);
switch (sizeMode_) {
case UI::IS_FIXED:
w = fixedSizeW_;
h = fixedSizeH_;
break;
case UI::IS_KEEP_ASPECT:
w = texw;
h = texh;
if (desiredW != w || desiredH != h) {
float aspect = w / h;
// We need the other dimension based on the desired scale to find the best aspect.
float desiredWOther = DesiredSize(layoutParams_->height, h * (desiredW / w), vert);
float desiredHOther = DesiredSize(layoutParams_->width, w * (desiredH / h), horiz);
float diffW = fabsf(aspect - desiredW / desiredWOther);
float diffH = fabsf(aspect - desiredH / desiredHOther);
if (diffW < diffH) {
w = desiredW;
h = desiredWOther;
} else {
w = desiredHOther;
h = desiredH;
}
}
break;
case UI::IS_DEFAULT:
default:
w = texw;
h = texh;
break;
}
} else {
w = 16;
h = 16;
}
}
void AsyncImageFileView::SetFilename(const Path &filename) {
if (filename_ != filename) {
textureFailed_ = false;
filename_ = filename;
texture_.reset(nullptr);
}
}
void AsyncImageFileView::DeviceLost() {
if (texture_.get())
texture_->DeviceLost();
}
void AsyncImageFileView::DeviceRestored(Draw::DrawContext *draw) {
if (texture_.get())
texture_->DeviceRestored(draw);
}
void AsyncImageFileView::Draw(UIContext &dc) {
using namespace Draw;
if (!texture_ && !textureFailed_ && !filename_.empty()) {
texture_ = CreateTextureFromFile(dc.GetDrawContext(), filename_.c_str(), DETECT, true);
if (!texture_.get())
textureFailed_ = true;
}
if (HasFocus()) {
dc.FillRect(dc.theme->itemFocusedStyle.background, bounds_.Expand(3));
}
// TODO: involve sizemode
if (texture_ && texture_->GetTexture()) {
dc.Flush();
dc.GetDrawContext()->BindTexture(0, texture_->GetTexture());
dc.Draw()->Rect(bounds_.x, bounds_.y, bounds_.w, bounds_.h, color_);
dc.Flush();
dc.RebindTexture();
if (!text_.empty()) {
dc.DrawText(text_.c_str(), bounds_.centerX() + 1, bounds_.centerY() + 1, 0x80000000, ALIGN_CENTER | FLAG_DYNAMIC_ASCII);
dc.DrawText(text_.c_str(), bounds_.centerX(), bounds_.centerY(), 0xFFFFFFFF, ALIGN_CENTER | FLAG_DYNAMIC_ASCII);
}
} else {
if (!filename_.empty()) {
// draw a black rectangle to represent the missing screenshot.
dc.FillRect(UI::Drawable(0xFF000000), GetBounds());
} else {
// draw a dark gray rectangle to represent no save state.
dc.FillRect(UI::Drawable(0x50202020), GetBounds());
}
if (!text_.empty()) {
dc.DrawText(text_.c_str(), bounds_.centerX(), bounds_.centerY(), 0xFFFFFFFF, ALIGN_CENTER | FLAG_DYNAMIC_ASCII);
}
}
}
+44
View File
@@ -0,0 +1,44 @@
#pragma once
#include "Common/UI/View.h"
#include "Common/Render/ManagedTexture.h"
#include "Common/File/Path.h"
class UIContext;
// AsyncImageFileView loads a texture from a file, and reloads it as necessary.
// TODO: Actually make async, doh.
class AsyncImageFileView : public UI::Clickable {
public:
AsyncImageFileView(const Path &filename, UI::ImageSizeMode sizeMode, UI::LayoutParams *layoutParams = 0);
~AsyncImageFileView();
void GetContentDimensionsBySpec(const UIContext &dc, UI::MeasureSpec horiz, UI::MeasureSpec vert, float &w, float &h) const override;
void Draw(UIContext &dc) override;
std::string DescribeText() const override { return text_; }
void DeviceLost() override;
void DeviceRestored(Draw::DrawContext *draw) override;
void SetFilename(const Path &filename);
void SetColor(uint32_t color) { color_ = color; }
void SetOverlayText(std::string text) { text_ = text; }
void SetFixedSize(float fixW, float fixH) { fixedSizeW_ = fixW; fixedSizeH_ = fixH; }
void SetCanBeFocused(bool can) { canFocus_ = can; }
bool CanBeFocused() const override { return canFocus_; }
const Path &GetFilename() const { return filename_; }
private:
bool canFocus_;
Path filename_;
std::string text_;
uint32_t color_;
UI::ImageSizeMode sizeMode_;
std::unique_ptr<ManagedTexture> texture_;
bool textureFailed_;
float fixedSizeW_;
float fixedSizeH_;
};
+1 -1
View File
@@ -10,9 +10,9 @@
#include "Common/UI/Context.h"
#include "Common/Render/DrawBuffer.h"
#include "Common/Render/Text/draw_text.h"
#include "Common/Render/ManagedTexture.h"
#include "Common/Log.h"
#include "Common/LogReporting.h"
#include "UI/TextureUtil.h"
UIContext::UIContext() {
fontStyle_ = new UI::FontStyle();
+6 -1
View File
@@ -71,7 +71,6 @@ public:
DrawBuffer *Draw() const { return uidrawbuffer_; }
DrawBuffer *DrawTop() const { return uidrawbufferTop_; }
const UI::Theme *theme;
// Utility methods
TextDrawer *Text() const { return textDrawer_; }
@@ -94,6 +93,9 @@ public:
const Bounds &GetBounds() const { return bounds_; }
Bounds GetLayoutBounds() const;
Draw::DrawContext *GetDrawContext() { return draw_; }
const UI::Theme &GetTheme() const {
return *theme;
}
void SetCurZ(float curZ);
void PushTransform(const UITransform &transform);
@@ -106,6 +108,9 @@ public:
screenTag_ = tag;
}
// TODO: Move to private.
const UI::Theme *theme;
private:
Draw::DrawContext *draw_ = nullptr;
Bounds bounds_;
+1 -3
View File
@@ -33,9 +33,7 @@ std::atomic<bool> hasDispatchQueue;
std::deque<DispatchQueueItem> g_dispatchQueue;
void EventTriggered(Event *e, EventParams params) {
DispatchQueueItem item;
item.e = e;
item.params = params;
DispatchQueueItem item{ e, params };
std::unique_lock<std::mutex> guard(eventMutex_);
// Set before adding so we lock and check the added value.
+3 -2
View File
@@ -43,6 +43,7 @@ extern DrawBuffer ui_draw2d_front; // for things that need to be on top of the r
// Implement this interface to style your lists
class UIListAdapter {
public:
virtual ~UIListAdapter() {}
virtual size_t getCount() const = 0;
virtual void drawItem(int item, int x, int y, int w, int h, bool active) const = 0;
virtual float itemHeight(int itemIndex) const { return 64; }
@@ -52,8 +53,8 @@ public:
class StringVectorListAdapter : public UIListAdapter {
public:
StringVectorListAdapter(const std::vector<std::string> *items) : items_(items) {}
virtual size_t getCount() const { return items_->size(); }
virtual void drawItem(int item, int x, int y, int w, int h, bool active) const;
size_t getCount() const override { return items_->size(); }
void drawItem(int item, int x, int y, int w, int h, bool active) const override;
private:
const std::vector<std::string> *items_;
+76 -25
View File
@@ -12,7 +12,6 @@
#include "Common/UI/Root.h"
#include "Common/Data/Text/I18n.h"
#include "Common/Render/DrawBuffer.h"
#include "Common/VR/PPSSPPVR.h"
#include "Common/Log.h"
#include "Common/StringUtils.h"
@@ -21,12 +20,17 @@ static const bool ClickDebug = false;
UIScreen::UIScreen()
: Screen() {
lastVertical_ = UseVerticalLayout();
}
UIScreen::~UIScreen() {
delete root_;
}
bool UIScreen::UseVerticalLayout() const {
return dp_yres > dp_xres * 1.1f;
}
void UIScreen::DoRecreateViews() {
if (recreateViews_) {
std::lock_guard<std::recursive_mutex> guard(screenManager()->inputLock_);
@@ -61,6 +65,12 @@ void UIScreen::DoRecreateViews() {
}
void UIScreen::update() {
bool vertical = UseVerticalLayout();
if (vertical != lastVertical_) {
RecreateViews();
lastVertical_ = vertical;
}
DoRecreateViews();
if (root_) {
@@ -89,10 +99,6 @@ void UIScreen::preRender() {
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 }, "UI");
screenManager()->getUIContext()->BeginFrame();
if (IsVRBuild()) {
BindVRFramebuffer();
}
Draw::Viewport viewport;
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
@@ -317,10 +323,6 @@ void PopupScreen::TriggerFinish(DialogResult result) {
}
}
void PopupScreen::resized() {
RecreateViews();
}
void PopupScreen::CreateViews() {
using namespace UI;
UIContext &dc = *screenManager()->getUIContext();
@@ -341,7 +343,9 @@ void PopupScreen::CreateViews() {
box_->SetDropShadowExpand(std::max(dp_xres, dp_yres));
View *title = new PopupHeader(title_);
box_->Add(title);
if (HasTitleBar()) {
box_->Add(title);
}
CreatePopupContents(box_);
root_->SetDefaultFocusView(box_);
@@ -408,6 +412,39 @@ UI::EventReturn ListPopupScreen::OnListChoice(UI::EventParams &e) {
namespace UI {
PopupContextMenuScreen::PopupContextMenuScreen(const ContextMenuItem *items, size_t itemCount, I18NCategory *category, UI::View *sourceView)
: PopupScreen("", "", ""), items_(items), itemCount_(itemCount), category_(category), sourceView_(sourceView)
{
enabled_.resize(itemCount, true);
SetPopupOrigin(sourceView);
}
void PopupContextMenuScreen::CreatePopupContents(UI::ViewGroup *parent) {
for (size_t i = 0; i < itemCount_; i++) {
if (items_[i].imageID) {
Choice *choice = new Choice(category_->T(items_[i].text), ImageID(items_[i].imageID));
parent->Add(choice);
if (enabled_[i]) {
choice->OnClick.Add([=](EventParams &p) {
TriggerFinish(DR_OK);
p.a = (uint32_t)i;
OnChoice.Dispatch(p);
return EVENT_DONE;
});
}
else {
choice->SetEnabled(false);
}
}
}
// Hacky: Override the position to look like a popup menu.
AnchorLayoutParams *ap = (AnchorLayoutParams *)parent->GetLayoutParams();
ap->center = false;
ap->left = sourceView_->GetBounds().x;
ap->top = sourceView_->GetBounds().y2();
}
std::string ChopTitle(const std::string &title) {
size_t pos = title.find('\n');
if (pos != title.npos) {
@@ -819,7 +856,7 @@ void AbstractChoiceWithValueDisplay::GetContentDimensionsBySpec(const UIContext
const std::string valueText = ValueText();
int paddingX = 12;
// Assume we want at least 20% of the size for the label, at a minimum.
float availWidth = (horiz.size - paddingX * 2) * 0.8f;
float availWidth = (horiz.size - paddingX * 2) * (text_.empty() ? 1.0f : 0.8f);
if (availWidth < 0) {
availWidth = 65535.0f;
}
@@ -831,11 +868,15 @@ void AbstractChoiceWithValueDisplay::GetContentDimensionsBySpec(const UIContext
valueW += paddingX;
// Give the choice itself less space to grow in, so it shrinks if needed.
MeasureSpec horizLabel = horiz;
horizLabel.size -= valueW;
// MeasureSpec horizLabel = horiz;
// horizLabel.size -= valueW;
Choice::GetContentDimensionsBySpec(dc, horiz, vert, w, h);
w += valueW;
// Fill out anyway if there's space.
if (horiz.type == AT_MOST && w < horiz.size) {
w = horiz.size;
}
h = std::max(h, valueH);
}
@@ -854,20 +895,30 @@ void AbstractChoiceWithValueDisplay::Draw(UIContext &dc) {
dc.SetFontStyle(dc.theme->uiFont);
const std::string valueText = ValueText();
// Assume we want at least 20% of the size for the label, at a minimum.
float availWidth = (bounds_.w - paddingX * 2) * 0.8f;
float scale = CalculateValueScale(dc, valueText, availWidth);
float w, h;
Bounds availBounds(0, 0, availWidth, bounds_.h);
dc.MeasureTextRect(dc.theme->uiFont, scale, scale, valueText.c_str(), (int)valueText.size(), availBounds, &w, &h, ALIGN_RIGHT | ALIGN_VCENTER | FLAG_WRAP_TEXT);
textPadding_.right = w + paddingX;
// If there is a label, assume we want at least 20% of the size for it, at a minimum.
Choice::Draw(dc);
dc.SetFontScale(scale, scale);
Bounds valueBounds(bounds_.x2() - textPadding_.right, bounds_.y, w, bounds_.h);
dc.DrawTextRect(valueText.c_str(), valueBounds, style.fgColor, ALIGN_RIGHT | ALIGN_VCENTER | FLAG_WRAP_TEXT);
dc.SetFontScale(1.0f, 1.0f);
if (!text_.empty()) {
float availWidth = (bounds_.w - paddingX * 2) * 0.8f;
float scale = CalculateValueScale(dc, valueText, availWidth);
float w, h;
Bounds availBounds(0, 0, availWidth, bounds_.h);
dc.MeasureTextRect(dc.theme->uiFont, scale, scale, valueText.c_str(), (int)valueText.size(), availBounds, &w, &h, ALIGN_RIGHT | ALIGN_VCENTER | FLAG_WRAP_TEXT);
textPadding_.right = w + paddingX;
Choice::Draw(dc);
dc.SetFontScale(scale, scale);
Bounds valueBounds(bounds_.x2() - textPadding_.right, bounds_.y, w, bounds_.h);
dc.DrawTextRect(valueText.c_str(), valueBounds, style.fgColor, ALIGN_RIGHT | ALIGN_VCENTER | FLAG_WRAP_TEXT);
dc.SetFontScale(1.0f, 1.0f);
} else {
Choice::Draw(dc);
float scale = CalculateValueScale(dc, valueText, bounds_.w);
dc.SetFontScale(scale, scale);
dc.DrawTextRect(valueText.c_str(), bounds_.Expand(-paddingX, 0.0f), style.fgColor, ALIGN_LEFT | ALIGN_VCENTER | FLAG_WRAP_TEXT);
dc.SetFontScale(1.0f, 1.0f);
}
}
float AbstractChoiceWithValueDisplay::CalculateValueScale(const UIContext &dc, const std::string &valueText, float availWidth) const {
Loaded 100 of 584 files, more files were not shown because too many files have changed in this diff. Show more