mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-16 16:00:30 +02:00
Merge branch 'master' into Font-ltn12-hack
This commit is contained in:
584 files changed
+24063
-11166
No files matched your search
+41
-25
@@ -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/
|
||||
|
||||
@@ -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 }}
|
||||
@@ -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/
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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,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, };
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -7,6 +7,7 @@ public:
|
||||
WordWrapper(const char *str, float maxW, int flags)
|
||||
: str_(str), maxW_(maxW), flags_(flags) {
|
||||
}
|
||||
virtual ~WordWrapper() {}
|
||||
|
||||
std::string Wrapped();
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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];
|
||||
{
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
};
|
||||
@@ -9,4 +9,5 @@ void VulkanBarrier::Flush(VkCommandBuffer cmd) {
|
||||
imageBarriers_.clear();
|
||||
srcStageMask_ = 0;
|
||||
dstStageMask_ = 0;
|
||||
dependencyFlags_ = 0;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
@@ -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_);
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, ©);
|
||||
|
||||
if (multisampled) {
|
||||
vkCmdCopyImage(cmd, src->msaaColor.image, src->msaaColor.layout, dst->msaaColor.image, dst->msaaColor.layout, 1, ©);
|
||||
}
|
||||
}
|
||||
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, ©);
|
||||
|
||||
if (multisampled) {
|
||||
vkCmdCopyImage(cmd, src->msaaDepth.image, src->msaaDepth.layout, dst->msaaDepth.image, dst->msaaDepth.layout, 1, ©);
|
||||
}
|
||||
}
|
||||
|
||||
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, ®ion);
|
||||
|
||||
// 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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
@@ -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
@@ -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
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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);
|
||||
};
|
||||
@@ -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())
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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 + "\"";
|
||||
}
|
||||
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -113,7 +113,7 @@ struct FixupBranch {
|
||||
~FixupBranch();
|
||||
|
||||
const u8 *ptr = nullptr;
|
||||
FixupBranchType type;
|
||||
FixupBranchType type = FixupBranchType::B;
|
||||
};
|
||||
|
||||
class RiscVEmitter {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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[] = {
|
||||
{ "&", "&" },
|
||||
// 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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
Reference in new issue
Block a user