Merge branch 'hrydgard:master' into vfpu-sincos

This commit is contained in:
fp64 authored and GitHub committed 2023-03-10 15:06:17 -05:00
commit ba9234d1c4
278 files changed
+5752 -11311

No files matched your search

+21 -7
View File
@@ -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
+4 -2
View File
@@ -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" />
+12 -6
View File
@@ -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">
+4 -3
View File
@@ -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;
}
+3 -1
View File
@@ -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)); }
+1 -1
View File
@@ -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 {
+11
View File
@@ -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;
}
+24 -3
View File
@@ -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); }
};
+1 -1
View File
@@ -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;
+3 -3
View File
@@ -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;
+2
View File
@@ -1,3 +1,5 @@
#include <inttypes.h>
#include "Common/File/AndroidStorage.h"
#include "Common/StringUtils.h"
#include "Common/Log.h"
+1 -3
View File
@@ -2,10 +2,8 @@
#include <string>
#include <vector>
#include <cstdio>
#include <inttypes.h>
#include <cstdint>
#include "Common/File/Path.h"
-204
View File
@@ -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);
}
-65
View File
@@ -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_;
};
+99
View File
@@ -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;
}
+30
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+281
View File
@@ -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;
}
+48
View 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];
};
+13 -15
View File
@@ -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) {
+7 -7
View File
@@ -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]) {
+2 -2
View File
@@ -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);
+2 -2
View File
@@ -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 {
+13 -11
View File
@@ -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:
+2 -1
View File
@@ -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);
}
}
+6
View File
@@ -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;
{
+5 -2
View File
@@ -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;
}
+5
View File
@@ -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;
+31
View File
@@ -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_));
+5 -2
View File
@@ -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;
+2 -1
View File
@@ -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 {
+2
View File
@@ -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();
+14 -14
View File
@@ -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
View File
@@ -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;
+4
View File
@@ -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) {
+8 -8
View File
@@ -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) {
+1 -1
View File
@@ -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());
+1 -1
View File
@@ -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;
}
+7 -2
View File
@@ -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);
+1
View File
@@ -40,6 +40,7 @@ private:
jmethodID method_renderText;
uint32_t fontHash_;
bool use4444Format_ = false;
std::map<uint32_t, AndroidFontEntry> fontMap_;
+29 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+60 -1
View File
@@ -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;
+3
View File
@@ -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
+5
View File
@@ -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;
}
+4 -2
View File
@@ -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) {
+1
View File
@@ -98,6 +98,7 @@ struct CompatFlags {
bool RockmanDash2SoundFix;
bool ReadbackDepth;
bool BlockTransferDepth;
bool DaxterRotatedAnalogStick;
};
struct VRCompat {
+20 -12
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
-2
View File
@@ -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" />
-6
View File
@@ -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>
+2 -2
View File
@@ -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;
+16
View File
@@ -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;
+1 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
-311
View File
@@ -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_;
};
+1 -1
View File
@@ -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
View File
@@ -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;
+2 -2
View File
@@ -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);
+5 -2
View File
@@ -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) {
+7 -2
View File
@@ -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);
+41 -10
View File
@@ -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(&copyInfo, framebuffer, flags, layer);
bool partial = false;
CopyFramebufferForColorTexture(&copyInfo, 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);
+9 -1
View File
@@ -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;
+2 -2
View File
@@ -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.
}
+12 -12
View File
@@ -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;
+442
View File
@@ -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";
}
}
+157
View File
@@ -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;
};
+9
View File
@@ -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;
};
+7 -6
View File
@@ -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) {
+1 -1
View File
@@ -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);
+92 -38
View File
@@ -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();
}
+13 -8
View File
@@ -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
+173
View File
@@ -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_;
};
+1 -1
View File
@@ -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());
+2 -1
View File
@@ -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");
+2 -2
View File
@@ -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);
}
+3
View File
@@ -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;
}
-4
View File
@@ -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"
-7
View File
@@ -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
View File
@@ -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
View File
@@ -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_;
+5 -3
View File
@@ -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();
+1 -1
View File
@@ -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)) {
+65 -72
View File
@@ -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.
+3 -3
View File
@@ -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;
+3
View File
@@ -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;
}
+3 -21
View File
@@ -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