mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-14 18:19:36 +01:00
Merge branch 'hrydgard:master' into vfpu-sincos
This commit is contained in:
278 files changed
+5752
-11311
No files matched your search
+21
-7
@@ -588,8 +588,10 @@ add_library(Common STATIC
|
||||
Common/Data/Random/Rng.h
|
||||
Common/File/VFS/VFS.h
|
||||
Common/File/VFS/VFS.cpp
|
||||
Common/File/VFS/AssetReader.cpp
|
||||
Common/File/VFS/AssetReader.h
|
||||
Common/File/VFS/ZipFileReader.cpp
|
||||
Common/File/VFS/ZipFileReader.h
|
||||
Common/File/VFS/DirectoryReader.cpp
|
||||
Common/File/VFS/DirectoryReader.h
|
||||
Common/File/AndroidStorage.h
|
||||
Common/File/AndroidStorage.cpp
|
||||
Common/File/DiskFree.h
|
||||
@@ -1150,7 +1152,7 @@ elseif(IOS AND NOT LIBRETRO)
|
||||
UI/DarwinFileSystemServices.h
|
||||
Common/Battery/AppleBatteryClient.m
|
||||
)
|
||||
|
||||
|
||||
set(nativeExtraLibs ${nativeExtraLibs} "-framework Foundation -framework MediaPlayer -framework AudioToolbox -framework CoreGraphics -framework QuartzCore -framework UIKit -framework GLKit -framework OpenAL -framework AVFoundation -framework CoreLocation -framework CoreVideo -framework CoreMedia -framework CoreServices" )
|
||||
if(EXISTS "${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks/GameController.framework")
|
||||
set(nativeExtraLibs ${nativeExtraLibs} "-weak_framework GameController")
|
||||
@@ -1188,7 +1190,15 @@ elseif(USING_QT_UI)
|
||||
if(USING_GLES2)
|
||||
add_definitions(-DQT_OPENGL_ES -DQT_OPENGL_ES_2)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND NativeAppSource
|
||||
UI/DarwinFileSystemServices.mm
|
||||
UI/DarwinFileSystemServices.h
|
||||
Common/Battery/AppleBatteryClient.m)
|
||||
set_source_files_properties(Common/Battery/AppleBatteryClient.m PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||
set_source_files_properties(UI/DarwinFileSystemServices.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||
set(nativeExtraLibs ${nativeExtraLibs} ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY})
|
||||
endif()
|
||||
include_directories(Qt)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(nativeExtraLibs ${nativeExtraLibs} Qt5::OpenGL Qt5::Gui Qt5::Core Qt5::Multimedia)
|
||||
@@ -1346,7 +1356,7 @@ if(LINUX AND NOT ANDROID)
|
||||
endif()
|
||||
|
||||
set(ATOMIC_LIB)
|
||||
if(ANDROID OR (LINUX AND ARM_DEVICE))
|
||||
if(ANDROID OR (LINUX AND ARM_DEVICE) OR (LINUX AND RISCV64))
|
||||
set(ATOMIC_LIB atomic)
|
||||
endif()
|
||||
|
||||
@@ -1629,6 +1639,10 @@ set(GPU_SOURCES
|
||||
GPU/Common/TextureScalerCommon.h
|
||||
GPU/Common/PostShader.cpp
|
||||
GPU/Common/PostShader.h
|
||||
GPU/Common/TextureReplacer.cpp
|
||||
GPU/Common/TextureReplacer.h
|
||||
GPU/Common/ReplacedTexture.cpp
|
||||
GPU/Common/ReplacedTexture.h
|
||||
GPU/Debugger/Breakpoints.cpp
|
||||
GPU/Debugger/Breakpoints.h
|
||||
GPU/Debugger/Debugger.cpp
|
||||
@@ -1651,6 +1665,8 @@ set(GPU_SOURCES
|
||||
GPU/GPU.h
|
||||
GPU/GPUCommon.cpp
|
||||
GPU/GPUCommon.h
|
||||
GPU/GPUCommonHW.cpp
|
||||
GPU/GPUCommonHW.h
|
||||
GPU/GPUState.cpp
|
||||
GPU/GPUState.h
|
||||
GPU/Math3D.cpp
|
||||
@@ -2035,8 +2051,6 @@ add_library(${CoreLibName} ${CoreLinkType}
|
||||
Core/Screenshot.h
|
||||
Core/System.cpp
|
||||
Core/System.h
|
||||
Core/TextureReplacer.cpp
|
||||
Core/TextureReplacer.h
|
||||
Core/ThreadPools.cpp
|
||||
Core/ThreadPools.h
|
||||
Core/Util/AudioFormat.cpp
|
||||
|
||||
@@ -425,8 +425,9 @@
|
||||
<ClInclude Include="File\FileUtil.h" />
|
||||
<ClInclude Include="File\Path.h" />
|
||||
<ClInclude Include="File\PathBrowser.h" />
|
||||
<ClInclude Include="File\VFS\DirectoryReader.h" />
|
||||
<ClInclude Include="File\VFS\VFS.h" />
|
||||
<ClInclude Include="File\VFS\AssetReader.h" />
|
||||
<ClInclude Include="File\VFS\ZipFileReader.h" />
|
||||
<ClInclude Include="GPU\D3D11\D3D11Loader.h" />
|
||||
<ClInclude Include="GPU\D3D9\D3DCompilerLoader.h" />
|
||||
<ClInclude Include="GPU\D3D9\D3D9ShaderCompiler.h" />
|
||||
@@ -861,8 +862,9 @@
|
||||
<ClCompile Include="File\FileUtil.cpp" />
|
||||
<ClCompile Include="File\Path.cpp" />
|
||||
<ClCompile Include="File\PathBrowser.cpp" />
|
||||
<ClCompile Include="File\VFS\DirectoryReader.cpp" />
|
||||
<ClCompile Include="File\VFS\VFS.cpp" />
|
||||
<ClCompile Include="File\VFS\AssetReader.cpp" />
|
||||
<ClCompile Include="File\VFS\ZipFileReader.cpp" />
|
||||
<ClCompile Include="GPU\D3D11\D3D11Loader.cpp" />
|
||||
<ClCompile Include="GPU\D3D11\thin3d_d3d11.cpp" />
|
||||
<ClCompile Include="GPU\D3D9\D3DCompilerLoader.cpp" />
|
||||
|
||||
@@ -176,9 +176,6 @@
|
||||
<ClInclude Include="File\VFS\VFS.h">
|
||||
<Filter>File\VFS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="File\VFS\AssetReader.h">
|
||||
<Filter>File\VFS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Data\Format\IniFile.h">
|
||||
<Filter>Data\Format</Filter>
|
||||
</ClInclude>
|
||||
@@ -467,6 +464,12 @@
|
||||
<ClInclude Include="GPU\OpenGL\GLFrameData.h">
|
||||
<Filter>GPU\OpenGL</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="File\VFS\DirectoryReader.h">
|
||||
<Filter>File\VFS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="File\VFS\ZipFileReader.h">
|
||||
<Filter>File\VFS</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ABI.cpp" />
|
||||
@@ -628,9 +631,6 @@
|
||||
<ClCompile Include="File\VFS\VFS.cpp">
|
||||
<Filter>File\VFS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="File\VFS\AssetReader.cpp">
|
||||
<Filter>File\VFS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Data\Format\IniFile.cpp">
|
||||
<Filter>Data\Format</Filter>
|
||||
</ClCompile>
|
||||
@@ -884,6 +884,12 @@
|
||||
<ClCompile Include="GPU\OpenGL\GLFrameData.cpp">
|
||||
<Filter>GPU\OpenGL</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="File\VFS\DirectoryReader.cpp">
|
||||
<Filter>File\VFS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="File\VFS\ZipFileReader.cpp">
|
||||
<Filter>File\VFS</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Crypto">
|
||||
|
||||
@@ -567,9 +567,9 @@ bool IniFile::Load(const Path &path)
|
||||
return success;
|
||||
}
|
||||
|
||||
bool IniFile::LoadFromVFS(const std::string &filename) {
|
||||
bool IniFile::LoadFromVFS(VFSInterface &vfs, const std::string &filename) {
|
||||
size_t size;
|
||||
uint8_t *data = VFSReadFile(filename.c_str(), &size);
|
||||
uint8_t *data = vfs.ReadFile(filename.c_str(), &size);
|
||||
if (!data)
|
||||
return false;
|
||||
std::string str((const char*)data, size);
|
||||
@@ -582,10 +582,10 @@ bool IniFile::LoadFromVFS(const std::string &filename) {
|
||||
bool IniFile::Load(std::istream &in) {
|
||||
// Maximum number of letters in a line
|
||||
static const int MAX_BYTES = 1024*32;
|
||||
char *templine = new char[MAX_BYTES]; // avoid using up massive stack space
|
||||
|
||||
while (!(in.eof() || in.fail()))
|
||||
{
|
||||
char templine[MAX_BYTES];
|
||||
in.getline(templine, MAX_BYTES);
|
||||
std::string line = templine;
|
||||
|
||||
@@ -624,6 +624,7 @@ bool IniFile::Load(std::istream &in) {
|
||||
}
|
||||
}
|
||||
|
||||
delete[] templine;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#include "Common/File/Path.h"
|
||||
|
||||
class VFSInterface;
|
||||
|
||||
class Section {
|
||||
friend class IniFile;
|
||||
|
||||
@@ -86,7 +88,7 @@ public:
|
||||
bool Load(const Path &path);
|
||||
bool Load(const std::string &filename) { return Load(Path(filename)); }
|
||||
bool Load(std::istream &istream);
|
||||
bool LoadFromVFS(const std::string &filename);
|
||||
bool LoadFromVFS(VFSInterface &vfs, const std::string &filename);
|
||||
|
||||
bool Save(const Path &path);
|
||||
bool Save(const std::string &filename) { return Save(Path(filename)); }
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace json {
|
||||
|
||||
JsonReader::JsonReader(const std::string &filename) {
|
||||
size_t buf_size;
|
||||
buffer_ = (char *)VFSReadFile(filename.c_str(), &buf_size);
|
||||
buffer_ = (char *)g_VFS.ReadFile(filename.c_str(), &buf_size);
|
||||
if (buffer_) {
|
||||
parse();
|
||||
} else {
|
||||
|
||||
@@ -59,3 +59,14 @@ int pngLoadPtr(const unsigned char *input_ptr, size_t input_len, int *pwidth, in
|
||||
png_image_finish_read(&png, NULL, *image_data_ptr, stride, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool PNGHeaderPeek::IsValidPNGHeader() const {
|
||||
if (magic != 0x474e5089 || ihdrTag != 0x52444849) {
|
||||
return false;
|
||||
}
|
||||
// Reject crazy sized images, too.
|
||||
if (Width() > 32768 && Height() > 32768) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
#ifndef _PNG_LOAD_H
|
||||
#define _PNG_LOAD_H
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Common/BitSet.h"
|
||||
|
||||
// *image_data_ptr should be deleted with free()
|
||||
// return value of 1 == success.
|
||||
@@ -9,4 +12,22 @@ int pngLoad(const char *file, int *pwidth,
|
||||
int pngLoadPtr(const unsigned char *input_ptr, size_t input_len, int *pwidth,
|
||||
int *pheight, unsigned char **image_data_ptr);
|
||||
|
||||
#endif // _PNG_LOAD_H
|
||||
// PNG peeker - just read the start of a PNG straight into this struct, in order to
|
||||
// look at basic parameters like width and height. Note that while PNG is a chunk-based
|
||||
// format, the IHDR chunk is REQUIRED to be the first one, so this will work.
|
||||
// Does not handle Apple's weirdo extension CgBI. http://iphonedevwiki.net/index.php/CgBI_file_format
|
||||
// That should not be an issue.
|
||||
struct PNGHeaderPeek {
|
||||
uint32_t magic;
|
||||
uint32_t ignore0;
|
||||
uint32_t ignore1;
|
||||
uint32_t ihdrTag;
|
||||
uint32_t be_width; // big endian
|
||||
uint32_t be_height;
|
||||
uint8_t bitDepth; // bits per channel, can be 1, 2, 4, 8, 16
|
||||
uint8_t colorType; // really, pixel format. 0 = grayscale, 2 = rgb, 3 = palette index, 4 = gray+alpha, 6 = rgba
|
||||
|
||||
bool IsValidPNGHeader() const;
|
||||
int Width() const { return swap32(be_width); }
|
||||
int Height() const { return swap32(be_height); }
|
||||
};
|
||||
@@ -126,7 +126,7 @@ int LoadZIMPtr(const uint8_t *zim, size_t datasize, int *width, int *height, int
|
||||
|
||||
int LoadZIM(const char *filename, int *width, int *height, int *format, uint8_t **image) {
|
||||
size_t size;
|
||||
uint8_t *buffer = VFSReadFile(filename, &size);
|
||||
uint8_t *buffer = g_VFS.ReadFile(filename, &size);
|
||||
if (!buffer) {
|
||||
ERROR_LOG(IO, "Couldn't read data for '%s'", filename);
|
||||
return 0;
|
||||
|
||||
@@ -73,7 +73,7 @@ Path I18NRepo::GetIniPath(const std::string &languageID) const {
|
||||
|
||||
bool I18NRepo::IniExists(const std::string &languageID) const {
|
||||
File::FileInfo info;
|
||||
if (!VFSGetFileInfo(GetIniPath(languageID).ToString().c_str(), &info))
|
||||
if (!g_VFS.GetFileInfo(GetIniPath(languageID).ToString().c_str(), &info))
|
||||
return false;
|
||||
if (!info.exists)
|
||||
return false;
|
||||
@@ -91,7 +91,7 @@ bool I18NRepo::LoadIni(const std::string &languageID, const Path &overridePath)
|
||||
iniPath = GetIniPath(languageID);
|
||||
}
|
||||
|
||||
if (!ini.LoadFromVFS(iniPath.ToString()))
|
||||
if (!ini.LoadFromVFS(g_VFS, iniPath.ToString()))
|
||||
return false;
|
||||
|
||||
Clear();
|
||||
@@ -127,7 +127,7 @@ I18NCategory *I18NRepo::LoadSection(const Section *section, const char *name) {
|
||||
return cat;
|
||||
}
|
||||
|
||||
// This is a very light touched save variant - it won't overwrite
|
||||
// This is a very light touched save variant - it won't overwrite
|
||||
// anything, only create new entries.
|
||||
void I18NRepo::SaveIni(const std::string &languageID) {
|
||||
IniFile ini;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "Common/File/AndroidStorage.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/Log.h"
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cstdint>
|
||||
|
||||
#include "Common/File/Path.h"
|
||||
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
#include <algorithm>
|
||||
#include <ctype.h>
|
||||
#include <set>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <zip.h>
|
||||
#endif
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/File/VFS/AssetReader.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
uint8_t *ReadFromZip(zip *archive, const char* filename, size_t *size) {
|
||||
// Figure out the file size first.
|
||||
struct zip_stat zstat;
|
||||
zip_file *file = zip_fopen(archive, filename, ZIP_FL_NOCASE|ZIP_FL_UNCHANGED);
|
||||
if (!file) {
|
||||
ERROR_LOG(IO, "Error opening %s from ZIP", filename);
|
||||
return 0;
|
||||
}
|
||||
zip_stat(archive, filename, ZIP_FL_NOCASE|ZIP_FL_UNCHANGED, &zstat);
|
||||
|
||||
uint8_t *contents = new uint8_t[zstat.size + 1];
|
||||
zip_fread(file, contents, zstat.size);
|
||||
zip_fclose(file);
|
||||
contents[zstat.size] = 0;
|
||||
|
||||
*size = zstat.size;
|
||||
return contents;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
ZipAssetReader::ZipAssetReader(const char *zip_file, const char *in_zip_path) {
|
||||
zip_file_ = zip_open(zip_file, 0, NULL);
|
||||
strcpy(in_zip_path_, in_zip_path);
|
||||
if (!zip_file_) {
|
||||
ERROR_LOG(IO, "Failed to open %s as a zip file", zip_file);
|
||||
}
|
||||
|
||||
std::vector<File::FileInfo> info;
|
||||
GetFileListing("assets", &info, 0);
|
||||
for (size_t i = 0; i < info.size(); i++) {
|
||||
if (info[i].isDirectory) {
|
||||
DEBUG_LOG(IO, "Directory: %s", info[i].name.c_str());
|
||||
} else {
|
||||
DEBUG_LOG(IO, "File: %s", info[i].name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ZipAssetReader::~ZipAssetReader() {
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
zip_close(zip_file_);
|
||||
}
|
||||
|
||||
uint8_t *ZipAssetReader::ReadAsset(const char *path, size_t *size) {
|
||||
char temp_path[1024];
|
||||
strcpy(temp_path, in_zip_path_);
|
||||
strcat(temp_path, path);
|
||||
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
return ReadFromZip(zip_file_, temp_path, size);
|
||||
}
|
||||
|
||||
bool ZipAssetReader::GetFileListing(const char *orig_path, std::vector<File::FileInfo> *listing, const char *filter = 0) {
|
||||
char path[1024];
|
||||
strcpy(path, in_zip_path_);
|
||||
strcat(path, orig_path);
|
||||
|
||||
std::set<std::string> filters;
|
||||
std::string tmp;
|
||||
if (filter) {
|
||||
while (*filter) {
|
||||
if (*filter == ':') {
|
||||
filters.insert("." + tmp);
|
||||
tmp.clear();
|
||||
} else {
|
||||
tmp.push_back(*filter);
|
||||
}
|
||||
filter++;
|
||||
}
|
||||
}
|
||||
if (tmp.size())
|
||||
filters.insert("." + tmp);
|
||||
|
||||
// We just loop through the whole ZIP file and deduce what files are in this directory, and what subdirectories there are.
|
||||
std::set<std::string> files;
|
||||
std::set<std::string> directories;
|
||||
GetZipListings(path, files, directories);
|
||||
|
||||
for (auto diter = directories.begin(); diter != directories.end(); ++diter) {
|
||||
File::FileInfo info;
|
||||
info.name = *diter;
|
||||
|
||||
// Remove the "inzip" part of the fullname.
|
||||
info.fullName = Path(std::string(path).substr(strlen(in_zip_path_))) / *diter;
|
||||
info.exists = true;
|
||||
info.isWritable = false;
|
||||
info.isDirectory = true;
|
||||
listing->push_back(info);
|
||||
}
|
||||
|
||||
for (auto fiter = files.begin(); fiter != files.end(); ++fiter) {
|
||||
std::string fpath = path;
|
||||
File::FileInfo info;
|
||||
info.name = *fiter;
|
||||
info.fullName = Path(std::string(path).substr(strlen(in_zip_path_))) / *fiter;
|
||||
info.exists = true;
|
||||
info.isWritable = false;
|
||||
info.isDirectory = false;
|
||||
std::string ext = info.fullName.GetFileExtension();
|
||||
if (filter) {
|
||||
if (filters.find(ext) == filters.end()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
listing->push_back(info);
|
||||
}
|
||||
|
||||
std::sort(listing->begin(), listing->end());
|
||||
return true;
|
||||
}
|
||||
|
||||
void ZipAssetReader::GetZipListings(const char *path, std::set<std::string> &files, std::set<std::string> &directories) {
|
||||
size_t pathlen = strlen(path);
|
||||
if (path[pathlen - 1] == '/')
|
||||
pathlen--;
|
||||
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
int numFiles = zip_get_num_files(zip_file_);
|
||||
for (int i = 0; i < numFiles; i++) {
|
||||
const char* name = zip_get_name(zip_file_, i, 0);
|
||||
if (!name)
|
||||
continue;
|
||||
if (!memcmp(name, path, pathlen)) {
|
||||
// The prefix is right. Let's see if this is a file or path.
|
||||
const char *slashPos = strchr(name + pathlen + 1, '/');
|
||||
if (slashPos != 0) {
|
||||
// A directory.
|
||||
std::string dirName = std::string(name + pathlen + 1, slashPos - (name + pathlen + 1));
|
||||
directories.insert(dirName);
|
||||
} else if (name[pathlen] == '/') {
|
||||
const char *fn = name + pathlen + 1;
|
||||
files.insert(std::string(fn));
|
||||
} // else, it was a file with the same prefix as the path. like langregion.ini next to lang/.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ZipAssetReader::GetFileInfo(const char *path, File::FileInfo *info) {
|
||||
struct zip_stat zstat;
|
||||
char temp_path[1024];
|
||||
strcpy(temp_path, in_zip_path_);
|
||||
strcat(temp_path, path);
|
||||
if (0 != zip_stat(zip_file_, temp_path, ZIP_FL_NOCASE|ZIP_FL_UNCHANGED, &zstat)) {
|
||||
// ZIP files do not have real directories, so we'll end up here if we
|
||||
// try to stat one. For now that's fine.
|
||||
info->exists = false;
|
||||
info->size = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
info->fullName = Path(path);
|
||||
info->exists = true; // TODO
|
||||
info->isWritable = false;
|
||||
info->isDirectory = false; // TODO
|
||||
info->size = zstat.size;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
DirectoryAssetReader::DirectoryAssetReader(const Path &path) {
|
||||
path_ = path;
|
||||
}
|
||||
|
||||
uint8_t *DirectoryAssetReader::ReadAsset(const char *path, size_t *size) {
|
||||
Path new_path = Path(path).StartsWith(path_) ? Path(path) : path_ / path;
|
||||
return File::ReadLocalFile(new_path, size);
|
||||
}
|
||||
|
||||
bool DirectoryAssetReader::GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter = nullptr) {
|
||||
Path new_path = Path(path).StartsWith(path_) ? Path(path) : path_ / path;
|
||||
|
||||
File::FileInfo info;
|
||||
if (!File::GetFileInfo(new_path, &info))
|
||||
return false;
|
||||
|
||||
if (info.isDirectory) {
|
||||
File::GetFilesInDir(new_path, listing, filter);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DirectoryAssetReader::GetFileInfo(const char *path, File::FileInfo *info) {
|
||||
Path new_path = Path(path).StartsWith(path_) ? Path(path) : path_ / path;
|
||||
return File::GetFileInfo(new_path, info);
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
// TODO: Move much of this code to vfs.cpp
|
||||
#pragma once
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <zip.h>
|
||||
#endif
|
||||
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
#include "Common/File/FileUtil.h"
|
||||
#include "Common/File/Path.h"
|
||||
|
||||
class AssetReader {
|
||||
public:
|
||||
virtual ~AssetReader() {}
|
||||
// use delete[]
|
||||
virtual uint8_t *ReadAsset(const char *path, size_t *size) = 0;
|
||||
// Filter support is optional but nice to have
|
||||
virtual bool GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter = 0) = 0;
|
||||
virtual bool GetFileInfo(const char *path, File::FileInfo *info) = 0;
|
||||
virtual std::string toString() const = 0;
|
||||
};
|
||||
|
||||
#ifdef __ANDROID__
|
||||
uint8_t *ReadFromZip(zip *archive, const char* filename, size_t *size);
|
||||
class ZipAssetReader : public AssetReader {
|
||||
public:
|
||||
ZipAssetReader(const char *zip_file, const char *in_zip_path);
|
||||
~ZipAssetReader();
|
||||
// use delete[]
|
||||
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 in_zip_path_;
|
||||
}
|
||||
|
||||
private:
|
||||
void GetZipListings(const char *path, std::set<std::string> &files, std::set<std::string> &directories);
|
||||
|
||||
zip *zip_file_;
|
||||
std::mutex lock_;
|
||||
char in_zip_path_[256];
|
||||
};
|
||||
#endif
|
||||
|
||||
class DirectoryAssetReader : public AssetReader {
|
||||
public:
|
||||
explicit DirectoryAssetReader(const Path &path);
|
||||
// use delete[]
|
||||
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();
|
||||
}
|
||||
|
||||
private:
|
||||
Path path_;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
#include <cstdio>
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/File/VFS/DirectoryReader.h"
|
||||
|
||||
DirectoryReader::DirectoryReader(const Path &path) {
|
||||
path_ = path;
|
||||
}
|
||||
|
||||
uint8_t *DirectoryReader::ReadFile(const char *path, size_t *size) {
|
||||
Path new_path = Path(path).StartsWith(path_) ? Path(path) : path_ / path;
|
||||
return File::ReadLocalFile(new_path, size);
|
||||
}
|
||||
|
||||
bool DirectoryReader::GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter = nullptr) {
|
||||
Path new_path = Path(path).StartsWith(path_) ? Path(path) : path_ / path;
|
||||
|
||||
File::FileInfo info;
|
||||
if (!File::GetFileInfo(new_path, &info))
|
||||
return false;
|
||||
|
||||
if (info.isDirectory) {
|
||||
File::GetFilesInDir(new_path, listing, filter);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DirectoryReader::GetFileInfo(const char *path, File::FileInfo *info) {
|
||||
Path new_path = Path(path).StartsWith(path_) ? Path(path) : path_ / path;
|
||||
return File::GetFileInfo(new_path, info);
|
||||
}
|
||||
|
||||
class DirectoryReaderFileReference : public VFSFileReference {
|
||||
public:
|
||||
Path path;
|
||||
};
|
||||
|
||||
class DirectoryReaderOpenFile : public VFSOpenFile {
|
||||
public:
|
||||
~DirectoryReaderOpenFile() {
|
||||
_dbg_assert_(file == nullptr);
|
||||
}
|
||||
FILE *file = nullptr;
|
||||
};
|
||||
|
||||
VFSFileReference *DirectoryReader::GetFile(const char *path) {
|
||||
Path filePath = path_ / path;
|
||||
if (!File::Exists(filePath)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DirectoryReaderFileReference *reference = new DirectoryReaderFileReference();
|
||||
reference->path = filePath;
|
||||
return reference;
|
||||
}
|
||||
|
||||
bool DirectoryReader::GetFileInfo(VFSFileReference *vfsReference, File::FileInfo *fileInfo) {
|
||||
DirectoryReaderFileReference *reference = (DirectoryReaderFileReference *)vfsReference;
|
||||
return File::GetFileInfo(reference->path, fileInfo);
|
||||
}
|
||||
|
||||
void DirectoryReader::ReleaseFile(VFSFileReference *vfsReference) {
|
||||
DirectoryReaderFileReference *reference = (DirectoryReaderFileReference *)vfsReference;
|
||||
delete reference;
|
||||
}
|
||||
|
||||
VFSOpenFile *DirectoryReader::OpenFileForRead(VFSFileReference *vfsReference, size_t *size) {
|
||||
DirectoryReaderFileReference *reference = (DirectoryReaderFileReference *)vfsReference;
|
||||
FILE *file = File::OpenCFile(reference->path, "rb");
|
||||
if (!file) {
|
||||
return nullptr;
|
||||
}
|
||||
fseek(file, 0, SEEK_END);
|
||||
*size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
DirectoryReaderOpenFile *openFile = new DirectoryReaderOpenFile();
|
||||
openFile->file = file;
|
||||
return openFile;
|
||||
}
|
||||
|
||||
void DirectoryReader::Rewind(VFSOpenFile *vfsOpenFile) {
|
||||
DirectoryReaderOpenFile *openFile = (DirectoryReaderOpenFile *)vfsOpenFile;
|
||||
fseek(openFile->file, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
size_t DirectoryReader::Read(VFSOpenFile *vfsOpenFile, void *buffer, size_t length) {
|
||||
DirectoryReaderOpenFile *openFile = (DirectoryReaderOpenFile *)vfsOpenFile;
|
||||
return fread(buffer, 1, length, openFile->file);
|
||||
}
|
||||
|
||||
void DirectoryReader::CloseFile(VFSOpenFile *vfsOpenFile) {
|
||||
DirectoryReaderOpenFile *openFile = (DirectoryReaderOpenFile *)vfsOpenFile;
|
||||
_dbg_assert_(openFile->file != nullptr);
|
||||
fclose(openFile->file);
|
||||
openFile->file = nullptr;
|
||||
delete openFile;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
#include "Common/File/FileUtil.h"
|
||||
#include "Common/File/Path.h"
|
||||
|
||||
class DirectoryReader : public VFSBackend {
|
||||
public:
|
||||
explicit DirectoryReader(const Path &path);
|
||||
// use delete[] on the returned value.
|
||||
uint8_t *ReadFile(const char *path, size_t *size) override;
|
||||
|
||||
VFSFileReference *GetFile(const char *path) override;
|
||||
bool GetFileInfo(VFSFileReference *vfsReference, File::FileInfo *fileInfo) override;
|
||||
void ReleaseFile(VFSFileReference *vfsReference) override;
|
||||
|
||||
VFSOpenFile *OpenFileForRead(VFSFileReference *vfsReference, size_t *size) override;
|
||||
void Rewind(VFSOpenFile *vfsOpenFile) override;
|
||||
size_t Read(VFSOpenFile *vfsOpenFile, void *buffer, size_t length) override;
|
||||
void CloseFile(VFSOpenFile *vfsOpenFile) 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();
|
||||
}
|
||||
|
||||
private:
|
||||
Path path_;
|
||||
};
|
||||
+31
-33
@@ -1,28 +1,26 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "Common/Log.h"
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
#include "Common/File/VFS/AssetReader.h"
|
||||
#include "Common/File/FileUtil.h"
|
||||
#include "Common/File/AndroidStorage.h"
|
||||
|
||||
struct VFSEntry {
|
||||
const char *prefix;
|
||||
AssetReader *reader;
|
||||
};
|
||||
VFS g_VFS;
|
||||
|
||||
static VFSEntry entries[16];
|
||||
static int num_entries = 0;
|
||||
|
||||
void VFSRegister(const char *prefix, AssetReader *reader) {
|
||||
entries[num_entries].prefix = prefix;
|
||||
entries[num_entries].reader = reader;
|
||||
DEBUG_LOG(IO, "Registered VFS for prefix %s: %s", prefix, reader->toString().c_str());
|
||||
num_entries++;
|
||||
void VFS::Register(const char *prefix, VFSBackend *reader) {
|
||||
if (reader) {
|
||||
entries_.push_back(VFSEntry{ prefix, reader });
|
||||
DEBUG_LOG(IO, "Registered VFS for prefix %s: %s", prefix, reader->toString().c_str());
|
||||
} else {
|
||||
ERROR_LOG(IO, "Trying to register null VFS backend for prefix %s", prefix);
|
||||
}
|
||||
}
|
||||
|
||||
void VFSShutdown() {
|
||||
for (int i = 0; i < num_entries; i++) {
|
||||
delete entries[i].reader;
|
||||
void VFS::Clear() {
|
||||
for (auto &entry : entries_) {
|
||||
delete entry.reader;
|
||||
}
|
||||
num_entries = 0;
|
||||
entries_.clear();
|
||||
}
|
||||
|
||||
// TODO: Use Path more.
|
||||
@@ -38,7 +36,7 @@ static bool IsLocalAbsolutePath(const char *path) {
|
||||
}
|
||||
|
||||
// The returned data should be free'd with delete[].
|
||||
uint8_t *VFSReadFile(const char *filename, size_t *size) {
|
||||
uint8_t *VFS::ReadFile(const char *filename, size_t *size) {
|
||||
if (IsLocalAbsolutePath(filename)) {
|
||||
// Local path, not VFS.
|
||||
// INFO_LOG(IO, "Not a VFS path: %s . Reading local file.", filename);
|
||||
@@ -47,13 +45,13 @@ uint8_t *VFSReadFile(const char *filename, size_t *size) {
|
||||
|
||||
int fn_len = (int)strlen(filename);
|
||||
bool fileSystemFound = false;
|
||||
for (int i = 0; i < num_entries; i++) {
|
||||
int prefix_len = (int)strlen(entries[i].prefix);
|
||||
for (const auto &entry : entries_) {
|
||||
int prefix_len = (int)strlen(entry.prefix);
|
||||
if (prefix_len >= fn_len) continue;
|
||||
if (0 == memcmp(filename, entries[i].prefix, prefix_len)) {
|
||||
if (0 == memcmp(filename, entry.prefix, prefix_len)) {
|
||||
fileSystemFound = true;
|
||||
// INFO_LOG(IO, "Prefix match: %s (%s) -> %s", entries[i].prefix, filename, filename + prefix_len);
|
||||
uint8_t *data = entries[i].reader->ReadAsset(filename + prefix_len, size);
|
||||
uint8_t *data = entry.reader->ReadFile(filename + prefix_len, size);
|
||||
if (data)
|
||||
return data;
|
||||
else
|
||||
@@ -67,7 +65,7 @@ uint8_t *VFSReadFile(const char *filename, size_t *size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool VFSGetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter) {
|
||||
bool VFS::GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter) {
|
||||
if (IsLocalAbsolutePath(path)) {
|
||||
// Local path, not VFS.
|
||||
// INFO_LOG(IO, "Not a VFS path: %s . Reading local directory.", path);
|
||||
@@ -77,12 +75,12 @@ bool VFSGetFileListing(const char *path, std::vector<File::FileInfo> *listing, c
|
||||
|
||||
int fn_len = (int)strlen(path);
|
||||
bool fileSystemFound = false;
|
||||
for (int i = 0; i < num_entries; i++) {
|
||||
int prefix_len = (int)strlen(entries[i].prefix);
|
||||
for (const auto &entry : entries_) {
|
||||
int prefix_len = (int)strlen(entry.prefix);
|
||||
if (prefix_len >= fn_len) continue;
|
||||
if (0 == memcmp(path, entries[i].prefix, prefix_len)) {
|
||||
if (0 == memcmp(path, entry.prefix, prefix_len)) {
|
||||
fileSystemFound = true;
|
||||
if (entries[i].reader->GetFileListing(path + prefix_len, listing, filter)) {
|
||||
if (entry.reader->GetFileListing(path + prefix_len, listing, filter)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -94,7 +92,7 @@ bool VFSGetFileListing(const char *path, std::vector<File::FileInfo> *listing, c
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VFSGetFileInfo(const char *path, File::FileInfo *info) {
|
||||
bool VFS::GetFileInfo(const char *path, File::FileInfo *info) {
|
||||
if (IsLocalAbsolutePath(path)) {
|
||||
// Local path, not VFS.
|
||||
// INFO_LOG(IO, "Not a VFS path: %s . Getting local file info.", path);
|
||||
@@ -103,19 +101,19 @@ bool VFSGetFileInfo(const char *path, File::FileInfo *info) {
|
||||
|
||||
bool fileSystemFound = false;
|
||||
int fn_len = (int)strlen(path);
|
||||
for (int i = 0; i < num_entries; i++) {
|
||||
int prefix_len = (int)strlen(entries[i].prefix);
|
||||
for (const auto &entry : entries_) {
|
||||
int prefix_len = (int)strlen(entry.prefix);
|
||||
if (prefix_len >= fn_len) continue;
|
||||
if (0 == memcmp(path, entries[i].prefix, prefix_len)) {
|
||||
if (0 == memcmp(path, entry.prefix, prefix_len)) {
|
||||
fileSystemFound = true;
|
||||
if (entries[i].reader->GetFileInfo(path + prefix_len, info))
|
||||
if (entry.reader->GetFileInfo(path + prefix_len, info))
|
||||
return true;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!fileSystemFound) {
|
||||
ERROR_LOG(IO, "Missing filesystem for %s", path);
|
||||
ERROR_LOG(IO, "Missing filesystem for '%s'", path);
|
||||
} // Otherwise, the file was just missing. No need to log.
|
||||
return false;
|
||||
}
|
||||
+71
-10
@@ -1,21 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
#include "Common/File/DirListing.h"
|
||||
|
||||
// Basic virtual file system. Used to manage assets on Android, where we have to
|
||||
// Basic read-only virtual file system. Used to manage assets on Android, where we have to
|
||||
// read them manually out of the APK zipfile, while being able to run on other
|
||||
// platforms as well with the appropriate directory set-up.
|
||||
|
||||
class AssetReader;
|
||||
// Note that this is kinda similar in concept to Core/MetaFileSystem.h, but that one
|
||||
// is specifically for operations done by the emulated PSP, while this is for operations
|
||||
// on the system level, like loading assets, and maybe texture packs. Also, as mentioned,
|
||||
// this one is read-only, so a bit smaller and simpler.
|
||||
|
||||
void VFSRegister(const char *prefix, AssetReader *reader);
|
||||
void VFSShutdown();
|
||||
// VFSBackend instances can be used on their own, without the VFS, to serve as an abstraction of
|
||||
// a single directory or ZIP file.
|
||||
|
||||
// Use delete [] to release the returned memory.
|
||||
// Always allocates an extra zero byte at the end, so that it
|
||||
// can be used for text like shader sources.
|
||||
uint8_t *VFSReadFile(const char *filename, size_t *size);
|
||||
bool VFSGetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter = 0);
|
||||
bool VFSGetFileInfo(const char *filename, File::FileInfo *fileInfo);
|
||||
// The VFSFileReference level of abstraction is there to hold things like zip file indices,
|
||||
// for fast re-open etc.
|
||||
|
||||
class VFSFileReference {
|
||||
public:
|
||||
virtual ~VFSFileReference() {}
|
||||
};
|
||||
|
||||
class VFSOpenFile {
|
||||
public:
|
||||
virtual ~VFSOpenFile() {}
|
||||
};
|
||||
|
||||
// Common interface parts between VFSBackend and VFS.
|
||||
// Sometimes you don't need the VFS multiplexing and only have a VFSBackend *, sometimes you do need it,
|
||||
// and it would be cool to be able to use the same interface, like when loading INI files.
|
||||
class VFSInterface {
|
||||
public:
|
||||
virtual ~VFSInterface() {}
|
||||
virtual uint8_t *ReadFile(const char *path, size_t *size) = 0;
|
||||
virtual bool GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter = nullptr) = 0;
|
||||
};
|
||||
|
||||
class VFSBackend : public VFSInterface {
|
||||
public:
|
||||
virtual VFSFileReference *GetFile(const char *path) = 0;
|
||||
virtual bool GetFileInfo(VFSFileReference *vfsReference, File::FileInfo *fileInfo) = 0;
|
||||
virtual void ReleaseFile(VFSFileReference *vfsReference) = 0;
|
||||
|
||||
// Must write the size of the file to *size. Both backends can do this efficiently here,
|
||||
// avoiding a call to GetFileInfo.
|
||||
virtual VFSOpenFile *OpenFileForRead(VFSFileReference *vfsReference, size_t *size) = 0;
|
||||
virtual void Rewind(VFSOpenFile *vfsOpenFile) = 0;
|
||||
virtual size_t Read(VFSOpenFile *vfsOpenFile, void *buffer, size_t length) = 0;
|
||||
virtual void CloseFile(VFSOpenFile *vfsOpenFile) = 0;
|
||||
|
||||
// Filter support is optional but nice to have
|
||||
virtual bool GetFileInfo(const char *path, File::FileInfo *info) = 0;
|
||||
virtual std::string toString() const = 0;
|
||||
};
|
||||
|
||||
class VFS : public VFSInterface {
|
||||
public:
|
||||
~VFS() { Clear(); }
|
||||
void Register(const char *prefix, VFSBackend *reader);
|
||||
void Clear();
|
||||
|
||||
// Use delete [] to release the returned memory.
|
||||
// Always allocates an extra zero byte at the end, so that it
|
||||
// can be used for text like shader sources.
|
||||
uint8_t *ReadFile(const char *filename, size_t *size) override;
|
||||
bool GetFileInfo(const char *filename, File::FileInfo *fileInfo);
|
||||
bool GetFileListing(const char *path, std::vector<File::FileInfo> *listing, const char *filter = nullptr) override;
|
||||
|
||||
private:
|
||||
struct VFSEntry {
|
||||
const char *prefix;
|
||||
VFSBackend *reader;
|
||||
};
|
||||
std::vector<VFSEntry> entries_;
|
||||
};
|
||||
|
||||
extern VFS g_VFS;
|
||||
@@ -0,0 +1,281 @@
|
||||
#include <algorithm>
|
||||
#include <ctype.h>
|
||||
#include <set>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef SHARED_LIBZIP
|
||||
#include <zip.h>
|
||||
#else
|
||||
#include "ext/libzip/zip.h"
|
||||
#endif
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/File/VFS/ZipFileReader.h"
|
||||
#include "Common/StringUtils.h"
|
||||
|
||||
ZipFileReader *ZipFileReader::Create(const Path &zipFile, const char *inZipPath) {
|
||||
int error = 0;
|
||||
zip *zip_file;
|
||||
if (zipFile.Type() == PathType::CONTENT_URI) {
|
||||
int fd = File::OpenFD(zipFile, File::OPEN_READ);
|
||||
if (!fd) {
|
||||
ERROR_LOG(IO, "Failed to open FD for %s as zip file", zipFile.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
zip_file = zip_fdopen(fd, 0, &error);
|
||||
} else {
|
||||
zip_file = zip_open(zipFile.c_str(), 0, &error);
|
||||
}
|
||||
|
||||
if (!zip_file) {
|
||||
ERROR_LOG(IO, "Failed to open %s as a zip file", zipFile.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ZipFileReader *reader = new ZipFileReader();
|
||||
reader->zip_file_ = zip_file;
|
||||
truncate_cpy(reader->inZipPath_, inZipPath);
|
||||
return reader;
|
||||
}
|
||||
|
||||
ZipFileReader::~ZipFileReader() {
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
zip_close(zip_file_);
|
||||
}
|
||||
|
||||
uint8_t *ZipFileReader::ReadFile(const char *path, size_t *size) {
|
||||
char temp_path[2048];
|
||||
snprintf(temp_path, sizeof(temp_path), "%s%s", inZipPath_, path);
|
||||
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
// Figure out the file size first.
|
||||
struct zip_stat zstat;
|
||||
zip_stat(zip_file_, temp_path, ZIP_FL_NOCASE | ZIP_FL_UNCHANGED, &zstat);
|
||||
zip_file *file = zip_fopen(zip_file_, temp_path, ZIP_FL_NOCASE | ZIP_FL_UNCHANGED);
|
||||
if (!file) {
|
||||
ERROR_LOG(IO, "Error opening %s from ZIP", temp_path);
|
||||
return 0;
|
||||
}
|
||||
uint8_t *contents = new uint8_t[zstat.size + 1];
|
||||
zip_fread(file, contents, zstat.size);
|
||||
zip_fclose(file);
|
||||
contents[zstat.size] = 0;
|
||||
|
||||
*size = zstat.size;
|
||||
return contents;
|
||||
}
|
||||
|
||||
bool ZipFileReader::GetFileListing(const char *orig_path, std::vector<File::FileInfo> *listing, const char *filter = 0) {
|
||||
char path[2048];
|
||||
snprintf(path, sizeof(path), "%s%s", inZipPath_, orig_path);
|
||||
|
||||
std::set<std::string> filters;
|
||||
std::string tmp;
|
||||
if (filter) {
|
||||
while (*filter) {
|
||||
if (*filter == ':') {
|
||||
filters.insert("." + tmp);
|
||||
tmp.clear();
|
||||
} else {
|
||||
tmp.push_back(*filter);
|
||||
}
|
||||
filter++;
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp.size())
|
||||
filters.insert("." + tmp);
|
||||
|
||||
// We just loop through the whole ZIP file and deduce what files are in this directory, and what subdirectories there are.
|
||||
std::set<std::string> files;
|
||||
std::set<std::string> directories;
|
||||
GetZipListings(path, files, directories);
|
||||
|
||||
for (auto diter = directories.begin(); diter != directories.end(); ++diter) {
|
||||
File::FileInfo info;
|
||||
info.name = *diter;
|
||||
|
||||
// Remove the "inzip" part of the fullname.
|
||||
info.fullName = Path(std::string(path).substr(strlen(inZipPath_))) / *diter;
|
||||
info.exists = true;
|
||||
info.isWritable = false;
|
||||
info.isDirectory = true;
|
||||
listing->push_back(info);
|
||||
}
|
||||
|
||||
for (auto fiter = files.begin(); fiter != files.end(); ++fiter) {
|
||||
std::string fpath = path;
|
||||
File::FileInfo info;
|
||||
info.name = *fiter;
|
||||
info.fullName = Path(std::string(path).substr(strlen(inZipPath_))) / *fiter;
|
||||
info.exists = true;
|
||||
info.isWritable = false;
|
||||
info.isDirectory = false;
|
||||
std::string ext = info.fullName.GetFileExtension();
|
||||
if (filter) {
|
||||
if (filters.find(ext) == filters.end()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
listing->push_back(info);
|
||||
}
|
||||
|
||||
std::sort(listing->begin(), listing->end());
|
||||
return true;
|
||||
}
|
||||
|
||||
void ZipFileReader::GetZipListings(const char *path, std::set<std::string> &files, std::set<std::string> &directories) {
|
||||
size_t pathlen = strlen(path);
|
||||
if (path[pathlen - 1] == '/')
|
||||
pathlen--;
|
||||
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
int numFiles = zip_get_num_files(zip_file_);
|
||||
for (int i = 0; i < numFiles; i++) {
|
||||
const char* name = zip_get_name(zip_file_, i, 0);
|
||||
if (!name)
|
||||
continue;
|
||||
if (!memcmp(name, path, pathlen)) {
|
||||
// The prefix is right. Let's see if this is a file or path.
|
||||
const char *slashPos = strchr(name + pathlen + 1, '/');
|
||||
if (slashPos != 0) {
|
||||
// A directory.
|
||||
std::string dirName = std::string(name + pathlen + 1, slashPos - (name + pathlen + 1));
|
||||
directories.insert(dirName);
|
||||
} else if (name[pathlen] == '/') {
|
||||
const char *fn = name + pathlen + 1;
|
||||
files.insert(std::string(fn));
|
||||
} // else, it was a file with the same prefix as the path. like langregion.ini next to lang/.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ZipFileReader::GetFileInfo(const char *path, File::FileInfo *info) {
|
||||
struct zip_stat zstat;
|
||||
char temp_path[1024];
|
||||
snprintf(temp_path, sizeof(temp_path), "%s%s", inZipPath_, path);
|
||||
|
||||
// Clear some things to start.
|
||||
info->isDirectory = false;
|
||||
info->isWritable = false;
|
||||
info->size = 0;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
if (0 != zip_stat(zip_file_, temp_path, ZIP_FL_NOCASE | ZIP_FL_UNCHANGED, &zstat)) {
|
||||
// ZIP files do not have real directories, so we'll end up here if we
|
||||
// try to stat one. For now that's fine.
|
||||
info->exists = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Zips usually don't contain directory entries, but they may.
|
||||
if ((zstat.valid & ZIP_STAT_NAME) != 0 && zstat.name) {
|
||||
info->isDirectory = zstat.name[strlen(zstat.name) - 1] == '/';
|
||||
}
|
||||
if ((zstat.valid & ZIP_STAT_SIZE) != 0) {
|
||||
info->size = zstat.size;
|
||||
}
|
||||
|
||||
info->fullName = Path(path);
|
||||
info->exists = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
class ZipFileReaderFileReference : public VFSFileReference {
|
||||
public:
|
||||
int zi;
|
||||
};
|
||||
|
||||
class ZipFileReaderOpenFile : public VFSOpenFile {
|
||||
public:
|
||||
~ZipFileReaderOpenFile() {
|
||||
// Needs to be closed properly and unlocked.
|
||||
_dbg_assert_(zf == nullptr);
|
||||
}
|
||||
ZipFileReaderFileReference *reference;
|
||||
zip_file_t *zf = nullptr;
|
||||
};
|
||||
|
||||
VFSFileReference *ZipFileReader::GetFile(const char *path) {
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
int zi = zip_name_locate(zip_file_, path, ZIP_FL_NOCASE);
|
||||
if (zi < 0) {
|
||||
// Not found.
|
||||
return nullptr;
|
||||
}
|
||||
ZipFileReaderFileReference *ref = new ZipFileReaderFileReference();
|
||||
ref->zi = zi;
|
||||
return ref;
|
||||
}
|
||||
|
||||
bool ZipFileReader::GetFileInfo(VFSFileReference *vfsReference, File::FileInfo *fileInfo) {
|
||||
ZipFileReaderFileReference *reference = (ZipFileReaderFileReference *)vfsReference;
|
||||
// If you crash here, you called this while having the lock held by having the file open.
|
||||
// Don't do that, check the info before you open the file.
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
zip_stat_t zstat;
|
||||
if (zip_stat_index(zip_file_, reference->zi, 0, &zstat) != 0)
|
||||
return false;
|
||||
*fileInfo = File::FileInfo{};
|
||||
fileInfo->size = 0;
|
||||
if (zstat.valid & ZIP_STAT_SIZE)
|
||||
fileInfo->size = zstat.size;
|
||||
return zstat.size;
|
||||
}
|
||||
|
||||
void ZipFileReader::ReleaseFile(VFSFileReference *vfsReference) {
|
||||
ZipFileReaderFileReference *reference = (ZipFileReaderFileReference *)vfsReference;
|
||||
// Don't do anything other than deleting it.
|
||||
delete reference;
|
||||
}
|
||||
|
||||
VFSOpenFile *ZipFileReader::OpenFileForRead(VFSFileReference *vfsReference, size_t *size) {
|
||||
ZipFileReaderFileReference *reference = (ZipFileReaderFileReference *)vfsReference;
|
||||
ZipFileReaderOpenFile *openFile = new ZipFileReaderOpenFile();
|
||||
openFile->reference = reference;
|
||||
*size = 0;
|
||||
// We only allow one file to be open for read concurrently. It's possible that this can be improved,
|
||||
// especially if we only access by index like this.
|
||||
lock_.lock();
|
||||
zip_stat_t zstat;
|
||||
if (zip_stat_index(zip_file_, reference->zi, 0, &zstat) != 0) {
|
||||
lock_.unlock();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
openFile->zf = zip_fopen_index(zip_file_, reference->zi, 0);
|
||||
if (!openFile->zf) {
|
||||
WARN_LOG(G3D, "File with index %d not found in zip", reference->zi);
|
||||
lock_.unlock();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*size = zstat.size;
|
||||
// Intentionally leaving the mutex locked, will be closed in CloseFile.
|
||||
return openFile;
|
||||
}
|
||||
|
||||
void ZipFileReader::Rewind(VFSOpenFile *vfsOpenFile) {
|
||||
ZipFileReaderOpenFile *openFile = (ZipFileReaderOpenFile *)vfsOpenFile;
|
||||
// Close and re-open.
|
||||
zip_fclose(openFile->zf);
|
||||
openFile->zf = zip_fopen_index(zip_file_, openFile->reference->zi, 0);
|
||||
}
|
||||
|
||||
size_t ZipFileReader::Read(VFSOpenFile *vfsOpenFile, void *buffer, size_t length) {
|
||||
ZipFileReaderOpenFile *file = (ZipFileReaderOpenFile *)vfsOpenFile;
|
||||
return zip_fread(file->zf, buffer, length);
|
||||
}
|
||||
|
||||
void ZipFileReader::CloseFile(VFSOpenFile *vfsOpenFile) {
|
||||
ZipFileReaderOpenFile *file = (ZipFileReaderOpenFile *)vfsOpenFile;
|
||||
_dbg_assert_(file->zf != nullptr);
|
||||
zip_fclose(file->zf);
|
||||
file->zf = nullptr;
|
||||
lock_.unlock();
|
||||
delete file;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef SHARED_LIBZIP
|
||||
#include <zip.h>
|
||||
#else
|
||||
#include "ext/libzip/zip.h"
|
||||
#endif
|
||||
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
#include "Common/File/FileUtil.h"
|
||||
#include "Common/File/Path.h"
|
||||
|
||||
class ZipFileReader : public VFSBackend {
|
||||
public:
|
||||
static ZipFileReader *Create(const Path &zipFile, const char *inZipPath);
|
||||
~ZipFileReader();
|
||||
|
||||
bool IsValid() const { return zip_file_ != nullptr; }
|
||||
|
||||
// use delete[] on the returned value.
|
||||
uint8_t *ReadFile(const char *path, size_t *size) override;
|
||||
|
||||
VFSFileReference *GetFile(const char *path) override;
|
||||
bool GetFileInfo(VFSFileReference *vfsReference, File::FileInfo *fileInfo) override;
|
||||
void ReleaseFile(VFSFileReference *vfsReference) override;
|
||||
|
||||
VFSOpenFile *OpenFileForRead(VFSFileReference *vfsReference, size_t *size) override;
|
||||
void Rewind(VFSOpenFile *vfsOpenFile) override;
|
||||
size_t Read(VFSOpenFile *vfsOpenFile, void *buffer, size_t length) override;
|
||||
void CloseFile(VFSOpenFile *vfsOpenFile) 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 inZipPath_;
|
||||
}
|
||||
|
||||
private:
|
||||
void GetZipListings(const char *path, std::set<std::string> &files, std::set<std::string> &directories);
|
||||
|
||||
zip *zip_file_ = nullptr;
|
||||
std::mutex lock_;
|
||||
char inZipPath_[256];
|
||||
};
|
||||
@@ -112,7 +112,7 @@ public:
|
||||
|
||||
// Raster state
|
||||
void SetScissorRect(int left, int top, int width, int height) override;
|
||||
void SetViewports(int count, Viewport *viewports) override;
|
||||
void SetViewport(const Viewport &viewport) override;
|
||||
void SetBlendFactor(float color[4]) override {
|
||||
if (memcmp(blendFactor_, color, sizeof(float) * 4)) {
|
||||
memcpy(blendFactor_, color, sizeof(float) * 4);
|
||||
@@ -420,20 +420,18 @@ void D3D11DrawContext::EndFrame() {
|
||||
curPipeline_ = nullptr;
|
||||
}
|
||||
|
||||
void D3D11DrawContext::SetViewports(int count, Viewport *viewports) {
|
||||
D3D11_VIEWPORT vp[4];
|
||||
for (int i = 0; i < count; i++) {
|
||||
DisplayRect<float> rc{ viewports[i].TopLeftX , viewports[i].TopLeftY, viewports[i].Width, viewports[i].Height };
|
||||
if (curRenderTargetView_ == bbRenderTargetView_) // Only the backbuffer is actually rotated wrong!
|
||||
RotateRectToDisplay(rc, curRTWidth_, curRTHeight_);
|
||||
vp[i].TopLeftX = rc.x;
|
||||
vp[i].TopLeftY = rc.y;
|
||||
vp[i].Width = rc.w;
|
||||
vp[i].Height = rc.h;
|
||||
vp[i].MinDepth = viewports[i].MinDepth;
|
||||
vp[i].MaxDepth = viewports[i].MaxDepth;
|
||||
}
|
||||
context_->RSSetViewports(count, vp);
|
||||
void D3D11DrawContext::SetViewport(const Viewport &viewport) {
|
||||
DisplayRect<float> rc{ viewport.TopLeftX , viewport.TopLeftY, viewport.Width, viewport.Height };
|
||||
if (curRenderTargetView_ == bbRenderTargetView_) // Only the backbuffer is actually rotated wrong!
|
||||
RotateRectToDisplay(rc, curRTWidth_, curRTHeight_);
|
||||
D3D11_VIEWPORT vp;
|
||||
vp.TopLeftX = rc.x;
|
||||
vp.TopLeftY = rc.y;
|
||||
vp.Width = rc.w;
|
||||
vp.Height = rc.h;
|
||||
vp.MinDepth = viewport.MinDepth;
|
||||
vp.MaxDepth = viewport.MaxDepth;
|
||||
context_->RSSetViewports(1, &vp);
|
||||
}
|
||||
|
||||
void D3D11DrawContext::SetScissorRect(int left, int top, int width, int height) {
|
||||
|
||||
@@ -573,7 +573,7 @@ public:
|
||||
|
||||
// Raster state
|
||||
void SetScissorRect(int left, int top, int width, int height) override;
|
||||
void SetViewports(int count, Viewport *viewports) override;
|
||||
void SetViewport(const Viewport &viewport) override;
|
||||
void SetBlendFactor(float color[4]) override;
|
||||
void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override;
|
||||
|
||||
@@ -1173,12 +1173,12 @@ void D3D9Context::SetScissorRect(int left, int top, int width, int height) {
|
||||
dxstate.scissorTest.set(true);
|
||||
}
|
||||
|
||||
void D3D9Context::SetViewports(int count, Viewport *viewports) {
|
||||
int x = (int)viewports[0].TopLeftX;
|
||||
int y = (int)viewports[0].TopLeftY;
|
||||
int w = (int)viewports[0].Width;
|
||||
int h = (int)viewports[0].Height;
|
||||
dxstate.viewport.set(x, y, w, h, viewports[0].MinDepth, viewports[0].MaxDepth);
|
||||
void D3D9Context::SetViewport(const Viewport &viewport) {
|
||||
int x = (int)viewport.TopLeftX;
|
||||
int y = (int)viewport.TopLeftY;
|
||||
int w = (int)viewport.Width;
|
||||
int h = (int)viewport.Height;
|
||||
dxstate.viewport.set(x, y, w, h, viewport.MinDepth, viewport.MaxDepth);
|
||||
}
|
||||
|
||||
void D3D9Context::SetBlendFactor(float color[4]) {
|
||||
|
||||
@@ -102,7 +102,7 @@ bool glsl_recompile(GLSLProgram *program, std::string *error_message) {
|
||||
|
||||
if (!program->vshader_source && !vsh_src) {
|
||||
size_t sz;
|
||||
vsh_src.reset((char *)VFSReadFile(program->vshader_filename, &sz));
|
||||
vsh_src.reset((char *)g_VFS.ReadFile(program->vshader_filename, &sz));
|
||||
}
|
||||
if (!program->vshader_source && !vsh_src) {
|
||||
ERROR_LOG(G3D, "File missing: %s", program->vshader_filename);
|
||||
@@ -113,7 +113,7 @@ bool glsl_recompile(GLSLProgram *program, std::string *error_message) {
|
||||
}
|
||||
if (!program->fshader_source && !fsh_src) {
|
||||
size_t sz;
|
||||
fsh_src.reset((char *)VFSReadFile(program->fshader_filename, &sz));
|
||||
fsh_src.reset((char *)g_VFS.ReadFile(program->fshader_filename, &sz));
|
||||
}
|
||||
if (!program->fshader_source && !fsh_src) {
|
||||
ERROR_LOG(G3D, "File missing: %s", program->fshader_filename);
|
||||
|
||||
@@ -385,9 +385,9 @@ public:
|
||||
renderManager_.SetScissor({ left, top, width, height });
|
||||
}
|
||||
|
||||
void SetViewports(int count, Viewport *viewports) override {
|
||||
void SetViewport(const Viewport &viewport) override {
|
||||
// Same structure, different name.
|
||||
renderManager_.SetViewport((GLRViewport &)*viewports);
|
||||
renderManager_.SetViewport((GLRViewport &)viewport);
|
||||
}
|
||||
|
||||
void SetBlendFactor(float color[4]) override {
|
||||
|
||||
@@ -535,7 +535,7 @@ int VulkanContext::GetBestPhysicalDevice() {
|
||||
|
||||
void VulkanContext::ChooseDevice(int physical_device) {
|
||||
physical_device_ = physical_device;
|
||||
INFO_LOG(G3D, "Chose physical device %d: %p", physical_device, physical_devices_[physical_device]);
|
||||
INFO_LOG(G3D, "Chose physical device %d: %s", physical_device, physicalDeviceProperties_[physical_device].properties.deviceName);
|
||||
|
||||
GetDeviceLayerProperties();
|
||||
if (!CheckLayers(device_layer_properties_, device_layer_names_)) {
|
||||
@@ -711,7 +711,9 @@ VkResult VulkanContext::CreateDevice() {
|
||||
} else {
|
||||
VulkanLoadDeviceFunctions(device_, extensionsLookup_);
|
||||
}
|
||||
INFO_LOG(G3D, "Vulkan Device created");
|
||||
INFO_LOG(G3D, "Vulkan Device created: %s", physicalDeviceProperties_[physical_device_].properties.deviceName);
|
||||
|
||||
// Since we successfully created a device (however we got here, might be interesting in debug), we force the choice to be visible in the menu.
|
||||
VulkanSetAvailable(true);
|
||||
|
||||
VmaAllocatorCreateInfo allocatorInfo = {};
|
||||
@@ -1288,8 +1290,8 @@ bool VulkanContext::InitSwapchain() {
|
||||
VkSurfaceTransformFlagBitsKHR preTransform;
|
||||
std::string supportedTransforms = surface_transforms_to_string(surfCapabilities_.supportedTransforms);
|
||||
std::string currentTransform = surface_transforms_to_string(surfCapabilities_.currentTransform);
|
||||
g_display_rotation = DisplayRotation::ROTATE_0;
|
||||
g_display_rot_matrix.setIdentity();
|
||||
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).
|
||||
@@ -1300,20 +1302,20 @@ bool VulkanContext::InitSwapchain() {
|
||||
} else if (surfCapabilities_.currentTransform & allowedRotations) {
|
||||
// Normal, sensible rotations. Let's handle it.
|
||||
preTransform = surfCapabilities_.currentTransform;
|
||||
g_display_rot_matrix.setIdentity();
|
||||
g_display.rot_matrix.setIdentity();
|
||||
switch (surfCapabilities_.currentTransform) {
|
||||
case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
|
||||
g_display_rotation = DisplayRotation::ROTATE_90;
|
||||
g_display_rot_matrix.setRotationZ90();
|
||||
g_display.rotation = DisplayRotation::ROTATE_90;
|
||||
g_display.rot_matrix.setRotationZ90();
|
||||
std::swap(swapChainExtent_.width, swapChainExtent_.height);
|
||||
break;
|
||||
case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
|
||||
g_display_rotation = DisplayRotation::ROTATE_180;
|
||||
g_display_rot_matrix.setRotationZ180();
|
||||
g_display.rotation = DisplayRotation::ROTATE_180;
|
||||
g_display.rot_matrix.setRotationZ180();
|
||||
break;
|
||||
case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
|
||||
g_display_rotation = DisplayRotation::ROTATE_270;
|
||||
g_display_rot_matrix.setRotationZ270();
|
||||
g_display.rotation = DisplayRotation::ROTATE_270;
|
||||
g_display.rot_matrix.setRotationZ270();
|
||||
std::swap(swapChainExtent_.width, swapChainExtent_.height);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -222,7 +222,8 @@ public:
|
||||
// Simple workaround for the casting warning.
|
||||
template <class T>
|
||||
void SetDebugName(T handle, VkObjectType type, const char *name) {
|
||||
if (extensionsLookup_.EXT_debug_utils) {
|
||||
if (extensionsLookup_.EXT_debug_utils && handle != VK_NULL_HANDLE) {
|
||||
_dbg_assert_(handle != VK_NULL_HANDLE);
|
||||
SetDebugNameImpl((uint64_t)handle, type, name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,12 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback(
|
||||
// See https://github.com/hrydgard/ppsspp/pull/16354
|
||||
return false;
|
||||
}
|
||||
if (messageCode == -375211665) {
|
||||
// VUID-vkAllocateMemory-pAllocateInfo-01713
|
||||
// Can happen when VMA aggressively tries to allocate aperture memory for upload. It gracefully
|
||||
// falls back to regular video memory, so we just ignore this. I'd argue this is a VMA bug, actually.
|
||||
return false;
|
||||
}
|
||||
|
||||
int count;
|
||||
{
|
||||
|
||||
@@ -310,6 +310,7 @@ static void VulkanFreeLibrary(VulkanLibraryHandle &h) {
|
||||
}
|
||||
|
||||
void VulkanSetAvailable(bool available) {
|
||||
INFO_LOG(G3D, "Forcing Vulkan availability to true");
|
||||
g_vulkanAvailabilityChecked = true;
|
||||
g_vulkanMayBeAvailable = available;
|
||||
}
|
||||
@@ -470,6 +471,7 @@ bool VulkanMayBeAvailable() {
|
||||
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
|
||||
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
|
||||
anyGood = true;
|
||||
INFO_LOG(G3D, "VulkanMayBeAvailable: Eligible device found: '%s'", props.deviceName);
|
||||
break;
|
||||
default:
|
||||
INFO_LOG(G3D, "VulkanMayBeAvailable: Ineligible device found and ignored: '%s'", props.deviceName);
|
||||
@@ -494,8 +496,9 @@ bail:
|
||||
}
|
||||
if (lib) {
|
||||
VulkanFreeLibrary(lib);
|
||||
} else {
|
||||
ERROR_LOG(G3D, "Vulkan with working device not detected.");
|
||||
}
|
||||
if (!g_vulkanMayBeAvailable) {
|
||||
WARN_LOG(G3D, "Vulkan with working device not detected.");
|
||||
}
|
||||
return g_vulkanMayBeAvailable;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,11 @@
|
||||
|
||||
#include "ext/vulkan/vulkan.h"
|
||||
|
||||
// Hacky X11 header workaround
|
||||
#ifdef Opposite
|
||||
#undef Opposite
|
||||
#endif
|
||||
|
||||
namespace PPSSPP_VK {
|
||||
// Putting our own Vulkan function pointers in a namespace ensures that ppsspp_libretro.so doesn't collide with libvulkan.so.
|
||||
extern PFN_vkCreateInstance vkCreateInstance;
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
// Additionally, Common/Vulkan/* , including this file, are also licensed
|
||||
// under the public domain.
|
||||
|
||||
#include <set>
|
||||
#include <mutex>
|
||||
|
||||
#include "Common/Math/math_util.h"
|
||||
|
||||
#include "Common/Log.h"
|
||||
@@ -26,17 +29,41 @@
|
||||
|
||||
using namespace PPSSPP_VK;
|
||||
|
||||
// Global push buffer tracker for vulkan memory profiling.
|
||||
// Don't want to manually dig up all the active push buffers.
|
||||
static std::mutex g_pushBufferListMutex;
|
||||
static std::set<VulkanPushBuffer *> g_pushBuffers;
|
||||
|
||||
VulkanPushBuffer::VulkanPushBuffer(VulkanContext *vulkan, const char *name, size_t size, VkBufferUsageFlags usage, PushBufferType type)
|
||||
: vulkan_(vulkan), name_(name), size_(size), usage_(usage), type_(type) {
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(g_pushBufferListMutex);
|
||||
g_pushBuffers.insert(this);
|
||||
}
|
||||
|
||||
bool res = AddBuffer();
|
||||
_assert_(res);
|
||||
}
|
||||
|
||||
VulkanPushBuffer::~VulkanPushBuffer() {
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(g_pushBufferListMutex);
|
||||
g_pushBuffers.erase(this);
|
||||
}
|
||||
|
||||
_dbg_assert_(!writePtr_);
|
||||
_assert_(buffers_.empty());
|
||||
}
|
||||
|
||||
std::vector<VulkanPushBuffer *> VulkanPushBuffer::GetAllActive() {
|
||||
std::vector<VulkanPushBuffer *> buffers;
|
||||
std::lock_guard<std::mutex> guard(g_pushBufferListMutex);
|
||||
for (auto iter : g_pushBuffers) {
|
||||
buffers.push_back(iter);
|
||||
}
|
||||
return buffers;
|
||||
}
|
||||
|
||||
bool VulkanPushBuffer::AddBuffer() {
|
||||
BufInfo info;
|
||||
VkDevice device = vulkan_->GetDevice();
|
||||
@@ -122,6 +149,10 @@ size_t VulkanPushBuffer::GetTotalSize() const {
|
||||
return sum;
|
||||
}
|
||||
|
||||
size_t VulkanPushBuffer::GetTotalCapacity() const {
|
||||
return size_ * buffers_.size();
|
||||
}
|
||||
|
||||
void VulkanPushBuffer::Map() {
|
||||
_dbg_assert_(!writePtr_);
|
||||
VkResult res = vmaMapMemory(vulkan_->Allocator(), buffers_[buf_].allocation, (void **)(&writePtr_));
|
||||
|
||||
@@ -131,7 +131,10 @@ public:
|
||||
info->range = sizeof(T);
|
||||
}
|
||||
|
||||
size_t GetTotalSize() const;
|
||||
size_t GetTotalSize() const; // Used size
|
||||
size_t GetTotalCapacity() const;
|
||||
|
||||
static std::vector<VulkanPushBuffer *> GetAllActive();
|
||||
|
||||
private:
|
||||
bool AddBuffer();
|
||||
@@ -173,7 +176,7 @@ private:
|
||||
const char *tag_;
|
||||
VulkanContext *vulkan_ = nullptr;
|
||||
VkDescriptorPool descPool_ = VK_NULL_HANDLE;
|
||||
VkDescriptorPoolCreateInfo info_;
|
||||
VkDescriptorPoolCreateInfo info_{};
|
||||
std::vector<VkDescriptorPoolSize> sizes_;
|
||||
std::function<void()> clear_;
|
||||
uint32_t usage_ = 0;
|
||||
|
||||
@@ -884,7 +884,8 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR
|
||||
} else {
|
||||
curWidthRaw_ = vulkan_->GetBackbufferWidth();
|
||||
curHeightRaw_ = vulkan_->GetBackbufferHeight();
|
||||
if (g_display_rotation == DisplayRotation::ROTATE_90 || g_display_rotation == DisplayRotation::ROTATE_270) {
|
||||
if (g_display.rotation == DisplayRotation::ROTATE_90 ||
|
||||
g_display.rotation == DisplayRotation::ROTATE_270) {
|
||||
curWidth_ = curHeightRaw_;
|
||||
curHeight_ = curWidthRaw_;
|
||||
} else {
|
||||
|
||||
@@ -416,8 +416,10 @@ public:
|
||||
// These can be useful both when inspecting in RenderDoc, and when manually inspecting recorded commands
|
||||
// in the debugger.
|
||||
void DebugAnnotate(const char *annotation) {
|
||||
_dbg_assert_(curRenderStep_);
|
||||
VkRenderData data{ VKRRenderCommand::DEBUG_ANNOTATION };
|
||||
data.debugAnnotation.annotation = annotation;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
VkCommandBuffer GetInitCmd();
|
||||
|
||||
@@ -425,7 +425,7 @@ public:
|
||||
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
|
||||
|
||||
void SetScissorRect(int left, int top, int width, int height) override;
|
||||
void SetViewports(int count, Viewport *viewports) override;
|
||||
void SetViewport(const Viewport &viewport) override;
|
||||
void SetBlendFactor(float color[4]) override;
|
||||
void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override;
|
||||
|
||||
@@ -742,7 +742,9 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushBuffer *push, const Textur
|
||||
usageBits |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
|
||||
if (!vkTex_->CreateDirect(cmd, width_, height_, 1, mipLevels_, vulkanFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, usageBits)) {
|
||||
VkComponentMapping r8AsAlpha[4] = { VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_R };
|
||||
|
||||
if (!vkTex_->CreateDirect(cmd, width_, height_, 1, mipLevels_, vulkanFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, usageBits, desc.swizzle == TextureSwizzle::R8_AS_ALPHA ? r8AsAlpha : nullptr)) {
|
||||
ERROR_LOG(G3D, "Failed to create VulkanTexture: %dx%dx%d fmt %d, %d levels", width_, height_, depth_, (int)vulkanFormat, mipLevels_);
|
||||
return false;
|
||||
}
|
||||
@@ -1237,18 +1239,16 @@ void VKContext::SetScissorRect(int left, int top, int width, int height) {
|
||||
renderManager_.SetScissor(left, top, width, height);
|
||||
}
|
||||
|
||||
void VKContext::SetViewports(int count, Viewport *viewports) {
|
||||
if (count > 0) {
|
||||
// Ignore viewports more than the first.
|
||||
VkViewport viewport;
|
||||
viewport.x = viewports[0].TopLeftX;
|
||||
viewport.y = viewports[0].TopLeftY;
|
||||
viewport.width = viewports[0].Width;
|
||||
viewport.height = viewports[0].Height;
|
||||
viewport.minDepth = viewports[0].MinDepth;
|
||||
viewport.maxDepth = viewports[0].MaxDepth;
|
||||
renderManager_.SetViewport(viewport);
|
||||
}
|
||||
void VKContext::SetViewport(const Viewport &viewport) {
|
||||
// Ignore viewports more than the first.
|
||||
VkViewport vkViewport;
|
||||
vkViewport.x = viewport.TopLeftX;
|
||||
vkViewport.y = viewport.TopLeftY;
|
||||
vkViewport.width = viewport.Width;
|
||||
vkViewport.height = viewport.Height;
|
||||
vkViewport.minDepth = viewport.MinDepth;
|
||||
vkViewport.maxDepth = viewport.MaxDepth;
|
||||
renderManager_.SetViewport(vkViewport);
|
||||
}
|
||||
|
||||
void VKContext::SetBlendFactor(float color[4]) {
|
||||
|
||||
+7
-1
@@ -598,6 +598,11 @@ struct DeviceCaps {
|
||||
// Important: only write to the provided pointer, don't read from it.
|
||||
typedef std::function<bool(uint8_t *data, const uint8_t *initData, uint32_t w, uint32_t h, uint32_t d, uint32_t byteStride, uint32_t sliceByteStride)> TextureCallback;
|
||||
|
||||
enum class TextureSwizzle {
|
||||
DEFAULT,
|
||||
R8_AS_ALPHA,
|
||||
};
|
||||
|
||||
struct TextureDesc {
|
||||
TextureType type;
|
||||
DataFormat format;
|
||||
@@ -607,6 +612,7 @@ struct TextureDesc {
|
||||
int depth;
|
||||
int mipLevels;
|
||||
bool generateMips;
|
||||
TextureSwizzle swizzle;
|
||||
// Optional, for tracking memory usage and graphcis debuggers.
|
||||
const char *tag;
|
||||
// Does not take ownership over pointed-to data.
|
||||
@@ -733,7 +739,7 @@ public:
|
||||
|
||||
// Dynamic state
|
||||
virtual void SetScissorRect(int left, int top, int width, int height) = 0;
|
||||
virtual void SetViewports(int count, Viewport *viewports) = 0;
|
||||
virtual void SetViewport(const Viewport &viewport) = 0;
|
||||
virtual void SetBlendFactor(float color[4]) = 0;
|
||||
virtual void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) = 0;
|
||||
|
||||
|
||||
@@ -40,6 +40,10 @@ inline uint32_t RoundUpToPowerOf2(uint32_t v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
inline uint32_t RoundUpToPowerOf2(uint32_t v, uint32_t power) {
|
||||
return (v + power - 1) & ~(power - 1);
|
||||
}
|
||||
|
||||
inline uint32_t log2i(uint32_t val) {
|
||||
unsigned int ret = -1;
|
||||
while (val != 0) {
|
||||
|
||||
@@ -111,14 +111,14 @@ void DrawBuffer::Rect(float x, float y, float w, float h, uint32_t color, int al
|
||||
|
||||
void DrawBuffer::hLine(float x1, float y, float x2, uint32_t color) {
|
||||
// Round Y to the closest full pixel, since we're making it 1-pixel-thin.
|
||||
y -= fmodf(y, pixel_in_dps_y);
|
||||
Rect(x1, y, x2 - x1, pixel_in_dps_y, color);
|
||||
y -= fmodf(y, g_display.pixel_in_dps_y);
|
||||
Rect(x1, y, x2 - x1, g_display.pixel_in_dps_y, color);
|
||||
}
|
||||
|
||||
void DrawBuffer::vLine(float x, float y1, float y2, uint32_t color) {
|
||||
// Round X to the closest full pixel, since we're making it 1-pixel-thin.
|
||||
x -= fmodf(x, pixel_in_dps_x);
|
||||
Rect(x, y1, pixel_in_dps_x, y2 - y1, color);
|
||||
x -= fmodf(x, g_display.pixel_in_dps_x);
|
||||
Rect(x, y1, g_display.pixel_in_dps_x, y2 - y1, color);
|
||||
}
|
||||
|
||||
void DrawBuffer::RectVGradient(float x, float y, float w, float h, uint32_t colorTop, uint32_t colorBottom) {
|
||||
@@ -131,11 +131,11 @@ void DrawBuffer::RectVGradient(float x, float y, float w, float h, uint32_t colo
|
||||
}
|
||||
|
||||
void DrawBuffer::RectOutline(float x, float y, float w, float h, uint32_t color, int align) {
|
||||
hLine(x, y, x + w + pixel_in_dps_x, color);
|
||||
hLine(x, y + h, x + w + pixel_in_dps_x, color);
|
||||
hLine(x, y, x + w + g_display.pixel_in_dps_x, color);
|
||||
hLine(x, y + h, x + w + g_display.pixel_in_dps_x, color);
|
||||
|
||||
vLine(x, y, y + h + pixel_in_dps_y, color);
|
||||
vLine(x + w, y, y + h + pixel_in_dps_y, color);
|
||||
vLine(x, y, y + h + g_display.pixel_in_dps_y, color);
|
||||
vLine(x + w, y, y + h + g_display.pixel_in_dps_y, color);
|
||||
}
|
||||
|
||||
void DrawBuffer::MultiVGradient(float x, float y, float w, float h, const GradientStop *stops, int numStops) {
|
||||
|
||||
@@ -148,7 +148,7 @@ bool ManagedTexture::LoadFromFileData(const uint8_t *data, size_t dataSize, Imag
|
||||
bool ManagedTexture::LoadFromFile(const std::string &filename, ImageFileType type, bool generateMips) {
|
||||
generateMips_ = generateMips;
|
||||
size_t fileSize;
|
||||
uint8_t *buffer = VFSReadFile(filename.c_str(), &fileSize);
|
||||
uint8_t *buffer = g_VFS.ReadFile(filename.c_str(), &fileSize);
|
||||
if (!buffer) {
|
||||
filename_.clear();
|
||||
ERROR_LOG(IO, "Failed to read file '%s'", filename.c_str());
|
||||
|
||||
@@ -38,7 +38,7 @@ void TextDrawer::SetFontScale(float xscale, float yscale) {
|
||||
float TextDrawer::CalculateDPIScale() {
|
||||
if (ignoreGlobalDpi_)
|
||||
return dpiScale_;
|
||||
float scale = g_dpi_scale_y;
|
||||
float scale = g_display.dpi_scale_y;
|
||||
if (scale >= 1.0f) {
|
||||
scale = 1.0f;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,10 @@ TextDrawerAndroid::TextDrawerAndroid(Draw::DrawContext *draw) : TextDrawer(draw)
|
||||
ERROR_LOG(G3D, "Failed to find class: '%s'", textRendererClassName);
|
||||
}
|
||||
dpiScale_ = CalculateDPIScale();
|
||||
INFO_LOG(G3D, "Initializing TextDrawerAndroid with DPI scale %f", dpiScale_);
|
||||
|
||||
use4444Format_ = (draw->GetDataFormatSupport(Draw::DataFormat::R4G4B4A4_UNORM_PACK16) & Draw::FMT_TEXTURE) != 0;
|
||||
|
||||
INFO_LOG(G3D, "Initializing TextDrawerAndroid with DPI scale %f, use4444=%d", dpiScale_, (int)use4444Format_);
|
||||
}
|
||||
|
||||
TextDrawerAndroid::~TextDrawerAndroid() {
|
||||
@@ -244,7 +247,8 @@ void TextDrawerAndroid::DrawString(DrawBuffer &target, const char *str, float x,
|
||||
entry = iter->second.get();
|
||||
entry->lastUsedFrame = frameCount_;
|
||||
} else {
|
||||
DataFormat texFormat = Draw::DataFormat::R4G4B4A4_UNORM_PACK16;
|
||||
// Actually, I don't know why we don't always use R8_UNORM..
|
||||
DataFormat texFormat = use4444Format_ ? Draw::DataFormat::R4G4B4A4_UNORM_PACK16 : Draw::DataFormat::R8_UNORM;
|
||||
|
||||
entry = new TextStringEntry();
|
||||
|
||||
@@ -260,6 +264,7 @@ void TextDrawerAndroid::DrawString(DrawBuffer &target, const char *str, float x,
|
||||
desc.depth = 1;
|
||||
desc.mipLevels = 1;
|
||||
desc.generateMips = false;
|
||||
desc.swizzle = use4444Format_ ? Draw::TextureSwizzle::DEFAULT : Draw::TextureSwizzle::R8_AS_ALPHA,
|
||||
desc.tag = "TextDrawer";
|
||||
entry->texture = draw_->CreateTexture(desc);
|
||||
cache_[key] = std::unique_ptr<TextStringEntry>(entry);
|
||||
|
||||
@@ -40,6 +40,7 @@ private:
|
||||
jmethodID method_renderText;
|
||||
|
||||
uint32_t fontHash_;
|
||||
bool use4444Format_ = false;
|
||||
|
||||
std::map<uint32_t, AndroidFontEntry> fontMap_;
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
const char procfile[] = "/proc/cpuinfo";
|
||||
// https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-devices-system-cpu
|
||||
const char syscpupresentfile[] = "/sys/devices/system/cpu/present";
|
||||
const char firmwarefile[] = "/sys/firmware/devicetree/base/compatible";
|
||||
|
||||
class RiscVCPUInfoParser {
|
||||
public:
|
||||
@@ -49,9 +50,12 @@ public:
|
||||
int TotalLogicalCount();
|
||||
|
||||
std::string ISAString();
|
||||
bool FirmwareMatchesCompatible(const std::string &str);
|
||||
|
||||
private:
|
||||
std::vector<std::vector<std::string>> cores_;
|
||||
std::vector<std::string> firmware_;
|
||||
bool firmwareLoaded_ = false;
|
||||
};
|
||||
|
||||
RiscVCPUInfoParser::RiscVCPUInfoParser() {
|
||||
@@ -126,6 +130,25 @@ std::string RiscVCPUInfoParser::ISAString() {
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
bool RiscVCPUInfoParser::FirmwareMatchesCompatible(const std::string &str) {
|
||||
if (!firmwareLoaded_) {
|
||||
firmwareLoaded_ = true;
|
||||
|
||||
std::string data;
|
||||
if (!File::ReadFileToString(true, Path(firmwarefile), data))
|
||||
return false;
|
||||
|
||||
SplitString(data, '\0', firmware_);
|
||||
}
|
||||
|
||||
for (auto compatible : firmware_) {
|
||||
if (compatible == str)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool ExtensionSupported(unsigned long v, char c) {
|
||||
@@ -170,6 +193,12 @@ void CPUInfo::Detect()
|
||||
logical_cpu_count = 1;
|
||||
|
||||
truncate_cpy(cpu_string, parser.ISAString().c_str());
|
||||
|
||||
// A number of CPUs support a limited set of B. It's not all U74, so we use SOC for now...
|
||||
if (parser.FirmwareMatchesCompatible("starfive,jh7110")) {
|
||||
RiscV_Zba = true;
|
||||
RiscV_Zbb = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned long hwcap = getauxval(AT_HWCAP);
|
||||
@@ -183,12 +212,6 @@ void CPUInfo::Detect()
|
||||
// Let's assume for now...
|
||||
RiscV_Zicsr = RiscV_M && RiscV_A && RiscV_F && RiscV_D;
|
||||
|
||||
// A number of CPUs support a limited set of B.
|
||||
if (!strcmp(brand_string, "sifive,u74-mc")) {
|
||||
RiscV_Zba = true;
|
||||
RiscV_Zbb = true;
|
||||
}
|
||||
|
||||
#ifdef USE_CPU_FEATURES
|
||||
cpu_features::RiscvInfo info = cpu_features::GetRiscvInfo();
|
||||
CPU64bit = info.features.RV64I;
|
||||
|
||||
+22
-18
@@ -1,27 +1,13 @@
|
||||
#include <cstdio>
|
||||
|
||||
#include "Common/System/Display.h"
|
||||
#include "Common/Math/math_util.h"
|
||||
|
||||
int dp_xres;
|
||||
int dp_yres;
|
||||
|
||||
int pixel_xres;
|
||||
int pixel_yres;
|
||||
|
||||
float g_dpi = 1.0f; // will be overwritten with a value that makes sense.
|
||||
float g_dpi_scale_x = 1.0f;
|
||||
float g_dpi_scale_y = 1.0f;
|
||||
float g_dpi_scale_real_x = 1.0f;
|
||||
float g_dpi_scale_real_y = 1.0f;
|
||||
float pixel_in_dps_x = 1.0f;
|
||||
float pixel_in_dps_y = 1.0f;
|
||||
float display_hz = 60.0f;
|
||||
|
||||
DisplayRotation g_display_rotation;
|
||||
Lin::Matrix4x4 g_display_rot_matrix = Lin::Matrix4x4::identity();
|
||||
DisplayProperties g_display;
|
||||
|
||||
template<class T>
|
||||
void RotateRectToDisplayImpl(DisplayRect<T> &rect, T curRTWidth, T curRTHeight) {
|
||||
switch (g_display_rotation) {
|
||||
switch (g_display.rotation) {
|
||||
case DisplayRotation::ROTATE_180:
|
||||
rect.x = curRTWidth - rect.w - rect.x;
|
||||
rect.y = curRTHeight - rect.h - rect.y;
|
||||
@@ -62,3 +48,21 @@ void RotateRectToDisplay(DisplayRect<int> &rect, int curRTWidth, int curRTHeight
|
||||
void RotateRectToDisplay(DisplayRect<float> &rect, float curRTWidth, float curRTHeight) {
|
||||
RotateRectToDisplayImpl<float>(rect, curRTWidth, curRTHeight);
|
||||
}
|
||||
|
||||
DisplayProperties::DisplayProperties() {
|
||||
rot_matrix.setIdentity();
|
||||
}
|
||||
|
||||
void DisplayProperties::Print() {
|
||||
printf("dp_xres/yres: %d, %d\n", dp_xres, dp_yres);
|
||||
printf("pixel_xres/yres: %d, %d\n", pixel_xres, pixel_yres);
|
||||
|
||||
printf("dpi, x, y: %f, %f, %f\n", dpi, dpi_scale_x, dpi_scale_y);
|
||||
printf("pixel_in_dps: %f, %f\n", pixel_in_dps_x, pixel_in_dps_y);
|
||||
|
||||
printf("dpi_real: %f, %f\n", dpi_scale_real_x, dpi_scale_real_y);
|
||||
printf("display_hz: %f\n", display_hz);
|
||||
|
||||
printf("rotation: %d\n", (int)rotation);
|
||||
rot_matrix.print();
|
||||
}
|
||||
+28
-16
@@ -5,20 +5,6 @@
|
||||
// This is meant to be a framework for handling DPI scaling etc.
|
||||
// For now, it just consists of these ugly globals.
|
||||
|
||||
extern int dp_xres;
|
||||
extern int dp_yres;
|
||||
extern int pixel_xres;
|
||||
extern int pixel_yres;
|
||||
|
||||
extern float g_dpi;
|
||||
extern float g_dpi_scale_x;
|
||||
extern float g_dpi_scale_y;
|
||||
extern float g_dpi_scale_real_x;
|
||||
extern float g_dpi_scale_real_y;
|
||||
extern float pixel_in_dps_x;
|
||||
extern float pixel_in_dps_y;
|
||||
extern float display_hz;
|
||||
|
||||
// On some platforms (currently only Windows UWP) we need to manually rotate
|
||||
// our rendered output to match the display. Use these to do so.
|
||||
enum class DisplayRotation {
|
||||
@@ -28,8 +14,34 @@ enum class DisplayRotation {
|
||||
ROTATE_270,
|
||||
};
|
||||
|
||||
extern DisplayRotation g_display_rotation;
|
||||
extern Lin::Matrix4x4 g_display_rot_matrix;
|
||||
struct DisplayProperties {
|
||||
int dp_xres;
|
||||
int dp_yres;
|
||||
int pixel_xres;
|
||||
int pixel_yres;
|
||||
|
||||
float dpi = 1.0f; // will be overwritten with a value that makes sense.
|
||||
float dpi_scale_x = 1.0f;
|
||||
float dpi_scale_y = 1.0f;
|
||||
|
||||
// pixel_xres/yres in dps
|
||||
float pixel_in_dps_x = 1.0f;
|
||||
float pixel_in_dps_y = 1.0f;
|
||||
|
||||
// If DPI is overridden (like in small window mode), these are still the original DPI.
|
||||
float dpi_scale_real_x = 1.0f;
|
||||
float dpi_scale_real_y = 1.0f;
|
||||
|
||||
float display_hz = 60.0f;
|
||||
|
||||
DisplayRotation rotation;
|
||||
Lin::Matrix4x4 rot_matrix;
|
||||
|
||||
DisplayProperties();
|
||||
void Print();
|
||||
};
|
||||
|
||||
extern DisplayProperties g_display;
|
||||
|
||||
template<class T>
|
||||
struct DisplayRect {
|
||||
|
||||
+12
-12
@@ -15,7 +15,7 @@
|
||||
|
||||
UIContext::UIContext() {
|
||||
fontStyle_ = new UI::FontStyle();
|
||||
bounds_ = Bounds(0, 0, dp_xres, dp_yres);
|
||||
bounds_ = Bounds(0, 0, g_display.dp_xres, g_display.dp_yres);
|
||||
}
|
||||
|
||||
UIContext::~UIContext() {
|
||||
@@ -163,22 +163,22 @@ Bounds UIContext::GetLayoutBounds() const {
|
||||
void UIContext::ActivateTopScissor() {
|
||||
Bounds bounds;
|
||||
if (scissorStack_.size()) {
|
||||
float scale_x = pixel_in_dps_x;
|
||||
float scale_y = pixel_in_dps_y;
|
||||
float scale_x = g_display.pixel_in_dps_x;
|
||||
float scale_y = g_display.pixel_in_dps_y;
|
||||
bounds = scissorStack_.back();
|
||||
int x = floorf(scale_x * bounds.x);
|
||||
int y = floorf(scale_y * bounds.y);
|
||||
int w = std::max(0.0f, ceilf(scale_x * bounds.w));
|
||||
int h = std::max(0.0f, ceilf(scale_y * bounds.h));
|
||||
if (x < 0 || y < 0 || x + w > pixel_xres || y + h > pixel_yres) {
|
||||
if (x < 0 || y < 0 || x + w > g_display.pixel_xres || y + h > g_display.pixel_yres) {
|
||||
// This won't actually report outside a game, but we can try.
|
||||
DEBUG_LOG(G3D, "UI scissor out of bounds in %sScreen: %d,%d-%d,%d / %d,%d", screenTag_ ? screenTag_ : "N/A", x, y, w, h, pixel_xres, pixel_yres);
|
||||
DEBUG_LOG(G3D, "UI scissor out of bounds in %sScreen: %d,%d-%d,%d / %d,%d", screenTag_ ? screenTag_ : "N/A", x, y, w, h, g_display.pixel_xres, g_display.pixel_yres);
|
||||
if (x < 0) { w += x; x = 0; }
|
||||
if (y < 0) { h += y; y = 0; }
|
||||
if (x >= pixel_xres) { x = pixel_xres - 1; }
|
||||
if (y >= pixel_yres) { y = pixel_yres - 1; }
|
||||
if (x + w > pixel_xres) { w = std::min(w, pixel_xres - x); }
|
||||
if (y + w > pixel_yres) { h = std::min(h, pixel_yres - y); }
|
||||
if (x >= g_display.pixel_xres) { x = g_display.pixel_xres - 1; }
|
||||
if (y >= g_display.pixel_yres) { y = g_display.pixel_yres - 1; }
|
||||
if (x + w > g_display.pixel_xres) { w = std::min(w, g_display.pixel_xres - x); }
|
||||
if (y + w > g_display.pixel_yres) { h = std::min(h, g_display.pixel_yres - y); }
|
||||
if (w == 0) w = 1;
|
||||
if (h == 0) h = 1;
|
||||
draw_->SetScissorRect(x, y, w, h);
|
||||
@@ -190,7 +190,7 @@ void UIContext::ActivateTopScissor() {
|
||||
}
|
||||
} else {
|
||||
// Avoid rounding errors
|
||||
draw_->SetScissorRect(0, 0, pixel_xres, pixel_yres);
|
||||
draw_->SetScissorRect(0, 0, g_display.pixel_xres, g_display.pixel_yres);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,8 +351,8 @@ Bounds UIContext::TransformBounds(const Bounds &bounds) {
|
||||
Bounds translated = bounds.Offset(t.translate.x, t.translate.y);
|
||||
|
||||
// Scale around the center as the origin.
|
||||
float scaledX = (translated.x - dp_xres * 0.5f) * t.scale.x + dp_xres * 0.5f;
|
||||
float scaledY = (translated.y - dp_yres * 0.5f) * t.scale.y + dp_yres * 0.5f;
|
||||
float scaledX = (translated.x - g_display.dp_xres * 0.5f) * t.scale.x + g_display.dp_xres * 0.5f;
|
||||
float scaledY = (translated.y - g_display.dp_yres * 0.5f) * t.scale.y + g_display.dp_yres * 0.5f;
|
||||
|
||||
return Bounds(scaledX, scaledY, translated.w * t.scale.x, translated.h * t.scale.y);
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ void ScreenManager::deviceRestored() {
|
||||
}
|
||||
|
||||
void ScreenManager::resized() {
|
||||
INFO_LOG(SYSTEM, "ScreenManager::resized(dp: %dx%d)", dp_xres, dp_yres);
|
||||
INFO_LOG(SYSTEM, "ScreenManager::resized(dp: %dx%d)", g_display.dp_xres, g_display.dp_yres);
|
||||
std::lock_guard<std::recursive_mutex> guard(inputLock_);
|
||||
// Have to notify the whole stack, otherwise there will be problems when going back
|
||||
// to non-top screens.
|
||||
|
||||
+11
-11
@@ -24,7 +24,7 @@ UIScreen::~UIScreen() {
|
||||
}
|
||||
|
||||
bool UIScreen::UseVerticalLayout() const {
|
||||
return dp_yres > dp_xres * 1.1f;
|
||||
return g_display.dp_yres > g_display.dp_xres * 1.1f;
|
||||
}
|
||||
|
||||
void UIScreen::DoRecreateViews() {
|
||||
@@ -98,12 +98,12 @@ void UIScreen::preRender() {
|
||||
Draw::Viewport viewport;
|
||||
viewport.TopLeftX = 0;
|
||||
viewport.TopLeftY = 0;
|
||||
viewport.Width = pixel_xres;
|
||||
viewport.Height = pixel_yres;
|
||||
viewport.Width = g_display.pixel_xres;
|
||||
viewport.Height = g_display.pixel_yres;
|
||||
viewport.MaxDepth = 1.0;
|
||||
viewport.MinDepth = 0.0;
|
||||
draw->SetViewports(1, &viewport);
|
||||
draw->SetTargetSize(pixel_xres, pixel_yres);
|
||||
draw->SetViewport(viewport);
|
||||
draw->SetTargetSize(g_display.pixel_xres, g_display.pixel_yres);
|
||||
}
|
||||
|
||||
void UIScreen::postRender() {
|
||||
@@ -141,8 +141,8 @@ TouchInput UIScreen::transformTouch(const TouchInput &touch) {
|
||||
float x = touch.x - translation_.x;
|
||||
float y = touch.y - translation_.y;
|
||||
// Scale around the center as the origin.
|
||||
updated.x = (x - dp_xres * 0.5f) / scale_.x + dp_xres * 0.5f;
|
||||
updated.y = (y - dp_yres * 0.5f) / scale_.y + dp_yres * 0.5f;
|
||||
updated.x = (x - g_display.dp_xres * 0.5f) / scale_.x + g_display.dp_xres * 0.5f;
|
||||
updated.y = (y - g_display.dp_yres * 0.5f) / scale_.y + g_display.dp_yres * 0.5f;
|
||||
|
||||
return updated;
|
||||
}
|
||||
@@ -280,14 +280,14 @@ void PopupScreen::update() {
|
||||
scale_.y = 0.9f + animatePos * 0.1f;
|
||||
|
||||
if (hasPopupOrigin_) {
|
||||
float xoff = popupOrigin_.x - dp_xres / 2;
|
||||
float yoff = popupOrigin_.y - dp_yres / 2;
|
||||
float xoff = popupOrigin_.x - g_display.dp_xres / 2;
|
||||
float yoff = popupOrigin_.y - g_display.dp_yres / 2;
|
||||
|
||||
// Pull toward the origin a bit.
|
||||
translation_.x = xoff * (1.0f - animatePos) * 0.2f;
|
||||
translation_.y = yoff * (1.0f - animatePos) * 0.2f;
|
||||
} else {
|
||||
translation_.y = -dp_yres * (1.0f - animatePos) * 0.2f;
|
||||
translation_.y = -g_display.dp_yres * (1.0f - animatePos) * 0.2f;
|
||||
}
|
||||
} else {
|
||||
alpha_ = 1.0f;
|
||||
@@ -333,7 +333,7 @@ void PopupScreen::CreateViews() {
|
||||
box_->SetBG(dc.theme->popupStyle.background);
|
||||
box_->SetHasDropShadow(hasDropShadow_);
|
||||
// Since we scale a bit, make the dropshadow bleed past the edges.
|
||||
box_->SetDropShadowExpand(std::max(dp_xres, dp_yres));
|
||||
box_->SetDropShadowExpand(std::max(g_display.dp_xres, g_display.dp_yres));
|
||||
box_->SetSpacing(0.0f);
|
||||
|
||||
View *title = new PopupHeader(title_);
|
||||
|
||||
+5
-24
@@ -150,10 +150,11 @@ void InitVROnAndroid(void* vm, void* activity, const char* system, int version,
|
||||
//Set platform flags
|
||||
if (strcmp(vendor, "PICO") == 0) {
|
||||
VR_SetPlatformFLag(VR_PLATFORM_CONTROLLER_PICO, true);
|
||||
VR_SetPlatformFLag(VR_PLATFORM_INSTANCE_EXT, true);
|
||||
VR_SetPlatformFLag(VR_PLATFORM_EXTENSION_INSTANCE, true);
|
||||
} else if ((strcmp(vendor, "META") == 0) || (strcmp(vendor, "OCULUS") == 0)) {
|
||||
VR_SetPlatformFLag(VR_PLATFORM_CONTROLLER_QUEST, true);
|
||||
VR_SetPlatformFLag(VR_PLATFORM_PERFORMANCE_EXT, true);
|
||||
VR_SetPlatformFLag(VR_PLATFORM_EXTENSION_FOVEATION, true);
|
||||
VR_SetPlatformFLag(VR_PLATFORM_EXTENSION_PERFORMANCE, true);
|
||||
}
|
||||
VR_SetPlatformFLag(VR_PLATFORM_RENDERER_VULKAN, (GPUBackend)g_Config.iGPUBackend == GPUBackend::VULKAN);
|
||||
|
||||
@@ -315,7 +316,7 @@ void UpdateVRInput(bool haptics, float dp_xscale, float dp_yscale) {
|
||||
}
|
||||
|
||||
// Head control
|
||||
if (g_Config.iHeadRotation) {
|
||||
if (g_Config.bHeadRotationEnabled) {
|
||||
float pitch = -VR_GetHMDAngles().x;
|
||||
float yaw = -VR_GetHMDAngles().y;
|
||||
bool disable = vrFlatForced || appMode == VR_MENU_MODE;
|
||||
@@ -342,25 +343,6 @@ void UpdateVRInput(bool haptics, float dp_xscale, float dp_yscale) {
|
||||
float limit = isVR ? g_Config.fHeadRotationScale : 20;
|
||||
keyInput.deviceId = DEVICE_ID_XR_HMD;
|
||||
|
||||
// vertical rotations
|
||||
if (g_Config.iHeadRotation == 2) {
|
||||
//up
|
||||
activate = !disable && pitch > limit;
|
||||
keyInput.flags = activate ? KEY_DOWN : KEY_UP;
|
||||
keyInput.keyCode = NKCODE_EXT_ROTATION_UP;
|
||||
if (hmdMotion[0] != activate) NativeKey(keyInput);
|
||||
if (isVR && activate) hmdMotionDiff[0] -= limit;
|
||||
hmdMotion[0] = activate;
|
||||
|
||||
//down
|
||||
activate = !disable && pitch < -limit;
|
||||
keyInput.flags = activate ? KEY_DOWN : KEY_UP;
|
||||
keyInput.keyCode = NKCODE_EXT_ROTATION_DOWN;
|
||||
if (hmdMotion[1] != activate) NativeKey(keyInput);
|
||||
if (isVR && activate) hmdMotionDiff[0] += limit;
|
||||
hmdMotion[1] = activate;
|
||||
}
|
||||
|
||||
//left
|
||||
activate = !disable && yaw < -limit;
|
||||
keyInput.flags = activate ? KEY_DOWN : KEY_UP;
|
||||
@@ -690,8 +672,7 @@ bool StartVRRender() {
|
||||
float mRoll = mz * ToRadians(rotation.z);
|
||||
|
||||
// use in-game camera interpolated rotation
|
||||
if (g_Config.iHeadRotation >= 2) mPitch = -mx * ToRadians(hmdMotionDiffLast[0]); // vertical
|
||||
if (g_Config.iHeadRotation >= 1) mYaw = -my * ToRadians(hmdMotionDiffLast[1]); // horizontal
|
||||
if (g_Config.bHeadRotationEnabled) mYaw = -my * ToRadians(hmdMotionDiffLast[1]); // horizontal
|
||||
|
||||
// create updated quaternion
|
||||
XrQuaternionf pitch = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, mPitch);
|
||||
|
||||
+13
-4
@@ -49,10 +49,19 @@ void VR_Init( void* system, const char* name, int version ) {
|
||||
}
|
||||
extensions.push_back(XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME);
|
||||
#ifdef ANDROID
|
||||
if (VR_GetPlatformFlag(VR_PLATFORM_INSTANCE_EXT)) {
|
||||
if (VR_GetPlatformFlag(VR_PLATFORM_EXTENSION_FOVEATION)) {
|
||||
extensions.push_back(XR_FB_SWAPCHAIN_UPDATE_STATE_EXTENSION_NAME);
|
||||
extensions.push_back(XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME);
|
||||
extensions.push_back(XR_FB_FOVEATION_EXTENSION_NAME);
|
||||
extensions.push_back(XR_FB_FOVEATION_CONFIGURATION_EXTENSION_NAME);
|
||||
if (VR_GetPlatformFlag(VR_PLATFORM_RENDERER_VULKAN)) {
|
||||
extensions.push_back(XR_FB_SWAPCHAIN_UPDATE_STATE_VULKAN_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
if (VR_GetPlatformFlag(VR_PLATFORM_EXTENSION_INSTANCE)) {
|
||||
extensions.push_back(XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME);
|
||||
}
|
||||
if (VR_GetPlatformFlag(VR_PLATFORM_PERFORMANCE_EXT)) {
|
||||
if (VR_GetPlatformFlag(VR_PLATFORM_EXTENSION_PERFORMANCE)) {
|
||||
extensions.push_back(XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME);
|
||||
extensions.push_back(XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME);
|
||||
}
|
||||
@@ -80,7 +89,7 @@ void VR_Init( void* system, const char* name, int version ) {
|
||||
|
||||
#ifdef ANDROID
|
||||
XrInstanceCreateInfoAndroidKHR instanceCreateInfoAndroid = {XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR};
|
||||
if (VR_GetPlatformFlag(VR_PLATFORM_INSTANCE_EXT)) {
|
||||
if (VR_GetPlatformFlag(VR_PLATFORM_EXTENSION_INSTANCE)) {
|
||||
ovrJava* java = (ovrJava*)system;
|
||||
instanceCreateInfoAndroid.applicationVM = java->Vm;
|
||||
instanceCreateInfoAndroid.applicationActivity = java->ActivityObject;
|
||||
@@ -182,7 +191,7 @@ void VR_EnterVR( engine_t* engine, XrGraphicsBindingVulkanKHR* graphicsBindingVu
|
||||
graphicsBindingGL.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR;
|
||||
graphicsBindingGL.next = NULL;
|
||||
graphicsBindingGL.display = eglGetCurrentDisplay();
|
||||
graphicsBindingGL.config = eglGetCurrentSurface(EGL_DRAW);
|
||||
graphicsBindingGL.config = NULL;
|
||||
graphicsBindingGL.context = eglGetCurrentContext();
|
||||
sessionCreateInfo.next = &graphicsBindingGL;
|
||||
#else
|
||||
|
||||
+3
-2
@@ -120,8 +120,9 @@ typedef struct {
|
||||
enum VRPlatformFlag {
|
||||
VR_PLATFORM_CONTROLLER_PICO,
|
||||
VR_PLATFORM_CONTROLLER_QUEST,
|
||||
VR_PLATFORM_INSTANCE_EXT,
|
||||
VR_PLATFORM_PERFORMANCE_EXT,
|
||||
VR_PLATFORM_EXTENSION_FOVEATION,
|
||||
VR_PLATFORM_EXTENSION_INSTANCE,
|
||||
VR_PLATFORM_EXTENSION_PERFORMANCE,
|
||||
VR_PLATFORM_RENDERER_VULKAN,
|
||||
VR_PLATFORM_TRACKING_FLOOR,
|
||||
VR_PLATFORM_MAX
|
||||
|
||||
@@ -105,6 +105,15 @@ static bool ovrFramebuffer_CreateGLES(XrSession session, ovrFramebuffer* frameBu
|
||||
swapChainCreateInfo.mipCount = 1;
|
||||
swapChainCreateInfo.arraySize = multiview ? 2 : 1;
|
||||
|
||||
#ifdef ANDROID
|
||||
if (VR_GetPlatformFlag(VR_PLATFORM_EXTENSION_FOVEATION)) {
|
||||
XrSwapchainCreateInfoFoveationFB swapChainFoveationCreateInfo;
|
||||
memset(&swapChainFoveationCreateInfo, 0, sizeof(swapChainFoveationCreateInfo));
|
||||
swapChainFoveationCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB;
|
||||
swapChainCreateInfo.next = &swapChainFoveationCreateInfo;
|
||||
}
|
||||
#endif
|
||||
|
||||
frameBuffer->ColorSwapChain.Width = swapChainCreateInfo.width;
|
||||
frameBuffer->ColorSwapChain.Height = swapChainCreateInfo.height;
|
||||
|
||||
@@ -198,6 +207,15 @@ static bool ovrFramebuffer_CreateVK(XrSession session, ovrFramebuffer* frameBuff
|
||||
swapChainCreateInfo.mipCount = 1;
|
||||
swapChainCreateInfo.arraySize = multiview ? 2 : 1;
|
||||
|
||||
#ifdef ANDROID
|
||||
if (VR_GetPlatformFlag(VR_PLATFORM_EXTENSION_FOVEATION)) {
|
||||
XrSwapchainCreateInfoFoveationFB swapChainFoveationCreateInfo;
|
||||
memset(&swapChainFoveationCreateInfo, 0, sizeof(swapChainFoveationCreateInfo));
|
||||
swapChainFoveationCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB;
|
||||
swapChainCreateInfo.next = &swapChainFoveationCreateInfo;
|
||||
}
|
||||
#endif
|
||||
|
||||
frameBuffer->ColorSwapChain.Width = swapChainCreateInfo.width;
|
||||
frameBuffer->ColorSwapChain.Height = swapChainCreateInfo.height;
|
||||
|
||||
@@ -407,6 +425,47 @@ void ovrRenderer_MouseCursor(ovrRenderer* renderer, int x, int y, int sx, int sy
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
void ovrRenderer_SetFoveation(XrInstance* instance, XrSession* session, ovrRenderer* renderer, XrFoveationLevelFB level, float verticalOffset, XrFoveationDynamicFB dynamic) {
|
||||
PFN_xrCreateFoveationProfileFB pfnCreateFoveationProfileFB;
|
||||
OXR(xrGetInstanceProcAddr(*instance, "xrCreateFoveationProfileFB", (PFN_xrVoidFunction*)(&pfnCreateFoveationProfileFB)));
|
||||
|
||||
PFN_xrDestroyFoveationProfileFB pfnDestroyFoveationProfileFB;
|
||||
OXR(xrGetInstanceProcAddr(*instance, "xrDestroyFoveationProfileFB", (PFN_xrVoidFunction*)(&pfnDestroyFoveationProfileFB)));
|
||||
|
||||
PFN_xrUpdateSwapchainFB pfnUpdateSwapchainFB;
|
||||
OXR(xrGetInstanceProcAddr(*instance, "xrUpdateSwapchainFB", (PFN_xrVoidFunction*)(&pfnUpdateSwapchainFB)));
|
||||
|
||||
int instances = renderer->Multiview ? 1 : ovrMaxNumEyes;
|
||||
for (int eye = 0; eye < instances; eye++) {
|
||||
XrFoveationLevelProfileCreateInfoFB levelProfileCreateInfo;
|
||||
memset(&levelProfileCreateInfo, 0, sizeof(levelProfileCreateInfo));
|
||||
levelProfileCreateInfo.type = XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB;
|
||||
levelProfileCreateInfo.level = level;
|
||||
levelProfileCreateInfo.verticalOffset = verticalOffset;
|
||||
levelProfileCreateInfo.dynamic = dynamic;
|
||||
|
||||
XrFoveationProfileCreateInfoFB profileCreateInfo;
|
||||
memset(&profileCreateInfo, 0, sizeof(profileCreateInfo));
|
||||
profileCreateInfo.type = XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB;
|
||||
profileCreateInfo.next = &levelProfileCreateInfo;
|
||||
|
||||
XrFoveationProfileFB foveationProfile;
|
||||
|
||||
pfnCreateFoveationProfileFB(*session, &profileCreateInfo, &foveationProfile);
|
||||
|
||||
XrSwapchainStateFoveationFB foveationUpdateState;
|
||||
memset(&foveationUpdateState, 0, sizeof(foveationUpdateState));
|
||||
foveationUpdateState.type = XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB;
|
||||
foveationUpdateState.profile = foveationProfile;
|
||||
|
||||
pfnUpdateSwapchainFB(renderer->FrameBuffer[eye].ColorSwapChain.Handle, (XrSwapchainStateBaseHeaderFB*)(&foveationUpdateState));
|
||||
|
||||
pfnDestroyFoveationProfileFB(foveationProfile);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
@@ -456,7 +515,7 @@ void ovrApp_HandleSessionStateChanges(ovrApp* app, XrSessionState state) {
|
||||
app->SessionActive = (result == XR_SUCCESS);
|
||||
|
||||
#ifdef ANDROID
|
||||
if (app->SessionActive && VR_GetPlatformFlag(VR_PLATFORM_PERFORMANCE_EXT)) {
|
||||
if (app->SessionActive && VR_GetPlatformFlag(VR_PLATFORM_EXTENSION_PERFORMANCE)) {
|
||||
XrPerfSettingsLevelEXT cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_PERFORMANCE_MAX_EXT;
|
||||
XrPerfSettingsLevelEXT gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_PERFORMANCE_MAX_EXT;
|
||||
|
||||
|
||||
@@ -13,3 +13,6 @@ void* ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer);
|
||||
void ovrRenderer_Create(XrSession session, ovrRenderer* renderer, int width, int height, bool multiview, void* vulkanContext);
|
||||
void ovrRenderer_Destroy(ovrRenderer* renderer);
|
||||
void ovrRenderer_MouseCursor(ovrRenderer* renderer, int x, int y, int sx, int sy);
|
||||
#ifdef ANDROID
|
||||
void ovrRenderer_SetFoveation(XrInstance* instance, XrSession* session, ovrRenderer* renderer, XrFoveationLevelFB level, float verticalOffset, XrFoveationDynamicFB dynamic);
|
||||
#endif
|
||||
@@ -216,6 +216,11 @@ void VR_InitRenderer( engine_t* engine, bool multiview ) {
|
||||
engine->appState.ViewConfigurationView[0].recommendedImageRectWidth,
|
||||
engine->appState.ViewConfigurationView[0].recommendedImageRectHeight,
|
||||
multiview, vulkanContext);
|
||||
#ifdef ANDROID
|
||||
if (VR_GetPlatformFlag(VR_PLATFORM_EXTENSION_FOVEATION)) {
|
||||
ovrRenderer_SetFoveation(&engine->appState.Instance, &engine->appState.Session, &engine->appState.Renderer, XR_FOVEATION_LEVEL_HIGH_TOP_FB, 0, XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_FB);
|
||||
}
|
||||
#endif
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "Common/Log.h"
|
||||
#include "Common/Data/Format/IniFile.h"
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Core/Compatibility.h"
|
||||
#include "Core/Config.h"
|
||||
@@ -38,7 +39,7 @@ void Compatibility::Load(const std::string &gameID) {
|
||||
{
|
||||
IniFile compat;
|
||||
// This loads from assets.
|
||||
if (compat.LoadFromVFS("compat.ini")) {
|
||||
if (compat.LoadFromVFS(g_VFS, "compat.ini")) {
|
||||
CheckSettings(compat, gameID);
|
||||
}
|
||||
}
|
||||
@@ -55,7 +56,7 @@ void Compatibility::Load(const std::string &gameID) {
|
||||
{
|
||||
IniFile compat;
|
||||
// This loads from assets.
|
||||
if (compat.LoadFromVFS("compatvr.ini")) {
|
||||
if (compat.LoadFromVFS(g_VFS, "compatvr.ini")) {
|
||||
CheckVRSettings(compat, gameID);
|
||||
}
|
||||
}
|
||||
@@ -127,6 +128,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
|
||||
CheckSetting(iniFile, gameID, "RockmanDash2SoundFix", &flags_.RockmanDash2SoundFix);
|
||||
CheckSetting(iniFile, gameID, "ReadbackDepth", &flags_.ReadbackDepth);
|
||||
CheckSetting(iniFile, gameID, "BlockTransferDepth", &flags_.BlockTransferDepth);
|
||||
CheckSetting(iniFile, gameID, "DaxterRotatedAnalogStick", &flags_.DaxterRotatedAnalogStick);
|
||||
}
|
||||
|
||||
void Compatibility::CheckVRSettings(IniFile &iniFile, const std::string &gameID) {
|
||||
|
||||
@@ -98,6 +98,7 @@ struct CompatFlags {
|
||||
bool RockmanDash2SoundFix;
|
||||
bool ReadbackDepth;
|
||||
bool BlockTransferDepth;
|
||||
bool DaxterRotatedAnalogStick;
|
||||
};
|
||||
|
||||
struct VRCompat {
|
||||
|
||||
+20
-12
@@ -39,6 +39,7 @@
|
||||
#include "Common/Data/Text/Parsers.h"
|
||||
#include "Common/CPUDetect.h"
|
||||
#include "Common/File/FileUtil.h"
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
#include "Common/LogManager.h"
|
||||
#include "Common/OSVersion.h"
|
||||
#include "Common/System/Display.h"
|
||||
@@ -436,7 +437,7 @@ const char *DefaultLangRegion() {
|
||||
} else if (langRegion.length() >= 3) {
|
||||
// Don't give up. Let's try a fuzzy match - so nl_BE can match nl_NL.
|
||||
IniFile mapping;
|
||||
mapping.LoadFromVFS("langregion.ini");
|
||||
mapping.LoadFromVFS(g_VFS, "langregion.ini");
|
||||
std::vector<std::string> keys;
|
||||
mapping.GetKeys("LangRegionNames", keys);
|
||||
|
||||
@@ -876,7 +877,6 @@ static const ConfigSetting graphicsSettings[] = {
|
||||
ReportedConfigSetting("ReplaceTextures", &g_Config.bReplaceTextures, true, true, true),
|
||||
ReportedConfigSetting("SaveNewTextures", &g_Config.bSaveNewTextures, false, true, true),
|
||||
ConfigSetting("IgnoreTextureFilenames", &g_Config.bIgnoreTextureFilenames, false, true, true),
|
||||
ConfigSetting("ReplaceTexturesAllowLate", &g_Config.bReplaceTexturesAllowLate, true, true, true),
|
||||
|
||||
ReportedConfigSetting("TexScalingLevel", &g_Config.iTexScalingLevel, 1, true, true),
|
||||
ReportedConfigSetting("TexScalingType", &g_Config.iTexScalingType, 0, true, true),
|
||||
@@ -1152,8 +1152,8 @@ static const ConfigSetting vrSettings[] = {
|
||||
ConfigSetting("VRHeadUpDisplayScale", &g_Config.fHeadUpDisplayScale, 0.3f),
|
||||
ConfigSetting("VRMotionLength", &g_Config.fMotionLength, 0.5f),
|
||||
ConfigSetting("VRHeadRotationScale", &g_Config.fHeadRotationScale, 5.0f),
|
||||
ConfigSetting("VRHeadRotationEnabled", &g_Config.bHeadRotationEnabled, false),
|
||||
ConfigSetting("VRHeadRotationSmoothing", &g_Config.bHeadRotationSmoothing, false),
|
||||
ConfigSetting("VRHeadRotation", &g_Config.iHeadRotation, 0),
|
||||
};
|
||||
|
||||
struct ConfigSectionSettings {
|
||||
@@ -1222,7 +1222,7 @@ Config::~Config() {
|
||||
|
||||
void Config::LoadLangValuesMapping() {
|
||||
IniFile mapping;
|
||||
mapping.LoadFromVFS("langregion.ini");
|
||||
mapping.LoadFromVFS(g_VFS, "langregion.ini");
|
||||
std::vector<std::string> keys;
|
||||
mapping.GetKeys("LangRegionNames", keys);
|
||||
|
||||
@@ -1558,8 +1558,8 @@ bool Config::Save(const char *saveReason) {
|
||||
|
||||
void Config::PostLoadCleanup(bool gameSpecific) {
|
||||
// Override ppsspp.ini JIT value to prevent crashing
|
||||
if (DefaultCpuCore() != (int)CPUCore::JIT && g_Config.iCpuCore == (int)CPUCore::JIT) {
|
||||
jitForcedOff = true;
|
||||
jitForcedOff = DefaultCpuCore() != (int)CPUCore::JIT && g_Config.iCpuCore == (int)CPUCore::JIT;
|
||||
if (jitForcedOff) {
|
||||
g_Config.iCpuCore = (int)CPUCore::IR_JIT;
|
||||
}
|
||||
|
||||
@@ -1589,19 +1589,27 @@ void Config::PostLoadCleanup(bool gameSpecific) {
|
||||
|
||||
void Config::PreSaveCleanup(bool gameSpecific) {
|
||||
if (jitForcedOff) {
|
||||
// if JIT has been forced off, we don't want to screw up the user's ppsspp.ini
|
||||
g_Config.iCpuCore = (int)CPUCore::JIT;
|
||||
// If we forced jit off and it's still set to IR, change it back to jit.
|
||||
if (g_Config.iCpuCore == (int)CPUCore::IR_JIT)
|
||||
g_Config.iCpuCore = (int)CPUCore::JIT;
|
||||
}
|
||||
}
|
||||
|
||||
void Config::PostSaveCleanup(bool gameSpecific) {
|
||||
if (jitForcedOff) {
|
||||
// force JIT off again just in case Config::Save() is called without exiting PPSSPP
|
||||
if (g_Config.iCpuCore != (int)CPUCore::INTERPRETER)
|
||||
// Force JIT off again just in case Config::Save() is called without exiting PPSSPP.
|
||||
if (g_Config.iCpuCore == (int)CPUCore::JIT)
|
||||
g_Config.iCpuCore = (int)CPUCore::IR_JIT;
|
||||
}
|
||||
}
|
||||
|
||||
void Config::NotifyUpdatedCpuCore() {
|
||||
if (jitForcedOff && g_Config.iCpuCore == (int)CPUCore::IR_JIT) {
|
||||
// No longer forced off, the user set it to IR jit.
|
||||
jitForcedOff = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Use for debugging the version check without messing with the server
|
||||
#if 0
|
||||
#define PPSSPP_GIT_VERSION "v0.0.1-gaaaaaaaaa"
|
||||
@@ -1924,8 +1932,8 @@ bool Config::loadGameConfig(const std::string &pGameId, const std::string &title
|
||||
});
|
||||
|
||||
KeyMap::LoadFromIni(iniFile);
|
||||
|
||||
if (!appendedConfigFileName_.ToString().empty() &&
|
||||
|
||||
if (!appendedConfigFileName_.ToString().empty() &&
|
||||
std::find(appendedConfigUpdatedGames_.begin(), appendedConfigUpdatedGames_.end(), pGameId) == appendedConfigUpdatedGames_.end()) {
|
||||
|
||||
LoadAppendedConfig();
|
||||
|
||||
+3
-2
@@ -213,7 +213,6 @@ public:
|
||||
bool bReplaceTextures;
|
||||
bool bSaveNewTextures;
|
||||
bool bIgnoreTextureFilenames;
|
||||
bool bReplaceTexturesAllowLate;
|
||||
int iTexScalingLevel; // 0 = auto, 1 = off, 2 = 2x, ..., 5 = 5x
|
||||
int iTexScalingType; // 0 = xBRZ, 1 = Hybrid
|
||||
bool bTexDeposterize;
|
||||
@@ -471,8 +470,8 @@ public:
|
||||
float fHeadUpDisplayScale;
|
||||
float fMotionLength;
|
||||
float fHeadRotationScale;
|
||||
bool bHeadRotationEnabled;
|
||||
bool bHeadRotationSmoothing;
|
||||
int iHeadRotation;
|
||||
|
||||
// Debugger
|
||||
int iDisasmWindowX;
|
||||
@@ -568,6 +567,8 @@ public:
|
||||
bool LoadAppendedConfig();
|
||||
void SetAppendedConfigIni(const Path &path);
|
||||
|
||||
void NotifyUpdatedCpuCore();
|
||||
|
||||
protected:
|
||||
void LoadStandardControllerIni();
|
||||
void LoadLangValuesMapping();
|
||||
|
||||
+26
-26
@@ -154,8 +154,8 @@ bool Core_GetPowerSaving() {
|
||||
|
||||
static bool IsWindowSmall(int pixelWidth, int pixelHeight) {
|
||||
// Can't take this from config as it will not be set if windows is maximized.
|
||||
int w = (int)(pixelWidth * g_dpi_scale_x);
|
||||
int h = (int)(pixelHeight * g_dpi_scale_y);
|
||||
int w = (int)(pixelWidth * g_display.dpi_scale_x);
|
||||
int h = (int)(pixelHeight * g_display.dpi_scale_y);
|
||||
return g_Config.IsPortrait() ? (h < 480 + 80) : (w < 480 + 80);
|
||||
}
|
||||
|
||||
@@ -163,42 +163,42 @@ static bool IsWindowSmall(int pixelWidth, int pixelHeight) {
|
||||
bool UpdateScreenScale(int width, int height) {
|
||||
bool smallWindow;
|
||||
#if defined(USING_QT_UI)
|
||||
g_dpi = System_GetPropertyFloat(SYSPROP_DISPLAY_DPI);
|
||||
g_display.dpi = System_GetPropertyFloat(SYSPROP_DISPLAY_DPI);
|
||||
float g_logical_dpi = System_GetPropertyFloat(SYSPROP_DISPLAY_LOGICAL_DPI);
|
||||
g_dpi_scale_x = g_logical_dpi / g_dpi;
|
||||
g_dpi_scale_y = g_logical_dpi / g_dpi;
|
||||
g_display.dpi_scale_x = g_logical_dpi / g_display.dpi;
|
||||
g_display.dpi_scale_y = g_logical_dpi / g_display.dpi;
|
||||
#elif PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
|
||||
g_dpi = System_GetPropertyFloat(SYSPROP_DISPLAY_DPI);
|
||||
g_dpi_scale_x = 96.0f / g_dpi;
|
||||
g_dpi_scale_y = 96.0f / g_dpi;
|
||||
g_display.dpi = System_GetPropertyFloat(SYSPROP_DISPLAY_DPI);
|
||||
g_display.dpi_scale_x = 96.0f / g_display.dpi;
|
||||
g_display.dpi_scale_y = 96.0f / g_display.dpi;
|
||||
#else
|
||||
g_dpi = 96.0f;
|
||||
g_dpi_scale_x = 1.0f;
|
||||
g_dpi_scale_y = 1.0f;
|
||||
g_display.dpi = 96.0f;
|
||||
g_display.dpi_scale_x = 1.0f;
|
||||
g_display.dpi_scale_y = 1.0f;
|
||||
#endif
|
||||
g_dpi_scale_real_x = g_dpi_scale_x;
|
||||
g_dpi_scale_real_y = g_dpi_scale_y;
|
||||
g_display.dpi_scale_real_x = g_display.dpi_scale_x;
|
||||
g_display.dpi_scale_real_y = g_display.dpi_scale_y;
|
||||
|
||||
smallWindow = IsWindowSmall(width, height);
|
||||
if (smallWindow) {
|
||||
g_dpi /= 2.0f;
|
||||
g_dpi_scale_x *= 2.0f;
|
||||
g_dpi_scale_y *= 2.0f;
|
||||
g_display.dpi /= 2.0f;
|
||||
g_display.dpi_scale_x *= 2.0f;
|
||||
g_display.dpi_scale_y *= 2.0f;
|
||||
}
|
||||
pixel_in_dps_x = 1.0f / g_dpi_scale_x;
|
||||
pixel_in_dps_y = 1.0f / g_dpi_scale_y;
|
||||
g_display.pixel_in_dps_x = 1.0f / g_display.dpi_scale_x;
|
||||
g_display.pixel_in_dps_y = 1.0f / g_display.dpi_scale_y;
|
||||
|
||||
int new_dp_xres = (int)(width * g_dpi_scale_x);
|
||||
int new_dp_yres = (int)(height * g_dpi_scale_y);
|
||||
int new_dp_xres = (int)(width * g_display.dpi_scale_x);
|
||||
int new_dp_yres = (int)(height * g_display.dpi_scale_y);
|
||||
|
||||
bool dp_changed = new_dp_xres != dp_xres || new_dp_yres != dp_yres;
|
||||
bool px_changed = pixel_xres != width || pixel_yres != height;
|
||||
bool dp_changed = new_dp_xres != g_display.dp_xres || new_dp_yres != g_display.dp_yres;
|
||||
bool px_changed = g_display.pixel_xres != width || g_display.pixel_yres != height;
|
||||
|
||||
if (dp_changed || px_changed) {
|
||||
dp_xres = new_dp_xres;
|
||||
dp_yres = new_dp_yres;
|
||||
pixel_xres = width;
|
||||
pixel_yres = height;
|
||||
g_display.dp_xres = new_dp_xres;
|
||||
g_display.dp_yres = new_dp_yres;
|
||||
g_display.pixel_xres = width;
|
||||
g_display.pixel_yres = height;
|
||||
NativeResized();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -591,7 +591,6 @@
|
||||
<ClCompile Include="MIPS\IR\IRPassSimplify.cpp" />
|
||||
<ClCompile Include="MIPS\IR\IRRegCache.cpp" />
|
||||
<ClCompile Include="Replay.cpp" />
|
||||
<ClCompile Include="TextureReplacer.cpp" />
|
||||
<ClCompile Include="Compatibility.cpp" />
|
||||
<ClCompile Include="Config.cpp" />
|
||||
<ClCompile Include="Core.cpp" />
|
||||
@@ -1157,7 +1156,6 @@
|
||||
<ClInclude Include="MIPS\IR\IRPassSimplify.h" />
|
||||
<ClInclude Include="MIPS\IR\IRRegCache.h" />
|
||||
<ClInclude Include="Replay.h" />
|
||||
<ClInclude Include="TextureReplacer.h" />
|
||||
<ClInclude Include="Compatibility.h" />
|
||||
<ClInclude Include="Config.h" />
|
||||
<ClInclude Include="Core.h" />
|
||||
|
||||
@@ -652,9 +652,6 @@
|
||||
<ClCompile Include="FileLoaders\RamCachingFileLoader.cpp">
|
||||
<Filter>FileLoaders</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TextureReplacer.cpp">
|
||||
<Filter>Core</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\IR\IRAsm.cpp">
|
||||
<Filter>MIPS\IR</Filter>
|
||||
</ClCompile>
|
||||
@@ -1737,9 +1734,6 @@
|
||||
<ClInclude Include="FileLoaders\RamCachingFileLoader.h">
|
||||
<Filter>FileLoaders</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TextureReplacer.h">
|
||||
<Filter>Core</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MIPS\IR\IRJit.h">
|
||||
<Filter>MIPS\IR</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -980,7 +980,7 @@ int VFSFileSystem::OpenFile(std::string filename, FileAccess access, const char
|
||||
VERBOSE_LOG(FILESYS, "VFSFileSystem actually opening %s (%s)", fullNameC, filename.c_str());
|
||||
|
||||
size_t size;
|
||||
u8 *data = VFSReadFile(fullNameC, &size);
|
||||
u8 *data = g_VFS.ReadFile(fullNameC, &size);
|
||||
if (!data) {
|
||||
ERROR_LOG(FILESYS, "VFSFileSystem failed to open %s", filename.c_str());
|
||||
return SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND;
|
||||
@@ -1001,7 +1001,7 @@ PSPFileInfo VFSFileSystem::GetFileInfo(std::string filename) {
|
||||
|
||||
std::string fullName = GetLocalPath(filename);
|
||||
File::FileInfo fo;
|
||||
if (VFSGetFileInfo(fullName.c_str(), &fo)) {
|
||||
if (g_VFS.GetFileInfo(fullName.c_str(), &fo)) {
|
||||
x.exists = fo.exists;
|
||||
if (x.exists) {
|
||||
x.size = fo.size;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "Common/Serialize/Serializer.h"
|
||||
#include "Common/Serialize/SerializeFuncs.h"
|
||||
#include "Common/Math/math_util.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
#include "Core/HLE/FunctionWrappers.h"
|
||||
@@ -28,6 +29,7 @@
|
||||
#include "Core/HLE/sceKernelThread.h"
|
||||
#include "Core/HLE/sceKernelInterrupt.h"
|
||||
#include "Core/HW/Display.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/MemMapHelpers.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/Replay.h"
|
||||
@@ -118,6 +120,20 @@ static void __CtrlUpdateLatch()
|
||||
|
||||
// Copy in the current data to the current buffer.
|
||||
ctrlBufs[ctrlBuf] = ctrlCurrent;
|
||||
|
||||
if (PSP_CoreParameter().compat.flags().DaxterRotatedAnalogStick) {
|
||||
// For some reason, Daxter rotates the analog input. See #17015
|
||||
float angle = (15.0f / 360.0f) * (2.0f * M_PI);
|
||||
float cosAngle = cosf(angle);
|
||||
float sinAngle = sinf(angle);
|
||||
float x = ctrlBufs[ctrlBuf].analog[0][0] - 128;
|
||||
float y = ctrlBufs[ctrlBuf].analog[0][1] - 128;
|
||||
float rX = x * cosAngle - y * sinAngle;
|
||||
float rY = x * sinAngle + y * cosAngle;
|
||||
ctrlBufs[ctrlBuf].analog[0][0] = (u8)Clamp(rX + 128, 0.0f, 255.0f);
|
||||
ctrlBufs[ctrlBuf].analog[0][1] = (u8)Clamp(rY + 128, 0.0f, 255.0f);
|
||||
}
|
||||
|
||||
ctrlBufs[ctrlBuf].buttons = buttons;
|
||||
|
||||
u32 changed = buttons ^ ctrlOldButtons;
|
||||
|
||||
@@ -285,10 +285,6 @@ void __DisplayDoState(PointerWrap &p) {
|
||||
|
||||
if (p.mode == p.MODE_READ) {
|
||||
gpu->ReapplyGfxState();
|
||||
|
||||
if (hasSetMode) {
|
||||
gpu->InitClear();
|
||||
}
|
||||
gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.stride, framebuf.fmt);
|
||||
}
|
||||
}
|
||||
@@ -749,10 +745,7 @@ static u32 sceDisplaySetMode(int displayMode, int displayWidth, int displayHeigh
|
||||
return hleLogWarning(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_SIZE, "invalid size");
|
||||
}
|
||||
|
||||
if (!hasSetMode) {
|
||||
gpu->InitClear();
|
||||
hasSetMode = true;
|
||||
}
|
||||
hasSetMode = true;
|
||||
mode = displayMode;
|
||||
width = displayWidth;
|
||||
height = displayHeight;
|
||||
|
||||
+2
-2
@@ -2055,7 +2055,7 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o
|
||||
// I have a planned further refactoring.
|
||||
float ar;
|
||||
if (g_Config.bDisplayStretch) {
|
||||
ar = (float)dp_xres / (float)dp_yres;
|
||||
ar = (float)g_display.dp_xres / (float)g_display.dp_yres;
|
||||
} else {
|
||||
ar = g_Config.fDisplayAspectRatio * (480.0f / 272.0f);
|
||||
}
|
||||
@@ -2066,7 +2066,7 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o
|
||||
if (Memory::IsValidAddress(outPtr)) {
|
||||
// TODO: Maybe do something more sophisticated taking the longest side and screen rotation
|
||||
// into account, etc.
|
||||
float scale = float(dp_xres) * g_Config.fDisplayScale / 480.0f;
|
||||
float scale = (float)g_display.dp_xres * g_Config.fDisplayScale / 480.0f;
|
||||
Memory::Write_Float(scale, outPtr);
|
||||
}
|
||||
return 0;
|
||||
|
||||
+1
-1
@@ -569,7 +569,7 @@ namespace Reporting
|
||||
return false;
|
||||
#else
|
||||
File::FileInfo fo;
|
||||
if (!VFSGetFileInfo("flash0/font/jpn0.pgf", &fo))
|
||||
if (!g_VFS.GetFileInfo("flash0/font/jpn0.pgf", &fo))
|
||||
return false;
|
||||
#endif
|
||||
|
||||
|
||||
+3
-3
@@ -291,10 +291,10 @@ namespace SaveState
|
||||
}
|
||||
|
||||
private:
|
||||
static const int BLOCK_SIZE = 8192;
|
||||
static const int REWIND_NUM_STATES = 20;
|
||||
const int BLOCK_SIZE = 8192;
|
||||
const int REWIND_NUM_STATES = 20;
|
||||
// TODO: Instead, based on size of compressed state?
|
||||
static const int BASE_USAGE_INTERVAL = 15;
|
||||
const int BASE_USAGE_INTERVAL = 15;
|
||||
|
||||
typedef std::vector<u8> StateBuffer;
|
||||
|
||||
|
||||
+2
-2
@@ -344,11 +344,11 @@ bool TakeGameScreenshot(const Path &filename, ScreenshotFormat fmt, ScreenshotTy
|
||||
// Only crop to the top left when using a render screenshot.
|
||||
w = maxRes > 0 ? 480 * maxRes : PSP_CoreParameter().renderWidth;
|
||||
h = maxRes > 0 ? 272 * maxRes : PSP_CoreParameter().renderHeight;
|
||||
} else if (g_display_rotation != DisplayRotation::ROTATE_0) {
|
||||
} else if (g_display.rotation != DisplayRotation::ROTATE_0) {
|
||||
GPUDebugBuffer temp;
|
||||
success = gpuDebug->GetOutputFramebuffer(temp);
|
||||
if (success) {
|
||||
buf = ApplyRotation(temp, g_display_rotation);
|
||||
buf = ApplyRotation(temp, g_display.rotation);
|
||||
}
|
||||
} else {
|
||||
success = gpuDebug->GetOutputFramebuffer(buf);
|
||||
|
||||
@@ -1,311 +0,0 @@
|
||||
// Copyright (c) 2016- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Common/File/Path.h"
|
||||
#include "Common/GPU/DataFormat.h"
|
||||
|
||||
#include "GPU/Common/TextureDecoder.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
|
||||
class IniFile;
|
||||
class TextureCacheCommon;
|
||||
class TextureReplacer;
|
||||
class ReplacedTextureTask;
|
||||
class LimitedWaitable;
|
||||
struct zip;
|
||||
|
||||
// These must match the constants in TextureCacheCommon.
|
||||
enum class ReplacedTextureAlpha {
|
||||
UNKNOWN = 0x04,
|
||||
FULL = 0x00,
|
||||
};
|
||||
|
||||
// For forward comatibility, we specify the hash.
|
||||
enum class ReplacedTextureHash {
|
||||
QUICK,
|
||||
XXH32,
|
||||
XXH64,
|
||||
};
|
||||
|
||||
struct ReplacerZipInfo {
|
||||
zip *z = nullptr;
|
||||
std::mutex lock;
|
||||
|
||||
void Close();
|
||||
};
|
||||
|
||||
struct ReplacedTextureLevel {
|
||||
int w;
|
||||
int h;
|
||||
Draw::DataFormat fmt; // NOTE: Right now, the only supported format is Draw::DataFormat::R8G8B8A8_UNORM.
|
||||
Path file;
|
||||
// Can be ignored for hashing/equal, since file has all uniqueness.
|
||||
// To be able to reload, we need to be able to reopen, unfortunate we can't use zip_file_t.
|
||||
ReplacerZipInfo *zinfo = nullptr;
|
||||
int64_t zi = -1;
|
||||
|
||||
bool operator ==(const ReplacedTextureLevel &other) const {
|
||||
if (w != other.w || h != other.h || fmt != other.fmt)
|
||||
return false;
|
||||
return file == other.file;
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<ReplacedTextureLevel> {
|
||||
std::size_t operator()(const ReplacedTextureLevel &k) const {
|
||||
#if PPSSPP_ARCH(64BIT)
|
||||
uint64_t v = (uint64_t)k.w | ((uint64_t)k.h << 32);
|
||||
v = __rotl64(v ^ (uint64_t)k.fmt, 13);
|
||||
#else
|
||||
uint32_t v = k.w ^ (uint32_t)k.fmt;
|
||||
v = __rotl(__rotl(v, 13) ^ k.h, 13);
|
||||
#endif
|
||||
return v ^ hash<string>()(k.file.ToString());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct ReplacedLevelCache {
|
||||
std::mutex lock;
|
||||
std::vector<uint8_t> data;
|
||||
double lastUsed = 0.0;
|
||||
};
|
||||
|
||||
struct ReplacementCacheKey {
|
||||
u64 cachekey;
|
||||
u32 hash;
|
||||
|
||||
ReplacementCacheKey(u64 c, u32 h) : cachekey(c), hash(h) { }
|
||||
|
||||
bool operator ==(const ReplacementCacheKey &k) const {
|
||||
return k.cachekey == cachekey && k.hash == hash;
|
||||
}
|
||||
|
||||
bool operator <(const ReplacementCacheKey &k) const {
|
||||
if (k.cachekey == cachekey) {
|
||||
return k.hash < hash;
|
||||
}
|
||||
return k.cachekey < cachekey;
|
||||
}
|
||||
};
|
||||
|
||||
struct ReplacementAliasKey {
|
||||
u64 cachekey;
|
||||
union {
|
||||
u64 hashAndLevel;
|
||||
struct {
|
||||
u32 level;
|
||||
u32 hash;
|
||||
};
|
||||
};
|
||||
|
||||
ReplacementAliasKey(u64 c, u32 h, u32 l) : cachekey(c), level(l), hash(h) { }
|
||||
|
||||
bool operator ==(const ReplacementAliasKey &k) const {
|
||||
return k.cachekey == cachekey && k.hashAndLevel == hashAndLevel;
|
||||
}
|
||||
|
||||
bool operator <(const ReplacementAliasKey &k) const {
|
||||
if (k.cachekey == cachekey) {
|
||||
return k.hashAndLevel < hashAndLevel;
|
||||
}
|
||||
return k.cachekey < cachekey;
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<ReplacementCacheKey> {
|
||||
size_t operator()(const ReplacementCacheKey &k) const {
|
||||
return std::hash<u64>()(k.cachekey ^ ((u64)k.hash << 32));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<ReplacementAliasKey> {
|
||||
size_t operator()(const ReplacementAliasKey &k) const {
|
||||
return std::hash<u64>()(k.cachekey ^ k.hashAndLevel);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct ReplacedTexture {
|
||||
~ReplacedTexture();
|
||||
|
||||
inline bool Valid() const {
|
||||
if (!initDone_)
|
||||
return false;
|
||||
return !levels_.empty();
|
||||
}
|
||||
|
||||
inline bool IsInvalid() const {
|
||||
if (!initDone_)
|
||||
return false;
|
||||
return levels_.empty();
|
||||
}
|
||||
|
||||
bool GetSize(int level, int &w, int &h) const {
|
||||
if (!initDone_)
|
||||
return false;
|
||||
if ((size_t)level < levels_.size()) {
|
||||
w = levels_[level].w;
|
||||
h = levels_[level].h;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int NumLevels() const {
|
||||
if (!initDone_)
|
||||
return 0;
|
||||
return (int)levels_.size();
|
||||
}
|
||||
|
||||
Draw::DataFormat Format(int level) const {
|
||||
if (initDone_) {
|
||||
if ((size_t)level < levels_.size()) {
|
||||
return levels_[level].fmt;
|
||||
}
|
||||
}
|
||||
return Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
}
|
||||
|
||||
u8 AlphaStatus() const {
|
||||
return (u8)alphaStatus_;
|
||||
}
|
||||
|
||||
bool IsReady(double budget);
|
||||
|
||||
bool Load(int level, void *out, int rowPitch);
|
||||
|
||||
protected:
|
||||
void Prepare();
|
||||
void PrepareData(int level);
|
||||
void PurgeIfOlder(double t);
|
||||
|
||||
std::vector<ReplacedTextureLevel> levels_;
|
||||
std::vector<ReplacedLevelCache *> levelData_;
|
||||
ReplacedTextureAlpha alphaStatus_ = ReplacedTextureAlpha::UNKNOWN;
|
||||
double lastUsed_ = 0.0;
|
||||
LimitedWaitable *threadWaitable_ = nullptr;
|
||||
std::mutex mutex_;
|
||||
bool cancelPrepare_ = false;
|
||||
bool initDone_ = false;
|
||||
bool prepareDone_ = false;
|
||||
|
||||
friend TextureReplacer;
|
||||
friend ReplacedTextureTask;
|
||||
};
|
||||
|
||||
struct ReplacedTextureDecodeInfo {
|
||||
u64 cachekey;
|
||||
u32 hash;
|
||||
u32 addr;
|
||||
bool isVideo;
|
||||
bool isFinal;
|
||||
int scaleFactor;
|
||||
Draw::DataFormat fmt;
|
||||
};
|
||||
|
||||
enum class ReplacerDecimateMode {
|
||||
NEW_FRAME,
|
||||
FORCE_PRESSURE,
|
||||
ALL,
|
||||
};
|
||||
|
||||
class TextureReplacer {
|
||||
public:
|
||||
TextureReplacer();
|
||||
~TextureReplacer();
|
||||
|
||||
void Init();
|
||||
void NotifyConfigChanged();
|
||||
|
||||
inline bool Enabled() {
|
||||
return enabled_;
|
||||
}
|
||||
|
||||
u32 ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV);
|
||||
|
||||
ReplacedTexture &FindReplacement(u64 cachekey, u32 hash, int w, int h, double budget);
|
||||
bool FindFiltering(u64 cachekey, u32 hash, TextureFiltering *forceFiltering);
|
||||
ReplacedTexture &FindNone() {
|
||||
return none_;
|
||||
}
|
||||
|
||||
// Check if a NotifyTextureDecoded for this texture is desired (used to avoid reads from write-combined memory.)
|
||||
bool WillSave(const ReplacedTextureDecodeInfo &replacedInfo);
|
||||
|
||||
// Notify that a new texture was decoded. May already be upscaled, saves the data passed.
|
||||
void NotifyTextureDecoded(const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int w, int h);
|
||||
|
||||
void Decimate(ReplacerDecimateMode mode);
|
||||
|
||||
static bool GenerateIni(const std::string &gameID, Path &generatedFilename);
|
||||
static bool IniExists(const std::string &gameID);
|
||||
|
||||
protected:
|
||||
bool LoadIni();
|
||||
bool LoadIniValues(IniFile &ini, bool isOverride = false);
|
||||
void ParseHashRange(const std::string &key, const std::string &value);
|
||||
void ParseFiltering(const std::string &key, const std::string &value);
|
||||
void ParseReduceHashRange(const std::string& key, const std::string& value);
|
||||
bool LookupHashRange(u32 addr, int &w, int &h);
|
||||
float LookupReduceHashRange(int& w, int& h);
|
||||
std::string LookupHashFile(u64 cachekey, u32 hash, int level);
|
||||
std::string HashName(u64 cachekey, u32 hash, int level);
|
||||
void PopulateReplacement(ReplacedTexture *result, u64 cachekey, u32 hash, int w, int h);
|
||||
bool PopulateLevelFromPath(ReplacedTextureLevel &level, bool ignoreError);
|
||||
bool PopulateLevelFromZip(ReplacedTextureLevel &level, bool ignoreError);
|
||||
|
||||
bool enabled_ = false;
|
||||
bool allowVideo_ = false;
|
||||
bool ignoreAddress_ = false;
|
||||
bool reduceHash_ = false;
|
||||
float reduceHashSize = 1.0; // default value with reduceHash to false
|
||||
float reduceHashGlobalValue = 0.5; // Global value for textures dump pngs of all sizes, 0.5 by default but can be set in textures.ini
|
||||
double lastTextureCacheSizeGB_ = 0.0;
|
||||
bool ignoreMipmap_ = false;
|
||||
std::string gameID_;
|
||||
Path basePath_;
|
||||
ReplacedTextureHash hash_ = ReplacedTextureHash::QUICK;
|
||||
ReplacerZipInfo zip_;
|
||||
|
||||
typedef std::pair<int, int> WidthHeightPair;
|
||||
std::unordered_map<u64, WidthHeightPair> hashranges_;
|
||||
std::unordered_map<u64, float> reducehashranges_;
|
||||
std::unordered_map<ReplacementAliasKey, std::string> aliases_;
|
||||
std::unordered_map<ReplacementCacheKey, TextureFiltering> filtering_;
|
||||
|
||||
ReplacedTexture none_;
|
||||
std::unordered_map<ReplacementCacheKey, ReplacedTexture> cache_;
|
||||
std::unordered_map<ReplacementCacheKey, std::pair<ReplacedTextureLevel, double>> savedCache_;
|
||||
std::unordered_map<ReplacedTextureLevel, ReplacedLevelCache> levelCache_;
|
||||
};
|
||||
@@ -252,7 +252,7 @@ void __PPGeInit() {
|
||||
if (loadedZIM) {
|
||||
size_t atlas_data_size;
|
||||
if (!g_ppge_atlas.IsMetadataLoaded()) {
|
||||
uint8_t *atlas_data = VFSReadFile("ppge_atlas.meta", &atlas_data_size);
|
||||
uint8_t *atlas_data = g_VFS.ReadFile("ppge_atlas.meta", &atlas_data_size);
|
||||
if (atlas_data)
|
||||
g_ppge_atlas.Load(atlas_data, atlas_data_size);
|
||||
delete[] atlas_data;
|
||||
|
||||
+1
-1
@@ -237,7 +237,7 @@ static bool ServeDebuggerFile(const http::Request &request) {
|
||||
return false;
|
||||
|
||||
size_t size;
|
||||
uint8_t *data = VFSReadFile(filename, &size);
|
||||
uint8_t *data = g_VFS.ReadFile(filename, &size);
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -204,10 +204,10 @@ bool FramebufferManagerCommon::ReadbackDepthbuffer(Draw::Framebuffer *fbo, int x
|
||||
}
|
||||
|
||||
shaderManager_->DirtyLastShader();
|
||||
auto *blitFBO = GetTempFBO(TempFBO::COPY, fbo->Width() * scaleX, fbo->Height() * scaleY);
|
||||
auto *blitFBO = GetTempFBO(TempFBO::Z_COPY, fbo->Width() * scaleX, fbo->Height() * scaleY);
|
||||
draw_->BindFramebufferAsRenderTarget(blitFBO, { RPAction::DONT_CARE, RPAction::DONT_CARE, RPAction::DONT_CARE }, "ReadbackDepthbufferSync");
|
||||
Draw::Viewport viewport = { 0.0f, 0.0f, (float)destW, (float)destH, 0.0f, 1.0f };
|
||||
draw_->SetViewports(1, &viewport);
|
||||
draw_->SetViewport(viewport);
|
||||
draw_->SetScissorRect(0, 0, fbo->Width() * scaleX, fbo->Height() * scaleY);
|
||||
|
||||
draw_->BindFramebufferAsTexture(fbo, TEX_SLOT_PSP_TEXTURE, FB_DEPTH_BIT, 0);
|
||||
|
||||
@@ -36,7 +36,9 @@ enum {
|
||||
};
|
||||
|
||||
DrawEngineCommon::DrawEngineCommon() : decoderMap_(16) {
|
||||
decJitCache_ = new VertexDecoderJitCache();
|
||||
if (g_Config.bVertexDecoderJit && g_Config.iCpuCore == (int)CPUCore::JIT) {
|
||||
decJitCache_ = new VertexDecoderJitCache();
|
||||
}
|
||||
transformed = (TransformedVertex *)AllocateMemoryPages(TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE);
|
||||
transformedExpanded = (TransformedVertex *)AllocateMemoryPages(3 * TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE);
|
||||
}
|
||||
@@ -169,7 +171,8 @@ static Vec3f ScreenToDrawing(const Vec3f& coords) {
|
||||
}
|
||||
|
||||
void DrawEngineCommon::NotifyConfigChanged() {
|
||||
decJitCache_->Clear();
|
||||
if (decJitCache_)
|
||||
decJitCache_->Clear();
|
||||
lastVType_ = -1;
|
||||
dec_ = nullptr;
|
||||
decoderMap_.Iterate([&](const uint32_t vtype, VertexDecoder *decoder) {
|
||||
|
||||
@@ -74,6 +74,8 @@ public:
|
||||
virtual ~DrawEngineCommon();
|
||||
|
||||
void Init();
|
||||
virtual void DeviceLost() = 0;
|
||||
virtual void DeviceRestore(Draw::DrawContext *draw) = 0;
|
||||
|
||||
bool GetCurrentSimpleVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices);
|
||||
|
||||
@@ -115,7 +117,9 @@ public:
|
||||
}
|
||||
|
||||
bool IsCodePtrVertexDecoder(const u8 *ptr) const {
|
||||
return decJitCache_->IsInSpace(ptr);
|
||||
if (decJitCache_)
|
||||
return decJitCache_->IsInSpace(ptr);
|
||||
return false;
|
||||
}
|
||||
int GetNumDrawCalls() const {
|
||||
return numDrawCalls;
|
||||
@@ -123,9 +127,10 @@ public:
|
||||
|
||||
VertexDecoder *GetVertexDecoder(u32 vtype);
|
||||
|
||||
virtual void ClearTrackedVertexArrays() {}
|
||||
|
||||
protected:
|
||||
virtual bool UpdateUseHWTessellation(bool enabled) { return enabled; }
|
||||
virtual void ClearTrackedVertexArrays() {}
|
||||
|
||||
int ComputeNumVertsToDecode() const;
|
||||
void DecodeVerts(u8 *dest);
|
||||
|
||||
@@ -546,6 +546,8 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer
|
||||
// TODO: Is it worth trying to upload the depth buffer (only if it wasn't copied above..?)
|
||||
}
|
||||
|
||||
DiscardFramebufferCopy();
|
||||
|
||||
// We already have it!
|
||||
} else if (vfb != currentRenderVfb_) {
|
||||
// Use it as a render target.
|
||||
@@ -562,6 +564,8 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer
|
||||
NotifyRenderFramebufferSwitched(prev, vfb, params.isClearingDepth);
|
||||
CopyToColorFromOverlappingFramebuffers(vfb);
|
||||
gstate_c.usingDepth = false; // reset depth buffer tracking
|
||||
|
||||
DiscardFramebufferCopy();
|
||||
} else {
|
||||
// Something changed, but we still got the same framebuffer we were already rendering to.
|
||||
// Might not be a lot to do here, we check in NotifyRenderFramebufferUpdated
|
||||
@@ -928,6 +932,7 @@ void FramebufferManagerCommon::DestroyFramebuf(VirtualFramebuffer *v) {
|
||||
}
|
||||
|
||||
// Wipe some pointers
|
||||
DiscardFramebufferCopy();
|
||||
if (currentRenderVfb_ == v)
|
||||
currentRenderVfb_ = nullptr;
|
||||
if (displayFramebuf_ == v)
|
||||
@@ -1205,8 +1210,8 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int
|
||||
|
||||
// TODO: Replace with draw2D_.Blit() directly.
|
||||
DrawActiveTexture(dstX, dstY, width, height,
|
||||
vfb ? vfb->bufferWidth : pixel_xres,
|
||||
vfb ? vfb->bufferHeight : pixel_yres,
|
||||
vfb ? vfb->bufferWidth : g_display.pixel_xres,
|
||||
vfb ? vfb->bufferHeight : g_display.pixel_yres,
|
||||
u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags);
|
||||
|
||||
gpuStats.numUploads++;
|
||||
@@ -1233,13 +1238,27 @@ bool FramebufferManagerCommon::BindFramebufferAsColorTexture(int stage, VirtualF
|
||||
// Self-texturing, need a copy currently (some backends can potentially support it though).
|
||||
WARN_LOG_ONCE(selfTextureCopy, G3D, "Attempting to texture from current render target (src=%08x / target=%08x / flags=%d), making a copy", framebuffer->fb_address, currentRenderVfb_->fb_address, flags);
|
||||
// TODO: Maybe merge with bvfbs_? Not sure if those could be packing, and they're created at a different size.
|
||||
if (currentFramebufferCopy_) {
|
||||
// We have a copy already that hasn't been invalidated, let's keep using it.
|
||||
draw_->BindFramebufferAsTexture(currentFramebufferCopy_, stage, Draw::FB_COLOR_BIT, layer);
|
||||
return true;
|
||||
}
|
||||
|
||||
Draw::Framebuffer *renderCopy = GetTempFBO(TempFBO::COPY, framebuffer->renderWidth, framebuffer->renderHeight);
|
||||
if (renderCopy) {
|
||||
VirtualFramebuffer copyInfo = *framebuffer;
|
||||
copyInfo.fbo = renderCopy;
|
||||
CopyFramebufferForColorTexture(©Info, framebuffer, flags, layer);
|
||||
|
||||
bool partial = false;
|
||||
CopyFramebufferForColorTexture(©Info, framebuffer, flags, layer, &partial);
|
||||
RebindFramebuffer("After BindFramebufferAsColorTexture");
|
||||
draw_->BindFramebufferAsTexture(renderCopy, stage, Draw::FB_COLOR_BIT, layer);
|
||||
|
||||
// Only cache the copy if it wasn't a partial copy.
|
||||
// TODO: Improve on this.
|
||||
if (!partial) {
|
||||
currentFramebufferCopy_ = renderCopy;
|
||||
}
|
||||
gpuStats.numCopiesForSelfTex++;
|
||||
} else {
|
||||
// Failed to get temp FBO? Weird.
|
||||
@@ -1262,14 +1281,17 @@ bool FramebufferManagerCommon::BindFramebufferAsColorTexture(int stage, VirtualF
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::CopyFramebufferForColorTexture(VirtualFramebuffer *dst, VirtualFramebuffer *src, int flags, int layer) {
|
||||
void FramebufferManagerCommon::CopyFramebufferForColorTexture(VirtualFramebuffer *dst, VirtualFramebuffer *src, int flags, int layer, bool *partial) {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int w = src->drawnWidth;
|
||||
int h = src->drawnHeight;
|
||||
|
||||
*partial = false;
|
||||
|
||||
// If max is not > min, we probably could not detect it. Skip.
|
||||
// See the vertex decoder, where this is updated.
|
||||
// TODO: We're currently not hitting this path in Dante. See #17032
|
||||
if ((flags & BINDFBCOLOR_MAY_COPY_WITH_UV) == BINDFBCOLOR_MAY_COPY_WITH_UV && gstate_c.vertBounds.maxU > gstate_c.vertBounds.minU) {
|
||||
x = std::max(gstate_c.vertBounds.minU, (u16)0);
|
||||
y = std::max(gstate_c.vertBounds.minV, (u16)0);
|
||||
@@ -1287,6 +1309,9 @@ void FramebufferManagerCommon::CopyFramebufferForColorTexture(VirtualFramebuffer
|
||||
}
|
||||
|
||||
if (x < src->drawnWidth && y < src->drawnHeight && w > 0 && h > 0) {
|
||||
if (x != 0 || y != 0 || w < src->drawnWidth || h < src->drawnHeight) {
|
||||
*partial = true;
|
||||
}
|
||||
BlitFramebuffer(dst, x, y, src, x, y, w, h, 0, RASTER_COLOR, "CopyFBForColorTexture");
|
||||
}
|
||||
}
|
||||
@@ -1384,6 +1409,7 @@ Draw::Texture *FramebufferManagerCommon::MakePixelTexture(const u8 *srcPixels, G
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
Draw::TextureSwizzle::DEFAULT,
|
||||
"DrawPixels",
|
||||
{ (uint8_t *)srcPixels },
|
||||
generateTexture,
|
||||
@@ -1425,12 +1451,13 @@ void FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, int
|
||||
// PresentationCommon sets all kinds of state, we can't rely on anything.
|
||||
gstate_c.Dirty(DIRTY_ALL);
|
||||
|
||||
DiscardFramebufferCopy();
|
||||
currentRenderVfb_ = nullptr;
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::SetViewport2D(int x, int y, int w, int h) {
|
||||
Draw::Viewport vp{ (float)x, (float)y, (float)w, (float)h, 0.0f, 1.0f };
|
||||
draw_->SetViewports(1, &vp);
|
||||
Draw::Viewport viewport{ (float)x, (float)y, (float)w, (float)h, 0.0f, 1.0f };
|
||||
draw_->SetViewport(viewport);
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::CopyDisplayToOutput(bool reallyDirty) {
|
||||
@@ -1582,10 +1609,12 @@ void FramebufferManagerCommon::CopyDisplayToOutput(bool reallyDirty) {
|
||||
// This may get called mid-draw if the game uses an immediate flip.
|
||||
// PresentationCommon sets all kinds of state, we can't rely on anything.
|
||||
gstate_c.Dirty(DIRTY_ALL);
|
||||
DiscardFramebufferCopy();
|
||||
currentRenderVfb_ = nullptr;
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::DecimateFBOs() {
|
||||
DiscardFramebufferCopy();
|
||||
currentRenderVfb_ = nullptr;
|
||||
|
||||
for (auto iter : fbosToDelete_) {
|
||||
@@ -1742,6 +1771,7 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w,
|
||||
} else {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "ResizeFramebufFBO");
|
||||
}
|
||||
DiscardFramebufferCopy();
|
||||
currentRenderVfb_ = vfb;
|
||||
|
||||
if (!vfb->fbo) {
|
||||
@@ -2543,6 +2573,7 @@ void FramebufferManagerCommon::NotifyConfigChanged() {
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::DestroyAllFBOs() {
|
||||
DiscardFramebufferCopy();
|
||||
currentRenderVfb_ = nullptr;
|
||||
displayFramebuf_ = nullptr;
|
||||
prevDisplayFramebuf_ = nullptr;
|
||||
@@ -3081,11 +3112,11 @@ void FramebufferManagerCommon::DrawActiveTexture(float x, float y, float w, floa
|
||||
coord[i].y = coord[i].y * invDestH - 1.0f;
|
||||
}
|
||||
|
||||
if ((flags & DRAWTEX_TO_BACKBUFFER) && g_display_rotation != DisplayRotation::ROTATE_0) {
|
||||
if ((flags & DRAWTEX_TO_BACKBUFFER) && g_display.rotation != DisplayRotation::ROTATE_0) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// backwards notation, should fix that...
|
||||
Lin::Vec3 pos = Lin::Vec3(coord[i].x, coord[i].y, 0.0);
|
||||
pos = pos * g_display_rot_matrix;
|
||||
pos = pos * g_display.rot_matrix;
|
||||
coord[i].x = pos.x;
|
||||
coord[i].y = pos.y;
|
||||
}
|
||||
@@ -3235,8 +3266,8 @@ void FramebufferManagerCommon::BlitUsingRaster(
|
||||
draw_->InvalidateFramebuffer(Draw::FB_INVALIDATION_LOAD, pipeline->info.writeChannel == RASTER_COLOR ? Draw::FB_COLOR_BIT : Draw::FB_DEPTH_BIT);
|
||||
}
|
||||
|
||||
Draw::Viewport vp{ 0.0f, 0.0f, (float)dest->Width(), (float)dest->Height(), 0.0f, 1.0f };
|
||||
draw_->SetViewports(1, &vp);
|
||||
Draw::Viewport viewport{ 0.0f, 0.0f, (float)dest->Width(), (float)dest->Height(), 0.0f, 1.0f };
|
||||
draw_->SetViewport(viewport);
|
||||
draw_->SetScissorRect(0, 0, (int)dest->Width(), (int)dest->Height());
|
||||
|
||||
draw2D_.Blit(pipeline, srcX1, srcY1, srcX2, srcY2, destX1, destY1, destX2, destY2, (float)srcW, (float)srcH, (float)destW, (float)destH, linearFilter, scaleFactor);
|
||||
|
||||
@@ -217,6 +217,8 @@ enum class TempFBO {
|
||||
BLIT,
|
||||
// For copies of framebuffers (e.g. shader blending.)
|
||||
COPY,
|
||||
// Used for copies when setting color to depth.
|
||||
Z_COPY,
|
||||
// Used to copy stencil data, means we need a stencil backing.
|
||||
STENCIL,
|
||||
};
|
||||
@@ -466,6 +468,10 @@ public:
|
||||
return msaaLevel_;
|
||||
}
|
||||
|
||||
void DiscardFramebufferCopy() {
|
||||
currentFramebufferCopy_ = nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void ReadbackFramebuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel, Draw::ReadbackMode mode);
|
||||
// Used for when a shader is required, such as GLES.
|
||||
@@ -486,7 +492,7 @@ protected:
|
||||
// Used by ReadFramebufferToMemory and later framebuffer block copies
|
||||
void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, RasterChannel channel, const char *tag);
|
||||
|
||||
void CopyFramebufferForColorTexture(VirtualFramebuffer *dst, VirtualFramebuffer *src, int flags, int layer);
|
||||
void CopyFramebufferForColorTexture(VirtualFramebuffer *dst, VirtualFramebuffer *src, int flags, int layer, bool *partial);
|
||||
|
||||
void EstimateDrawingSize(u32 fb_address, int fb_stride, GEBufferFormat fb_format, int viewport_width, int viewport_height, int region_width, int region_height, int scissor_width, int scissor_height, int &drawing_width, int &drawing_height);
|
||||
|
||||
@@ -552,6 +558,8 @@ protected:
|
||||
|
||||
VirtualFramebuffer *currentRenderVfb_ = nullptr;
|
||||
|
||||
Draw::Framebuffer *currentFramebufferCopy_ = nullptr;
|
||||
|
||||
// The range of PSP memory that may contain FBOs. So we can skip iterating.
|
||||
u32 framebufRangeEnd_ = 0;
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ void LoadPostShaderInfo(Draw::DrawContext *draw, const std::vector<Path> &direct
|
||||
|
||||
for (size_t d = 0; d < directories.size(); d++) {
|
||||
std::vector<File::FileInfo> fileInfo;
|
||||
VFSGetFileListing(directories[d].c_str(), &fileInfo, "ini:");
|
||||
g_VFS.GetFileListing(directories[d].c_str(), &fileInfo, "ini:");
|
||||
|
||||
if (fileInfo.empty()) {
|
||||
File::GetFilesInDir(directories[d], &fileInfo, "ini:");
|
||||
@@ -90,7 +90,7 @@ void LoadPostShaderInfo(Draw::DrawContext *draw, const std::vector<Path> &direct
|
||||
if (path.ToString().substr(0, 7) == "assets/")
|
||||
path = Path(path.ToString().substr(7));
|
||||
|
||||
if (ini.LoadFromVFS(name.ToString()) || ini.Load(fileInfo[f].fullName)) {
|
||||
if (ini.LoadFromVFS(g_VFS, name.ToString()) || ini.Load(fileInfo[f].fullName)) {
|
||||
success = true;
|
||||
// vsh load. meh.
|
||||
}
|
||||
|
||||
@@ -55,10 +55,10 @@ FRect GetScreenFrame(float pixelWidth, float pixelHeight) {
|
||||
|
||||
if (applyInset) {
|
||||
// Remove the DPI scale to get back to pixels.
|
||||
float left = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT) / g_dpi_scale_x;
|
||||
float right = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_RIGHT) / g_dpi_scale_x;
|
||||
float top = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP) / g_dpi_scale_y;
|
||||
float bottom = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_BOTTOM) / g_dpi_scale_y;
|
||||
float left = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT) / g_display.dpi_scale_x;
|
||||
float right = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_RIGHT) / g_display.dpi_scale_x;
|
||||
float top = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP) / g_display.dpi_scale_y;
|
||||
float bottom = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_BOTTOM) / g_display.dpi_scale_y;
|
||||
|
||||
// Adjust left edge to compensate for cutouts (notches) if any.
|
||||
rc.x += left;
|
||||
@@ -100,8 +100,8 @@ void CenterDisplayOutputRect(FRect *rc, float origW, float origH, const FRect &f
|
||||
if (stretch) {
|
||||
// Automatically set aspect ratio to match the display, IF the rotation matches the output display ratio! Otherwise, just
|
||||
// sets standard aspect ratio because actually stretching will just look silly.
|
||||
bool globalRotated = g_display_rotation == DisplayRotation::ROTATE_90 || g_display_rotation == DisplayRotation::ROTATE_270;
|
||||
if (rotated == dp_yres > dp_xres) {
|
||||
bool globalRotated = g_display.rotation == DisplayRotation::ROTATE_90 || g_display.rotation == DisplayRotation::ROTATE_270;
|
||||
if (rotated == g_display.dp_yres > g_display.dp_xres) {
|
||||
origRatio = frameRatio;
|
||||
} else {
|
||||
origRatio *= aspectRatioAdjust;
|
||||
@@ -212,7 +212,7 @@ void PresentationCommon::CalculatePostShaderUniforms(int bufferWidth, int buffer
|
||||
|
||||
static std::string ReadShaderSrc(const Path &filename) {
|
||||
size_t sz = 0;
|
||||
char *data = (char *)VFSReadFile(filename.c_str(), &sz);
|
||||
char *data = (char *)g_VFS.ReadFile(filename.c_str(), &sz);
|
||||
if (!data) {
|
||||
return "";
|
||||
}
|
||||
@@ -699,11 +699,11 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u
|
||||
|
||||
if (isFinalAtOutputResolution || useStereo) {
|
||||
// In this mode, we ignore the g_display_rot_matrix. Apply manually.
|
||||
if (g_display_rotation != DisplayRotation::ROTATE_0) {
|
||||
if (g_display.rotation != DisplayRotation::ROTATE_0) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Lin::Vec3 v(verts[i].x, verts[i].y, verts[i].z);
|
||||
// Backwards notation, should fix that...
|
||||
v = v * g_display_rot_matrix;
|
||||
v = v * g_display.rot_matrix;
|
||||
verts[i].x = v.x;
|
||||
verts[i].y = v.y;
|
||||
}
|
||||
@@ -734,7 +734,7 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u
|
||||
int nextWidth, nextHeight;
|
||||
draw_->GetFramebufferDimensions(postShaderFramebuffer, &nextWidth, &nextHeight);
|
||||
Draw::Viewport viewport{ 0, 0, (float)nextWidth, (float)nextHeight, 0.0f, 1.0f };
|
||||
draw_->SetViewports(1, &viewport);
|
||||
draw_->SetViewport(viewport);
|
||||
draw_->SetScissorRect(0, 0, nextWidth, nextHeight);
|
||||
|
||||
CalculatePostShaderUniforms(lastWidth, lastHeight, nextWidth, nextHeight, shaderInfo, &uniforms);
|
||||
@@ -840,7 +840,7 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u
|
||||
draw_->UpdateDynamicUniformBuffer(&uniforms, sizeof(uniforms));
|
||||
} else {
|
||||
Draw::VsTexColUB ub{};
|
||||
memcpy(ub.WorldViewProj, g_display_rot_matrix.m, sizeof(float) * 16);
|
||||
memcpy(ub.WorldViewProj, g_display.rot_matrix.m, sizeof(float) * 16);
|
||||
draw_->UpdateDynamicUniformBuffer(&ub, sizeof(ub));
|
||||
}
|
||||
|
||||
@@ -853,7 +853,7 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u
|
||||
|
||||
auto setViewport = [&](float x, float y, float w, float h) {
|
||||
Draw::Viewport viewport{ x, y, w, h, 0.0f, 1.0f };
|
||||
draw_->SetViewports(1, &viewport);
|
||||
draw_->SetViewport(viewport);
|
||||
};
|
||||
|
||||
CardboardSettings cardboardSettings;
|
||||
|
||||
@@ -0,0 +1,442 @@
|
||||
// Copyright (c) 2016- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
|
||||
#include <png.h>
|
||||
|
||||
#include "GPU/Common/ReplacedTexture.h"
|
||||
#include "GPU/Common/TextureReplacer.h"
|
||||
|
||||
#include "Common/Data/Format/IniFile.h"
|
||||
#include "Common/Data/Format/ZIMLoad.h"
|
||||
#include "Common/Data/Format/PNGLoad.h"
|
||||
#include "Common/Thread/ParallelLoop.h"
|
||||
#include "Common/Thread/Waitable.h"
|
||||
#include "Common/Thread/ThreadManager.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/TimeUtil.h"
|
||||
|
||||
class ReplacedTextureTask : public Task {
|
||||
public:
|
||||
ReplacedTextureTask(VFSBackend *vfs, ReplacedTexture &tex, LimitedWaitable *w) : vfs_(vfs), tex_(tex), waitable_(w) {}
|
||||
|
||||
TaskType Type() const override {
|
||||
return TaskType::IO_BLOCKING;
|
||||
}
|
||||
|
||||
TaskPriority Priority() const override {
|
||||
return TaskPriority::NORMAL;
|
||||
}
|
||||
|
||||
void Run() override {
|
||||
tex_.Prepare(vfs_);
|
||||
waitable_->Notify();
|
||||
}
|
||||
|
||||
private:
|
||||
VFSBackend *vfs_;
|
||||
ReplacedTexture &tex_;
|
||||
LimitedWaitable *waitable_;
|
||||
};
|
||||
|
||||
// This can only return true if ACTIVE or NOT_FOUND.
|
||||
bool ReplacedTexture::IsReady(double budget) {
|
||||
_assert_(vfs_ != nullptr);
|
||||
|
||||
switch (State()) {
|
||||
case ReplacementState::ACTIVE:
|
||||
case ReplacementState::NOT_FOUND:
|
||||
if (threadWaitable_) {
|
||||
if (!threadWaitable_->WaitFor(budget)) {
|
||||
return false;
|
||||
}
|
||||
// Successfully waited! Can get rid of it.
|
||||
threadWaitable_->WaitAndRelease();
|
||||
threadWaitable_ = nullptr;
|
||||
}
|
||||
lastUsed_ = time_now_d();
|
||||
return true;
|
||||
case ReplacementState::UNINITIALIZED:
|
||||
// _dbg_assert_(false);
|
||||
return false;
|
||||
case ReplacementState::CANCEL_INIT:
|
||||
case ReplacementState::PENDING:
|
||||
return false;
|
||||
case ReplacementState::POPULATED:
|
||||
// We're gonna need to spawn a task.
|
||||
break;
|
||||
}
|
||||
|
||||
lastUsed_ = time_now_d();
|
||||
|
||||
// Let's not even start a new texture if we're already behind.
|
||||
if (budget < 0.0)
|
||||
return false;
|
||||
|
||||
_assert_(!threadWaitable_);
|
||||
threadWaitable_ = new LimitedWaitable();
|
||||
g_threadManager.EnqueueTask(new ReplacedTextureTask(vfs_, *this, threadWaitable_));
|
||||
if (threadWaitable_->WaitFor(budget)) {
|
||||
// If we successfully wait here, we're done. The thread will set state accordingly.
|
||||
_assert_(State() == ReplacementState::ACTIVE || State() == ReplacementState::NOT_FOUND || State() == ReplacementState::CANCEL_INIT);
|
||||
return true;
|
||||
}
|
||||
SetState(ReplacementState::PENDING);
|
||||
// Still pending on thread.
|
||||
return false;
|
||||
}
|
||||
|
||||
void ReplacedTexture::FinishPopulate(const ReplacementDesc &desc) {
|
||||
logId_ = desc.logId;
|
||||
levelData_ = desc.cache;
|
||||
|
||||
// TODO: The rest can be done on the thread.
|
||||
|
||||
for (int i = 0; i < std::min(MAX_REPLACEMENT_MIP_LEVELS, (int)desc.filenames.size()); ++i) {
|
||||
if (desc.filenames[i].empty()) {
|
||||
// Out of valid mip levels. Bail out.
|
||||
break;
|
||||
}
|
||||
|
||||
const Path filename = desc.basePath / desc.filenames[i];
|
||||
|
||||
VFSFileReference *fileRef = vfs_->GetFile(desc.filenames[i].c_str());
|
||||
if (!fileRef) {
|
||||
// If the file doesn't exist, let's just bail immediately here.
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Here, if we find a file with multiple built-in mipmap levels,
|
||||
// we'll have to change a bit how things work...
|
||||
ReplacedTextureLevel level;
|
||||
level.file = filename;
|
||||
|
||||
if (i == 0) {
|
||||
fmt = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
}
|
||||
|
||||
bool good;
|
||||
|
||||
level.fileRef = fileRef;
|
||||
good = PopulateLevel(level, false);
|
||||
|
||||
// We pad files that have been hashrange'd so they are the same texture size.
|
||||
level.w = (level.w * desc.w) / desc.newW;
|
||||
level.h = (level.h * desc.h) / desc.newH;
|
||||
|
||||
if (good && i != 0) {
|
||||
// Check that the mipmap size is correct. Can't load mips of the wrong size.
|
||||
if (level.w != (levels_[0].w >> i) || level.h != (levels_[0].h >> i)) {
|
||||
WARN_LOG(G3D, "Replacement mipmap invalid: size=%dx%d, expected=%dx%d (level %d, '%s')", level.w, level.h, levels_[0].w >> i, levels_[0].h >> i, i, filename.c_str());
|
||||
good = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (good)
|
||||
levels_.push_back(level);
|
||||
// Otherwise, we're done loading mips (bad PNG or bad size, either way.)
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (levels_.empty()) {
|
||||
// Bad.
|
||||
SetState(ReplacementState::NOT_FOUND);
|
||||
levelData_ = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate the data pointer.
|
||||
SetState(ReplacementState::POPULATED);
|
||||
}
|
||||
|
||||
bool ReplacedTexture::PopulateLevel(ReplacedTextureLevel &level, bool ignoreError) {
|
||||
bool good = false;
|
||||
|
||||
if (!level.fileRef) {
|
||||
if (!ignoreError)
|
||||
ERROR_LOG(G3D, "Error opening replacement texture file '%s' in textures.zip", level.file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t fileSize;
|
||||
VFSOpenFile *file = vfs_->OpenFileForRead(level.fileRef, &fileSize);
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string magic;
|
||||
auto imageType = Identify(vfs_, file, &magic);
|
||||
|
||||
if (imageType == ReplacedImageType::ZIM) {
|
||||
uint32_t ignore = 0;
|
||||
struct ZimHeader {
|
||||
uint32_t magic;
|
||||
uint32_t w;
|
||||
uint32_t h;
|
||||
uint32_t flags;
|
||||
} header;
|
||||
good = vfs_->Read(file, &header, sizeof(header)) == sizeof(header);
|
||||
level.w = header.w;
|
||||
level.h = header.h;
|
||||
good = (header.flags & ZIM_FORMAT_MASK) == ZIM_RGBA8888;
|
||||
} else if (imageType == ReplacedImageType::PNG) {
|
||||
PNGHeaderPeek headerPeek;
|
||||
good = vfs_->Read(file, &headerPeek, sizeof(headerPeek)) == sizeof(headerPeek);
|
||||
if (good && headerPeek.IsValidPNGHeader()) {
|
||||
level.w = headerPeek.Width();
|
||||
level.h = headerPeek.Height();
|
||||
good = true;
|
||||
} else {
|
||||
ERROR_LOG(G3D, "Could not get PNG dimensions: %s (zip)", level.file.ToVisualString().c_str());
|
||||
good = false;
|
||||
}
|
||||
} else {
|
||||
ERROR_LOG(G3D, "Could not load texture replacement info: %s - unsupported format %s", level.file.ToVisualString().c_str(), magic.c_str());
|
||||
}
|
||||
vfs_->CloseFile(file);
|
||||
|
||||
return good;
|
||||
}
|
||||
|
||||
void ReplacedTexture::Prepare(VFSBackend *vfs) {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
this->vfs_ = vfs;
|
||||
|
||||
if (State() == ReplacementState::CANCEL_INIT) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)levels_.size(); ++i) {
|
||||
if (State() == ReplacementState::CANCEL_INIT)
|
||||
break;
|
||||
PrepareData(i);
|
||||
}
|
||||
|
||||
if (threadWaitable_)
|
||||
threadWaitable_->Notify();
|
||||
}
|
||||
|
||||
void ReplacedTexture::PrepareData(int level) {
|
||||
_assert_msg_((size_t)level < levels_.size(), "Invalid miplevel");
|
||||
_assert_msg_(levelData_ != nullptr, "Level cache not set");
|
||||
|
||||
// We must lock around access to levelData_ in case two textures try to load it at once.
|
||||
std::lock_guard<std::mutex> guard(levelData_->lock);
|
||||
|
||||
const ReplacedTextureLevel &info = levels_[level];
|
||||
|
||||
if (levelData_->data.size() <= level) {
|
||||
levelData_->data.resize(level + 1);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> &out = levelData_->data[level];
|
||||
|
||||
// Already populated from cache.
|
||||
if (!out.empty()) {
|
||||
SetState(ReplacementState::ACTIVE);
|
||||
return;
|
||||
}
|
||||
|
||||
ReplacedImageType imageType;
|
||||
|
||||
size_t fileSize;
|
||||
VFSOpenFile *openFile = vfs_->OpenFileForRead(info.fileRef, &fileSize);
|
||||
|
||||
std::string magic;
|
||||
imageType = Identify(vfs_, openFile, &magic);
|
||||
|
||||
auto cleanup = [&] {
|
||||
vfs_->CloseFile(openFile);
|
||||
};
|
||||
|
||||
if (imageType == ReplacedImageType::ZIM) {
|
||||
std::unique_ptr<uint8_t[]> zim(new uint8_t[fileSize]);
|
||||
if (!zim) {
|
||||
ERROR_LOG(G3D, "Failed to allocate memory for texture replacement");
|
||||
SetState(ReplacementState::NOT_FOUND);
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
if (vfs_->Read(openFile, &zim[0], fileSize) != fileSize) {
|
||||
ERROR_LOG(G3D, "Could not load texture replacement: %s - failed to read ZIM", info.file.c_str());
|
||||
SetState(ReplacementState::NOT_FOUND);
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
int w, h, f;
|
||||
uint8_t *image;
|
||||
if (LoadZIMPtr(&zim[0], fileSize, &w, &h, &f, &image)) {
|
||||
if (w > info.w || h > info.h) {
|
||||
ERROR_LOG(G3D, "Texture replacement changed since header read: %s", info.file.c_str());
|
||||
SetState(ReplacementState::NOT_FOUND);
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
out.resize(info.w * info.h * 4);
|
||||
if (w == info.w) {
|
||||
memcpy(&out[0], image, info.w * 4 * info.h);
|
||||
} else {
|
||||
for (int y = 0; y < h; ++y) {
|
||||
memcpy(&out[info.w * 4 * y], image + w * 4 * y, w * 4);
|
||||
}
|
||||
}
|
||||
free(image);
|
||||
}
|
||||
|
||||
CheckAlphaResult res = CheckAlpha32Rect((u32 *)&out[0], info.w, w, h, 0xFF000000);
|
||||
if (res == CHECKALPHA_ANY || level == 0) {
|
||||
alphaStatus_ = ReplacedTextureAlpha(res);
|
||||
}
|
||||
} else if (imageType == ReplacedImageType::PNG) {
|
||||
png_image png = {};
|
||||
png.version = PNG_IMAGE_VERSION;
|
||||
|
||||
std::string pngdata;
|
||||
pngdata.resize(fileSize);
|
||||
pngdata.resize(vfs_->Read(openFile, &pngdata[0], fileSize));
|
||||
if (!png_image_begin_read_from_memory(&png, &pngdata[0], pngdata.size())) {
|
||||
ERROR_LOG(G3D, "Could not load texture replacement info: %s - %s (zip)", info.file.c_str(), png.message);
|
||||
SetState(ReplacementState::NOT_FOUND);
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
if (png.width > (uint32_t)info.w || png.height > (uint32_t)info.h) {
|
||||
ERROR_LOG(G3D, "Texture replacement changed since header read: %s", info.file.c_str());
|
||||
SetState(ReplacementState::NOT_FOUND);
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
bool checkedAlpha = false;
|
||||
if ((png.format & PNG_FORMAT_FLAG_ALPHA) == 0) {
|
||||
// Well, we know for sure it doesn't have alpha.
|
||||
if (level == 0) {
|
||||
alphaStatus_ = ReplacedTextureAlpha::FULL;
|
||||
}
|
||||
checkedAlpha = true;
|
||||
}
|
||||
png.format = PNG_FORMAT_RGBA;
|
||||
|
||||
out.resize(info.w * info.h * 4);
|
||||
if (!png_image_finish_read(&png, nullptr, &out[0], info.w * 4, nullptr)) {
|
||||
ERROR_LOG(G3D, "Could not load texture replacement: %s - %s", info.file.c_str(), png.message);
|
||||
SetState(ReplacementState::NOT_FOUND);
|
||||
cleanup();
|
||||
out.resize(0);
|
||||
return;
|
||||
}
|
||||
png_image_free(&png);
|
||||
|
||||
if (!checkedAlpha) {
|
||||
// This will only check the hashed bits.
|
||||
CheckAlphaResult res = CheckAlpha32Rect((u32 *)&out[0], info.w, png.width, png.height, 0xFF000000);
|
||||
if (res == CHECKALPHA_ANY || level == 0) {
|
||||
alphaStatus_ = ReplacedTextureAlpha(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetState(ReplacementState::ACTIVE);
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void ReplacedTexture::PurgeIfOlder(double t) {
|
||||
if (threadWaitable_ && !threadWaitable_->WaitFor(0.0))
|
||||
return;
|
||||
if (lastUsed_ >= t)
|
||||
return;
|
||||
|
||||
if (levelData_->lastUsed < t) {
|
||||
// We have to lock since multiple textures might reference this same data.
|
||||
std::lock_guard<std::mutex> guard(levelData_->lock);
|
||||
levelData_->data.clear();
|
||||
// This means we have to reload. If we never purge any, there's no need.
|
||||
SetState(ReplacementState::POPULATED);
|
||||
}
|
||||
}
|
||||
|
||||
ReplacedTexture::~ReplacedTexture() {
|
||||
if (threadWaitable_) {
|
||||
SetState(ReplacementState::CANCEL_INIT);
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
threadWaitable_->WaitAndRelease();
|
||||
threadWaitable_ = nullptr;
|
||||
}
|
||||
|
||||
for (auto &level : levels_) {
|
||||
vfs_->ReleaseFile(level.fileRef);
|
||||
level.fileRef = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ReplacedTexture::CopyLevelTo(int level, void *out, int rowPitch) {
|
||||
_assert_msg_((size_t)level < levels_.size(), "Invalid miplevel");
|
||||
_assert_msg_(out != nullptr && rowPitch > 0, "Invalid out/pitch");
|
||||
|
||||
if (State() != ReplacementState::ACTIVE) {
|
||||
WARN_LOG(G3D, "Init not done yet");
|
||||
return false;
|
||||
}
|
||||
|
||||
// We probably could avoid this lock, but better to play it safe.
|
||||
std::lock_guard<std::mutex> guard(levelData_->lock);
|
||||
|
||||
const ReplacedTextureLevel &info = levels_[level];
|
||||
const std::vector<uint8_t> &data = levelData_->data[level];
|
||||
|
||||
if (data.empty()) {
|
||||
WARN_LOG(G3D, "Level %d is empty", level);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rowPitch < info.w * 4) {
|
||||
ERROR_LOG(G3D, "Replacement rowPitch=%d, but w=%d (level=%d)", rowPitch, info.w * 4, level);
|
||||
return false;
|
||||
}
|
||||
_assert_msg_(data.size() == info.w * info.h * 4, "Data has wrong size");
|
||||
|
||||
if (rowPitch == info.w * 4) {
|
||||
ParallelMemcpy(&g_threadManager, out, &data[0], info.w * 4 * info.h);
|
||||
} else {
|
||||
const int MIN_LINES_PER_THREAD = 4;
|
||||
ParallelRangeLoop(&g_threadManager, [&](int l, int h) {
|
||||
for (int y = l; y < h; ++y) {
|
||||
memcpy((uint8_t *)out + rowPitch * y, &data[0] + info.w * 4 * y, info.w * 4);
|
||||
}
|
||||
}, 0, info.h, MIN_LINES_PER_THREAD);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *StateString(ReplacementState state) {
|
||||
switch (state) {
|
||||
case ReplacementState::UNINITIALIZED: return "UNINITIALIZED";
|
||||
case ReplacementState::POPULATED: return "PREPARED";
|
||||
case ReplacementState::PENDING: return "PENDING";
|
||||
case ReplacementState::NOT_FOUND: return "NOT_FOUND";
|
||||
case ReplacementState::ACTIVE: return "ACTIVE";
|
||||
case ReplacementState::CANCEL_INIT: return "CANCEL_INIT";
|
||||
default: return "N/A";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) 2016- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
#include "Common/GPU/thin3d.h"
|
||||
#include "Common/Log.h"
|
||||
|
||||
struct ReplacedLevelsCache;
|
||||
class TextureReplacer;
|
||||
class LimitedWaitable;
|
||||
|
||||
// These must match the constants in TextureCacheCommon.
|
||||
enum class ReplacedTextureAlpha {
|
||||
UNKNOWN = 0x04,
|
||||
FULL = 0x00,
|
||||
};
|
||||
|
||||
// For forward compatibility, we specify the hash.
|
||||
enum class ReplacedTextureHash {
|
||||
QUICK,
|
||||
XXH32,
|
||||
XXH64,
|
||||
};
|
||||
|
||||
enum class ReplacedImageType {
|
||||
PNG,
|
||||
ZIM,
|
||||
INVALID,
|
||||
};
|
||||
|
||||
static const int MAX_REPLACEMENT_MIP_LEVELS = 12; // 12 should be plenty, 8 is the max mip levels supported by the PSP.
|
||||
|
||||
// Metadata about a given texture level.
|
||||
struct ReplacedTextureLevel {
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
Path file;
|
||||
|
||||
// To be able to reload, we need to be able to reopen, unfortunate we can't use zip_file_t.
|
||||
// TODO: This really belongs on the level in the cache, not in the individual ReplacedTextureLevel objects.
|
||||
VFSFileReference *fileRef = nullptr;
|
||||
};
|
||||
|
||||
ReplacedImageType Identify(VFSBackend *vfs, VFSOpenFile *openFile, std::string *outMagic);
|
||||
|
||||
enum class ReplacementState : uint32_t {
|
||||
UNINITIALIZED,
|
||||
POPULATED, // We located the texture files but have not started the thread.
|
||||
PENDING,
|
||||
NOT_FOUND, // Also used on error loading the images.
|
||||
ACTIVE,
|
||||
CANCEL_INIT,
|
||||
};
|
||||
|
||||
const char *StateString(ReplacementState state);
|
||||
|
||||
struct ReplacementDesc {
|
||||
int newW;
|
||||
int newH;
|
||||
uint64_t cachekey;
|
||||
uint32_t hash;
|
||||
int w;
|
||||
int h;
|
||||
std::string hashfiles;
|
||||
Path basePath;
|
||||
bool foundAlias;
|
||||
std::vector<std::string> filenames;
|
||||
std::string logId;
|
||||
ReplacedLevelsCache *cache;
|
||||
};
|
||||
|
||||
// These aren't actually all replaced, they can also represent a placeholder for a not-found
|
||||
// replacement (state_ == NOT_FOUND).
|
||||
struct ReplacedTexture {
|
||||
~ReplacedTexture();
|
||||
|
||||
inline ReplacementState State() const {
|
||||
return state_;
|
||||
}
|
||||
|
||||
void SetState(ReplacementState state) {
|
||||
_dbg_assert_(state != state_);
|
||||
#ifdef _DEBUG
|
||||
// WARN_LOG(G3D, "Texture %s changed state from %s to %s", logId_.c_str(), StateString(state_), StateString(state));
|
||||
#endif
|
||||
state_ = state;
|
||||
}
|
||||
|
||||
void GetSize(int level, int *w, int *h) const {
|
||||
_dbg_assert_(State() == ReplacementState::ACTIVE);
|
||||
_dbg_assert_(level < levels_.size());
|
||||
*w = levels_[level].w;
|
||||
*h = levels_[level].h;
|
||||
}
|
||||
|
||||
int NumLevels() const {
|
||||
_dbg_assert_(State() == ReplacementState::ACTIVE);
|
||||
return (int)levels_.size();
|
||||
}
|
||||
|
||||
Draw::DataFormat Format() const {
|
||||
_dbg_assert_(State() == ReplacementState::ACTIVE);
|
||||
return fmt;
|
||||
}
|
||||
|
||||
u8 AlphaStatus() const {
|
||||
return (u8)alphaStatus_;
|
||||
}
|
||||
|
||||
bool IsReady(double budget);
|
||||
bool CopyLevelTo(int level, void *out, int rowPitch);
|
||||
|
||||
void FinishPopulate(const ReplacementDesc &desc);
|
||||
std::string logId_;
|
||||
|
||||
private:
|
||||
void Prepare(VFSBackend *vfs);
|
||||
void PrepareData(int level);
|
||||
void PurgeIfOlder(double t);
|
||||
|
||||
bool PopulateLevel(ReplacedTextureLevel & level, bool ignoreError);
|
||||
|
||||
std::vector<ReplacedTextureLevel> levels_;
|
||||
ReplacedLevelsCache *levelData_ = nullptr;
|
||||
|
||||
ReplacedTextureAlpha alphaStatus_ = ReplacedTextureAlpha::UNKNOWN;
|
||||
double lastUsed_ = 0.0;
|
||||
LimitedWaitable *threadWaitable_ = nullptr;
|
||||
std::mutex mutex_;
|
||||
Draw::DataFormat fmt = Draw::DataFormat::UNDEFINED; // NOTE: Right now, the only supported format is Draw::DataFormat::R8G8B8A8_UNORM.
|
||||
|
||||
ReplacementState state_ = ReplacementState::UNINITIALIZED;
|
||||
|
||||
VFSBackend *vfs_ = nullptr;
|
||||
|
||||
friend class TextureReplacer;
|
||||
friend class ReplacedTextureTask;
|
||||
};
|
||||
@@ -18,6 +18,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace Draw {
|
||||
class DrawContext;
|
||||
@@ -126,8 +128,15 @@ public:
|
||||
ShaderManagerCommon(Draw::DrawContext *draw) : draw_(draw) {}
|
||||
virtual ~ShaderManagerCommon() {}
|
||||
|
||||
virtual void ClearShaders() = 0;
|
||||
virtual void DirtyLastShader() = 0;
|
||||
|
||||
virtual void DeviceLost() = 0;
|
||||
virtual void DeviceRestore(Draw::DrawContext *draw) = 0; // must set draw_ to draw
|
||||
|
||||
virtual std::vector<std::string> DebugGetShaderIDs(DebugShaderType type) = 0;
|
||||
virtual std::string DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) = 0;
|
||||
|
||||
protected:
|
||||
Draw::DrawContext *draw_ = nullptr;
|
||||
};
|
||||
|
||||
@@ -135,12 +135,12 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView
|
||||
ConvertProjMatrixToVulkan(flippedMatrix);
|
||||
}
|
||||
|
||||
if (!useBufferedRendering && g_display_rotation != DisplayRotation::ROTATE_0) {
|
||||
flippedMatrix = flippedMatrix * g_display_rot_matrix;
|
||||
if (!useBufferedRendering && g_display.rotation != DisplayRotation::ROTATE_0) {
|
||||
flippedMatrix = flippedMatrix * g_display.rot_matrix;
|
||||
}
|
||||
CopyMatrix4x4(ub->proj, flippedMatrix.getReadPtr());
|
||||
|
||||
ub->rotation = useBufferedRendering ? 0 : (float)g_display_rotation;
|
||||
ub->rotation = useBufferedRendering ? 0 : (float)g_display.rotation;
|
||||
}
|
||||
|
||||
if (dirtyUniforms & DIRTY_PROJTHROUGHMATRIX) {
|
||||
@@ -150,8 +150,8 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView
|
||||
} else {
|
||||
proj_through.setOrthoVulkan(0.0f, gstate_c.curRTWidth, 0, gstate_c.curRTHeight, 0, 1);
|
||||
}
|
||||
if (!useBufferedRendering && g_display_rotation != DisplayRotation::ROTATE_0) {
|
||||
proj_through = proj_through * g_display_rot_matrix;
|
||||
if (!useBufferedRendering && g_display.rotation != DisplayRotation::ROTATE_0) {
|
||||
proj_through = proj_through * g_display.rot_matrix;
|
||||
}
|
||||
|
||||
// Negative RT offsets come from split framebuffers (Killzone)
|
||||
@@ -252,6 +252,7 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView
|
||||
|
||||
// These are just the reverse of the formulas in GPUStateUtils.
|
||||
float halfActualZRange = gstate_c.vpDepthScale != 0.0f ? vpZScale / gstate_c.vpDepthScale : 0.0f;
|
||||
float inverseDepthScale = gstate_c.vpDepthScale != 0.0f ? 1.0f / gstate_c.vpDepthScale : 0.0f;
|
||||
float minz = -((gstate_c.vpZOffset * halfActualZRange) - vpZCenter) - halfActualZRange;
|
||||
float viewZScale = halfActualZRange * 2.0f;
|
||||
float viewZCenter = minz;
|
||||
@@ -259,7 +260,7 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView
|
||||
ub->depthRange[0] = viewZScale;
|
||||
ub->depthRange[1] = viewZCenter;
|
||||
ub->depthRange[2] = gstate_c.vpZOffset * 0.5f + 0.5f;
|
||||
ub->depthRange[3] = 2.0f * (1.0f / gstate_c.vpDepthScale);
|
||||
ub->depthRange[3] = 2.0f * inverseDepthScale;
|
||||
}
|
||||
|
||||
if (dirtyUniforms & DIRTY_CULLRANGE) {
|
||||
|
||||
@@ -304,7 +304,7 @@ bool FramebufferManagerCommon::PerformWriteStencilFromMemory(u32 addr, int size,
|
||||
}
|
||||
|
||||
Draw::Viewport viewport = { 0.0f, 0.0f, (float)w, (float)h, 0.0f, 1.0f };
|
||||
draw_->SetViewports(1, &viewport);
|
||||
draw_->SetViewport(viewport);
|
||||
|
||||
// TODO: Switch the format to a single channel format?
|
||||
Draw::Texture *tex = MakePixelTexture(src, dstBuffer->fb_format, dstBuffer->fb_stride, dstBuffer->width, dstBuffer->height);
|
||||
|
||||
@@ -107,7 +107,7 @@ inline int dimHeight(u16 dim) {
|
||||
}
|
||||
|
||||
TextureCacheCommon::TextureCacheCommon(Draw::DrawContext *draw, Draw2D *draw2D)
|
||||
: draw_(draw), draw2D_(draw2D) {
|
||||
: draw_(draw), draw2D_(draw2D), replacer_(draw) {
|
||||
decimationCounter_ = TEXCACHE_DECIMATION_INTERVAL;
|
||||
|
||||
// It's only possible to have 1KB of palette entries, although we allow 2KB in a hack.
|
||||
@@ -138,6 +138,30 @@ TextureCacheCommon::~TextureCacheCommon() {
|
||||
FreeAlignedMemory(expandClut_);
|
||||
}
|
||||
|
||||
void TextureCacheCommon::StartFrame() {
|
||||
ForgetLastTexture();
|
||||
textureShaderCache_->Decimate();
|
||||
timesInvalidatedAllThisFrame_ = 0;
|
||||
replacementTimeThisFrame_ = 0.0;
|
||||
|
||||
if (g_Config.bShowDebugStats) {
|
||||
gpuStats.numReplacerTrackedTex = replacer_.GetNumTrackedTextures();
|
||||
gpuStats.numCachedReplacedTextures = replacer_.GetNumCachedReplacedTextures();
|
||||
}
|
||||
|
||||
if (texelsScaledThisFrame_) {
|
||||
VERBOSE_LOG(G3D, "Scaled %d texels", texelsScaledThisFrame_);
|
||||
}
|
||||
texelsScaledThisFrame_ = 0;
|
||||
|
||||
if (clearCacheNextFrame_) {
|
||||
Clear(true);
|
||||
clearCacheNextFrame_ = false;
|
||||
} else {
|
||||
Decimate(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Produces a signed 1.23.8 value.
|
||||
static int TexLog2(float delta) {
|
||||
union FloatBits {
|
||||
@@ -509,17 +533,31 @@ TexCacheEntry *TextureCacheCommon::SetTexture() {
|
||||
int w0 = gstate.getTextureWidth(0);
|
||||
int h0 = gstate.getTextureHeight(0);
|
||||
int d0 = 1;
|
||||
ReplacedTexture &replaced = FindReplacement(entry, w0, h0, d0);
|
||||
if (replaced.IsInvalid()) {
|
||||
entry->status &= ~TexCacheEntry::STATUS_TO_REPLACE;
|
||||
if (g_Config.bSaveNewTextures) {
|
||||
// Load once more to actually save.
|
||||
ReplacedTexture *replaced = FindReplacement(entry, w0, h0, d0);
|
||||
if (replaced) {
|
||||
// This texture is pending a replacement load.
|
||||
// So check the replacer if it's reached a conclusion.
|
||||
switch (replaced->State()) {
|
||||
case ReplacementState::NOT_FOUND:
|
||||
// Didn't find a replacement, so stop looking.
|
||||
WARN_LOG(G3D, "No replacement for texture %dx%d", w0, h0);
|
||||
entry->status &= ~TexCacheEntry::STATUS_TO_REPLACE;
|
||||
if (g_Config.bSaveNewTextures) {
|
||||
// Load it once more to actually save it. Since we don't set STATUS_TO_REPLACE, we won't end up looping.
|
||||
match = false;
|
||||
reason = "replacing";
|
||||
}
|
||||
break;
|
||||
case ReplacementState::ACTIVE:
|
||||
// There is now replacement data available!
|
||||
// Just reload the texture to process the replacement.
|
||||
match = false;
|
||||
reason = "replacing";
|
||||
break;
|
||||
default:
|
||||
// We'll just wait for a result.
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
match = false;
|
||||
reason = "replacing";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -817,7 +855,7 @@ void TextureCacheCommon::HandleTextureChange(TexCacheEntry *const entry, const c
|
||||
gpuStats.numTextureInvalidations++;
|
||||
DEBUG_LOG(G3D, "Texture different or overwritten, reloading at %08x: %s", entry->addr, reason);
|
||||
if (doDelete) {
|
||||
InvalidateLastTexture();
|
||||
ForgetLastTexture();
|
||||
ReleaseTexture(entry, true);
|
||||
entry->status &= ~TexCacheEntry::STATUS_IS_SCALED;
|
||||
}
|
||||
@@ -1463,17 +1501,21 @@ u32 TextureCacheCommon::EstimateTexMemoryUsage(const TexCacheEntry *entry) {
|
||||
return pixelSize << (dimW + dimH);
|
||||
}
|
||||
|
||||
ReplacedTexture &TextureCacheCommon::FindReplacement(TexCacheEntry *entry, int &w, int &h, int &d) {
|
||||
ReplacedTexture *TextureCacheCommon::FindReplacement(TexCacheEntry *entry, int &w, int &h, int &d) {
|
||||
if (d != 1) {
|
||||
// We don't yet support replacing 3D textures.
|
||||
return replacer_.FindNone();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Short circuit the non-enabled case.
|
||||
// Otherwise, due to bReplaceTexturesAllowLate, we'll still spawn tasks looking for replacements
|
||||
// that then won't be used.
|
||||
if (!replacer_.Enabled()) {
|
||||
return replacer_.FindNone();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((entry->status & TexCacheEntry::STATUS_VIDEO) && replacer_.AllowVideo()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allow some delay to reduce pop-in.
|
||||
@@ -1482,17 +1524,32 @@ ReplacedTexture &TextureCacheCommon::FindReplacement(TexCacheEntry *entry, int &
|
||||
double replaceStart = time_now_d();
|
||||
double budget = std::min(MAX_BUDGET_PER_TEX, replacementFrameBudget_ - replacementTimeThisFrame_);
|
||||
u64 cachekey = replacer_.Enabled() ? entry->CacheKey() : 0;
|
||||
ReplacedTexture &replaced = replacer_.FindReplacement(cachekey, entry->fullhash, w, h, budget);
|
||||
if (replaced.IsReady(budget)) {
|
||||
if (replaced.GetSize(0, w, h)) {
|
||||
// Consider it already "scaled."
|
||||
ReplacedTexture *replaced = replacer_.FindReplacement(cachekey, entry->fullhash, w, h, budget);
|
||||
if (!replaced) {
|
||||
// TODO: Remove the flag here?
|
||||
// entry->status &= ~TexCacheEntry::STATUS_TO_REPLACE;
|
||||
replacementTimeThisFrame_ += time_now_d() - replaceStart;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (replaced->IsReady(budget)) {
|
||||
if (replaced->State() == ReplacementState::ACTIVE) {
|
||||
replaced->GetSize(0, &w, &h);
|
||||
// Consider it already "scaled.".
|
||||
entry->status |= TexCacheEntry::STATUS_IS_SCALED;
|
||||
}
|
||||
|
||||
// Remove the flag, even if it was invalid.
|
||||
entry->status &= ~TexCacheEntry::STATUS_TO_REPLACE;
|
||||
} else if (!replaced.IsInvalid()) {
|
||||
}
|
||||
|
||||
switch (replaced->State()) {
|
||||
case ReplacementState::POPULATED:
|
||||
case ReplacementState::PENDING:
|
||||
case ReplacementState::UNINITIALIZED:
|
||||
// Make sure we keep polling.
|
||||
entry->status |= TexCacheEntry::STATUS_TO_REPLACE;
|
||||
break;
|
||||
}
|
||||
replacementTimeThisFrame_ += time_now_d() - replaceStart;
|
||||
return replaced;
|
||||
@@ -1990,7 +2047,7 @@ void TextureCacheCommon::ApplyTexture() {
|
||||
TexCacheEntry *entry = nextTexture_;
|
||||
if (!entry) {
|
||||
// Maybe we bound a framebuffer?
|
||||
InvalidateLastTexture();
|
||||
ForgetLastTexture();
|
||||
if (failedTexture_) {
|
||||
// Backends should handle this by binding a black texture with 0 alpha.
|
||||
BindTexture(nullptr);
|
||||
@@ -2013,7 +2070,7 @@ void TextureCacheCommon::ApplyTexture() {
|
||||
// Regardless of hash fails or otherwise, if this is a video, mark it frequently changing.
|
||||
// This prevents temporary scaling perf hits on the first second of video.
|
||||
if (IsVideo(entry->addr)) {
|
||||
entry->status |= TexCacheEntry::STATUS_CHANGE_FREQUENT;
|
||||
entry->status |= TexCacheEntry::STATUS_CHANGE_FREQUENT | TexCacheEntry::STATUS_VIDEO;
|
||||
}
|
||||
|
||||
if (nextNeedsRehash_) {
|
||||
@@ -2050,7 +2107,7 @@ void TextureCacheCommon::ApplyTexture() {
|
||||
if (nextNeedsRebuild_) {
|
||||
_assert_(!entry->texturePtr);
|
||||
BuildTexture(entry);
|
||||
InvalidateLastTexture();
|
||||
ForgetLastTexture();
|
||||
}
|
||||
|
||||
if (entry->status & TexCacheEntry::STATUS_CLUT_GPU) {
|
||||
@@ -2205,7 +2262,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
|
||||
gstate_c.SetTextureFullAlpha(alphaStatus == CHECKALPHA_FULL);
|
||||
|
||||
draw_->Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
|
||||
InvalidateLastTexture();
|
||||
ForgetLastTexture();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2247,8 +2304,8 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
|
||||
draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Depal");
|
||||
draw_->InvalidateFramebuffer(Draw::FB_INVALIDATION_STORE, Draw::FB_DEPTH_BIT | Draw::FB_STENCIL_BIT);
|
||||
draw_->SetScissorRect(u1, v1, u2 - u1, v2 - v1);
|
||||
Draw::Viewport vp{ 0.0f, 0.0f, (float)depalWidth, (float)framebuffer->renderHeight, 0.0f, 1.0f };
|
||||
draw_->SetViewports(1, &vp);
|
||||
Draw::Viewport viewport{ 0.0f, 0.0f, (float)depalWidth, (float)framebuffer->renderHeight, 0.0f, 1.0f };
|
||||
draw_->SetViewport(viewport);
|
||||
|
||||
draw_->BindFramebufferAsTexture(framebuffer->fbo, 0, depth ? Draw::FB_DEPTH_BIT : Draw::FB_COLOR_BIT, Draw::ALL_LAYERS);
|
||||
if (clutRenderAddress_ == 0xFFFFFFFF) {
|
||||
@@ -2352,8 +2409,8 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) {
|
||||
draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Depal");
|
||||
draw_->InvalidateFramebuffer(Draw::FB_INVALIDATION_STORE, Draw::FB_DEPTH_BIT | Draw::FB_STENCIL_BIT);
|
||||
draw_->SetScissorRect(u1, v1, u2 - u1, v2 - v1);
|
||||
Draw::Viewport vp{ 0.0f, 0.0f, (float)texWidth, (float)texHeight, 0.0f, 1.0f };
|
||||
draw_->SetViewports(1, &vp);
|
||||
Draw::Viewport viewport{ 0.0f, 0.0f, (float)texWidth, (float)texHeight, 0.0f, 1.0f };
|
||||
draw_->SetViewport(viewport);
|
||||
|
||||
draw_->BindNativeTexture(0, GetNativeTextureView(entry));
|
||||
draw_->BindFramebufferAsTexture(dynamicClutFbo_, 1, Draw::FB_COLOR_BIT, 0);
|
||||
@@ -2759,10 +2816,10 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt
|
||||
}
|
||||
|
||||
if (canReplace) {
|
||||
plan.replaced = &FindReplacement(entry, plan.w, plan.h, plan.depth);
|
||||
plan.replaceValid = plan.replaced->Valid();
|
||||
plan.replaced = FindReplacement(entry, plan.w, plan.h, plan.depth);
|
||||
plan.replaceValid = plan.replaced ? plan.replaced->State() == ReplacementState::ACTIVE : false;
|
||||
} else {
|
||||
plan.replaced = &replacer_.FindNone();
|
||||
plan.replaced = nullptr;
|
||||
plan.replaceValid = false;
|
||||
}
|
||||
|
||||
@@ -2776,7 +2833,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt
|
||||
plan.levelsToCreate = std::min(plan.levelsToLoad, plan.levelsToCreate);
|
||||
plan.badMipSizes = false;
|
||||
// But, we still need to create the texture at a larger size.
|
||||
plan.replaced->GetSize(0, plan.createW, plan.createH);
|
||||
plan.replaced->GetSize(0, &plan.createW, &plan.createH);
|
||||
} else {
|
||||
if (replacer_.Enabled() && !plan.replaceValid && plan.depth == 1 && canReplace) {
|
||||
ReplacedTextureDecodeInfo replacedInfo;
|
||||
@@ -2784,9 +2841,9 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt
|
||||
replacedInfo.cachekey = entry->CacheKey();
|
||||
replacedInfo.hash = entry->fullhash;
|
||||
replacedInfo.addr = entry->addr;
|
||||
replacedInfo.isVideo = plan.isVideo;
|
||||
replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0;
|
||||
replacedInfo.scaleFactor = plan.scaleFactor;
|
||||
replacedInfo.isVideo = plan.isVideo;
|
||||
replacedInfo.fmt = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
plan.saveTexture = replacer_.WillSave(replacedInfo);
|
||||
}
|
||||
@@ -2828,9 +2885,10 @@ void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, i
|
||||
|
||||
PROFILE_THIS_SCOPE("decodetex");
|
||||
|
||||
if (plan.replaceValid && plan.replaced->GetSize(srcLevel, w, h)) {
|
||||
if (plan.replaceValid) {
|
||||
plan.replaced->GetSize(srcLevel, &w, &h);
|
||||
double replaceStart = time_now_d();
|
||||
plan.replaced->Load(srcLevel, data, stride);
|
||||
plan.replaced->CopyLevelTo(srcLevel, data, stride);
|
||||
replacementTimeThisFrame_ += time_now_d() - replaceStart;
|
||||
} else {
|
||||
GETextureFormat tfmt = (GETextureFormat)entry.format;
|
||||
@@ -2877,7 +2935,7 @@ void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, i
|
||||
}
|
||||
}
|
||||
|
||||
if (replacer_.Enabled() && plan.replaced->IsInvalid()) {
|
||||
if (plan.saveTexture && !lowMemoryMode_) {
|
||||
ReplacedTextureDecodeInfo replacedInfo;
|
||||
replacedInfo.cachekey = entry.CacheKey();
|
||||
replacedInfo.hash = entry.fullhash;
|
||||
@@ -2906,7 +2964,3 @@ CheckAlphaResult TextureCacheCommon::CheckCLUTAlpha(const uint8_t *pixelData, GE
|
||||
return CheckAlpha32((const u32 *)pixelData, w, 0xFF000000);
|
||||
}
|
||||
}
|
||||
|
||||
void TextureCacheCommon::StartFrame() {
|
||||
textureShaderCache_->Decimate();
|
||||
}
|
||||
@@ -23,13 +23,13 @@
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Core/TextureReplacer.h"
|
||||
#include "Core/System.h"
|
||||
#include "GPU/GPU.h"
|
||||
#include "GPU/Common/GPUDebugInterface.h"
|
||||
#include "GPU/Common/TextureDecoder.h"
|
||||
#include "GPU/Common/TextureScalerCommon.h"
|
||||
#include "GPU/Common/TextureShaderCommon.h"
|
||||
#include "GPU/Common/TextureReplacer.h"
|
||||
|
||||
class Draw2D;
|
||||
|
||||
@@ -149,10 +149,12 @@ struct TexCacheEntry {
|
||||
STATUS_3D = 0x4000,
|
||||
|
||||
STATUS_CLUT_GPU = 0x8000,
|
||||
|
||||
STATUS_VIDEO = 0x10000,
|
||||
};
|
||||
|
||||
// Status, but int so we can zero initialize.
|
||||
int status;
|
||||
// TexStatus enum flag combination.
|
||||
u32 status;
|
||||
|
||||
u32 addr;
|
||||
u32 minihash;
|
||||
@@ -290,7 +292,8 @@ struct BuildTexturePlan {
|
||||
bool decodeToClut8;
|
||||
|
||||
void GetMipSize(int level, int *w, int *h) const {
|
||||
if (replaceValid && replaced->GetSize(level, *w, *h)) {
|
||||
if (replaceValid) {
|
||||
replaced->GetSize(level, w, h);
|
||||
return;
|
||||
}
|
||||
if (depth == 1) {
|
||||
@@ -329,7 +332,6 @@ public:
|
||||
TextureShaderCache *GetTextureShaderCache() { return textureShaderCache_; }
|
||||
|
||||
virtual void ForgetLastTexture() = 0;
|
||||
virtual void InvalidateLastTexture() = 0;
|
||||
virtual void Clear(bool delete_them);
|
||||
virtual void NotifyConfigChanged();
|
||||
virtual void ApplySamplingParams(const SamplerCacheKey &key) = 0;
|
||||
@@ -351,6 +353,11 @@ public:
|
||||
}
|
||||
virtual bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level, bool *isFramebuffer) { return false; }
|
||||
|
||||
virtual void StartFrame();
|
||||
|
||||
virtual void DeviceLost() = 0;
|
||||
virtual void DeviceRestore(Draw::DrawContext *draw) = 0;
|
||||
|
||||
protected:
|
||||
virtual void *GetNativeTextureView(const TexCacheEntry *entry) = 0;
|
||||
bool PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEntry *entry);
|
||||
@@ -374,7 +381,7 @@ protected:
|
||||
CheckAlphaResult DecodeTextureLevel(u8 *out, int outPitch, GETextureFormat format, GEPaletteFormat clutformat, uint32_t texaddr, int level, int bufw, TexDecodeFlags flags);
|
||||
void UnswizzleFromMem(u32 *dest, u32 destPitch, const u8 *texptr, u32 bufw, u32 height, u32 bytesPerPixel);
|
||||
CheckAlphaResult ReadIndexedTex(u8 *out, int outPitch, int level, const u8 *texptr, int bytesPerIndex, int bufw, bool reverseColors, bool expandTo32Bit);
|
||||
ReplacedTexture &FindReplacement(TexCacheEntry *entry, int &w, int &h, int &d);
|
||||
ReplacedTexture *FindReplacement(TexCacheEntry *entry, int &w, int &h, int &d);
|
||||
|
||||
// Return value is mapData normally, but could be another buffer allocated with AllocateAlignedMemory.
|
||||
void LoadTextureLevel(TexCacheEntry &entry, uint8_t *mapData, int mapRowPitch, BuildTexturePlan &plan, int srcLevel, Draw::DataFormat dstFmt, TexDecodeFlags texDecFlags);
|
||||
@@ -404,8 +411,6 @@ protected:
|
||||
|
||||
virtual void BoundFramebufferTexture() {}
|
||||
|
||||
virtual void StartFrame();
|
||||
|
||||
void DecimateVideos();
|
||||
bool IsVideo(u32 texaddr) const;
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,173 @@
|
||||
// Copyright (c) 2016- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Common/File/Path.h"
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
#include "Common/GPU/DataFormat.h"
|
||||
|
||||
#include "GPU/Common/TextureDecoder.h"
|
||||
#include "GPU/Common/ReplacedTexture.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
|
||||
class IniFile;
|
||||
class TextureCacheCommon;
|
||||
class TextureReplacer;
|
||||
class ReplacedTextureTask;
|
||||
class LimitedWaitable;
|
||||
class VFSBackend;
|
||||
|
||||
struct SavedTextureCacheData {
|
||||
int levelW[8]{};
|
||||
int levelH[8]{};
|
||||
bool levelSaved[8]{};
|
||||
double lastTimeSaved = 0.0;
|
||||
};
|
||||
|
||||
struct ReplacedLevelsCache {
|
||||
std::mutex lock;
|
||||
std::vector<std::vector<uint8_t>> data;
|
||||
double lastUsed = 0.0;
|
||||
};
|
||||
|
||||
struct ReplacementCacheKey {
|
||||
u64 cachekey;
|
||||
u32 hash;
|
||||
|
||||
ReplacementCacheKey(u64 c, u32 h) : cachekey(c), hash(h) { }
|
||||
|
||||
bool operator ==(const ReplacementCacheKey &k) const {
|
||||
return k.cachekey == cachekey && k.hash == hash;
|
||||
}
|
||||
|
||||
bool operator <(const ReplacementCacheKey &k) const {
|
||||
if (k.cachekey == cachekey) {
|
||||
return k.hash < hash;
|
||||
}
|
||||
return k.cachekey < cachekey;
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<ReplacementCacheKey> {
|
||||
size_t operator()(const ReplacementCacheKey &k) const {
|
||||
return std::hash<u64>()(k.cachekey ^ ((u64)k.hash << 32));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct ReplacedTextureDecodeInfo {
|
||||
u64 cachekey;
|
||||
u32 hash;
|
||||
u32 addr;
|
||||
bool isVideo;
|
||||
bool isFinal;
|
||||
int scaleFactor;
|
||||
Draw::DataFormat fmt;
|
||||
};
|
||||
|
||||
enum class ReplacerDecimateMode {
|
||||
NEW_FRAME,
|
||||
FORCE_PRESSURE,
|
||||
ALL,
|
||||
};
|
||||
|
||||
class TextureReplacer {
|
||||
public:
|
||||
// The draw context will be checked for supported texture formats.
|
||||
TextureReplacer(Draw::DrawContext *draw);
|
||||
~TextureReplacer();
|
||||
|
||||
void Init();
|
||||
void NotifyConfigChanged();
|
||||
|
||||
bool Enabled() const { return enabled_; }
|
||||
bool AllowVideo() const { return allowVideo_; }
|
||||
|
||||
u32 ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV);
|
||||
|
||||
// Returns nullptr if not found.
|
||||
ReplacedTexture *FindReplacement(u64 cachekey, u32 hash, int w, int h, double budget);
|
||||
bool FindFiltering(u64 cachekey, u32 hash, TextureFiltering *forceFiltering);
|
||||
|
||||
// Check if a NotifyTextureDecoded for this texture is desired (used to avoid reads from write-combined memory.)
|
||||
bool WillSave(const ReplacedTextureDecodeInfo &replacedInfo);
|
||||
|
||||
// Notify that a new texture was decoded. May already be upscaled, saves the data passed.
|
||||
void NotifyTextureDecoded(const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int w, int h);
|
||||
|
||||
void Decimate(ReplacerDecimateMode mode);
|
||||
|
||||
static bool GenerateIni(const std::string &gameID, Path &generatedFilename);
|
||||
static bool IniExists(const std::string &gameID);
|
||||
|
||||
int GetNumTrackedTextures() const { return (int)cache_.size(); }
|
||||
int GetNumCachedReplacedTextures() const { return (int)levelCache_.size(); }
|
||||
|
||||
static std::string HashName(u64 cachekey, u32 hash, int level);
|
||||
|
||||
protected:
|
||||
bool LoadIni();
|
||||
bool LoadIniValues(IniFile &ini, bool isOverride = false);
|
||||
void ParseHashRange(const std::string &key, const std::string &value);
|
||||
void ParseFiltering(const std::string &key, const std::string &value);
|
||||
void ParseReduceHashRange(const std::string& key, const std::string& value);
|
||||
bool LookupHashRange(u32 addr, int &w, int &h);
|
||||
float LookupReduceHashRange(int& w, int& h);
|
||||
std::string LookupHashFile(u64 cachekey, u32 hash, bool *foundAlias, bool *ignored);
|
||||
void PopulateReplacement(ReplacedTexture *result, u64 cachekey, u32 hash, int w, int h);
|
||||
|
||||
bool enabled_ = false;
|
||||
bool allowVideo_ = false;
|
||||
bool ignoreAddress_ = false;
|
||||
bool reduceHash_ = false;
|
||||
float reduceHashSize = 1.0f; // default value with reduceHash to false
|
||||
float reduceHashGlobalValue = 0.5f; // Global value for textures dump pngs of all sizes, 0.5 by default but can be set in textures.ini
|
||||
|
||||
double lastTextureCacheSizeGB_ = 0.0;
|
||||
bool ignoreMipmap_ = false;
|
||||
std::string gameID_;
|
||||
Path basePath_;
|
||||
Path newTextureDir_;
|
||||
ReplacedTextureHash hash_ = ReplacedTextureHash::QUICK;
|
||||
|
||||
VFSBackend *vfs_ = nullptr;
|
||||
bool vfsIsZip_ = false;
|
||||
|
||||
typedef std::pair<int, int> WidthHeightPair;
|
||||
std::unordered_map<u64, WidthHeightPair> hashranges_;
|
||||
std::unordered_map<u64, float> reducehashranges_;
|
||||
std::unordered_map<ReplacementCacheKey, std::string> aliases_;
|
||||
std::unordered_map<ReplacementCacheKey, TextureFiltering> filtering_;
|
||||
|
||||
std::unordered_map<ReplacementCacheKey, ReplacedTexture *> cache_;
|
||||
std::unordered_map<ReplacementCacheKey, SavedTextureCacheData> savedCache_;
|
||||
|
||||
// the key is from aliases_. It's a |-separated sequence of texture filenames of the levels of a texture.
|
||||
std::unordered_map<std::string, ReplacedLevelsCache> levelCache_;
|
||||
};
|
||||
@@ -1274,7 +1274,7 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options,
|
||||
|
||||
// Attempt to JIT as well. But only do that if the main CPU JIT is enabled, in order to aid
|
||||
// debugging attempts - if the main JIT doesn't work, this one won't do any better, probably.
|
||||
if (jitCache && g_Config.bVertexDecoderJit && g_Config.iCpuCore == (int)CPUCore::JIT) {
|
||||
if (jitCache) {
|
||||
jitted_ = jitCache->Compile(*this, &jittedSize_);
|
||||
if (!jitted_) {
|
||||
WARN_LOG(G3D, "Vertex decoder JIT failed! fmt = %08x (%s)", fmt_, GetString(SHADER_STRING_SHORT_DESC).c_str());
|
||||
|
||||
@@ -1350,7 +1350,8 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
|
||||
}
|
||||
if (gstate_c.Use(GPU_USE_CULL_DISTANCE)) {
|
||||
// Cull any triangle fully outside in the same direction when depth clamp enabled.
|
||||
WRITE(p, " if (u_cullRangeMin.w > 0.0) {\n");
|
||||
// We check u_depthRange in case depthScale was zero - in that case we can't work out the cull distance.
|
||||
WRITE(p, " if (u_cullRangeMin.w > 0.0 && u_depthRange.w != 0.0f) {\n");
|
||||
WRITE(p, " %sgl_CullDistance%s = projPos.z - u_cullRangeMin.z;\n", compat.vsOutPrefix, cull0);
|
||||
WRITE(p, " %sgl_CullDistance%s = u_cullRangeMax.z - projPos.z;\n", compat.vsOutPrefix, cull1);
|
||||
WRITE(p, " } else {\n");
|
||||
|
||||
@@ -634,8 +634,8 @@ rotateVBO:
|
||||
swTransform.Decode(prim, dec_->VertexType(), dec_->GetDecVtxFmt(), maxIndex, &result);
|
||||
// Non-zero depth clears are unusual, but some drivers don't match drawn depth values to cleared values.
|
||||
// Games sometimes expect exact matches (see #12626, for example) for equal comparisons.
|
||||
if (result.action == SW_NOT_READY && everUsedEqualDepth_ && gstate.isClearModeDepthMask() && result.depth > 0.0f && result.depth < 1.0f)
|
||||
result.action = SW_DRAW_PRIMITIVES;
|
||||
if (result.action == SW_CLEAR && everUsedEqualDepth_ && gstate.isClearModeDepthMask() && result.depth > 0.0f && result.depth < 1.0f)
|
||||
result.action = SW_NOT_READY;
|
||||
if (result.action == SW_NOT_READY) {
|
||||
swTransform.DetectOffsetTexture(maxIndex);
|
||||
}
|
||||
|
||||
@@ -119,6 +119,9 @@ public:
|
||||
DrawEngineD3D11(Draw::DrawContext *draw, ID3D11Device *device, ID3D11DeviceContext *context);
|
||||
~DrawEngineD3D11();
|
||||
|
||||
void DeviceLost() override { draw_ = nullptr; }
|
||||
void DeviceRestore(Draw::DrawContext *draw) override { draw_ = draw; }
|
||||
|
||||
void SetShaderManager(ShaderManagerD3D11 *shaderManager) {
|
||||
shaderManager_ = shaderManager;
|
||||
}
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <algorithm>
|
||||
#include <d3d11.h>
|
||||
#include <D3Dcompiler.h>
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/GPU/thin3d.h"
|
||||
|
||||
|
||||
@@ -17,20 +17,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <d3d11.h>
|
||||
|
||||
// Keeps track of allocated FBOs.
|
||||
// Also provides facilities for drawing and later converting raw
|
||||
// pixel data.
|
||||
|
||||
#include "GPU/GPUCommon.h"
|
||||
#include "GPU/Common/FramebufferManagerCommon.h"
|
||||
#include "Common/GPU/thin3d.h"
|
||||
|
||||
class TextureCacheD3D11;
|
||||
class DrawEngineD3D11;
|
||||
class ShaderManagerD3D11;
|
||||
|
||||
class FramebufferManagerD3D11 : public FramebufferManagerCommon {
|
||||
public:
|
||||
FramebufferManagerD3D11(Draw::DrawContext *draw);
|
||||
|
||||
+13
-107
@@ -15,7 +15,7 @@
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "Common/Log.h"
|
||||
#include "Common/Serialize/Serializer.h"
|
||||
@@ -42,7 +42,7 @@
|
||||
#include "GPU/D3D11/D3D11Util.h"
|
||||
|
||||
GPU_D3D11::GPU_D3D11(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
: GPUCommon(gfxCtx, draw), drawEngine_(draw,
|
||||
: GPUCommonHW(gfxCtx, draw), drawEngine_(draw,
|
||||
(ID3D11Device *)draw->GetNativeObject(Draw::NativeObject::DEVICE),
|
||||
(ID3D11DeviceContext *)draw->GetNativeObject(Draw::NativeObject::CONTEXT)) {
|
||||
device_ = (ID3D11Device *)draw->GetNativeObject(Draw::NativeObject::DEVICE);
|
||||
@@ -87,16 +87,11 @@ GPU_D3D11::GPU_D3D11(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
}
|
||||
|
||||
GPU_D3D11::~GPU_D3D11() {
|
||||
framebufferManagerD3D11_->DestroyAllFBOs();
|
||||
delete framebufferManagerD3D11_;
|
||||
shaderManagerD3D11_->ClearShaders();
|
||||
delete shaderManagerD3D11_;
|
||||
delete textureCacheD3D11_;
|
||||
stockD3D11.Destroy();
|
||||
}
|
||||
|
||||
u32 GPU_D3D11::CheckGPUFeatures() const {
|
||||
u32 features = GPUCommon::CheckGPUFeatures();
|
||||
u32 features = GPUCommonHW::CheckGPUFeatures();
|
||||
|
||||
// Accurate depth is required because the Direct3D API does not support inverse Z.
|
||||
// So we cannot incorrectly use the viewport transform as the depth range on Direct3D.
|
||||
@@ -116,102 +111,49 @@ u32 GPU_D3D11::CheckGPUFeatures() const {
|
||||
return CheckGPUFeaturesLate(features);
|
||||
}
|
||||
|
||||
// Needs to be called on GPU thread, not reporting thread.
|
||||
void GPU_D3D11::BuildReportingInfo() {
|
||||
using namespace Draw;
|
||||
DrawContext *thin3d = gfxCtx_->GetDrawContext();
|
||||
|
||||
reportingPrimaryInfo_ = thin3d->GetInfoString(InfoField::VENDORSTRING);
|
||||
reportingFullInfo_ = reportingPrimaryInfo_ + " - " + System_GetProperty(SYSPROP_GPUDRIVER_VERSION) + " - " + thin3d->GetInfoString(InfoField::SHADELANGVERSION);
|
||||
}
|
||||
|
||||
void GPU_D3D11::DeviceLost() {
|
||||
draw_->Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
|
||||
// Simply drop all caches and textures.
|
||||
// FBOs appear to survive? Or no?
|
||||
shaderManagerD3D11_->ClearShaders();
|
||||
shaderManager_->ClearShaders();
|
||||
drawEngine_.ClearInputLayoutMap();
|
||||
textureCacheD3D11_->Clear(false);
|
||||
textureCache_->Clear(false);
|
||||
|
||||
GPUCommon::DeviceLost();
|
||||
GPUCommonHW::DeviceLost();
|
||||
}
|
||||
|
||||
void GPU_D3D11::DeviceRestore() {
|
||||
GPUCommon::DeviceRestore();
|
||||
void GPU_D3D11::DeviceRestore(Draw::DrawContext *draw) {
|
||||
GPUCommonHW::DeviceRestore(draw);
|
||||
// Nothing needed.
|
||||
}
|
||||
|
||||
void GPU_D3D11::InitClear() {
|
||||
if (!framebufferManager_->UseBufferedRendering()) {
|
||||
// device_->Clear(0, NULL, D3DCLEAR_STENCIL | D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.f, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_D3D11::BeginFrame() {
|
||||
GPUCommon::BeginFrame();
|
||||
GPUCommonHW::BeginFrame();
|
||||
|
||||
textureCacheD3D11_->StartFrame();
|
||||
textureCache_->StartFrame();
|
||||
drawEngine_.BeginFrame();
|
||||
|
||||
shaderManagerD3D11_->DirtyLastShader();
|
||||
shaderManager_->DirtyLastShader();
|
||||
|
||||
framebufferManagerD3D11_->BeginFrame();
|
||||
framebufferManager_->BeginFrame();
|
||||
gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX);
|
||||
|
||||
if (gstate_c.useFlagsChanged) {
|
||||
// TODO: It'd be better to recompile them in the background, probably?
|
||||
// This most likely means that saw equal depth changed.
|
||||
WARN_LOG(G3D, "Shader use flags changed, clearing all shaders and depth buffers");
|
||||
shaderManagerD3D11_->ClearShaders();
|
||||
shaderManager_->ClearShaders();
|
||||
framebufferManager_->ClearAllDepthBuffers();
|
||||
drawEngine_.ClearInputLayoutMap();
|
||||
gstate_c.useFlagsChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_D3D11::CopyDisplayToOutput(bool reallyDirty) {
|
||||
// Flush anything left over.
|
||||
drawEngine_.Flush();
|
||||
|
||||
shaderManager_->DirtyLastShader();
|
||||
|
||||
framebufferManagerD3D11_->CopyDisplayToOutput(reallyDirty);
|
||||
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_IMAGE);
|
||||
}
|
||||
|
||||
void GPU_D3D11::FinishDeferred() {
|
||||
// This finishes reading any vertex data that is pending.
|
||||
drawEngine_.FinishDeferred();
|
||||
}
|
||||
|
||||
inline void GPU_D3D11::CheckFlushOp(int cmd, u32 diff) {
|
||||
const u8 cmdFlags = cmdInfo_[cmd].flags;
|
||||
if (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE)) {
|
||||
if (dumpThisFrame_) {
|
||||
NOTICE_LOG(G3D, "================ FLUSH ================");
|
||||
}
|
||||
drawEngine_.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_D3D11::PreExecuteOp(u32 op, u32 diff) {
|
||||
CheckFlushOp(op >> 24, diff);
|
||||
}
|
||||
|
||||
void GPU_D3D11::ExecuteOp(u32 op, u32 diff) {
|
||||
const u8 cmd = op >> 24;
|
||||
const CommandInfo info = cmdInfo_[cmd];
|
||||
const u8 cmdFlags = info.flags;
|
||||
if ((cmdFlags & FLAG_EXECUTE) || (diff && (cmdFlags & FLAG_EXECUTEONCHANGE))) {
|
||||
(this->*info.func)(op, diff);
|
||||
} else if (diff) {
|
||||
uint64_t dirty = info.flags >> 8;
|
||||
if (dirty)
|
||||
gstate_c.Dirty(dirty);
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_D3D11::GetStats(char *buffer, size_t bufsize) {
|
||||
size_t offset = FormatGPUStatsCommon(buffer, bufsize);
|
||||
buffer += offset;
|
||||
@@ -224,39 +166,3 @@ void GPU_D3D11::GetStats(char *buffer, size_t bufsize) {
|
||||
shaderManagerD3D11_->GetNumFragmentShaders()
|
||||
);
|
||||
}
|
||||
|
||||
void GPU_D3D11::DoState(PointerWrap &p) {
|
||||
GPUCommon::DoState(p);
|
||||
|
||||
// TODO: Some of these things may not be necessary.
|
||||
// None of these are necessary when saving.
|
||||
if (p.mode == p.MODE_READ && !PSP_CoreParameter().frozen) {
|
||||
textureCache_->Clear(true);
|
||||
drawEngine_.ClearTrackedVertexArrays();
|
||||
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_IMAGE);
|
||||
framebufferManager_->DestroyAllFBOs();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> GPU_D3D11::DebugGetShaderIDs(DebugShaderType type) {
|
||||
switch (type) {
|
||||
case SHADER_TYPE_VERTEXLOADER:
|
||||
return drawEngine_.DebugGetVertexLoaderIDs();
|
||||
case SHADER_TYPE_TEXTURE:
|
||||
return textureCache_->GetTextureShaderCache()->DebugGetShaderIDs(type);
|
||||
default:
|
||||
return shaderManagerD3D11_->DebugGetShaderIDs(type);
|
||||
}
|
||||
}
|
||||
|
||||
std::string GPU_D3D11::DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) {
|
||||
switch (type) {
|
||||
case SHADER_TYPE_VERTEXLOADER:
|
||||
return drawEngine_.DebugGetVertexLoaderString(id, stringType);
|
||||
case SHADER_TYPE_TEXTURE:
|
||||
return textureCache_->GetTextureShaderCache()->DebugGetShaderString(id, type, stringType);
|
||||
default:
|
||||
return shaderManagerD3D11_->DebugGetShaderString(id, type, stringType);
|
||||
}
|
||||
}
|
||||
+3
-19
@@ -21,46 +21,30 @@
|
||||
#include <vector>
|
||||
#include <d3d11.h>
|
||||
|
||||
#include "GPU/GPUCommon.h"
|
||||
#include "GPU/GPUCommonHW.h"
|
||||
#include "GPU/D3D11/DrawEngineD3D11.h"
|
||||
#include "GPU/Common/TextureShaderCommon.h"
|
||||
#include "GPU/Common/VertexDecoderCommon.h"
|
||||
|
||||
class FramebufferManagerD3D11;
|
||||
class ShaderManagerD3D11;
|
||||
class LinkedShaderD3D11;
|
||||
class TextureCacheD3D11;
|
||||
|
||||
class GPU_D3D11 : public GPUCommon {
|
||||
class GPU_D3D11 : public GPUCommonHW {
|
||||
public:
|
||||
GPU_D3D11(GraphicsContext *gfxCtx, Draw::DrawContext *draw);
|
||||
~GPU_D3D11();
|
||||
|
||||
u32 CheckGPUFeatures() const override;
|
||||
void PreExecuteOp(u32 op, u32 diff) override;
|
||||
void ExecuteOp(u32 op, u32 diff) override;
|
||||
|
||||
void GetStats(char *buffer, size_t bufsize) override;
|
||||
void DeviceLost() override; // Only happens on Android. Drop all textures and shaders.
|
||||
void DeviceRestore() override;
|
||||
|
||||
void DoState(PointerWrap &p) override;
|
||||
|
||||
// Using string because it's generic - makes no assumptions on the size of the shader IDs of this backend.
|
||||
std::vector<std::string> DebugGetShaderIDs(DebugShaderType shader) override;
|
||||
std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override;
|
||||
void DeviceRestore(Draw::DrawContext *draw) override;
|
||||
|
||||
protected:
|
||||
void FinishDeferred() override;
|
||||
|
||||
private:
|
||||
// void ApplyDrawState(int prim);
|
||||
void CheckFlushOp(int cmd, u32 diff);
|
||||
void BuildReportingInfo() override;
|
||||
|
||||
void InitClear() override;
|
||||
void BeginFrame() override;
|
||||
void CopyDisplayToOutput(bool reallyDirty) override;
|
||||
|
||||
ID3D11Device *device_;
|
||||
ID3D11DeviceContext *context_;
|
||||
|
||||
@@ -86,14 +86,16 @@ public:
|
||||
~ShaderManagerD3D11();
|
||||
|
||||
void GetShaders(int prim, VertexDecoder *decoder, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat, bool useSkinInDecode);
|
||||
void ClearShaders();
|
||||
void ClearShaders() override;
|
||||
void DirtyLastShader() override;
|
||||
|
||||
void DeviceLost() override { draw_ = nullptr; }
|
||||
void DeviceRestore(Draw::DrawContext *draw) override { draw_ = draw; }
|
||||
int GetNumVertexShaders() const { return (int)vsCache_.size(); }
|
||||
int GetNumFragmentShaders() const { return (int)fsCache_.size(); }
|
||||
|
||||
std::vector<std::string> DebugGetShaderIDs(DebugShaderType type);
|
||||
std::string DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType);
|
||||
std::vector<std::string> DebugGetShaderIDs(DebugShaderType type) override;
|
||||
std::string DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) override;
|
||||
|
||||
uint64_t UpdateUniforms(bool useBufferedRendering);
|
||||
void BindUniforms();
|
||||
|
||||
@@ -442,7 +442,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
|
||||
void DrawEngineD3D11::ApplyDrawStateLate(bool applyStencilRef, uint8_t stencilRef) {
|
||||
// we go through Draw here because it automatically handles screen rotation, as needed in UWP on mobiles.
|
||||
if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) {
|
||||
draw_->SetViewports(1, &dynState_.viewport);
|
||||
draw_->SetViewport(dynState_.viewport);
|
||||
draw_->SetScissorRect(dynState_.scissor.left, dynState_.scissor.top, dynState_.scissor.right - dynState_.scissor.left, dynState_.scissor.bottom - dynState_.scissor.top);
|
||||
}
|
||||
if (gstate_c.IsDirty(DIRTY_RASTER_STATE)) {
|
||||
|
||||
@@ -179,35 +179,12 @@ void TextureCacheD3D11::ReleaseTexture(TexCacheEntry *entry, bool delete_them) {
|
||||
}
|
||||
|
||||
void TextureCacheD3D11::ForgetLastTexture() {
|
||||
InvalidateLastTexture();
|
||||
lastBoundTexture = INVALID_TEX;
|
||||
|
||||
ID3D11ShaderResourceView *nullTex[4]{};
|
||||
context_->PSSetShaderResources(0, 4, nullTex);
|
||||
}
|
||||
|
||||
void TextureCacheD3D11::InvalidateLastTexture() {
|
||||
lastBoundTexture = INVALID_TEX;
|
||||
}
|
||||
|
||||
void TextureCacheD3D11::StartFrame() {
|
||||
TextureCacheCommon::StartFrame();
|
||||
|
||||
InvalidateLastTexture();
|
||||
timesInvalidatedAllThisFrame_ = 0;
|
||||
replacementTimeThisFrame_ = 0.0;
|
||||
|
||||
if (texelsScaledThisFrame_) {
|
||||
// INFO_LOG(G3D, "Scaled %i texels", texelsScaledThisFrame_);
|
||||
}
|
||||
texelsScaledThisFrame_ = 0;
|
||||
if (clearCacheNextFrame_) {
|
||||
Clear(true);
|
||||
clearCacheNextFrame_ = false;
|
||||
} else {
|
||||
Decimate();
|
||||
}
|
||||
}
|
||||
|
||||
void TextureCacheD3D11::UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBase, bool clutIndexIsSimple) {
|
||||
const u32 clutBaseBytes = clutBase * (clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16));
|
||||
// Technically, these extra bytes weren't loaded, but hopefully it was loaded earlier.
|
||||
@@ -268,9 +245,7 @@ void TextureCacheD3D11::ApplySamplingParams(const SamplerCacheKey &key) {
|
||||
}
|
||||
|
||||
void TextureCacheD3D11::Unbind() {
|
||||
ID3D11ShaderResourceView *nullView = nullptr;
|
||||
context_->PSSetShaderResources(0, 1, &nullView);
|
||||
InvalidateLastTexture();
|
||||
ForgetLastTexture();
|
||||
}
|
||||
|
||||
void TextureCacheD3D11::BindAsClutTexture(Draw::Texture *tex, bool smooth) {
|
||||
@@ -288,7 +263,7 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
|
||||
|
||||
DXGI_FORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat());
|
||||
if (plan.replaceValid) {
|
||||
dstFmt = ToDXGIFormat(plan.replaced->Format(plan.baseLevelSrc));
|
||||
dstFmt = ToDXGIFormat(plan.replaced->Format());
|
||||
} else if (plan.scaleFactor > 1 || plan.saveTexture) {
|
||||
dstFmt = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
} else if (plan.decodeToClut8) {
|
||||
@@ -309,45 +284,16 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
|
||||
if (th > 16384)
|
||||
th = 16384;
|
||||
|
||||
// The PSP only supports 8 mip levels, but we support 12 in the texture replacer (4k textures down to 1).
|
||||
D3D11_SUBRESOURCE_DATA subresData[12]{};
|
||||
|
||||
if (plan.depth == 1) {
|
||||
// We don't yet have mip generation, so clamp the number of levels to the ones we can load directly.
|
||||
levels = std::min(plan.levelsToCreate, plan.levelsToLoad);
|
||||
|
||||
ID3D11Texture2D *tex = nullptr;
|
||||
D3D11_TEXTURE2D_DESC desc{};
|
||||
desc.CPUAccessFlags = 0;
|
||||
desc.Usage = D3D11_USAGE_DEFAULT;
|
||||
desc.ArraySize = 1;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.Width = tw;
|
||||
desc.Height = th;
|
||||
desc.Format = dstFmt;
|
||||
desc.MipLevels = levels;
|
||||
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
|
||||
ASSERT_SUCCESS(device_->CreateTexture2D(&desc, nullptr, &tex));
|
||||
texture = tex;
|
||||
} else {
|
||||
ID3D11Texture3D *tex = nullptr;
|
||||
D3D11_TEXTURE3D_DESC desc{};
|
||||
desc.CPUAccessFlags = 0;
|
||||
desc.Usage = D3D11_USAGE_DEFAULT;
|
||||
desc.Width = tw;
|
||||
desc.Height = th;
|
||||
desc.Depth = plan.depth;
|
||||
desc.Format = dstFmt;
|
||||
desc.MipLevels = 1;
|
||||
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
ASSERT_SUCCESS(device_->CreateTexture3D(&desc, nullptr, &tex));
|
||||
texture = tex;
|
||||
|
||||
levels = plan.depth;
|
||||
}
|
||||
|
||||
ASSERT_SUCCESS(device_->CreateShaderResourceView(texture, nullptr, &view));
|
||||
entry->texturePtr = texture;
|
||||
entry->textureView = view;
|
||||
|
||||
Draw::DataFormat texFmt = FromD3D11Format(dstFmt);
|
||||
|
||||
for (int i = 0; i < levels; i++) {
|
||||
@@ -364,7 +310,7 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
|
||||
// For UpdateSubresource, we can't decode directly into the texture so we allocate a buffer :(
|
||||
// NOTE: Could reuse it between levels or textures!
|
||||
if (plan.replaceValid) {
|
||||
bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel));
|
||||
bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format());
|
||||
} else {
|
||||
if (plan.scaleFactor > 1) {
|
||||
bpp = 4;
|
||||
@@ -374,7 +320,20 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
|
||||
}
|
||||
|
||||
stride = std::max(mipWidth * bpp, 16);
|
||||
data = (u8 *)AllocateAlignedMemory(stride * mipHeight, 16);
|
||||
|
||||
if (plan.depth == 1) {
|
||||
data = (u8 *)AllocateAlignedMemory(stride * mipHeight, 16);
|
||||
subresData[i].pSysMem = data;
|
||||
subresData[i].SysMemPitch = stride;
|
||||
subresData[i].SysMemSlicePitch = 0;
|
||||
} else {
|
||||
if (i == 0) {
|
||||
subresData[0].pSysMem = AllocateAlignedMemory(stride * mipHeight * plan.depth, 16);
|
||||
subresData[0].SysMemPitch = stride;
|
||||
subresData[0].SysMemSlicePitch = stride * mipHeight;
|
||||
}
|
||||
data = (uint8_t *)subresData[0].pSysMem + stride * mipHeight * i;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", mipWidth, mipHeight);
|
||||
@@ -382,17 +341,51 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
|
||||
}
|
||||
|
||||
LoadTextureLevel(*entry, data, stride, plan, srcLevel, texFmt, TexDecodeFlags{});
|
||||
if (plan.depth == 1) {
|
||||
context_->UpdateSubresource(texture, i, nullptr, data, stride, 0);
|
||||
} else {
|
||||
D3D11_BOX box{};
|
||||
box.front = i;
|
||||
box.back = i + 1;
|
||||
box.right = mipWidth;
|
||||
box.bottom = mipHeight;
|
||||
context_->UpdateSubresource(texture, 0, &box, data, stride, 0);
|
||||
}
|
||||
|
||||
if (plan.depth == 1) {
|
||||
// We don't yet have mip generation, so clamp the number of levels to the ones we can load directly.
|
||||
levels = std::min(plan.levelsToCreate, plan.levelsToLoad);
|
||||
|
||||
ID3D11Texture2D *tex = nullptr;
|
||||
D3D11_TEXTURE2D_DESC desc{};
|
||||
desc.CPUAccessFlags = 0;
|
||||
desc.Usage = D3D11_USAGE_DEFAULT;
|
||||
desc.ArraySize = 1;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.Width = tw;
|
||||
desc.Height = th;
|
||||
desc.Format = dstFmt;
|
||||
desc.MipLevels = levels;
|
||||
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
|
||||
ASSERT_SUCCESS(device_->CreateTexture2D(&desc, subresData, &tex));
|
||||
texture = tex;
|
||||
} else {
|
||||
ID3D11Texture3D *tex = nullptr;
|
||||
D3D11_TEXTURE3D_DESC desc{};
|
||||
desc.CPUAccessFlags = 0;
|
||||
desc.Usage = D3D11_USAGE_DEFAULT;
|
||||
desc.Width = tw;
|
||||
desc.Height = th;
|
||||
desc.Depth = plan.depth;
|
||||
desc.Format = dstFmt;
|
||||
desc.MipLevels = 1;
|
||||
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
ASSERT_SUCCESS(device_->CreateTexture3D(&desc, subresData, &tex));
|
||||
texture = tex;
|
||||
|
||||
levels = plan.depth;
|
||||
}
|
||||
|
||||
ASSERT_SUCCESS(device_->CreateShaderResourceView(texture, nullptr, &view));
|
||||
entry->texturePtr = texture;
|
||||
entry->textureView = view;
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
if (subresData[i].pSysMem) {
|
||||
FreeAlignedMemory((void *)subresData[i].pSysMem);
|
||||
}
|
||||
FreeAlignedMemory(data);
|
||||
}
|
||||
|
||||
// Signal that we support depth textures so use it as one.
|
||||
|
||||
@@ -46,15 +46,15 @@ public:
|
||||
TextureCacheD3D11(Draw::DrawContext *draw, Draw2D *draw2D);
|
||||
~TextureCacheD3D11();
|
||||
|
||||
void StartFrame() override;
|
||||
|
||||
void SetFramebufferManager(FramebufferManagerD3D11 *fbManager);
|
||||
|
||||
void ForgetLastTexture() override;
|
||||
void InvalidateLastTexture() override;
|
||||
|
||||
bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level, bool *isFramebuffer) override;
|
||||
|
||||
void DeviceLost() override { draw_ = nullptr; }
|
||||
void DeviceRestore(Draw::DrawContext *draw) override { draw_ = draw; }
|
||||
|
||||
protected:
|
||||
void BindTexture(TexCacheEntry *entry) override;
|
||||
void Unbind() override;
|
||||
|
||||
@@ -107,6 +107,9 @@ public:
|
||||
DrawEngineDX9(Draw::DrawContext *draw);
|
||||
~DrawEngineDX9();
|
||||
|
||||
void DeviceLost() override { draw_ = nullptr; }
|
||||
void DeviceRestore(Draw::DrawContext *draw) override { draw_ = draw; }
|
||||
|
||||
void SetShaderManager(ShaderManagerDX9 *shaderManager) {
|
||||
shaderManager_ = shaderManager;
|
||||
}
|
||||
|
||||
@@ -15,34 +15,16 @@
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <d3d9.h>
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/Data/Convert/ColorConv.h"
|
||||
#include "Common/GPU/thin3d.h"
|
||||
#include "Common/Math/lin/matrix4x4.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/ConfigValues.h"
|
||||
#include "Core/System.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "GPU/Debugger/Stepping.h"
|
||||
|
||||
#include "Common/GPU/D3D9/D3D9StateCache.h"
|
||||
#include "GPU/Common/FramebufferManagerCommon.h"
|
||||
#include "GPU/Common/GPUStateUtils.h"
|
||||
#include "GPU/Common/PresentationCommon.h"
|
||||
#include "GPU/Common/TextureDecoder.h"
|
||||
#include "GPU/Directx9/FramebufferManagerDX9.h"
|
||||
#include "GPU/Directx9/ShaderManagerDX9.h"
|
||||
#include "GPU/Directx9/TextureCacheDX9.h"
|
||||
#include "GPU/Directx9/DrawEngineDX9.h"
|
||||
|
||||
#include "Common/GPU/thin3d.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef _M_SSE
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
|
||||
FramebufferManagerDX9::FramebufferManagerDX9(Draw::DrawContext *draw)
|
||||
: FramebufferManagerCommon(draw) {
|
||||
|
||||
Loaded 100 of 278 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user