mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-09-03 08:07:45 +00:00
Merge branch 'master' of https://github.com/dolphin-emu/dolphin into dolphin-emu-master
This commit is contained in:
commit
c18016e795
767 changed files with 87644 additions and 70168 deletions
|
@ -19,7 +19,7 @@ void AbstractTexture::FinishedRendering()
|
|||
{
|
||||
}
|
||||
|
||||
bool AbstractTexture::Save(const std::string& filename, unsigned int level, int compression)
|
||||
bool AbstractTexture::Save(const std::string& filename, unsigned int level, int compression) const
|
||||
{
|
||||
// We can't dump compressed textures currently (it would mean drawing them to a RGBA8
|
||||
// framebuffer, and saving that). TextureCache does not call Save for custom textures
|
||||
|
@ -37,7 +37,8 @@ bool AbstractTexture::Save(const std::string& filename, unsigned int level, int
|
|||
// Use a temporary staging texture for the download. Certainly not optimal,
|
||||
// but this is not a frequently-executed code path..
|
||||
TextureConfig readback_texture_config(level_width, level_height, 1, 1, 1,
|
||||
AbstractTextureFormat::RGBA8, 0);
|
||||
AbstractTextureFormat::RGBA8, 0,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
auto readback_texture =
|
||||
g_gfx->CreateStagingTexture(StagingTextureType::Readback, readback_texture_config);
|
||||
if (!readback_texture)
|
||||
|
|
|
@ -38,7 +38,7 @@ public:
|
|||
MathUtil::Rectangle<int> GetRect() const { return m_config.GetRect(); }
|
||||
MathUtil::Rectangle<int> GetMipRect(u32 level) const { return m_config.GetMipRect(level); }
|
||||
bool IsMultisampled() const { return m_config.IsMultisampled(); }
|
||||
bool Save(const std::string& filename, unsigned int level, int compression = 6);
|
||||
bool Save(const std::string& filename, unsigned int level, int compression = 6) const;
|
||||
|
||||
static bool IsCompressedFormat(AbstractTextureFormat format);
|
||||
static bool IsDepthFormat(AbstractTextureFormat format);
|
||||
|
|
|
@ -6,36 +6,31 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "VideoCommon/Assets/CustomTextureData.h"
|
||||
#include "VideoCommon/Assets/TextureAsset.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::size_t GetAssetSize(const CustomTextureData& data)
|
||||
{
|
||||
std::size_t total = 0;
|
||||
for (const auto& slice : data.m_slices)
|
||||
{
|
||||
for (const auto& level : slice.m_levels)
|
||||
{
|
||||
total += level.data.size();
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
} // namespace
|
||||
CustomAssetLibrary::LoadInfo CustomAssetLibrary::LoadGameTexture(const AssetID& asset_id,
|
||||
CustomTextureData* data)
|
||||
TextureData* data)
|
||||
{
|
||||
const auto load_info = LoadTexture(asset_id, data);
|
||||
if (load_info.m_bytes_loaded == 0)
|
||||
return {};
|
||||
|
||||
// Note: 'LoadTexture()' ensures we have a level loaded
|
||||
for (std::size_t slice_index = 0; slice_index < data->m_slices.size(); slice_index++)
|
||||
if (data->m_type != TextureData::Type::Type_Texture2D)
|
||||
{
|
||||
auto& slice = data->m_slices[slice_index];
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Custom asset '{}' is not a valid game texture, it is expected to be a 2d texture "
|
||||
"but was a '{}'.",
|
||||
asset_id, data->m_type);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Note: 'LoadTexture()' ensures we have a level loaded
|
||||
for (std::size_t slice_index = 0; slice_index < data->m_texture.m_slices.size(); slice_index++)
|
||||
{
|
||||
auto& slice = data->m_texture.m_slices[slice_index];
|
||||
const auto& first_mip = slice.m_levels[0];
|
||||
|
||||
// Verify that each mip level is the correct size (divide by 2 each time).
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
|
||||
namespace VideoCommon
|
||||
{
|
||||
class CustomTextureData;
|
||||
struct MaterialData;
|
||||
struct PixelShaderData;
|
||||
struct TextureData;
|
||||
|
||||
// This class provides functionality to load
|
||||
// specific data (like textures). Where this data
|
||||
|
@ -28,18 +28,20 @@ public:
|
|||
struct LoadInfo
|
||||
{
|
||||
std::size_t m_bytes_loaded = 0;
|
||||
CustomAssetLibrary::TimeType m_load_time = {};
|
||||
TimeType m_load_time = {};
|
||||
};
|
||||
|
||||
virtual ~CustomAssetLibrary() = default;
|
||||
|
||||
// Loads a texture, if there are no levels, bytes loaded will be empty
|
||||
virtual LoadInfo LoadTexture(const AssetID& asset_id, CustomTextureData* data) = 0;
|
||||
virtual LoadInfo LoadTexture(const AssetID& asset_id, TextureData* data) = 0;
|
||||
|
||||
// Gets the last write time for a given asset id
|
||||
virtual TimeType GetLastAssetWriteTime(const AssetID& asset_id) const = 0;
|
||||
|
||||
// Loads a texture as a game texture, providing additional checks like confirming
|
||||
// each mip level size is correct and that the format is consistent across the data
|
||||
LoadInfo LoadGameTexture(const AssetID& asset_id, CustomTextureData* data);
|
||||
LoadInfo LoadGameTexture(const AssetID& asset_id, TextureData* data);
|
||||
|
||||
// Loads a pixel shader
|
||||
virtual LoadInfo LoadPixelShader(const AssetID& asset_id, PixelShaderData* data) = 0;
|
||||
|
|
|
@ -77,13 +77,6 @@ void CustomAssetLoader ::Shutdown()
|
|||
m_total_bytes_loaded = 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<RawTextureAsset>
|
||||
CustomAssetLoader::LoadTexture(const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::shared_ptr<CustomAssetLibrary> library)
|
||||
{
|
||||
return LoadOrCreateAsset<RawTextureAsset>(asset_id, m_textures, std::move(library));
|
||||
}
|
||||
|
||||
std::shared_ptr<GameTextureAsset>
|
||||
CustomAssetLoader::LoadGameTexture(const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::shared_ptr<CustomAssetLibrary> library)
|
||||
|
|
|
@ -38,9 +38,6 @@ public:
|
|||
// Loads happen asynchronously where the data will be set now or in the future
|
||||
// Callees are expected to query the underlying data with 'GetData()'
|
||||
// from the 'CustomLoadableAsset' class to determine if the data is ready for use
|
||||
std::shared_ptr<RawTextureAsset> LoadTexture(const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::shared_ptr<CustomAssetLibrary> library);
|
||||
|
||||
std::shared_ptr<GameTextureAsset> LoadGameTexture(const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::shared_ptr<CustomAssetLibrary> library);
|
||||
|
||||
|
@ -80,7 +77,6 @@ private:
|
|||
|
||||
static constexpr auto TIME_BETWEEN_ASSET_MONITOR_CHECKS = std::chrono::milliseconds{500};
|
||||
|
||||
std::map<CustomAssetLibrary::AssetID, std::weak_ptr<RawTextureAsset>> m_textures;
|
||||
std::map<CustomAssetLibrary::AssetID, std::weak_ptr<GameTextureAsset>> m_game_textures;
|
||||
std::map<CustomAssetLibrary::AssetID, std::weak_ptr<PixelShaderAsset>> m_pixel_shaders;
|
||||
std::map<CustomAssetLibrary::AssetID, std::weak_ptr<MaterialAsset>> m_materials;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "Common/StringUtil.h"
|
||||
#include "VideoCommon/Assets/MaterialAsset.h"
|
||||
#include "VideoCommon/Assets/ShaderAsset.h"
|
||||
#include "VideoCommon/Assets/TextureAsset.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
|
@ -219,66 +220,126 @@ CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadMaterial(const As
|
|||
}
|
||||
|
||||
CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadTexture(const AssetID& asset_id,
|
||||
CustomTextureData* data)
|
||||
TextureData* data)
|
||||
{
|
||||
const auto asset_map = GetAssetMapForID(asset_id);
|
||||
|
||||
// Raw texture is expected to have one asset mapped
|
||||
if (asset_map.empty() || asset_map.size() > 1)
|
||||
// Texture can optionally have a metadata file as well
|
||||
if (asset_map.empty() || asset_map.size() > 2)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - raw texture expected to have one file mapped!",
|
||||
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - raw texture expected to have one or two files mapped!",
|
||||
asset_id);
|
||||
return {};
|
||||
}
|
||||
const auto& asset_path = asset_map.begin()->second;
|
||||
|
||||
std::error_code ec;
|
||||
const auto last_loaded_time = std::filesystem::last_write_time(asset_path, ec);
|
||||
if (ec)
|
||||
const auto metadata = asset_map.find("metadata");
|
||||
const auto texture_path = asset_map.find("texture");
|
||||
|
||||
if (texture_path == asset_map.end())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - failed to get last write time with error '{}'!",
|
||||
asset_id, ec);
|
||||
ERROR_LOG_FMT(VIDEO, "Asset '{}' expected to have a texture entry mapped!", asset_id);
|
||||
return {};
|
||||
}
|
||||
auto ext = PathToString(asset_path.extension());
|
||||
|
||||
std::size_t metadata_size = 0;
|
||||
if (metadata != asset_map.end())
|
||||
{
|
||||
std::error_code ec;
|
||||
metadata_size = std::filesystem::file_size(metadata->second, ec);
|
||||
if (ec)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Asset '{}' error - failed to get texture metadata file size with error '{}'!",
|
||||
asset_id, ec);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string json_data;
|
||||
if (!File::ReadFileToString(PathToString(metadata->second), json_data))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - failed to load the json file '{}',", asset_id,
|
||||
PathToString(metadata->second));
|
||||
return {};
|
||||
}
|
||||
|
||||
picojson::value root;
|
||||
const auto error = picojson::parse(root, json_data);
|
||||
|
||||
if (!error.empty())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Asset '{}' error - failed to load the json file '{}', due to parse error: {}",
|
||||
asset_id, PathToString(metadata->second), error);
|
||||
return {};
|
||||
}
|
||||
if (!root.is<picojson::object>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Asset '{}' error - failed to load the json file '{}', due to root not being an object!",
|
||||
asset_id, PathToString(metadata->second));
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto& root_obj = root.get<picojson::object>();
|
||||
if (!TextureData::FromJson(asset_id, root_obj, data))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data->m_type = TextureData::Type::Type_Texture2D;
|
||||
}
|
||||
|
||||
auto ext = PathToString(texture_path->second.extension());
|
||||
Common::ToLower(&ext);
|
||||
if (ext == ".dds")
|
||||
{
|
||||
if (!LoadDDSTexture(data, PathToString(asset_path)))
|
||||
if (!LoadDDSTexture(&data->m_texture, PathToString(texture_path->second)))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - could not load dds texture!", asset_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (data->m_slices.empty()) [[unlikely]]
|
||||
data->m_slices.push_back({});
|
||||
if (data->m_texture.m_slices.empty()) [[unlikely]]
|
||||
data->m_texture.m_slices.push_back({});
|
||||
|
||||
if (!LoadMips(asset_path, &data->m_slices[0]))
|
||||
if (!LoadMips(texture_path->second, &data->m_texture.m_slices[0]))
|
||||
return {};
|
||||
|
||||
return LoadInfo{GetAssetSize(*data), FileTimeToSysTime(last_loaded_time)};
|
||||
return LoadInfo{GetAssetSize(data->m_texture) + metadata_size, GetLastAssetWriteTime(asset_id)};
|
||||
}
|
||||
else if (ext == ".png")
|
||||
{
|
||||
// If we have no slices, create one
|
||||
if (data->m_slices.empty())
|
||||
data->m_slices.push_back({});
|
||||
// PNG could support more complicated texture types in the future
|
||||
// but for now just error
|
||||
if (data->m_type != TextureData::Type::Type_Texture2D)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - PNG is not supported for texture type '{}'!",
|
||||
asset_id, data->m_type);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& slice = data->m_slices[0];
|
||||
// If we have no slices, create one
|
||||
if (data->m_texture.m_slices.empty())
|
||||
data->m_texture.m_slices.push_back({});
|
||||
|
||||
auto& slice = data->m_texture.m_slices[0];
|
||||
// If we have no levels, create one to pass into LoadPNGTexture
|
||||
if (slice.m_levels.empty())
|
||||
slice.m_levels.push_back({});
|
||||
|
||||
if (!LoadPNGTexture(&slice.m_levels[0], PathToString(asset_path)))
|
||||
if (!LoadPNGTexture(&slice.m_levels[0], PathToString(texture_path->second)))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - could not load png texture!", asset_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!LoadMips(asset_path, &slice))
|
||||
if (!LoadMips(texture_path->second, &slice))
|
||||
return {};
|
||||
|
||||
return LoadInfo{GetAssetSize(*data), FileTimeToSysTime(last_loaded_time)};
|
||||
return LoadInfo{GetAssetSize(data->m_texture) + metadata_size, GetLastAssetWriteTime(asset_id)};
|
||||
}
|
||||
|
||||
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - extension '{}' unknown!", asset_id, ext);
|
||||
|
|
|
@ -20,7 +20,7 @@ class DirectFilesystemAssetLibrary final : public CustomAssetLibrary
|
|||
public:
|
||||
using AssetMap = std::map<std::string, std::filesystem::path>;
|
||||
|
||||
LoadInfo LoadTexture(const AssetID& asset_id, CustomTextureData* data) override;
|
||||
LoadInfo LoadTexture(const AssetID& asset_id, TextureData* data) override;
|
||||
LoadInfo LoadPixelShader(const AssetID& asset_id, PixelShaderData* data) override;
|
||||
LoadInfo LoadMaterial(const AssetID& asset_id, MaterialData* data) override;
|
||||
|
||||
|
|
|
@ -3,32 +3,137 @@
|
|||
|
||||
#include "VideoCommon/Assets/MaterialAsset.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/VariantUtil.h"
|
||||
#include "VideoCommon/Assets/CustomAssetLibrary.h"
|
||||
#include "VideoCommon/ShaderGenCommon.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
namespace
|
||||
{
|
||||
bool ParsePropertyValue(const CustomAssetLibrary::AssetID& asset_id, MaterialProperty::Type type,
|
||||
const picojson::value& json_value,
|
||||
std::optional<MaterialProperty::Value>* value)
|
||||
// While not optimal, we pad our data to match std140 shader requirements
|
||||
// this memory constant indicates the memory stride for a single uniform
|
||||
// regardless of data type
|
||||
constexpr std::size_t MemorySize = sizeof(float) * 4;
|
||||
|
||||
template <typename ElementType, std::size_t ElementCount>
|
||||
bool ParseNumeric(const CustomAssetLibrary::AssetID& asset_id, const picojson::value& json_value,
|
||||
std::string_view code_name, MaterialProperty::Value* value)
|
||||
{
|
||||
switch (type)
|
||||
static_assert(ElementCount <= 4, "Numeric data expected to be four elements or less");
|
||||
if constexpr (ElementCount == 1)
|
||||
{
|
||||
case MaterialProperty::Type::Type_TextureAsset:
|
||||
{
|
||||
if (json_value.is<std::string>())
|
||||
if (!json_value.is<double>())
|
||||
{
|
||||
*value = json_value.to_str();
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Asset id '{}' material has attribute '{}' where "
|
||||
"a double was expected but not provided.",
|
||||
asset_id, code_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = static_cast<ElementType>(json_value.get<double>());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!json_value.is<picojson::array>())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Asset id '{}' material has attribute '{}' where "
|
||||
"an array was expected but not provided.",
|
||||
asset_id, code_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto json_data = json_value.get<picojson::array>();
|
||||
|
||||
if (json_data.size() != ElementCount)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Asset id '{}' material has attribute '{}' with incorrect number "
|
||||
"of elements, expected {}",
|
||||
asset_id, code_name, ElementCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!std::all_of(json_data.begin(), json_data.end(),
|
||||
[](const picojson::value& v) { return v.is<double>(); }))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Asset id '{}' material has attribute '{}' where "
|
||||
"all elements are not of type double.",
|
||||
asset_id, code_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::array<ElementType, ElementCount> data;
|
||||
for (std::size_t i = 0; i < ElementCount; i++)
|
||||
{
|
||||
data[i] = static_cast<ElementType>(json_data[i].get<double>());
|
||||
}
|
||||
*value = std::move(data);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool ParsePropertyValue(const CustomAssetLibrary::AssetID& asset_id,
|
||||
const picojson::value& json_value, std::string_view code_name,
|
||||
std::string_view type, MaterialProperty::Value* value)
|
||||
{
|
||||
if (type == "int")
|
||||
{
|
||||
return ParseNumeric<s32, 1>(asset_id, json_value, code_name, value);
|
||||
}
|
||||
else if (type == "int2")
|
||||
{
|
||||
return ParseNumeric<s32, 2>(asset_id, json_value, code_name, value);
|
||||
}
|
||||
else if (type == "int3")
|
||||
{
|
||||
return ParseNumeric<s32, 3>(asset_id, json_value, code_name, value);
|
||||
}
|
||||
else if (type == "int4")
|
||||
{
|
||||
return ParseNumeric<s32, 4>(asset_id, json_value, code_name, value);
|
||||
}
|
||||
else if (type == "float")
|
||||
{
|
||||
return ParseNumeric<float, 1>(asset_id, json_value, code_name, value);
|
||||
}
|
||||
else if (type == "float2")
|
||||
{
|
||||
return ParseNumeric<float, 2>(asset_id, json_value, code_name, value);
|
||||
}
|
||||
else if (type == "float3")
|
||||
{
|
||||
return ParseNumeric<float, 3>(asset_id, json_value, code_name, value);
|
||||
}
|
||||
else if (type == "float4")
|
||||
{
|
||||
return ParseNumeric<float, 4>(asset_id, json_value, code_name, value);
|
||||
}
|
||||
else if (type == "bool")
|
||||
{
|
||||
if (json_value.is<bool>())
|
||||
{
|
||||
*value = json_value.get<bool>();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (type == "texture_asset")
|
||||
{
|
||||
if (json_value.is<std::string>())
|
||||
{
|
||||
*value = json_value.get<std::string>();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
};
|
||||
|
||||
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse the json, value is not valid for type '{}'",
|
||||
asset_id, type);
|
||||
|
@ -67,18 +172,6 @@ bool ParseMaterialProperties(const CustomAssetLibrary::AssetID& asset_id,
|
|||
}
|
||||
std::string type = type_iter->second.to_str();
|
||||
Common::ToLower(&type);
|
||||
if (type == "texture_asset")
|
||||
{
|
||||
property.m_type = MaterialProperty::Type::Type_TextureAsset;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Asset '{}' failed to parse the json, value entry 'type' is "
|
||||
"an invalid option",
|
||||
asset_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto code_name_iter = value_data_obj.find("code_name");
|
||||
if (code_name_iter == value_data_obj.end())
|
||||
|
@ -102,8 +195,11 @@ bool ParseMaterialProperties(const CustomAssetLibrary::AssetID& asset_id,
|
|||
const auto value_iter = value_data_obj.find("value");
|
||||
if (value_iter != value_data_obj.end())
|
||||
{
|
||||
if (!ParsePropertyValue(asset_id, property.m_type, value_iter->second, &property.m_value))
|
||||
if (!ParsePropertyValue(asset_id, value_iter->second, property.m_code_name, type,
|
||||
&property.m_value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
material_property->push_back(std::move(property));
|
||||
|
@ -111,7 +207,90 @@ bool ParseMaterialProperties(const CustomAssetLibrary::AssetID& asset_id,
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
picojson::array ArrayToPicoArray(const std::array<T, N>& value)
|
||||
{
|
||||
picojson::array result;
|
||||
for (std::size_t i = 0; i < N; i++)
|
||||
{
|
||||
result.push_back(picojson::value{static_cast<double>(value[i])});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void MaterialProperty::WriteToMemory(u8*& buffer, const MaterialProperty& property)
|
||||
{
|
||||
const auto write_memory = [&](const void* raw_value, std::size_t data_size) {
|
||||
std::memcpy(buffer, raw_value, data_size);
|
||||
std::memset(buffer + data_size, 0, MemorySize - data_size);
|
||||
buffer += MemorySize;
|
||||
};
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](const CustomAssetLibrary::AssetID&) {},
|
||||
[&](s32 value) { write_memory(&value, sizeof(s32)); },
|
||||
[&](const std::array<s32, 2>& value) { write_memory(value.data(), sizeof(s32) * 2); },
|
||||
[&](const std::array<s32, 3>& value) { write_memory(value.data(), sizeof(s32) * 3); },
|
||||
[&](const std::array<s32, 4>& value) { write_memory(value.data(), sizeof(s32) * 4); },
|
||||
[&](float value) { write_memory(&value, sizeof(float)); },
|
||||
[&](const std::array<float, 2>& value) { write_memory(value.data(), sizeof(float) * 2); },
|
||||
[&](const std::array<float, 3>& value) { write_memory(value.data(), sizeof(float) * 3); },
|
||||
[&](const std::array<float, 4>& value) { write_memory(value.data(), sizeof(float) * 4); },
|
||||
[&](bool value) { write_memory(&value, sizeof(bool)); }},
|
||||
property.m_value);
|
||||
}
|
||||
|
||||
std::size_t MaterialProperty::GetMemorySize(const MaterialProperty& property)
|
||||
{
|
||||
std::size_t result = 0;
|
||||
std::visit(overloaded{[&](const CustomAssetLibrary::AssetID&) {},
|
||||
[&](s32) { result = MemorySize; },
|
||||
[&](const std::array<s32, 2>&) { result = MemorySize; },
|
||||
[&](const std::array<s32, 3>&) { result = MemorySize; },
|
||||
[&](const std::array<s32, 4>&) { result = MemorySize; },
|
||||
[&](float) { result = MemorySize; },
|
||||
[&](const std::array<float, 2>&) { result = MemorySize; },
|
||||
[&](const std::array<float, 3>&) { result = MemorySize; },
|
||||
[&](const std::array<float, 4>&) { result = MemorySize; },
|
||||
[&](bool) { result = MemorySize; }},
|
||||
property.m_value);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void MaterialProperty::WriteAsShaderCode(ShaderCode& shader_source,
|
||||
const MaterialProperty& property)
|
||||
{
|
||||
const auto write_shader = [&](std::string_view type, u32 element_count) {
|
||||
if (element_count == 1)
|
||||
{
|
||||
shader_source.Write("{} {};\n", type, property.m_code_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
shader_source.Write("{}{} {};\n", type, element_count, property.m_code_name);
|
||||
}
|
||||
|
||||
for (std::size_t i = element_count; i < 4; i++)
|
||||
{
|
||||
shader_source.Write("{} {}_padding_{};\n", type, property.m_code_name, i + 1);
|
||||
}
|
||||
};
|
||||
std::visit(overloaded{[&](const CustomAssetLibrary::AssetID&) {},
|
||||
[&](s32 value) { write_shader("int", 1); },
|
||||
[&](const std::array<s32, 2>& value) { write_shader("int", 2); },
|
||||
[&](const std::array<s32, 3>& value) { write_shader("int", 3); },
|
||||
[&](const std::array<s32, 4>& value) { write_shader("int", 4); },
|
||||
[&](float value) { write_shader("float", 1); },
|
||||
[&](const std::array<float, 2>& value) { write_shader("float", 2); },
|
||||
[&](const std::array<float, 3>& value) { write_shader("float", 3); },
|
||||
[&](const std::array<float, 4>& value) { write_shader("float", 4); },
|
||||
[&](bool value) { write_shader("bool", 1); }},
|
||||
property.m_value);
|
||||
}
|
||||
|
||||
bool MaterialData::FromJson(const CustomAssetLibrary::AssetID& asset_id,
|
||||
const picojson::object& json, MaterialData* data)
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
@ -14,20 +14,20 @@
|
|||
#include "Common/EnumFormatter.h"
|
||||
#include "VideoCommon/Assets/CustomAsset.h"
|
||||
|
||||
class ShaderCode;
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
struct MaterialProperty
|
||||
{
|
||||
using Value = std::variant<std::string>;
|
||||
enum class Type
|
||||
{
|
||||
Type_Undefined,
|
||||
Type_TextureAsset,
|
||||
Type_Max = Type_TextureAsset
|
||||
};
|
||||
static void WriteToMemory(u8*& buffer, const MaterialProperty& property);
|
||||
static std::size_t GetMemorySize(const MaterialProperty& property);
|
||||
static void WriteAsShaderCode(ShaderCode& shader_source, const MaterialProperty& property);
|
||||
using Value = std::variant<CustomAssetLibrary::AssetID, s32, std::array<s32, 2>,
|
||||
std::array<s32, 3>, std::array<s32, 4>, float, std::array<float, 2>,
|
||||
std::array<float, 3>, std::array<float, 4>, bool>;
|
||||
std::string m_code_name;
|
||||
Type m_type = Type::Type_Undefined;
|
||||
std::optional<Value> m_value;
|
||||
Value m_value;
|
||||
};
|
||||
|
||||
struct MaterialData
|
||||
|
@ -52,10 +52,3 @@ private:
|
|||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<VideoCommon::MaterialProperty::Type>
|
||||
: EnumFormatter<VideoCommon::MaterialProperty::Type::Type_Max>
|
||||
{
|
||||
constexpr formatter() : EnumFormatter({"Undefined", "Texture"}) {}
|
||||
};
|
||||
|
|
|
@ -3,14 +3,177 @@
|
|||
|
||||
#include "VideoCommon/Assets/ShaderAsset.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/VariantUtil.h"
|
||||
#include "VideoCommon/Assets/CustomAssetLibrary.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
bool ParseShaderProperties(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
|
||||
const picojson::array& properties_data,
|
||||
std::map<std::string, VideoCommon::ShaderProperty>* shader_properties)
|
||||
template <typename ElementType, std::size_t ElementCount, typename PropertyType>
|
||||
bool ParseNumeric(const CustomAssetLibrary::AssetID& asset_id, const picojson::value& json_value,
|
||||
std::string_view code_name, PropertyType* value)
|
||||
{
|
||||
static_assert(ElementCount <= 4, "Numeric data expected to be four elements or less");
|
||||
if constexpr (ElementCount == 1)
|
||||
{
|
||||
if (!json_value.is<double>())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Asset id '{}' shader has attribute '{}' where "
|
||||
"a double was expected but not provided.",
|
||||
asset_id, code_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = static_cast<ElementType>(json_value.get<double>());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!json_value.is<picojson::array>())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Asset id '{}' shader has attribute '{}' where "
|
||||
"an array was expected but not provided.",
|
||||
asset_id, code_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto json_data = json_value.get<picojson::array>();
|
||||
|
||||
if (json_data.size() != ElementCount)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Asset id '{}' shader has attribute '{}' with incorrect number "
|
||||
"of elements, expected {}",
|
||||
asset_id, code_name, ElementCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!std::all_of(json_data.begin(), json_data.end(),
|
||||
[](const picojson::value& v) { return v.is<double>(); }))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Asset id '{}' shader has attribute '{}' where "
|
||||
"all elements are not of type double.",
|
||||
asset_id, code_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::array<ElementType, ElementCount> data;
|
||||
for (std::size_t i = 0; i < ElementCount; i++)
|
||||
{
|
||||
data[i] = static_cast<ElementType>(json_data[i].get<double>());
|
||||
}
|
||||
*value = std::move(data);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseShaderValue(const CustomAssetLibrary::AssetID& asset_id,
|
||||
const picojson::value& json_value, std::string_view code_name,
|
||||
std::string_view type, ShaderProperty::Value* value)
|
||||
{
|
||||
if (type == "int")
|
||||
{
|
||||
return ParseNumeric<s32, 1>(asset_id, json_value, code_name, value);
|
||||
}
|
||||
else if (type == "int2")
|
||||
{
|
||||
return ParseNumeric<s32, 2>(asset_id, json_value, code_name, value);
|
||||
}
|
||||
else if (type == "int3")
|
||||
{
|
||||
return ParseNumeric<s32, 3>(asset_id, json_value, code_name, value);
|
||||
}
|
||||
else if (type == "int4")
|
||||
{
|
||||
return ParseNumeric<s32, 4>(asset_id, json_value, code_name, value);
|
||||
}
|
||||
else if (type == "float")
|
||||
{
|
||||
return ParseNumeric<float, 1>(asset_id, json_value, code_name, value);
|
||||
}
|
||||
else if (type == "float2")
|
||||
{
|
||||
return ParseNumeric<float, 2>(asset_id, json_value, code_name, value);
|
||||
}
|
||||
else if (type == "float3")
|
||||
{
|
||||
return ParseNumeric<float, 3>(asset_id, json_value, code_name, value);
|
||||
}
|
||||
else if (type == "float4")
|
||||
{
|
||||
return ParseNumeric<float, 4>(asset_id, json_value, code_name, value);
|
||||
}
|
||||
else if (type == "rgb")
|
||||
{
|
||||
ShaderProperty::RGB rgb;
|
||||
if (!ParseNumeric<float, 3>(asset_id, json_value, code_name, &rgb.value))
|
||||
return false;
|
||||
*value = std::move(rgb);
|
||||
return true;
|
||||
}
|
||||
else if (type == "rgba")
|
||||
{
|
||||
ShaderProperty::RGBA rgba;
|
||||
if (!ParseNumeric<float, 4>(asset_id, json_value, code_name, &rgba.value))
|
||||
return false;
|
||||
*value = std::move(rgba);
|
||||
return true;
|
||||
}
|
||||
else if (type == "bool")
|
||||
{
|
||||
if (json_value.is<bool>())
|
||||
{
|
||||
*value = json_value.get<bool>();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (type == "sampler2d")
|
||||
{
|
||||
if (json_value.is<std::string>())
|
||||
{
|
||||
ShaderProperty::Sampler2D sampler2d;
|
||||
sampler2d.value = json_value.get<std::string>();
|
||||
*value = std::move(sampler2d);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (type == "sampler2darray")
|
||||
{
|
||||
if (json_value.is<std::string>())
|
||||
{
|
||||
ShaderProperty::Sampler2DArray sampler2darray;
|
||||
sampler2darray.value = json_value.get<std::string>();
|
||||
*value = std::move(sampler2darray);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (type == "samplercube")
|
||||
{
|
||||
if (json_value.is<std::string>())
|
||||
{
|
||||
ShaderProperty::SamplerCube samplercube;
|
||||
samplercube.value = json_value.get<std::string>();
|
||||
*value = std::move(samplercube);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse the json, value is not valid for type '{}'",
|
||||
asset_id, type);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
ParseShaderProperties(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
|
||||
const picojson::array& properties_data,
|
||||
std::map<std::string, VideoCommon::ShaderProperty>* shader_properties)
|
||||
{
|
||||
if (!shader_properties) [[unlikely]]
|
||||
return false;
|
||||
|
@ -42,33 +205,7 @@ bool ParseShaderProperties(const VideoCommon::CustomAssetLibrary::AssetID& asset
|
|||
return false;
|
||||
}
|
||||
std::string type = type_iter->second.to_str();
|
||||
std::transform(type.begin(), type.end(), type.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
|
||||
if (type == "sampler2d")
|
||||
{
|
||||
property.m_type = ShaderProperty::Type::Type_Sampler2D;
|
||||
}
|
||||
else if (type == "samplercube")
|
||||
{
|
||||
property.m_type = ShaderProperty::Type::Type_SamplerCube;
|
||||
}
|
||||
else if (type == "samplerarrayshared_main")
|
||||
{
|
||||
property.m_type = ShaderProperty::Type::Type_SamplerArrayShared_Main;
|
||||
}
|
||||
else if (type == "samplerarrayshared_additional")
|
||||
{
|
||||
property.m_type = ShaderProperty::Type::Type_SamplerArrayShared_Additional;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Asset '{}' failed to parse json, property entry 'description' is "
|
||||
"an invalid option",
|
||||
asset_id);
|
||||
return false;
|
||||
}
|
||||
Common::ToLower(&type);
|
||||
|
||||
const auto description_iter = property_data_obj.find("description");
|
||||
if (description_iter == property_data_obj.end())
|
||||
|
@ -103,7 +240,18 @@ bool ParseShaderProperties(const VideoCommon::CustomAssetLibrary::AssetID& asset
|
|||
asset_id);
|
||||
return false;
|
||||
}
|
||||
shader_properties->try_emplace(code_name_iter->second.to_str(), std::move(property));
|
||||
std::string code_name = code_name_iter->second.to_str();
|
||||
|
||||
const auto default_iter = property_data_obj.find("default");
|
||||
if (default_iter != property_data_obj.end())
|
||||
{
|
||||
if (!ParseShaderValue(asset_id, default_iter->second, code_name, type, &property.m_default))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
shader_properties->try_emplace(std::move(code_name), std::move(property));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
|
@ -14,22 +16,36 @@ namespace VideoCommon
|
|||
{
|
||||
struct ShaderProperty
|
||||
{
|
||||
// "SamplerShared" denotes that the sampler
|
||||
// already exists outside of the shader source
|
||||
// (ex: in the Dolphin defined pixel shader)
|
||||
// "Main" is the first entry in a shared sampler array
|
||||
// and "Additional" denotes a subsequent entry
|
||||
// in the array
|
||||
enum class Type
|
||||
struct RGB
|
||||
{
|
||||
Type_Undefined,
|
||||
Type_SamplerArrayShared_Main,
|
||||
Type_SamplerArrayShared_Additional,
|
||||
Type_Sampler2D,
|
||||
Type_SamplerCube,
|
||||
Type_Max = Type_SamplerCube
|
||||
std::array<float, 3> value;
|
||||
};
|
||||
Type m_type;
|
||||
|
||||
struct RGBA
|
||||
{
|
||||
std::array<float, 4> value;
|
||||
};
|
||||
|
||||
struct Sampler2D
|
||||
{
|
||||
CustomAssetLibrary::AssetID value;
|
||||
};
|
||||
|
||||
struct Sampler2DArray
|
||||
{
|
||||
CustomAssetLibrary::AssetID value;
|
||||
};
|
||||
|
||||
struct SamplerCube
|
||||
{
|
||||
CustomAssetLibrary::AssetID value;
|
||||
};
|
||||
|
||||
using Value = std::variant<s32, std::array<s32, 2>, std::array<s32, 3>, std::array<s32, 4>, float,
|
||||
std::array<float, 2>, std::array<float, 3>, std::array<float, 4>, bool,
|
||||
RGB, RGBA, Sampler2D, Sampler2DArray, SamplerCube>;
|
||||
|
||||
Value m_default;
|
||||
std::string m_description;
|
||||
};
|
||||
struct PixelShaderData
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "VideoCommon/Assets/TextureAsset.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "VideoCommon/BPMemory.h"
|
||||
|
||||
namespace VideoCommon
|
||||
|
@ -31,8 +32,7 @@ bool ParseSampler(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
|
|||
return false;
|
||||
}
|
||||
std::string sampler_state_mode = sampler_state_mode_iter->second.to_str();
|
||||
std::transform(sampler_state_mode.begin(), sampler_state_mode.end(), sampler_state_mode.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
Common::ToLower(&sampler_state_mode);
|
||||
|
||||
if (sampler_state_mode == "clamp")
|
||||
{
|
||||
|
@ -71,8 +71,7 @@ bool ParseSampler(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
|
|||
return false;
|
||||
}
|
||||
std::string sampler_state_filter = sampler_state_filter_iter->second.to_str();
|
||||
std::transform(sampler_state_filter.begin(), sampler_state_filter.end(),
|
||||
sampler_state_filter.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||
Common::ToLower(&sampler_state_filter);
|
||||
if (sampler_state_filter == "linear")
|
||||
{
|
||||
sampler->tm0.min_filter = FilterMode::Linear;
|
||||
|
@ -97,20 +96,6 @@ bool ParseSampler(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
|
|||
return true;
|
||||
}
|
||||
} // namespace
|
||||
CustomAssetLibrary::LoadInfo RawTextureAsset::LoadImpl(const CustomAssetLibrary::AssetID& asset_id)
|
||||
{
|
||||
auto potential_data = std::make_shared<CustomTextureData>();
|
||||
const auto loaded_info = m_owning_library->LoadTexture(asset_id, potential_data.get());
|
||||
if (loaded_info.m_bytes_loaded == 0)
|
||||
return {};
|
||||
{
|
||||
std::lock_guard lk(m_data_lock);
|
||||
m_loaded = true;
|
||||
m_data = std::move(potential_data);
|
||||
}
|
||||
return loaded_info;
|
||||
}
|
||||
|
||||
bool TextureData::FromJson(const CustomAssetLibrary::AssetID& asset_id,
|
||||
const picojson::object& json, TextureData* data)
|
||||
{
|
||||
|
@ -130,8 +115,7 @@ bool TextureData::FromJson(const CustomAssetLibrary::AssetID& asset_id,
|
|||
return false;
|
||||
}
|
||||
std::string type = type_iter->second.to_str();
|
||||
std::transform(type.begin(), type.end(), type.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
Common::ToLower(&type);
|
||||
|
||||
if (type == "texture2d")
|
||||
{
|
||||
|
@ -160,7 +144,7 @@ bool TextureData::FromJson(const CustomAssetLibrary::AssetID& asset_id,
|
|||
|
||||
CustomAssetLibrary::LoadInfo GameTextureAsset::LoadImpl(const CustomAssetLibrary::AssetID& asset_id)
|
||||
{
|
||||
auto potential_data = std::make_shared<CustomTextureData>();
|
||||
auto potential_data = std::make_shared<TextureData>();
|
||||
const auto loaded_info = m_owning_library->LoadGameTexture(asset_id, potential_data.get());
|
||||
if (loaded_info.m_bytes_loaded == 0)
|
||||
return {};
|
||||
|
@ -184,7 +168,7 @@ bool GameTextureAsset::Validate(u32 native_width, u32 native_height) const
|
|||
return false;
|
||||
}
|
||||
|
||||
if (m_data->m_slices.empty())
|
||||
if (m_data->m_texture.m_slices.empty())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Game texture can't be validated for asset '{}' because no data was available.",
|
||||
|
@ -192,7 +176,7 @@ bool GameTextureAsset::Validate(u32 native_width, u32 native_height) const
|
|||
return false;
|
||||
}
|
||||
|
||||
if (m_data->m_slices.size() > 1)
|
||||
if (m_data->m_texture.m_slices.size() > 1)
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
|
@ -201,7 +185,7 @@ bool GameTextureAsset::Validate(u32 native_width, u32 native_height) const
|
|||
return false;
|
||||
}
|
||||
|
||||
const auto& slice = m_data->m_slices[0];
|
||||
const auto& slice = m_data->m_texture.m_slices[0];
|
||||
if (slice.m_levels.empty())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
|
|
|
@ -3,23 +3,16 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <picojson.h>
|
||||
|
||||
#include "Common/EnumFormatter.h"
|
||||
#include "VideoCommon/Assets/CustomAsset.h"
|
||||
#include "VideoCommon/Assets/CustomTextureData.h"
|
||||
#include "VideoCommon/RenderState.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
class RawTextureAsset final : public CustomLoadableAsset<CustomTextureData>
|
||||
{
|
||||
public:
|
||||
using CustomLoadableAsset::CustomLoadableAsset;
|
||||
|
||||
private:
|
||||
CustomAssetLibrary::LoadInfo LoadImpl(const CustomAssetLibrary::AssetID& asset_id) override;
|
||||
};
|
||||
|
||||
struct TextureData
|
||||
{
|
||||
static bool FromJson(const CustomAssetLibrary::AssetID& asset_id, const picojson::object& json,
|
||||
|
@ -32,11 +25,11 @@ struct TextureData
|
|||
Type_Max = Type_TextureCube
|
||||
};
|
||||
Type m_type;
|
||||
CustomTextureData m_data;
|
||||
CustomTextureData m_texture;
|
||||
SamplerState m_sampler;
|
||||
};
|
||||
|
||||
class GameTextureAsset final : public CustomLoadableAsset<CustomTextureData>
|
||||
class GameTextureAsset final : public CustomLoadableAsset<TextureData>
|
||||
{
|
||||
public:
|
||||
using CustomLoadableAsset::CustomLoadableAsset;
|
||||
|
@ -49,3 +42,10 @@ private:
|
|||
CustomAssetLibrary::LoadInfo LoadImpl(const CustomAssetLibrary::AssetID& asset_id) override;
|
||||
};
|
||||
} // namespace VideoCommon
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<VideoCommon::TextureData::Type>
|
||||
: EnumFormatter<VideoCommon::TextureData::Type::Type_Max>
|
||||
{
|
||||
constexpr formatter() : EnumFormatter({"Undefined", "Texture2D", "TextureCube"}) {}
|
||||
};
|
||||
|
|
|
@ -96,7 +96,7 @@ void AsyncRequests::PushEvent(const AsyncRequests::Event& event, bool blocking)
|
|||
m_queue.push(event);
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.GetFifo().RunGpu(system);
|
||||
system.GetFifo().RunGpu();
|
||||
if (blocking)
|
||||
{
|
||||
m_cond.wait(lock, [this] { return m_queue.empty(); });
|
||||
|
|
|
@ -11,8 +11,8 @@ BPMemory bpmem;
|
|||
|
||||
bool BlendMode::UseLogicOp() const
|
||||
{
|
||||
// Logicop bit has lowest priority.
|
||||
if (subtract || blendenable || !logicopenable)
|
||||
// Blending overrides the logicop bit.
|
||||
if (blendenable || !logicopenable)
|
||||
return false;
|
||||
|
||||
// Fast path for Kirby's Return to Dreamland, they use it with dstAlpha.
|
||||
|
|
|
@ -56,7 +56,7 @@ enum
|
|||
BPMEM_EFB_TL = 0x49,
|
||||
BPMEM_EFB_WH = 0x4A,
|
||||
BPMEM_EFB_ADDR = 0x4B,
|
||||
BPMEM_MIPMAP_STRIDE = 0x4D,
|
||||
BPMEM_EFB_STRIDE = 0x4D,
|
||||
BPMEM_COPYYSCALE = 0x4E,
|
||||
BPMEM_CLEAR_AR = 0x4F,
|
||||
BPMEM_CLEAR_GB = 0x50,
|
||||
|
@ -1037,12 +1037,12 @@ struct fmt::formatter<TexImage1>
|
|||
auto format(const TexImage1& teximg, FormatContext& ctx) const
|
||||
{
|
||||
return fmt::format_to(ctx.out(),
|
||||
"Even TMEM Offset: {:x}\n"
|
||||
"Even TMEM Line: 0x{:04x} (byte 0x{:05x})\n"
|
||||
"Even TMEM Width: {}\n"
|
||||
"Even TMEM Height: {}\n"
|
||||
"Cache is manually managed: {}",
|
||||
teximg.tmem_even, teximg.cache_width, teximg.cache_height,
|
||||
teximg.cache_manually_managed ? "Yes" : "No");
|
||||
teximg.tmem_even, teximg.tmem_even * 32, teximg.cache_width,
|
||||
teximg.cache_height, teximg.cache_manually_managed ? "Yes" : "No");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1061,10 +1061,11 @@ struct fmt::formatter<TexImage2>
|
|||
auto format(const TexImage2& teximg, FormatContext& ctx) const
|
||||
{
|
||||
return fmt::format_to(ctx.out(),
|
||||
"Odd TMEM Offset: {:x}\n"
|
||||
"Odd TMEM Line: 0x{:04x} (byte 0x{:05x})\n"
|
||||
"Odd TMEM Width: {}\n"
|
||||
"Odd TMEM Height: {}",
|
||||
teximg.tmem_odd, teximg.cache_width, teximg.cache_height);
|
||||
teximg.tmem_odd, teximg.tmem_odd * 32, teximg.cache_width,
|
||||
teximg.cache_height);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1080,7 +1081,7 @@ struct fmt::formatter<TexImage3>
|
|||
template <typename FormatContext>
|
||||
auto format(const TexImage3& teximg, FormatContext& ctx) const
|
||||
{
|
||||
return fmt::format_to(ctx.out(), "Source address (32 byte aligned): 0x{:06X}",
|
||||
return fmt::format_to(ctx.out(), "Source address (32 byte aligned): 0x{:06x}",
|
||||
teximg.image_base << 5);
|
||||
}
|
||||
};
|
||||
|
@ -1098,7 +1099,7 @@ struct fmt::formatter<TexTLUT>
|
|||
template <typename FormatContext>
|
||||
auto format(const TexTLUT& tlut, FormatContext& ctx) const
|
||||
{
|
||||
return fmt::format_to(ctx.out(), "Address: {:08x}\nFormat: {}", tlut.tmem_offset << 9,
|
||||
return fmt::format_to(ctx.out(), "Tmem address: 0x{:05x}\nFormat: {}", tlut.tmem_offset << 9,
|
||||
tlut.tlut_format);
|
||||
}
|
||||
};
|
||||
|
@ -1108,6 +1109,16 @@ union ZTex1
|
|||
BitField<0, 24, u32> bias;
|
||||
u32 hex;
|
||||
};
|
||||
template <>
|
||||
struct fmt::formatter<ZTex1>
|
||||
{
|
||||
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||
template <typename FormatContext>
|
||||
auto format(const ZTex1& ztex1, FormatContext& ctx) const
|
||||
{
|
||||
return fmt::format_to(ctx.out(), "Bias: 0x{:06x}", ztex1.bias);
|
||||
}
|
||||
};
|
||||
|
||||
union ZTex2
|
||||
{
|
||||
|
@ -2218,7 +2229,7 @@ union CopyFilterCoefficients
|
|||
union BPU_PreloadTileInfo
|
||||
{
|
||||
BitField<0, 15, u32> count;
|
||||
BitField<15, 2, u32> type;
|
||||
BitField<15, 2, u32> type; // TODO: enum for this?
|
||||
u32 hex;
|
||||
};
|
||||
template <>
|
||||
|
@ -2228,7 +2239,33 @@ struct fmt::formatter<BPU_PreloadTileInfo>
|
|||
template <typename FormatContext>
|
||||
auto format(const BPU_PreloadTileInfo& info, FormatContext& ctx) const
|
||||
{
|
||||
return fmt::format_to(ctx.out(), "Type: {}\nCount: {}", info.type, info.count);
|
||||
if (info.count == 0 && info.type == 0)
|
||||
{
|
||||
return fmt::format_to(ctx.out(), "GX_TexModeSync (type and count are both 0)");
|
||||
}
|
||||
else
|
||||
{
|
||||
return fmt::format_to(ctx.out(), "Type: {}\nCount: 0x{:x} lines (0x{:x} bytes)", info.type,
|
||||
info.count, info.count * 32);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
union BPU_LoadTlutInfo
|
||||
{
|
||||
BitField<0, 10, u32> tmem_addr;
|
||||
BitField<10, 11, u32> tmem_line_count;
|
||||
u32 hex;
|
||||
};
|
||||
template <>
|
||||
struct fmt::formatter<BPU_LoadTlutInfo>
|
||||
{
|
||||
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||
template <typename FormatContext>
|
||||
auto format(const BPU_LoadTlutInfo& info, FormatContext& ctx) const
|
||||
{
|
||||
return fmt::format_to(ctx.out(), "Tmem address: 0x{:05x}\nCount: 0x{:x} lines (0x{:x} bytes)",
|
||||
info.tmem_addr << 9, info.tmem_line_count, info.tmem_line_count * 32);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2239,7 +2276,7 @@ struct BPS_TmemConfig
|
|||
u32 preload_tmem_odd;
|
||||
BPU_PreloadTileInfo preload_tile_info;
|
||||
u32 tlut_src;
|
||||
u32 tlut_dest;
|
||||
BPU_LoadTlutInfo tlut_dest;
|
||||
u32 texinvalidate;
|
||||
};
|
||||
|
||||
|
@ -2426,35 +2463,32 @@ struct BPMemory
|
|||
// the 3 offset matrices can either be indirect type, S-type, or T-type
|
||||
// 6bit scale factor s is distributed across IND_MTXA/B/C.
|
||||
// before using matrices scale by 2^-(s-17)
|
||||
IND_MTX indmtx[3]; // 0x06-0x0e: GXSetIndTexMtx, 2x3 matrices
|
||||
IND_IMASK imask; // 0x0f
|
||||
TevStageIndirect tevind[16]; // 0x10-0x1f: GXSetTevIndirect
|
||||
ScissorPos scissorTL; // 0x20
|
||||
ScissorPos scissorBR; // 0x21
|
||||
LPSize lineptwidth; // 0x22
|
||||
u32 sucounter; // 0x23
|
||||
u32 rascounter; // 0x24
|
||||
TEXSCALE texscale[2]; // 0x25,0x26: GXSetIndTexCoordScale
|
||||
RAS1_IREF tevindref; // 0x27: GXSetIndTexOrder
|
||||
TwoTevStageOrders tevorders[8]; // 0x28-0x2f
|
||||
TCoordInfo texcoords[8]; // 0x30-0x4f: s,t,s,t,s,t,s,t...
|
||||
ZMode zmode; // 0x40
|
||||
BlendMode blendmode; // 0x41
|
||||
ConstantAlpha dstalpha; // 0x42
|
||||
PEControl zcontrol; // 0x43: GXSetZCompLoc, GXPixModeSync
|
||||
FieldMask fieldmask; // 0x44
|
||||
u32 drawdone; // 0x45: bit1=1 if end of list
|
||||
u32 unknown5; // 0x46: clock?
|
||||
u32 petoken; // 0x47
|
||||
u32 petokenint; // 0x48
|
||||
X10Y10 copyTexSrcXY; // 0x49
|
||||
X10Y10 copyTexSrcWH; // 0x4a
|
||||
u32 copyTexDest; // 0x4b: CopyAddress (GXDispCopy and GXTexCopy use it)
|
||||
u32 unknown6; // 0x4c
|
||||
// usually set to 4 when dest is single channel, 8 when dest is 2 channel, 16 when dest is RGBA
|
||||
// also, doubles whenever mipmap box filter option is set (excent on RGBA). Probably to do
|
||||
// with number of bytes to look at when smoothing
|
||||
u32 copyMipMapStrideChannels; // 0x4d
|
||||
IND_MTX indmtx[3]; // 0x06-0x0e: GXSetIndTexMtx, 2x3 matrices
|
||||
IND_IMASK imask; // 0x0f
|
||||
TevStageIndirect tevind[16]; // 0x10-0x1f: GXSetTevIndirect
|
||||
ScissorPos scissorTL; // 0x20
|
||||
ScissorPos scissorBR; // 0x21
|
||||
LPSize lineptwidth; // 0x22
|
||||
u32 sucounter; // 0x23
|
||||
u32 rascounter; // 0x24
|
||||
TEXSCALE texscale[2]; // 0x25,0x26: GXSetIndTexCoordScale
|
||||
RAS1_IREF tevindref; // 0x27: GXSetIndTexOrder
|
||||
TwoTevStageOrders tevorders[8]; // 0x28-0x2f
|
||||
TCoordInfo texcoords[8]; // 0x30-0x3f: s,t,s,t,s,t,s,t...
|
||||
ZMode zmode; // 0x40
|
||||
BlendMode blendmode; // 0x41
|
||||
ConstantAlpha dstalpha; // 0x42
|
||||
PEControl zcontrol; // 0x43: GXSetZCompLoc, GXPixModeSync
|
||||
FieldMask fieldmask; // 0x44
|
||||
u32 drawdone; // 0x45: bit1=1 if end of list
|
||||
u32 unknown5; // 0x46: clock?
|
||||
u32 petoken; // 0x47
|
||||
u32 petokenint; // 0x48
|
||||
X10Y10 copyTexSrcXY; // 0x49
|
||||
X10Y10 copyTexSrcWH; // 0x4a
|
||||
u32 copyTexDest; // 0x4b: CopyAddress (GXDispCopy and GXTexCopy use it)
|
||||
u32 unknown6; // 0x4c
|
||||
u32 copyDestStride; // 0x4d
|
||||
u32 dispcopyyscale; // 0x4e
|
||||
u32 clearcolorAR; // 0x4f
|
||||
u32 clearcolorGB; // 0x50
|
||||
|
|
|
@ -38,11 +38,11 @@
|
|||
#include "VideoCommon/TMEM.h"
|
||||
#include "VideoCommon/TextureCacheBase.h"
|
||||
#include "VideoCommon/TextureDecoder.h"
|
||||
#include "VideoCommon/VertexShaderManager.h"
|
||||
#include "VideoCommon/VideoBackendBase.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
#include "VideoCommon/VideoEvents.h"
|
||||
#include "VideoCommon/XFStateManager.h"
|
||||
|
||||
using namespace BPFunctions;
|
||||
|
||||
|
@ -55,8 +55,7 @@ void BPInit()
|
|||
bpmem.bpMask = 0xFFFFFF;
|
||||
}
|
||||
|
||||
static void BPWritten(PixelShaderManager& pixel_shader_manager,
|
||||
VertexShaderManager& vertex_shader_manager,
|
||||
static void BPWritten(PixelShaderManager& pixel_shader_manager, XFStateManager& xf_state_manager,
|
||||
GeometryShaderManager& geometry_shader_manager, const BPCmd& bp,
|
||||
int cycles_into_future)
|
||||
{
|
||||
|
@ -139,7 +138,7 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager,
|
|||
case BPMEM_SCISSORTL: // Scissor Rectable Top, Left
|
||||
case BPMEM_SCISSORBR: // Scissor Rectable Bottom, Right
|
||||
case BPMEM_SCISSOROFFSET: // Scissor Offset
|
||||
vertex_shader_manager.SetViewportChanged();
|
||||
xf_state_manager.SetViewportChanged();
|
||||
geometry_shader_manager.SetViewportChanged();
|
||||
return;
|
||||
case BPMEM_LINEPTWIDTH: // Line Width
|
||||
|
@ -191,7 +190,7 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager,
|
|||
g_framebuffer_manager->RefreshPeekCache();
|
||||
auto& system = Core::System::GetInstance();
|
||||
if (!system.GetFifo().UseDeterministicGPUThread())
|
||||
system.GetPixelEngine().SetFinish(system, cycles_into_future); // may generate interrupt
|
||||
system.GetPixelEngine().SetFinish(cycles_into_future); // may generate interrupt
|
||||
DEBUG_LOG_FMT(VIDEO, "GXSetDrawDone SetPEFinish (value: {:#04X})", bp.newvalue & 0xFFFF);
|
||||
return;
|
||||
}
|
||||
|
@ -211,7 +210,7 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager,
|
|||
auto& system = Core::System::GetInstance();
|
||||
if (!system.GetFifo().UseDeterministicGPUThread())
|
||||
{
|
||||
system.GetPixelEngine().SetToken(system, static_cast<u16>(bp.newvalue & 0xFFFF), false,
|
||||
system.GetPixelEngine().SetToken(static_cast<u16>(bp.newvalue & 0xFFFF), false,
|
||||
cycles_into_future);
|
||||
}
|
||||
DEBUG_LOG_FMT(VIDEO, "SetPEToken {:#06X}", bp.newvalue & 0xFFFF);
|
||||
|
@ -227,7 +226,7 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager,
|
|||
auto& system = Core::System::GetInstance();
|
||||
if (!system.GetFifo().UseDeterministicGPUThread())
|
||||
{
|
||||
system.GetPixelEngine().SetToken(system, static_cast<u16>(bp.newvalue & 0xFFFF), true,
|
||||
system.GetPixelEngine().SetToken(static_cast<u16>(bp.newvalue & 0xFFFF), true,
|
||||
cycles_into_future);
|
||||
}
|
||||
DEBUG_LOG_FMT(VIDEO, "SetPEToken + INT {:#06X}", bp.newvalue & 0xFFFF);
|
||||
|
@ -246,7 +245,7 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager,
|
|||
// this function
|
||||
|
||||
u32 destAddr = bpmem.copyTexDest << 5;
|
||||
u32 destStride = bpmem.copyMipMapStrideChannels << 5;
|
||||
u32 destStride = bpmem.copyDestStride << 5;
|
||||
|
||||
MathUtil::Rectangle<s32> srcRect;
|
||||
srcRect.left = bpmem.copyTexSrcXY.x;
|
||||
|
@ -380,24 +379,32 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager,
|
|||
|
||||
return;
|
||||
}
|
||||
case BPMEM_LOADTLUT0: // This one updates bpmem.tlutXferSrc, no need to do anything here.
|
||||
case BPMEM_LOADTLUT0: // This updates bpmem.tmem_config.tlut_src, no need to do anything here.
|
||||
return;
|
||||
case BPMEM_LOADTLUT1: // Load a Texture Look Up Table
|
||||
{
|
||||
u32 tlutTMemAddr = (bp.newvalue & 0x3FF) << 9;
|
||||
u32 tlutXferCount = (bp.newvalue & 0x1FFC00) >> 5;
|
||||
u32 tmem_addr = bpmem.tmem_config.tlut_dest.tmem_addr << 9;
|
||||
u32 tmem_transfer_count = bpmem.tmem_config.tlut_dest.tmem_line_count * TMEM_LINE_SIZE;
|
||||
u32 addr = bpmem.tmem_config.tlut_src << 5;
|
||||
|
||||
// The GameCube ignores the upper bits of this address. Some games (WW, MKDD) set them.
|
||||
if (!SConfig::GetInstance().bWii)
|
||||
addr = addr & 0x01FFFFFF;
|
||||
|
||||
// The copy below will always be in bounds as tmem is bigger than the maximum address a TLUT can
|
||||
// be loaded to.
|
||||
static constexpr u32 MAX_LOADABLE_TMEM_ADDR =
|
||||
(1 << bpmem.tmem_config.tlut_dest.tmem_addr.NumBits()) << 9;
|
||||
static constexpr u32 MAX_TMEM_LINE_COUNT =
|
||||
(1 << bpmem.tmem_config.tlut_dest.tmem_line_count.NumBits()) * TMEM_LINE_SIZE;
|
||||
static_assert(MAX_LOADABLE_TMEM_ADDR + MAX_TMEM_LINE_COUNT < TMEM_SIZE);
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
auto& memory = system.GetMemory();
|
||||
memory.CopyFromEmu(texMem + tlutTMemAddr, addr, tlutXferCount);
|
||||
memory.CopyFromEmu(texMem + tmem_addr, addr, tmem_transfer_count);
|
||||
|
||||
if (OpcodeDecoder::g_record_fifo_data)
|
||||
FifoRecorder::GetInstance().UseMemory(addr, tlutXferCount, MemoryUpdate::Type::TMEM);
|
||||
FifoRecorder::GetInstance().UseMemory(addr, tmem_transfer_count, MemoryUpdate::Type::TMEM);
|
||||
|
||||
TMEM::InvalidateAll();
|
||||
|
||||
|
@ -515,8 +522,9 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager,
|
|||
pixel_shader_manager.SetZModeControl();
|
||||
return;
|
||||
|
||||
case BPMEM_MIPMAP_STRIDE: // MipMap Stride Channel
|
||||
case BPMEM_COPYYSCALE: // Display Copy Y Scale
|
||||
case BPMEM_EFB_STRIDE: // Display Copy Stride
|
||||
case BPMEM_COPYYSCALE: // Display Copy Y Scale
|
||||
return;
|
||||
|
||||
/* 24 RID
|
||||
* 21 BC3 - Ind. Tex Stage 3 NTexCoord
|
||||
|
@ -781,7 +789,7 @@ void LoadBPReg(u8 reg, u32 value, int cycles_into_future)
|
|||
if (reg != BPMEM_BP_MASK)
|
||||
bpmem.bpMask = 0xFFFFFF;
|
||||
|
||||
BPWritten(system.GetPixelShaderManager(), system.GetVertexShaderManager(),
|
||||
BPWritten(system.GetPixelShaderManager(), system.GetXFStateManager(),
|
||||
system.GetGeometryShaderManager(), bp, cycles_into_future);
|
||||
}
|
||||
|
||||
|
@ -795,13 +803,13 @@ void LoadBPRegPreprocess(u8 reg, u32 value, int cycles_into_future)
|
|||
{
|
||||
case BPMEM_SETDRAWDONE:
|
||||
if ((newval & 0xff) == 0x02)
|
||||
system.GetPixelEngine().SetFinish(system, cycles_into_future);
|
||||
system.GetPixelEngine().SetFinish(cycles_into_future);
|
||||
break;
|
||||
case BPMEM_PE_TOKEN_ID:
|
||||
system.GetPixelEngine().SetToken(system, newval & 0xffff, false, cycles_into_future);
|
||||
system.GetPixelEngine().SetToken(newval & 0xffff, false, cycles_into_future);
|
||||
break;
|
||||
case BPMEM_PE_TOKEN_INT_ID: // Pixel Engine Interrupt Token ID
|
||||
system.GetPixelEngine().SetToken(system, newval & 0xffff, true, cycles_into_future);
|
||||
system.GetPixelEngine().SetToken(newval & 0xffff, true, cycles_into_future);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -982,9 +990,10 @@ std::pair<std::string, std::string> GetBPRegInfo(u8 cmd, u32 cmddata)
|
|||
RegName(BPMEM_EFB_ADDR),
|
||||
fmt::format("EFB Target address (32 byte aligned): 0x{:06X}", cmddata << 5));
|
||||
|
||||
case BPMEM_MIPMAP_STRIDE: // 0x4D
|
||||
return DescriptionlessReg(BPMEM_MIPMAP_STRIDE);
|
||||
// TODO: Description
|
||||
case BPMEM_EFB_STRIDE: // 0x4D
|
||||
return std::make_pair(
|
||||
RegName(BPMEM_EFB_STRIDE),
|
||||
fmt::format("EFB destination stride (32 byte aligned): 0x{:06X}", cmddata << 5));
|
||||
|
||||
case BPMEM_COPYYSCALE: // 0x4E
|
||||
return std::make_pair(
|
||||
|
@ -1030,12 +1039,14 @@ std::pair<std::string, std::string> GetBPRegInfo(u8 cmd, u32 cmddata)
|
|||
}
|
||||
|
||||
case BPMEM_CLEARBBOX1: // 0x55
|
||||
return DescriptionlessReg(BPMEM_CLEARBBOX1);
|
||||
// TODO: Description
|
||||
return std::make_pair(RegName(BPMEM_CLEARBBOX1),
|
||||
fmt::format("Bounding Box index 0: {}\nBounding Box index 1: {}",
|
||||
cmddata & 0x3ff, (cmddata >> 10) & 0x3ff));
|
||||
|
||||
case BPMEM_CLEARBBOX2: // 0x56
|
||||
return DescriptionlessReg(BPMEM_CLEARBBOX2);
|
||||
// TODO: Description
|
||||
return std::make_pair(RegName(BPMEM_CLEARBBOX2),
|
||||
fmt::format("Bounding Box index 2: {}\nBounding Box index 3: {}",
|
||||
cmddata & 0x3ff, (cmddata >> 10) & 0x3ff));
|
||||
|
||||
case BPMEM_CLEAR_PIXEL_PERF: // 0x57
|
||||
return DescriptionlessReg(BPMEM_CLEAR_PIXEL_PERF);
|
||||
|
@ -1050,28 +1061,33 @@ std::pair<std::string, std::string> GetBPRegInfo(u8 cmd, u32 cmddata)
|
|||
fmt::to_string(ScissorOffset{.hex = cmddata}));
|
||||
|
||||
case BPMEM_PRELOAD_ADDR: // 0x60
|
||||
return DescriptionlessReg(BPMEM_PRELOAD_ADDR);
|
||||
// TODO: Description
|
||||
return std::make_pair(
|
||||
RegName(BPMEM_PRELOAD_ADDR),
|
||||
fmt::format("Tmem preload address (32 byte aligned, in main memory): 0x{:06x}",
|
||||
cmddata << 5));
|
||||
|
||||
case BPMEM_PRELOAD_TMEMEVEN: // 0x61
|
||||
return DescriptionlessReg(BPMEM_PRELOAD_TMEMEVEN);
|
||||
// TODO: Description
|
||||
return std::make_pair(RegName(BPMEM_PRELOAD_TMEMEVEN),
|
||||
fmt::format("Tmem preload even line: 0x{:04x} (byte 0x{:05x})", cmddata,
|
||||
cmddata * TMEM_LINE_SIZE));
|
||||
|
||||
case BPMEM_PRELOAD_TMEMODD: // 0x62
|
||||
return DescriptionlessReg(BPMEM_PRELOAD_TMEMODD);
|
||||
// TODO: Description
|
||||
return std::make_pair(RegName(BPMEM_PRELOAD_TMEMODD),
|
||||
fmt::format("Tmem preload odd line: 0x{:04x} (byte 0x{:05x})", cmddata,
|
||||
cmddata * TMEM_LINE_SIZE));
|
||||
|
||||
case BPMEM_PRELOAD_MODE: // 0x63
|
||||
return std::make_pair(RegName(BPMEM_PRELOAD_MODE),
|
||||
fmt::to_string(BPU_PreloadTileInfo{.hex = cmddata}));
|
||||
|
||||
case BPMEM_LOADTLUT0: // 0x64
|
||||
return DescriptionlessReg(BPMEM_LOADTLUT0);
|
||||
// TODO: Description
|
||||
return std::make_pair(
|
||||
RegName(BPMEM_LOADTLUT0),
|
||||
fmt::format("TLUT load address (32 byte aligned, in main memory): 0x{:06x}", cmddata << 5));
|
||||
|
||||
case BPMEM_LOADTLUT1: // 0x65
|
||||
return DescriptionlessReg(BPMEM_LOADTLUT1);
|
||||
// TODO: Description
|
||||
return std::make_pair(RegName(BPMEM_LOADTLUT1),
|
||||
fmt::to_string(BPU_LoadTlutInfo{.hex = cmddata}));
|
||||
|
||||
case BPMEM_TEXINVALIDATE: // 0x66
|
||||
return DescriptionlessReg(BPMEM_TEXINVALIDATE);
|
||||
|
@ -1269,12 +1285,11 @@ std::pair<std::string, std::string> GetBPRegInfo(u8 cmd, u32 cmddata)
|
|||
return std::make_pair(RegName(BPMEM_FOGPARAM0), fmt::to_string(FogParam0{.hex = cmddata}));
|
||||
|
||||
case BPMEM_FOGBMAGNITUDE: // 0xEF
|
||||
return DescriptionlessReg(BPMEM_FOGBMAGNITUDE);
|
||||
// TODO: Description
|
||||
return std::make_pair(RegName(BPMEM_FOGBMAGNITUDE), fmt::format("B magnitude: {}", cmddata));
|
||||
|
||||
case BPMEM_FOGBEXPONENT: // 0xF0
|
||||
return DescriptionlessReg(BPMEM_FOGBEXPONENT);
|
||||
// TODO: Description
|
||||
return std::make_pair(RegName(BPMEM_FOGBEXPONENT),
|
||||
fmt::format("B shift: 1>>{} (1/{})", cmddata, 1 << cmddata));
|
||||
|
||||
case BPMEM_FOGPARAM3: // 0xF1
|
||||
return std::make_pair(RegName(BPMEM_FOGPARAM3), fmt::to_string(FogParam3{.hex = cmddata}));
|
||||
|
@ -1287,8 +1302,7 @@ std::pair<std::string, std::string> GetBPRegInfo(u8 cmd, u32 cmddata)
|
|||
return std::make_pair(RegName(BPMEM_ALPHACOMPARE), fmt::to_string(AlphaTest{.hex = cmddata}));
|
||||
|
||||
case BPMEM_BIAS: // 0xF4
|
||||
return DescriptionlessReg(BPMEM_BIAS);
|
||||
// TODO: Description
|
||||
return std::make_pair(RegName(BPMEM_BIAS), fmt::to_string(ZTex1{.hex = cmddata}));
|
||||
|
||||
case BPMEM_ZTEX2: // 0xF5
|
||||
return std::make_pair(RegName(BPMEM_ZTEX2), fmt::to_string(ZTex2{.hex = cmddata}));
|
||||
|
|
|
@ -51,7 +51,7 @@ void BoundingBox::Flush()
|
|||
for (u32 i = start; i < end; ++i)
|
||||
m_dirty[i] = false;
|
||||
|
||||
Write(start, std::vector<BBoxType>(m_values.begin() + start, m_values.begin() + end));
|
||||
Write(start, std::span(m_values.begin() + start, m_values.begin() + end));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
@ -38,8 +39,7 @@ public:
|
|||
|
||||
protected:
|
||||
virtual std::vector<BBoxType> Read(u32 index, u32 length) = 0;
|
||||
// TODO: This can likely use std::span once we're on C++20
|
||||
virtual void Write(u32 index, const std::vector<BBoxType>& values) = 0;
|
||||
virtual void Write(u32 index, std::span<const BBoxType> values) = 0;
|
||||
|
||||
private:
|
||||
void Readback();
|
||||
|
|
|
@ -155,6 +155,8 @@ add_library(videocommon
|
|||
TextureDecoder_Util.h
|
||||
TextureInfo.cpp
|
||||
TextureInfo.h
|
||||
TextureUtils.cpp
|
||||
TextureUtils.h
|
||||
TMEM.cpp
|
||||
TMEM.h
|
||||
UberShaderCommon.cpp
|
||||
|
@ -196,6 +198,8 @@ add_library(videocommon
|
|||
Widescreen.h
|
||||
XFMemory.cpp
|
||||
XFMemory.h
|
||||
XFStateManager.cpp
|
||||
XFStateManager.h
|
||||
XFStructs.cpp
|
||||
XFStructs.h
|
||||
)
|
||||
|
@ -212,7 +216,7 @@ PRIVATE
|
|||
glslang
|
||||
)
|
||||
|
||||
if(_M_X86)
|
||||
if(_M_X86_64)
|
||||
target_sources(videocommon PRIVATE
|
||||
TextureDecoder_x64.cpp
|
||||
VertexLoaderX64.cpp
|
||||
|
|
|
@ -575,6 +575,102 @@ struct VAT
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
void SetTexElements(size_t idx, TexComponentCount value)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0:
|
||||
g0.Tex0CoordElements = value;
|
||||
return;
|
||||
case 1:
|
||||
g1.Tex1CoordElements = value;
|
||||
return;
|
||||
case 2:
|
||||
g1.Tex2CoordElements = value;
|
||||
return;
|
||||
case 3:
|
||||
g1.Tex3CoordElements = value;
|
||||
return;
|
||||
case 4:
|
||||
g1.Tex4CoordElements = value;
|
||||
return;
|
||||
case 5:
|
||||
g2.Tex5CoordElements = value;
|
||||
return;
|
||||
case 6:
|
||||
g2.Tex6CoordElements = value;
|
||||
return;
|
||||
case 7:
|
||||
g2.Tex7CoordElements = value;
|
||||
return;
|
||||
default:
|
||||
PanicAlertFmt("Invalid tex coord index {}", idx);
|
||||
}
|
||||
}
|
||||
void SetTexFormat(size_t idx, ComponentFormat value)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0:
|
||||
g0.Tex0CoordFormat = value;
|
||||
return;
|
||||
case 1:
|
||||
g1.Tex1CoordFormat = value;
|
||||
return;
|
||||
case 2:
|
||||
g1.Tex2CoordFormat = value;
|
||||
return;
|
||||
case 3:
|
||||
g1.Tex3CoordFormat = value;
|
||||
return;
|
||||
case 4:
|
||||
g1.Tex4CoordFormat = value;
|
||||
return;
|
||||
case 5:
|
||||
g2.Tex5CoordFormat = value;
|
||||
return;
|
||||
case 6:
|
||||
g2.Tex6CoordFormat = value;
|
||||
return;
|
||||
case 7:
|
||||
g2.Tex7CoordFormat = value;
|
||||
return;
|
||||
default:
|
||||
PanicAlertFmt("Invalid tex coord index {}", idx);
|
||||
}
|
||||
}
|
||||
void SetTexFrac(size_t idx, u8 value)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0:
|
||||
g0.Tex0Frac = value;
|
||||
return;
|
||||
case 1:
|
||||
g1.Tex1Frac = value;
|
||||
return;
|
||||
case 2:
|
||||
g1.Tex2Frac = value;
|
||||
return;
|
||||
case 3:
|
||||
g1.Tex3Frac = value;
|
||||
return;
|
||||
case 4:
|
||||
g2.Tex4Frac = value;
|
||||
return;
|
||||
case 5:
|
||||
g2.Tex5Frac = value;
|
||||
return;
|
||||
case 6:
|
||||
g2.Tex6Frac = value;
|
||||
return;
|
||||
case 7:
|
||||
g2.Tex7Frac = value;
|
||||
return;
|
||||
default:
|
||||
PanicAlertFmt("Invalid tex coord index {}", idx);
|
||||
}
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct fmt::formatter<VAT>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "VideoCommon/VertexShaderManager.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
#include "VideoCommon/XFMemory.h"
|
||||
#include "VideoCommon/XFStateManager.h"
|
||||
|
||||
// We really want things like c.w * a.x - a.w * c.x to stay symmetric, so they cancel to zero on
|
||||
// degenerate triangles. Make sure the compiler doesn't optimize in fmas where not requested.
|
||||
|
@ -153,7 +154,8 @@ bool CPUCull::AreAllVerticesCulled(VertexLoaderBase* loader, OpcodeDecoder::Prim
|
|||
}
|
||||
|
||||
// transform functions need the projection matrix to tranform to clip space
|
||||
Core::System::GetInstance().GetVertexShaderManager().SetProjectionMatrix();
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.GetVertexShaderManager().SetProjectionMatrix(system.GetXFStateManager());
|
||||
|
||||
static constexpr Common::EnumMap<CullMode, CullMode::All> cullmode_invert = {
|
||||
CullMode::None, CullMode::Front, CullMode::Back, CullMode::All};
|
||||
|
|
|
@ -24,14 +24,14 @@
|
|||
|
||||
namespace CommandProcessor
|
||||
{
|
||||
static bool IsOnThread(Core::System& system)
|
||||
static bool IsOnThread(const Core::System& system)
|
||||
{
|
||||
return system.IsDualCoreMode();
|
||||
}
|
||||
|
||||
static void UpdateInterrupts_Wrapper(Core::System& system, u64 userdata, s64 cyclesLate)
|
||||
{
|
||||
system.GetCommandProcessor().UpdateInterrupts(system, userdata);
|
||||
system.GetCommandProcessor().UpdateInterrupts(userdata);
|
||||
}
|
||||
|
||||
void SCPFifoStruct::Init()
|
||||
|
@ -105,7 +105,11 @@ static inline void WriteHigh(std::atomic<u32>& reg, u16 highbits)
|
|||
std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void CommandProcessorManager::Init(Core::System& system)
|
||||
CommandProcessorManager::CommandProcessorManager(Core::System& system) : m_system{system}
|
||||
{
|
||||
}
|
||||
|
||||
void CommandProcessorManager::Init()
|
||||
{
|
||||
m_cp_status_reg.Hex = 0;
|
||||
m_cp_status_reg.CommandIdle = 1;
|
||||
|
@ -130,7 +134,7 @@ void CommandProcessorManager::Init(Core::System& system)
|
|||
m_interrupt_waiting.Clear();
|
||||
|
||||
m_event_type_update_interrupts =
|
||||
system.GetCoreTiming().RegisterEvent("CPInterrupt", UpdateInterrupts_Wrapper);
|
||||
m_system.GetCoreTiming().RegisterEvent("CPInterrupt", UpdateInterrupts_Wrapper);
|
||||
}
|
||||
|
||||
u32 GetPhysicalAddressMask()
|
||||
|
@ -140,15 +144,13 @@ u32 GetPhysicalAddressMask()
|
|||
return SConfig::GetInstance().bWii ? 0x1fffffff : 0x03ffffff;
|
||||
}
|
||||
|
||||
void CommandProcessorManager::RegisterMMIO(Core::System& system, MMIO::Mapping* mmio, u32 base)
|
||||
void CommandProcessorManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
{
|
||||
constexpr u16 WMASK_NONE = 0x0000;
|
||||
constexpr u16 WMASK_ALL = 0xffff;
|
||||
constexpr u16 WMASK_LO_ALIGN_32BIT = 0xffe0;
|
||||
const u16 WMASK_HI_RESTRICT = GetPhysicalAddressMask() >> 16;
|
||||
|
||||
auto& fifo = m_fifo;
|
||||
|
||||
struct
|
||||
{
|
||||
u32 addr;
|
||||
|
@ -166,25 +168,27 @@ void CommandProcessorManager::RegisterMMIO(Core::System& system, MMIO::Mapping*
|
|||
{FIFO_BOUNDING_BOX_RIGHT, &m_bbox_right, true, WMASK_NONE},
|
||||
{FIFO_BOUNDING_BOX_TOP, &m_bbox_top, true, WMASK_NONE},
|
||||
{FIFO_BOUNDING_BOX_BOTTOM, &m_bbox_bottom, true, WMASK_NONE},
|
||||
{FIFO_BASE_LO, MMIO::Utils::LowPart(&fifo.CPBase), false, WMASK_LO_ALIGN_32BIT},
|
||||
{FIFO_BASE_HI, MMIO::Utils::HighPart(&fifo.CPBase), false, WMASK_HI_RESTRICT},
|
||||
{FIFO_END_LO, MMIO::Utils::LowPart(&fifo.CPEnd), false, WMASK_LO_ALIGN_32BIT},
|
||||
{FIFO_END_HI, MMIO::Utils::HighPart(&fifo.CPEnd), false, WMASK_HI_RESTRICT},
|
||||
{FIFO_HI_WATERMARK_LO, MMIO::Utils::LowPart(&fifo.CPHiWatermark), false,
|
||||
{FIFO_BASE_LO, MMIO::Utils::LowPart(&m_fifo.CPBase), false, WMASK_LO_ALIGN_32BIT},
|
||||
{FIFO_BASE_HI, MMIO::Utils::HighPart(&m_fifo.CPBase), false, WMASK_HI_RESTRICT},
|
||||
{FIFO_END_LO, MMIO::Utils::LowPart(&m_fifo.CPEnd), false, WMASK_LO_ALIGN_32BIT},
|
||||
{FIFO_END_HI, MMIO::Utils::HighPart(&m_fifo.CPEnd), false, WMASK_HI_RESTRICT},
|
||||
{FIFO_HI_WATERMARK_LO, MMIO::Utils::LowPart(&m_fifo.CPHiWatermark), false,
|
||||
WMASK_LO_ALIGN_32BIT},
|
||||
{FIFO_HI_WATERMARK_HI, MMIO::Utils::HighPart(&fifo.CPHiWatermark), false, WMASK_HI_RESTRICT},
|
||||
{FIFO_LO_WATERMARK_LO, MMIO::Utils::LowPart(&fifo.CPLoWatermark), false,
|
||||
{FIFO_HI_WATERMARK_HI, MMIO::Utils::HighPart(&m_fifo.CPHiWatermark), false,
|
||||
WMASK_HI_RESTRICT},
|
||||
{FIFO_LO_WATERMARK_LO, MMIO::Utils::LowPart(&m_fifo.CPLoWatermark), false,
|
||||
WMASK_LO_ALIGN_32BIT},
|
||||
{FIFO_LO_WATERMARK_HI, MMIO::Utils::HighPart(&fifo.CPLoWatermark), false, WMASK_HI_RESTRICT},
|
||||
{FIFO_LO_WATERMARK_HI, MMIO::Utils::HighPart(&m_fifo.CPLoWatermark), false,
|
||||
WMASK_HI_RESTRICT},
|
||||
// FIFO_RW_DISTANCE has some complex read code different for
|
||||
// single/dual core.
|
||||
{FIFO_WRITE_POINTER_LO, MMIO::Utils::LowPart(&fifo.CPWritePointer), false,
|
||||
{FIFO_WRITE_POINTER_LO, MMIO::Utils::LowPart(&m_fifo.CPWritePointer), false,
|
||||
WMASK_LO_ALIGN_32BIT},
|
||||
{FIFO_WRITE_POINTER_HI, MMIO::Utils::HighPart(&fifo.CPWritePointer), false,
|
||||
{FIFO_WRITE_POINTER_HI, MMIO::Utils::HighPart(&m_fifo.CPWritePointer), false,
|
||||
WMASK_HI_RESTRICT},
|
||||
// FIFO_READ_POINTER has different code for single/dual core.
|
||||
{FIFO_BP_LO, MMIO::Utils::LowPart(&fifo.CPBreakpoint), false, WMASK_LO_ALIGN_32BIT},
|
||||
{FIFO_BP_HI, MMIO::Utils::HighPart(&fifo.CPBreakpoint), false, WMASK_HI_RESTRICT},
|
||||
{FIFO_BP_LO, MMIO::Utils::LowPart(&m_fifo.CPBreakpoint), false, WMASK_LO_ALIGN_32BIT},
|
||||
{FIFO_BP_HI, MMIO::Utils::HighPart(&m_fifo.CPBreakpoint), false, WMASK_HI_RESTRICT},
|
||||
};
|
||||
|
||||
for (auto& mapped_var : directly_mapped_vars)
|
||||
|
@ -224,8 +228,8 @@ void CommandProcessorManager::RegisterMMIO(Core::System& system, MMIO::Mapping*
|
|||
|
||||
mmio->Register(base | STATUS_REGISTER, MMIO::ComplexRead<u16>([](Core::System& system_, u32) {
|
||||
auto& cp = system_.GetCommandProcessor();
|
||||
system_.GetFifo().SyncGPUForRegisterAccess(system_);
|
||||
cp.SetCpStatusRegister(system_);
|
||||
system_.GetFifo().SyncGPUForRegisterAccess();
|
||||
cp.SetCpStatusRegister();
|
||||
return cp.m_cp_status_reg.Hex;
|
||||
}),
|
||||
MMIO::InvalidWrite<u16>());
|
||||
|
@ -235,8 +239,8 @@ void CommandProcessorManager::RegisterMMIO(Core::System& system, MMIO::Mapping*
|
|||
auto& cp = system_.GetCommandProcessor();
|
||||
UCPCtrlReg tmp(val);
|
||||
cp.m_cp_ctrl_reg.Hex = tmp.Hex;
|
||||
cp.SetCpControlRegister(system_);
|
||||
system_.GetFifo().RunGpu(system_);
|
||||
cp.SetCpControlRegister();
|
||||
system_.GetFifo().RunGpu();
|
||||
}));
|
||||
|
||||
mmio->Register(base | CLEAR_REGISTER, MMIO::DirectRead<u16>(&m_cp_clear_reg.Hex),
|
||||
|
@ -245,13 +249,13 @@ void CommandProcessorManager::RegisterMMIO(Core::System& system, MMIO::Mapping*
|
|||
UCPClearReg tmp(val);
|
||||
cp.m_cp_clear_reg.Hex = tmp.Hex;
|
||||
cp.SetCpClearRegister();
|
||||
system_.GetFifo().RunGpu(system_);
|
||||
system_.GetFifo().RunGpu();
|
||||
}));
|
||||
|
||||
mmio->Register(base | PERF_SELECT, MMIO::InvalidRead<u16>(), MMIO::Nop<u16>());
|
||||
|
||||
// Some MMIOs have different handlers for single core vs. dual core mode.
|
||||
const bool is_on_thread = IsOnThread(system);
|
||||
const bool is_on_thread = IsOnThread(m_system);
|
||||
MMIO::ReadHandlingMethod<u16>* fifo_rw_distance_lo_r;
|
||||
if (is_on_thread)
|
||||
{
|
||||
|
@ -274,10 +278,11 @@ void CommandProcessorManager::RegisterMMIO(Core::System& system, MMIO::Mapping*
|
|||
}
|
||||
else
|
||||
{
|
||||
fifo_rw_distance_lo_r = MMIO::DirectRead<u16>(MMIO::Utils::LowPart(&fifo.CPReadWriteDistance));
|
||||
fifo_rw_distance_lo_r =
|
||||
MMIO::DirectRead<u16>(MMIO::Utils::LowPart(&m_fifo.CPReadWriteDistance));
|
||||
}
|
||||
mmio->Register(base | FIFO_RW_DISTANCE_LO, fifo_rw_distance_lo_r,
|
||||
MMIO::DirectWrite<u16>(MMIO::Utils::LowPart(&fifo.CPReadWriteDistance),
|
||||
MMIO::DirectWrite<u16>(MMIO::Utils::LowPart(&m_fifo.CPReadWriteDistance),
|
||||
WMASK_LO_ALIGN_32BIT));
|
||||
|
||||
MMIO::ReadHandlingMethod<u16>* fifo_rw_distance_hi_r;
|
||||
|
@ -285,7 +290,7 @@ void CommandProcessorManager::RegisterMMIO(Core::System& system, MMIO::Mapping*
|
|||
{
|
||||
fifo_rw_distance_hi_r = MMIO::ComplexRead<u16>([](Core::System& system_, u32) {
|
||||
const auto& fifo_ = system_.GetCommandProcessor().GetFifo();
|
||||
system_.GetFifo().SyncGPUForRegisterAccess(system_);
|
||||
system_.GetFifo().SyncGPUForRegisterAccess();
|
||||
if (fifo_.CPWritePointer.load(std::memory_order_relaxed) >=
|
||||
fifo_.SafeCPReadPointer.load(std::memory_order_relaxed))
|
||||
{
|
||||
|
@ -307,23 +312,23 @@ void CommandProcessorManager::RegisterMMIO(Core::System& system, MMIO::Mapping*
|
|||
{
|
||||
fifo_rw_distance_hi_r = MMIO::ComplexRead<u16>([](Core::System& system_, u32) {
|
||||
const auto& fifo_ = system_.GetCommandProcessor().GetFifo();
|
||||
system_.GetFifo().SyncGPUForRegisterAccess(system_);
|
||||
system_.GetFifo().SyncGPUForRegisterAccess();
|
||||
return fifo_.CPReadWriteDistance.load(std::memory_order_relaxed) >> 16;
|
||||
});
|
||||
}
|
||||
mmio->Register(base | FIFO_RW_DISTANCE_HI, fifo_rw_distance_hi_r,
|
||||
MMIO::ComplexWrite<u16>([WMASK_HI_RESTRICT](Core::System& system_, u32, u16 val) {
|
||||
auto& fifo_ = system_.GetCommandProcessor().GetFifo();
|
||||
system_.GetFifo().SyncGPUForRegisterAccess(system_);
|
||||
system_.GetFifo().SyncGPUForRegisterAccess();
|
||||
WriteHigh(fifo_.CPReadWriteDistance, val & WMASK_HI_RESTRICT);
|
||||
system_.GetFifo().RunGpu(system_);
|
||||
system_.GetFifo().RunGpu();
|
||||
}));
|
||||
|
||||
mmio->Register(
|
||||
base | FIFO_READ_POINTER_LO,
|
||||
is_on_thread ? MMIO::DirectRead<u16>(MMIO::Utils::LowPart(&fifo.SafeCPReadPointer)) :
|
||||
MMIO::DirectRead<u16>(MMIO::Utils::LowPart(&fifo.CPReadPointer)),
|
||||
MMIO::DirectWrite<u16>(MMIO::Utils::LowPart(&fifo.CPReadPointer), WMASK_LO_ALIGN_32BIT));
|
||||
is_on_thread ? MMIO::DirectRead<u16>(MMIO::Utils::LowPart(&m_fifo.SafeCPReadPointer)) :
|
||||
MMIO::DirectRead<u16>(MMIO::Utils::LowPart(&m_fifo.CPReadPointer)),
|
||||
MMIO::DirectWrite<u16>(MMIO::Utils::LowPart(&m_fifo.CPReadPointer), WMASK_LO_ALIGN_32BIT));
|
||||
|
||||
MMIO::ReadHandlingMethod<u16>* fifo_read_hi_r;
|
||||
MMIO::WriteHandlingMethod<u16>* fifo_read_hi_w;
|
||||
|
@ -331,13 +336,13 @@ void CommandProcessorManager::RegisterMMIO(Core::System& system, MMIO::Mapping*
|
|||
{
|
||||
fifo_read_hi_r = MMIO::ComplexRead<u16>([](Core::System& system_, u32) {
|
||||
auto& fifo_ = system_.GetCommandProcessor().GetFifo();
|
||||
system_.GetFifo().SyncGPUForRegisterAccess(system_);
|
||||
system_.GetFifo().SyncGPUForRegisterAccess();
|
||||
return fifo_.SafeCPReadPointer.load(std::memory_order_relaxed) >> 16;
|
||||
});
|
||||
fifo_read_hi_w =
|
||||
MMIO::ComplexWrite<u16>([WMASK_HI_RESTRICT](Core::System& system_, u32, u16 val) {
|
||||
auto& fifo_ = system_.GetCommandProcessor().GetFifo();
|
||||
system_.GetFifo().SyncGPUForRegisterAccess(system_);
|
||||
system_.GetFifo().SyncGPUForRegisterAccess();
|
||||
WriteHigh(fifo_.CPReadPointer, val & WMASK_HI_RESTRICT);
|
||||
fifo_.SafeCPReadPointer.store(fifo_.CPReadPointer.load(std::memory_order_relaxed),
|
||||
std::memory_order_relaxed);
|
||||
|
@ -347,116 +352,114 @@ void CommandProcessorManager::RegisterMMIO(Core::System& system, MMIO::Mapping*
|
|||
{
|
||||
fifo_read_hi_r = MMIO::ComplexRead<u16>([](Core::System& system_, u32) {
|
||||
const auto& fifo_ = system_.GetCommandProcessor().GetFifo();
|
||||
system_.GetFifo().SyncGPUForRegisterAccess(system_);
|
||||
system_.GetFifo().SyncGPUForRegisterAccess();
|
||||
return fifo_.CPReadPointer.load(std::memory_order_relaxed) >> 16;
|
||||
});
|
||||
fifo_read_hi_w =
|
||||
MMIO::ComplexWrite<u16>([WMASK_HI_RESTRICT](Core::System& system_, u32, u16 val) {
|
||||
auto& fifo_ = system_.GetCommandProcessor().GetFifo();
|
||||
system_.GetFifo().SyncGPUForRegisterAccess(system_);
|
||||
system_.GetFifo().SyncGPUForRegisterAccess();
|
||||
WriteHigh(fifo_.CPReadPointer, val & WMASK_HI_RESTRICT);
|
||||
});
|
||||
}
|
||||
mmio->Register(base | FIFO_READ_POINTER_HI, fifo_read_hi_r, fifo_read_hi_w);
|
||||
}
|
||||
|
||||
void CommandProcessorManager::GatherPipeBursted(Core::System& system)
|
||||
void CommandProcessorManager::GatherPipeBursted()
|
||||
{
|
||||
auto& fifo = m_fifo;
|
||||
SetCPStatusFromCPU();
|
||||
|
||||
SetCPStatusFromCPU(system);
|
||||
|
||||
auto& processor_interface = system.GetProcessorInterface();
|
||||
auto& processor_interface = m_system.GetProcessorInterface();
|
||||
|
||||
// if we aren't linked, we don't care about gather pipe data
|
||||
if (!m_cp_ctrl_reg.GPLinkEnable)
|
||||
{
|
||||
if (IsOnThread(system) && !system.GetFifo().UseDeterministicGPUThread())
|
||||
if (IsOnThread(m_system) && !m_system.GetFifo().UseDeterministicGPUThread())
|
||||
{
|
||||
// In multibuffer mode is not allowed write in the same FIFO attached to the GPU.
|
||||
// Fix Pokemon XD in DC mode.
|
||||
if ((processor_interface.m_fifo_cpu_end == fifo.CPEnd.load(std::memory_order_relaxed)) &&
|
||||
(processor_interface.m_fifo_cpu_base == fifo.CPBase.load(std::memory_order_relaxed)) &&
|
||||
fifo.CPReadWriteDistance.load(std::memory_order_relaxed) > 0)
|
||||
if ((processor_interface.m_fifo_cpu_end == m_fifo.CPEnd.load(std::memory_order_relaxed)) &&
|
||||
(processor_interface.m_fifo_cpu_base == m_fifo.CPBase.load(std::memory_order_relaxed)) &&
|
||||
m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed) > 0)
|
||||
{
|
||||
system.GetFifo().FlushGpu(system);
|
||||
m_system.GetFifo().FlushGpu();
|
||||
}
|
||||
}
|
||||
system.GetFifo().RunGpu(system);
|
||||
m_system.GetFifo().RunGpu();
|
||||
return;
|
||||
}
|
||||
|
||||
// update the fifo pointer
|
||||
if (fifo.CPWritePointer.load(std::memory_order_relaxed) ==
|
||||
fifo.CPEnd.load(std::memory_order_relaxed))
|
||||
if (m_fifo.CPWritePointer.load(std::memory_order_relaxed) ==
|
||||
m_fifo.CPEnd.load(std::memory_order_relaxed))
|
||||
{
|
||||
fifo.CPWritePointer.store(fifo.CPBase, std::memory_order_relaxed);
|
||||
m_fifo.CPWritePointer.store(m_fifo.CPBase, std::memory_order_relaxed);
|
||||
}
|
||||
else
|
||||
{
|
||||
fifo.CPWritePointer.fetch_add(GPFifo::GATHER_PIPE_SIZE, std::memory_order_relaxed);
|
||||
m_fifo.CPWritePointer.fetch_add(GPFifo::GATHER_PIPE_SIZE, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
if (m_cp_ctrl_reg.GPReadEnable && m_cp_ctrl_reg.GPLinkEnable)
|
||||
{
|
||||
processor_interface.m_fifo_cpu_write_pointer =
|
||||
fifo.CPWritePointer.load(std::memory_order_relaxed);
|
||||
processor_interface.m_fifo_cpu_base = fifo.CPBase.load(std::memory_order_relaxed);
|
||||
processor_interface.m_fifo_cpu_end = fifo.CPEnd.load(std::memory_order_relaxed);
|
||||
m_fifo.CPWritePointer.load(std::memory_order_relaxed);
|
||||
processor_interface.m_fifo_cpu_base = m_fifo.CPBase.load(std::memory_order_relaxed);
|
||||
processor_interface.m_fifo_cpu_end = m_fifo.CPEnd.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// If the game is running close to overflowing, make the exception checking more frequent.
|
||||
if (fifo.bFF_HiWatermark.load(std::memory_order_relaxed) != 0)
|
||||
system.GetCoreTiming().ForceExceptionCheck(0);
|
||||
if (m_fifo.bFF_HiWatermark.load(std::memory_order_relaxed) != 0)
|
||||
m_system.GetCoreTiming().ForceExceptionCheck(0);
|
||||
|
||||
fifo.CPReadWriteDistance.fetch_add(GPFifo::GATHER_PIPE_SIZE, std::memory_order_seq_cst);
|
||||
m_fifo.CPReadWriteDistance.fetch_add(GPFifo::GATHER_PIPE_SIZE, std::memory_order_seq_cst);
|
||||
|
||||
system.GetFifo().RunGpu(system);
|
||||
m_system.GetFifo().RunGpu();
|
||||
|
||||
ASSERT_MSG(COMMANDPROCESSOR,
|
||||
fifo.CPReadWriteDistance.load(std::memory_order_relaxed) <=
|
||||
fifo.CPEnd.load(std::memory_order_relaxed) -
|
||||
fifo.CPBase.load(std::memory_order_relaxed),
|
||||
m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed) <=
|
||||
m_fifo.CPEnd.load(std::memory_order_relaxed) -
|
||||
m_fifo.CPBase.load(std::memory_order_relaxed),
|
||||
"FIFO is overflowed by GatherPipe !\nCPU thread is too fast!");
|
||||
|
||||
// check if we are in sync
|
||||
ASSERT_MSG(COMMANDPROCESSOR,
|
||||
fifo.CPWritePointer.load(std::memory_order_relaxed) ==
|
||||
m_fifo.CPWritePointer.load(std::memory_order_relaxed) ==
|
||||
processor_interface.m_fifo_cpu_write_pointer,
|
||||
"FIFOs linked but out of sync");
|
||||
ASSERT_MSG(COMMANDPROCESSOR,
|
||||
fifo.CPBase.load(std::memory_order_relaxed) == processor_interface.m_fifo_cpu_base,
|
||||
m_fifo.CPBase.load(std::memory_order_relaxed) == processor_interface.m_fifo_cpu_base,
|
||||
"FIFOs linked but out of sync");
|
||||
ASSERT_MSG(COMMANDPROCESSOR,
|
||||
fifo.CPEnd.load(std::memory_order_relaxed) == processor_interface.m_fifo_cpu_end,
|
||||
m_fifo.CPEnd.load(std::memory_order_relaxed) == processor_interface.m_fifo_cpu_end,
|
||||
"FIFOs linked but out of sync");
|
||||
}
|
||||
|
||||
void CommandProcessorManager::UpdateInterrupts(Core::System& system, u64 userdata)
|
||||
void CommandProcessorManager::UpdateInterrupts(u64 userdata)
|
||||
{
|
||||
if (userdata)
|
||||
{
|
||||
m_interrupt_set.Set();
|
||||
DEBUG_LOG_FMT(COMMANDPROCESSOR, "Interrupt set");
|
||||
system.GetProcessorInterface().SetInterrupt(INT_CAUSE_CP, true);
|
||||
m_system.GetProcessorInterface().SetInterrupt(INT_CAUSE_CP, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_interrupt_set.Clear();
|
||||
DEBUG_LOG_FMT(COMMANDPROCESSOR, "Interrupt cleared");
|
||||
system.GetProcessorInterface().SetInterrupt(INT_CAUSE_CP, false);
|
||||
m_system.GetProcessorInterface().SetInterrupt(INT_CAUSE_CP, false);
|
||||
}
|
||||
system.GetCoreTiming().ForceExceptionCheck(0);
|
||||
m_system.GetCoreTiming().ForceExceptionCheck(0);
|
||||
m_interrupt_waiting.Clear();
|
||||
system.GetFifo().RunGpu(system);
|
||||
m_system.GetFifo().RunGpu();
|
||||
}
|
||||
|
||||
void CommandProcessorManager::UpdateInterruptsFromVideoBackend(Core::System& system, u64 userdata)
|
||||
void CommandProcessorManager::UpdateInterruptsFromVideoBackend(u64 userdata)
|
||||
{
|
||||
if (!system.GetFifo().UseDeterministicGPUThread())
|
||||
if (!m_system.GetFifo().UseDeterministicGPUThread())
|
||||
{
|
||||
system.GetCoreTiming().ScheduleEvent(0, m_event_type_update_interrupts, userdata,
|
||||
CoreTiming::FromThread::NON_CPU);
|
||||
m_system.GetCoreTiming().ScheduleEvent(0, m_event_type_update_interrupts, userdata,
|
||||
CoreTiming::FromThread::NON_CPU);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -465,22 +468,20 @@ bool CommandProcessorManager::IsInterruptWaiting() const
|
|||
return m_interrupt_waiting.IsSet();
|
||||
}
|
||||
|
||||
void CommandProcessorManager::SetCPStatusFromGPU(Core::System& system)
|
||||
void CommandProcessorManager::SetCPStatusFromGPU()
|
||||
{
|
||||
auto& fifo = m_fifo;
|
||||
|
||||
// breakpoint
|
||||
const bool breakpoint = fifo.bFF_Breakpoint.load(std::memory_order_relaxed);
|
||||
if (fifo.bFF_BPEnable.load(std::memory_order_relaxed) != 0)
|
||||
const bool breakpoint = m_fifo.bFF_Breakpoint.load(std::memory_order_relaxed);
|
||||
if (m_fifo.bFF_BPEnable.load(std::memory_order_relaxed) != 0)
|
||||
{
|
||||
if (fifo.CPBreakpoint.load(std::memory_order_relaxed) ==
|
||||
fifo.CPReadPointer.load(std::memory_order_relaxed))
|
||||
if (m_fifo.CPBreakpoint.load(std::memory_order_relaxed) ==
|
||||
m_fifo.CPReadPointer.load(std::memory_order_relaxed))
|
||||
{
|
||||
if (!breakpoint)
|
||||
{
|
||||
DEBUG_LOG_FMT(COMMANDPROCESSOR, "Hit breakpoint at {}",
|
||||
fifo.CPReadPointer.load(std::memory_order_relaxed));
|
||||
fifo.bFF_Breakpoint.store(1, std::memory_order_relaxed);
|
||||
m_fifo.CPReadPointer.load(std::memory_order_relaxed));
|
||||
m_fifo.bFF_Breakpoint.store(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -488,8 +489,8 @@ void CommandProcessorManager::SetCPStatusFromGPU(Core::System& system)
|
|||
if (breakpoint)
|
||||
{
|
||||
DEBUG_LOG_FMT(COMMANDPROCESSOR, "Cleared breakpoint at {}",
|
||||
fifo.CPReadPointer.load(std::memory_order_relaxed));
|
||||
fifo.bFF_Breakpoint.store(0, std::memory_order_relaxed);
|
||||
m_fifo.CPReadPointer.load(std::memory_order_relaxed));
|
||||
m_fifo.bFF_Breakpoint.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -498,101 +499,97 @@ void CommandProcessorManager::SetCPStatusFromGPU(Core::System& system)
|
|||
if (breakpoint)
|
||||
{
|
||||
DEBUG_LOG_FMT(COMMANDPROCESSOR, "Cleared breakpoint at {}",
|
||||
fifo.CPReadPointer.load(std::memory_order_relaxed));
|
||||
fifo.bFF_Breakpoint = false;
|
||||
m_fifo.CPReadPointer.load(std::memory_order_relaxed));
|
||||
m_fifo.bFF_Breakpoint = false;
|
||||
}
|
||||
}
|
||||
|
||||
// overflow & underflow check
|
||||
fifo.bFF_HiWatermark.store(
|
||||
(fifo.CPReadWriteDistance.load(std::memory_order_relaxed) > fifo.CPHiWatermark),
|
||||
m_fifo.bFF_HiWatermark.store(
|
||||
(m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed) > m_fifo.CPHiWatermark),
|
||||
std::memory_order_relaxed);
|
||||
fifo.bFF_LoWatermark.store(
|
||||
(fifo.CPReadWriteDistance.load(std::memory_order_relaxed) < fifo.CPLoWatermark),
|
||||
m_fifo.bFF_LoWatermark.store(
|
||||
(m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed) < m_fifo.CPLoWatermark),
|
||||
std::memory_order_relaxed);
|
||||
|
||||
bool bpInt = fifo.bFF_Breakpoint.load(std::memory_order_relaxed) &&
|
||||
fifo.bFF_BPInt.load(std::memory_order_relaxed);
|
||||
bool ovfInt = fifo.bFF_HiWatermark.load(std::memory_order_relaxed) &&
|
||||
fifo.bFF_HiWatermarkInt.load(std::memory_order_relaxed);
|
||||
bool undfInt = fifo.bFF_LoWatermark.load(std::memory_order_relaxed) &&
|
||||
fifo.bFF_LoWatermarkInt.load(std::memory_order_relaxed);
|
||||
bool bpInt = m_fifo.bFF_Breakpoint.load(std::memory_order_relaxed) &&
|
||||
m_fifo.bFF_BPInt.load(std::memory_order_relaxed);
|
||||
bool ovfInt = m_fifo.bFF_HiWatermark.load(std::memory_order_relaxed) &&
|
||||
m_fifo.bFF_HiWatermarkInt.load(std::memory_order_relaxed);
|
||||
bool undfInt = m_fifo.bFF_LoWatermark.load(std::memory_order_relaxed) &&
|
||||
m_fifo.bFF_LoWatermarkInt.load(std::memory_order_relaxed);
|
||||
|
||||
bool interrupt = (bpInt || ovfInt || undfInt) && m_cp_ctrl_reg.GPReadEnable;
|
||||
|
||||
if (interrupt != m_interrupt_set.IsSet() && !m_interrupt_waiting.IsSet())
|
||||
{
|
||||
u64 userdata = interrupt ? 1 : 0;
|
||||
if (IsOnThread(system))
|
||||
const u64 userdata = interrupt ? 1 : 0;
|
||||
if (IsOnThread(m_system))
|
||||
{
|
||||
if (!interrupt || bpInt || undfInt || ovfInt)
|
||||
{
|
||||
// Schedule the interrupt asynchronously
|
||||
m_interrupt_waiting.Set();
|
||||
UpdateInterruptsFromVideoBackend(system, userdata);
|
||||
UpdateInterruptsFromVideoBackend(userdata);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateInterrupts(system, userdata);
|
||||
UpdateInterrupts(userdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommandProcessorManager::SetCPStatusFromCPU(Core::System& system)
|
||||
void CommandProcessorManager::SetCPStatusFromCPU()
|
||||
{
|
||||
auto& fifo = m_fifo;
|
||||
|
||||
// overflow & underflow check
|
||||
fifo.bFF_HiWatermark.store(
|
||||
(fifo.CPReadWriteDistance.load(std::memory_order_relaxed) > fifo.CPHiWatermark),
|
||||
m_fifo.bFF_HiWatermark.store(
|
||||
(m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed) > m_fifo.CPHiWatermark),
|
||||
std::memory_order_relaxed);
|
||||
fifo.bFF_LoWatermark.store(
|
||||
(fifo.CPReadWriteDistance.load(std::memory_order_relaxed) < fifo.CPLoWatermark),
|
||||
m_fifo.bFF_LoWatermark.store(
|
||||
(m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed) < m_fifo.CPLoWatermark),
|
||||
std::memory_order_relaxed);
|
||||
|
||||
bool bpInt = fifo.bFF_Breakpoint.load(std::memory_order_relaxed) &&
|
||||
fifo.bFF_BPInt.load(std::memory_order_relaxed);
|
||||
bool ovfInt = fifo.bFF_HiWatermark.load(std::memory_order_relaxed) &&
|
||||
fifo.bFF_HiWatermarkInt.load(std::memory_order_relaxed);
|
||||
bool undfInt = fifo.bFF_LoWatermark.load(std::memory_order_relaxed) &&
|
||||
fifo.bFF_LoWatermarkInt.load(std::memory_order_relaxed);
|
||||
bool bpInt = m_fifo.bFF_Breakpoint.load(std::memory_order_relaxed) &&
|
||||
m_fifo.bFF_BPInt.load(std::memory_order_relaxed);
|
||||
bool ovfInt = m_fifo.bFF_HiWatermark.load(std::memory_order_relaxed) &&
|
||||
m_fifo.bFF_HiWatermarkInt.load(std::memory_order_relaxed);
|
||||
bool undfInt = m_fifo.bFF_LoWatermark.load(std::memory_order_relaxed) &&
|
||||
m_fifo.bFF_LoWatermarkInt.load(std::memory_order_relaxed);
|
||||
|
||||
bool interrupt = (bpInt || ovfInt || undfInt) && m_cp_ctrl_reg.GPReadEnable;
|
||||
|
||||
if (interrupt != m_interrupt_set.IsSet() && !m_interrupt_waiting.IsSet())
|
||||
{
|
||||
u64 userdata = interrupt ? 1 : 0;
|
||||
if (IsOnThread(system))
|
||||
const u64 userdata = interrupt ? 1 : 0;
|
||||
if (IsOnThread(m_system))
|
||||
{
|
||||
if (!interrupt || bpInt || undfInt || ovfInt)
|
||||
{
|
||||
m_interrupt_set.Set(interrupt);
|
||||
DEBUG_LOG_FMT(COMMANDPROCESSOR, "Interrupt set");
|
||||
system.GetProcessorInterface().SetInterrupt(INT_CAUSE_CP, interrupt);
|
||||
m_system.GetProcessorInterface().SetInterrupt(INT_CAUSE_CP, interrupt);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateInterrupts(system, userdata);
|
||||
UpdateInterrupts(userdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommandProcessorManager::SetCpStatusRegister(Core::System& system)
|
||||
void CommandProcessorManager::SetCpStatusRegister()
|
||||
{
|
||||
const auto& fifo = m_fifo;
|
||||
|
||||
// Here always there is one fifo attached to the GPU
|
||||
m_cp_status_reg.Breakpoint = fifo.bFF_Breakpoint.load(std::memory_order_relaxed);
|
||||
m_cp_status_reg.ReadIdle = !fifo.CPReadWriteDistance.load(std::memory_order_relaxed) ||
|
||||
(fifo.CPReadPointer.load(std::memory_order_relaxed) ==
|
||||
fifo.CPWritePointer.load(std::memory_order_relaxed));
|
||||
m_cp_status_reg.CommandIdle = !fifo.CPReadWriteDistance.load(std::memory_order_relaxed) ||
|
||||
Fifo::AtBreakpoint(system) ||
|
||||
!fifo.bFF_GPReadEnable.load(std::memory_order_relaxed);
|
||||
m_cp_status_reg.UnderflowLoWatermark = fifo.bFF_LoWatermark.load(std::memory_order_relaxed);
|
||||
m_cp_status_reg.OverflowHiWatermark = fifo.bFF_HiWatermark.load(std::memory_order_relaxed);
|
||||
m_cp_status_reg.Breakpoint = m_fifo.bFF_Breakpoint.load(std::memory_order_relaxed);
|
||||
m_cp_status_reg.ReadIdle = !m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed) ||
|
||||
(m_fifo.CPReadPointer.load(std::memory_order_relaxed) ==
|
||||
m_fifo.CPWritePointer.load(std::memory_order_relaxed));
|
||||
m_cp_status_reg.CommandIdle = !m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed) ||
|
||||
Fifo::AtBreakpoint(m_system) ||
|
||||
!m_fifo.bFF_GPReadEnable.load(std::memory_order_relaxed);
|
||||
m_cp_status_reg.UnderflowLoWatermark = m_fifo.bFF_LoWatermark.load(std::memory_order_relaxed);
|
||||
m_cp_status_reg.OverflowHiWatermark = m_fifo.bFF_HiWatermark.load(std::memory_order_relaxed);
|
||||
|
||||
DEBUG_LOG_FMT(COMMANDPROCESSOR, "\t Read from STATUS_REGISTER : {:04x}", m_cp_status_reg.Hex);
|
||||
DEBUG_LOG_FMT(COMMANDPROCESSOR,
|
||||
|
@ -603,30 +600,28 @@ void CommandProcessorManager::SetCpStatusRegister(Core::System& system)
|
|||
m_cp_status_reg.UnderflowLoWatermark ? "ON" : "OFF");
|
||||
}
|
||||
|
||||
void CommandProcessorManager::SetCpControlRegister(Core::System& system)
|
||||
void CommandProcessorManager::SetCpControlRegister()
|
||||
{
|
||||
auto& fifo = m_fifo;
|
||||
m_fifo.bFF_BPInt.store(m_cp_ctrl_reg.BPInt, std::memory_order_relaxed);
|
||||
m_fifo.bFF_BPEnable.store(m_cp_ctrl_reg.BPEnable, std::memory_order_relaxed);
|
||||
m_fifo.bFF_HiWatermarkInt.store(m_cp_ctrl_reg.FifoOverflowIntEnable, std::memory_order_relaxed);
|
||||
m_fifo.bFF_LoWatermarkInt.store(m_cp_ctrl_reg.FifoUnderflowIntEnable, std::memory_order_relaxed);
|
||||
m_fifo.bFF_GPLinkEnable.store(m_cp_ctrl_reg.GPLinkEnable, std::memory_order_relaxed);
|
||||
|
||||
fifo.bFF_BPInt.store(m_cp_ctrl_reg.BPInt, std::memory_order_relaxed);
|
||||
fifo.bFF_BPEnable.store(m_cp_ctrl_reg.BPEnable, std::memory_order_relaxed);
|
||||
fifo.bFF_HiWatermarkInt.store(m_cp_ctrl_reg.FifoOverflowIntEnable, std::memory_order_relaxed);
|
||||
fifo.bFF_LoWatermarkInt.store(m_cp_ctrl_reg.FifoUnderflowIntEnable, std::memory_order_relaxed);
|
||||
fifo.bFF_GPLinkEnable.store(m_cp_ctrl_reg.GPLinkEnable, std::memory_order_relaxed);
|
||||
|
||||
if (fifo.bFF_GPReadEnable.load(std::memory_order_relaxed) && !m_cp_ctrl_reg.GPReadEnable)
|
||||
if (m_fifo.bFF_GPReadEnable.load(std::memory_order_relaxed) && !m_cp_ctrl_reg.GPReadEnable)
|
||||
{
|
||||
fifo.bFF_GPReadEnable.store(m_cp_ctrl_reg.GPReadEnable, std::memory_order_relaxed);
|
||||
system.GetFifo().FlushGpu(system);
|
||||
m_fifo.bFF_GPReadEnable.store(m_cp_ctrl_reg.GPReadEnable, std::memory_order_relaxed);
|
||||
m_system.GetFifo().FlushGpu();
|
||||
}
|
||||
else
|
||||
{
|
||||
fifo.bFF_GPReadEnable = m_cp_ctrl_reg.GPReadEnable;
|
||||
m_fifo.bFF_GPReadEnable = m_cp_ctrl_reg.GPReadEnable;
|
||||
}
|
||||
|
||||
DEBUG_LOG_FMT(COMMANDPROCESSOR, "\t GPREAD {} | BP {} | Int {} | OvF {} | UndF {} | LINK {}",
|
||||
fifo.bFF_GPReadEnable.load(std::memory_order_relaxed) ? "ON" : "OFF",
|
||||
fifo.bFF_BPEnable.load(std::memory_order_relaxed) ? "ON" : "OFF",
|
||||
fifo.bFF_BPInt.load(std::memory_order_relaxed) ? "ON" : "OFF",
|
||||
m_fifo.bFF_GPReadEnable.load(std::memory_order_relaxed) ? "ON" : "OFF",
|
||||
m_fifo.bFF_BPEnable.load(std::memory_order_relaxed) ? "ON" : "OFF",
|
||||
m_fifo.bFF_BPInt.load(std::memory_order_relaxed) ? "ON" : "OFF",
|
||||
m_cp_ctrl_reg.FifoOverflowIntEnable ? "ON" : "OFF",
|
||||
m_cp_ctrl_reg.FifoUnderflowIntEnable ? "ON" : "OFF",
|
||||
m_cp_ctrl_reg.GPLinkEnable ? "ON" : "OFF");
|
||||
|
@ -638,11 +633,8 @@ void CommandProcessorManager::SetCpClearRegister()
|
|||
{
|
||||
}
|
||||
|
||||
void CommandProcessorManager::HandleUnknownOpcode(Core::System& system, u8 cmd_byte,
|
||||
const u8* buffer, bool preprocess)
|
||||
void CommandProcessorManager::HandleUnknownOpcode(u8 cmd_byte, const u8* buffer, bool preprocess)
|
||||
{
|
||||
const auto& fifo = m_fifo;
|
||||
|
||||
// Datel software uses 0x01 during startup, and Mario Party 5's Wiggler capsule accidentally uses
|
||||
// 0x01-0x03 due to sending 4 more vertices than intended (see https://dolp.in/i8104).
|
||||
// Prince of Persia: Rival Swords sends 0x3f if the home menu is opened during the intro cutscene
|
||||
|
@ -667,7 +659,7 @@ void CommandProcessorManager::HandleUnknownOpcode(Core::System& system, u8 cmd_b
|
|||
// PC and LR are meaningless when using the fifoplayer, and will generally not be helpful if the
|
||||
// unknown opcode is inside of a display list. Also note that the changes in GPFifo.h are not
|
||||
// accurate and may introduce timing issues.
|
||||
const auto& ppc_state = system.GetPPCState();
|
||||
const auto& ppc_state = m_system.GetPPCState();
|
||||
GENERIC_LOG_FMT(
|
||||
Common::Log::LogType::VIDEO, log_level,
|
||||
"FIFO: Unknown Opcode {:#04x} @ {}, preprocessing = {}, CPBase: {:#010x}, CPEnd: "
|
||||
|
@ -677,19 +669,19 @@ void CommandProcessorManager::HandleUnknownOpcode(Core::System& system, u8 cmd_b
|
|||
"{}, bFF_GPLinkEnable: {}, bFF_HiWatermarkInt: {}, bFF_LoWatermarkInt: {}, "
|
||||
"approximate PC: {:08x}, approximate LR: {:08x}",
|
||||
cmd_byte, fmt::ptr(buffer), preprocess ? "yes" : "no",
|
||||
fifo.CPBase.load(std::memory_order_relaxed), fifo.CPEnd.load(std::memory_order_relaxed),
|
||||
fifo.CPHiWatermark, fifo.CPLoWatermark,
|
||||
fifo.CPReadWriteDistance.load(std::memory_order_relaxed),
|
||||
fifo.CPWritePointer.load(std::memory_order_relaxed),
|
||||
fifo.CPReadPointer.load(std::memory_order_relaxed),
|
||||
fifo.CPBreakpoint.load(std::memory_order_relaxed),
|
||||
fifo.bFF_GPReadEnable.load(std::memory_order_relaxed) ? "true" : "false",
|
||||
fifo.bFF_BPEnable.load(std::memory_order_relaxed) ? "true" : "false",
|
||||
fifo.bFF_BPInt.load(std::memory_order_relaxed) ? "true" : "false",
|
||||
fifo.bFF_Breakpoint.load(std::memory_order_relaxed) ? "true" : "false",
|
||||
fifo.bFF_GPLinkEnable.load(std::memory_order_relaxed) ? "true" : "false",
|
||||
fifo.bFF_HiWatermarkInt.load(std::memory_order_relaxed) ? "true" : "false",
|
||||
fifo.bFF_LoWatermarkInt.load(std::memory_order_relaxed) ? "true" : "false", ppc_state.pc,
|
||||
m_fifo.CPBase.load(std::memory_order_relaxed), m_fifo.CPEnd.load(std::memory_order_relaxed),
|
||||
m_fifo.CPHiWatermark, m_fifo.CPLoWatermark,
|
||||
m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed),
|
||||
m_fifo.CPWritePointer.load(std::memory_order_relaxed),
|
||||
m_fifo.CPReadPointer.load(std::memory_order_relaxed),
|
||||
m_fifo.CPBreakpoint.load(std::memory_order_relaxed),
|
||||
m_fifo.bFF_GPReadEnable.load(std::memory_order_relaxed) ? "true" : "false",
|
||||
m_fifo.bFF_BPEnable.load(std::memory_order_relaxed) ? "true" : "false",
|
||||
m_fifo.bFF_BPInt.load(std::memory_order_relaxed) ? "true" : "false",
|
||||
m_fifo.bFF_Breakpoint.load(std::memory_order_relaxed) ? "true" : "false",
|
||||
m_fifo.bFF_GPLinkEnable.load(std::memory_order_relaxed) ? "true" : "false",
|
||||
m_fifo.bFF_HiWatermarkInt.load(std::memory_order_relaxed) ? "true" : "false",
|
||||
m_fifo.bFF_LoWatermarkInt.load(std::memory_order_relaxed) ? "true" : "false", ppc_state.pc,
|
||||
LR(ppc_state));
|
||||
|
||||
if (!m_is_fifo_error_seen && !suppress_panic_alert)
|
||||
|
@ -699,9 +691,9 @@ void CommandProcessorManager::HandleUnknownOpcode(Core::System& system, u8 cmd_b
|
|||
// The panic alert contains an explanatory part that's worded differently depending on the
|
||||
// user's settings, so as to offer the most relevant advice to the user.
|
||||
const char* advice;
|
||||
if (IsOnThread(system) && !system.GetFifo().UseDeterministicGPUThread())
|
||||
if (IsOnThread(m_system) && !m_system.GetFifo().UseDeterministicGPUThread())
|
||||
{
|
||||
if (!system.GetCoreTiming().UseSyncOnSkipIdle() && !system.GetFifo().UseSyncGPU())
|
||||
if (!m_system.GetCoreTiming().UseSyncOnSkipIdle() && !m_system.GetFifo().UseSyncGPU())
|
||||
{
|
||||
// The SyncOnSkipIdle setting is only in the Android GUI, so we use the INI name on other platforms.
|
||||
//
|
||||
|
|
|
@ -160,24 +160,26 @@ u32 GetPhysicalAddressMask();
|
|||
class CommandProcessorManager
|
||||
{
|
||||
public:
|
||||
void Init(Core::System& system);
|
||||
explicit CommandProcessorManager(Core::System& system);
|
||||
|
||||
void Init();
|
||||
void DoState(PointerWrap& p);
|
||||
|
||||
void RegisterMMIO(Core::System& system, MMIO::Mapping* mmio, u32 base);
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||
|
||||
void SetCPStatusFromGPU(Core::System& system);
|
||||
void SetCPStatusFromCPU(Core::System& system);
|
||||
void GatherPipeBursted(Core::System& system);
|
||||
void UpdateInterrupts(Core::System& system, u64 userdata);
|
||||
void UpdateInterruptsFromVideoBackend(Core::System& system, u64 userdata);
|
||||
void SetCPStatusFromGPU();
|
||||
void SetCPStatusFromCPU();
|
||||
void GatherPipeBursted();
|
||||
void UpdateInterrupts(u64 userdata);
|
||||
void UpdateInterruptsFromVideoBackend(u64 userdata);
|
||||
|
||||
bool IsInterruptWaiting() const;
|
||||
|
||||
void SetCpClearRegister();
|
||||
void SetCpControlRegister(Core::System& system);
|
||||
void SetCpStatusRegister(Core::System& system);
|
||||
void SetCpControlRegister();
|
||||
void SetCpStatusRegister();
|
||||
|
||||
void HandleUnknownOpcode(Core::System& system, u8 cmd_byte, const u8* buffer, bool preprocess);
|
||||
void HandleUnknownOpcode(u8 cmd_byte, const u8* buffer, bool preprocess);
|
||||
|
||||
// This one is shared between gfx thread and emulator thread.
|
||||
// It is only used by the Fifo and by the CommandProcessor.
|
||||
|
@ -203,6 +205,8 @@ private:
|
|||
Common::Flag m_interrupt_waiting;
|
||||
|
||||
bool m_is_fifo_error_seen = false;
|
||||
|
||||
Core::System& m_system;
|
||||
};
|
||||
|
||||
} // namespace CommandProcessor
|
||||
|
|
|
@ -7,6 +7,13 @@
|
|||
|
||||
namespace VideoCommon
|
||||
{
|
||||
#ifdef ANDROID
|
||||
// Some devices seem to have graphical errors when providing 16 pixel samplers
|
||||
// given the logic is for a performance heavy feature (custom shaders), will just disable for now
|
||||
// TODO: handle this more elegantly
|
||||
constexpr u32 MAX_PIXEL_SHADER_SAMPLERS = 8;
|
||||
#else
|
||||
constexpr u32 MAX_PIXEL_SHADER_SAMPLERS = 16;
|
||||
#endif
|
||||
constexpr u32 MAX_COMPUTE_SHADER_SAMPLERS = 8;
|
||||
} // namespace VideoCommon
|
||||
|
|
|
@ -36,7 +36,10 @@ namespace Fifo
|
|||
{
|
||||
static constexpr int GPU_TIME_SLOT_SIZE = 1000;
|
||||
|
||||
FifoManager::FifoManager() = default;
|
||||
FifoManager::FifoManager(Core::System& system) : m_system{system}
|
||||
{
|
||||
}
|
||||
|
||||
FifoManager::~FifoManager() = default;
|
||||
|
||||
void FifoManager::RefreshConfig()
|
||||
|
@ -64,26 +67,26 @@ void FifoManager::DoState(PointerWrap& p)
|
|||
p.Do(m_syncing_suspended);
|
||||
}
|
||||
|
||||
void FifoManager::PauseAndLock(Core::System& system, bool doLock, bool unpauseOnUnlock)
|
||||
void FifoManager::PauseAndLock(bool do_lock, bool unpause_on_unlock)
|
||||
{
|
||||
if (doLock)
|
||||
if (do_lock)
|
||||
{
|
||||
SyncGPU(SyncGPUReason::Other);
|
||||
EmulatorState(false);
|
||||
|
||||
if (!system.IsDualCoreMode() || m_use_deterministic_gpu_thread)
|
||||
if (!m_system.IsDualCoreMode() || m_use_deterministic_gpu_thread)
|
||||
return;
|
||||
|
||||
m_gpu_mainloop.WaitYield(std::chrono::milliseconds(100), Host_YieldToUI);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unpauseOnUnlock)
|
||||
if (unpause_on_unlock)
|
||||
EmulatorState(true);
|
||||
}
|
||||
}
|
||||
|
||||
void FifoManager::Init(Core::System& system)
|
||||
void FifoManager::Init()
|
||||
{
|
||||
if (!m_config_callback_id)
|
||||
m_config_callback_id = Config::AddConfigChangedCallback([this] { RefreshConfig(); });
|
||||
|
@ -92,7 +95,7 @@ void FifoManager::Init(Core::System& system)
|
|||
// Padded so that SIMD overreads in the vertex loader are safe
|
||||
m_video_buffer = static_cast<u8*>(Common::AllocateMemoryPages(FIFO_SIZE + 4));
|
||||
ResetVideoBuffer();
|
||||
if (system.IsDualCoreMode())
|
||||
if (m_system.IsDualCoreMode())
|
||||
m_gpu_mainloop.Prepare();
|
||||
m_sync_ticks.store(0);
|
||||
}
|
||||
|
@ -120,14 +123,14 @@ void FifoManager::Shutdown()
|
|||
|
||||
// May be executed from any thread, even the graphics thread.
|
||||
// Created to allow for self shutdown.
|
||||
void FifoManager::ExitGpuLoop(Core::System& system)
|
||||
void FifoManager::ExitGpuLoop()
|
||||
{
|
||||
auto& command_processor = system.GetCommandProcessor();
|
||||
auto& command_processor = m_system.GetCommandProcessor();
|
||||
auto& fifo = command_processor.GetFifo();
|
||||
|
||||
// This should break the wait loop in CPU thread
|
||||
fifo.bFF_GPReadEnable.store(0, std::memory_order_relaxed);
|
||||
FlushGpu(system);
|
||||
FlushGpu();
|
||||
|
||||
// Terminate GPU thread loop
|
||||
m_emu_running_state.Set();
|
||||
|
@ -211,7 +214,7 @@ void* FifoManager::PopFifoAuxBuffer(size_t size)
|
|||
}
|
||||
|
||||
// Description: RunGpuLoop() sends data through this function.
|
||||
void FifoManager::ReadDataFromFifo(Core::System& system, u32 readPtr)
|
||||
void FifoManager::ReadDataFromFifo(u32 read_ptr)
|
||||
{
|
||||
if (GPFifo::GATHER_PIPE_SIZE >
|
||||
static_cast<size_t>(m_video_buffer + FIFO_SIZE - m_video_buffer_write_ptr))
|
||||
|
@ -228,13 +231,13 @@ void FifoManager::ReadDataFromFifo(Core::System& system, u32 readPtr)
|
|||
m_video_buffer_read_ptr = m_video_buffer;
|
||||
}
|
||||
// Copy new video instructions to m_video_buffer for future use in rendering the new picture
|
||||
auto& memory = system.GetMemory();
|
||||
memory.CopyFromEmu(m_video_buffer_write_ptr, readPtr, GPFifo::GATHER_PIPE_SIZE);
|
||||
auto& memory = m_system.GetMemory();
|
||||
memory.CopyFromEmu(m_video_buffer_write_ptr, read_ptr, GPFifo::GATHER_PIPE_SIZE);
|
||||
m_video_buffer_write_ptr += GPFifo::GATHER_PIPE_SIZE;
|
||||
}
|
||||
|
||||
// The deterministic_gpu_thread version.
|
||||
void FifoManager::ReadDataFromFifoOnCPU(Core::System& system, u32 readPtr)
|
||||
void FifoManager::ReadDataFromFifoOnCPU(u32 read_ptr)
|
||||
{
|
||||
u8* write_ptr = m_video_buffer_write_ptr;
|
||||
if (GPFifo::GATHER_PIPE_SIZE > static_cast<size_t>(m_video_buffer + FIFO_SIZE - write_ptr))
|
||||
|
@ -262,8 +265,8 @@ void FifoManager::ReadDataFromFifoOnCPU(Core::System& system, u32 readPtr)
|
|||
return;
|
||||
}
|
||||
}
|
||||
auto& memory = system.GetMemory();
|
||||
memory.CopyFromEmu(m_video_buffer_write_ptr, readPtr, GPFifo::GATHER_PIPE_SIZE);
|
||||
auto& memory = m_system.GetMemory();
|
||||
memory.CopyFromEmu(m_video_buffer_write_ptr, read_ptr, GPFifo::GATHER_PIPE_SIZE);
|
||||
m_video_buffer_pp_read_ptr = OpcodeDecoder::RunFifo<true>(
|
||||
DataReader(m_video_buffer_pp_read_ptr, write_ptr + GPFifo::GATHER_PIPE_SIZE), nullptr);
|
||||
// This would have to be locked if the GPU thread didn't spin.
|
||||
|
@ -282,13 +285,13 @@ void FifoManager::ResetVideoBuffer()
|
|||
|
||||
// Description: Main FIFO update loop
|
||||
// Purpose: Keep the Core HW updated about the CPU-GPU distance
|
||||
void FifoManager::RunGpuLoop(Core::System& system)
|
||||
void FifoManager::RunGpuLoop()
|
||||
{
|
||||
AsyncRequests::GetInstance()->SetEnable(true);
|
||||
AsyncRequests::GetInstance()->SetPassthrough(false);
|
||||
|
||||
m_gpu_mainloop.Run(
|
||||
[this, &system] {
|
||||
[this] {
|
||||
// Run events from the CPU thread.
|
||||
AsyncRequests::GetInstance()->PullEvents();
|
||||
|
||||
|
@ -311,21 +314,22 @@ void FifoManager::RunGpuLoop(Core::System& system)
|
|||
}
|
||||
else
|
||||
{
|
||||
auto& command_processor = system.GetCommandProcessor();
|
||||
auto& command_processor = m_system.GetCommandProcessor();
|
||||
auto& fifo = command_processor.GetFifo();
|
||||
command_processor.SetCPStatusFromGPU(system);
|
||||
command_processor.SetCPStatusFromGPU();
|
||||
|
||||
// check if we are able to run this buffer
|
||||
while (!command_processor.IsInterruptWaiting() &&
|
||||
fifo.bFF_GPReadEnable.load(std::memory_order_relaxed) &&
|
||||
fifo.CPReadWriteDistance.load(std::memory_order_relaxed) && !AtBreakpoint(system))
|
||||
fifo.CPReadWriteDistance.load(std::memory_order_relaxed) &&
|
||||
!AtBreakpoint(m_system))
|
||||
{
|
||||
if (m_config_sync_gpu && m_sync_ticks.load() < m_config_sync_gpu_min_distance)
|
||||
break;
|
||||
|
||||
u32 cyclesExecuted = 0;
|
||||
u32 readPtr = fifo.CPReadPointer.load(std::memory_order_relaxed);
|
||||
ReadDataFromFifo(system, readPtr);
|
||||
ReadDataFromFifo(readPtr);
|
||||
|
||||
if (readPtr == fifo.CPEnd.load(std::memory_order_relaxed))
|
||||
readPtr = fifo.CPBase.load(std::memory_order_relaxed);
|
||||
|
@ -352,7 +356,7 @@ void FifoManager::RunGpuLoop(Core::System& system)
|
|||
std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
command_processor.SetCPStatusFromGPU(system);
|
||||
command_processor.SetCPStatusFromGPU();
|
||||
|
||||
if (m_config_sync_gpu)
|
||||
{
|
||||
|
@ -392,9 +396,9 @@ void FifoManager::RunGpuLoop(Core::System& system)
|
|||
AsyncRequests::GetInstance()->SetPassthrough(true);
|
||||
}
|
||||
|
||||
void FifoManager::FlushGpu(Core::System& system)
|
||||
void FifoManager::FlushGpu()
|
||||
{
|
||||
if (!system.IsDualCoreMode() || m_use_deterministic_gpu_thread)
|
||||
if (!m_system.IsDualCoreMode() || m_use_deterministic_gpu_thread)
|
||||
return;
|
||||
|
||||
m_gpu_mainloop.Wait();
|
||||
|
@ -414,9 +418,9 @@ bool AtBreakpoint(Core::System& system)
|
|||
fifo.CPBreakpoint.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
void FifoManager::RunGpu(Core::System& system)
|
||||
void FifoManager::RunGpu()
|
||||
{
|
||||
const bool is_dual_core = system.IsDualCoreMode();
|
||||
const bool is_dual_core = m_system.IsDualCoreMode();
|
||||
|
||||
// wake up GPU thread
|
||||
if (is_dual_core && !m_use_deterministic_gpu_thread)
|
||||
|
@ -430,25 +434,25 @@ void FifoManager::RunGpu(Core::System& system)
|
|||
if (m_syncing_suspended)
|
||||
{
|
||||
m_syncing_suspended = false;
|
||||
system.GetCoreTiming().ScheduleEvent(GPU_TIME_SLOT_SIZE, m_event_sync_gpu,
|
||||
GPU_TIME_SLOT_SIZE);
|
||||
m_system.GetCoreTiming().ScheduleEvent(GPU_TIME_SLOT_SIZE, m_event_sync_gpu,
|
||||
GPU_TIME_SLOT_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int FifoManager::RunGpuOnCpu(Core::System& system, int ticks)
|
||||
int FifoManager::RunGpuOnCpu(int ticks)
|
||||
{
|
||||
auto& command_processor = system.GetCommandProcessor();
|
||||
auto& command_processor = m_system.GetCommandProcessor();
|
||||
auto& fifo = command_processor.GetFifo();
|
||||
bool reset_simd_state = false;
|
||||
int available_ticks = int(ticks * m_config_sync_gpu_overclock) + m_sync_ticks.load();
|
||||
while (fifo.bFF_GPReadEnable.load(std::memory_order_relaxed) &&
|
||||
fifo.CPReadWriteDistance.load(std::memory_order_relaxed) && !AtBreakpoint(system) &&
|
||||
fifo.CPReadWriteDistance.load(std::memory_order_relaxed) && !AtBreakpoint(m_system) &&
|
||||
available_ticks >= 0)
|
||||
{
|
||||
if (m_use_deterministic_gpu_thread)
|
||||
{
|
||||
ReadDataFromFifoOnCPU(system, fifo.CPReadPointer.load(std::memory_order_relaxed));
|
||||
ReadDataFromFifoOnCPU(fifo.CPReadPointer.load(std::memory_order_relaxed));
|
||||
m_gpu_mainloop.Wakeup();
|
||||
}
|
||||
else
|
||||
|
@ -459,7 +463,7 @@ int FifoManager::RunGpuOnCpu(Core::System& system, int ticks)
|
|||
Common::FPU::LoadDefaultSIMDState();
|
||||
reset_simd_state = true;
|
||||
}
|
||||
ReadDataFromFifo(system, fifo.CPReadPointer.load(std::memory_order_relaxed));
|
||||
ReadDataFromFifo(fifo.CPReadPointer.load(std::memory_order_relaxed));
|
||||
u32 cycles = 0;
|
||||
m_video_buffer_read_ptr = OpcodeDecoder::RunFifo(
|
||||
DataReader(m_video_buffer_read_ptr, m_video_buffer_write_ptr), &cycles);
|
||||
|
@ -480,7 +484,7 @@ int FifoManager::RunGpuOnCpu(Core::System& system, int ticks)
|
|||
fifo.CPReadWriteDistance.fetch_sub(GPFifo::GATHER_PIPE_SIZE, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
command_processor.SetCPStatusFromGPU(system);
|
||||
command_processor.SetCPStatusFromGPU();
|
||||
|
||||
if (reset_simd_state)
|
||||
{
|
||||
|
@ -498,7 +502,7 @@ int FifoManager::RunGpuOnCpu(Core::System& system, int ticks)
|
|||
return -available_ticks + GPU_TIME_SLOT_SIZE;
|
||||
}
|
||||
|
||||
void FifoManager::UpdateWantDeterminism(Core::System& system, bool want)
|
||||
void FifoManager::UpdateWantDeterminism(bool want)
|
||||
{
|
||||
// We are paused (or not running at all yet), so
|
||||
// it should be safe to change this.
|
||||
|
@ -516,7 +520,7 @@ void FifoManager::UpdateWantDeterminism(Core::System& system, bool want)
|
|||
break;
|
||||
}
|
||||
|
||||
gpu_thread = gpu_thread && system.IsDualCoreMode();
|
||||
gpu_thread = gpu_thread && m_system.IsDualCoreMode();
|
||||
|
||||
if (m_use_deterministic_gpu_thread != gpu_thread)
|
||||
{
|
||||
|
@ -536,7 +540,7 @@ void FifoManager::UpdateWantDeterminism(Core::System& system, bool want)
|
|||
* @ticks The gone emulated CPU time.
|
||||
* @return A good time to call WaitForGpuThread() next.
|
||||
*/
|
||||
int FifoManager::WaitForGpuThread(Core::System& system, int ticks)
|
||||
int FifoManager::WaitForGpuThread(int ticks)
|
||||
{
|
||||
int old = m_sync_ticks.fetch_add(ticks);
|
||||
int now = old + ticks;
|
||||
|
@ -547,7 +551,7 @@ int FifoManager::WaitForGpuThread(Core::System& system, int ticks)
|
|||
|
||||
// Wakeup GPU
|
||||
if (old < m_config_sync_gpu_min_distance && now >= m_config_sync_gpu_min_distance)
|
||||
RunGpu(system);
|
||||
RunGpu();
|
||||
|
||||
// If the GPU is still sleeping, wait for a longer time
|
||||
if (now < m_config_sync_gpu_min_distance)
|
||||
|
@ -568,11 +572,11 @@ void FifoManager::SyncGPUCallback(Core::System& system, u64 ticks, s64 cyclesLat
|
|||
auto& fifo = system.GetFifo();
|
||||
if (!system.IsDualCoreMode() || fifo.m_use_deterministic_gpu_thread)
|
||||
{
|
||||
next = fifo.RunGpuOnCpu(system, (int)ticks);
|
||||
next = fifo.RunGpuOnCpu(int(ticks));
|
||||
}
|
||||
else if (fifo.m_config_sync_gpu)
|
||||
{
|
||||
next = fifo.WaitForGpuThread(system, (int)ticks);
|
||||
next = fifo.WaitForGpuThread(int(ticks));
|
||||
}
|
||||
|
||||
fifo.m_syncing_suspended = next < 0;
|
||||
|
@ -580,20 +584,20 @@ void FifoManager::SyncGPUCallback(Core::System& system, u64 ticks, s64 cyclesLat
|
|||
system.GetCoreTiming().ScheduleEvent(next, fifo.m_event_sync_gpu, next);
|
||||
}
|
||||
|
||||
void FifoManager::SyncGPUForRegisterAccess(Core::System& system)
|
||||
void FifoManager::SyncGPUForRegisterAccess()
|
||||
{
|
||||
SyncGPU(SyncGPUReason::Other);
|
||||
|
||||
if (!system.IsDualCoreMode() || m_use_deterministic_gpu_thread)
|
||||
RunGpuOnCpu(system, GPU_TIME_SLOT_SIZE);
|
||||
if (!m_system.IsDualCoreMode() || m_use_deterministic_gpu_thread)
|
||||
RunGpuOnCpu(GPU_TIME_SLOT_SIZE);
|
||||
else if (m_config_sync_gpu)
|
||||
WaitForGpuThread(system, GPU_TIME_SLOT_SIZE);
|
||||
WaitForGpuThread(GPU_TIME_SLOT_SIZE);
|
||||
}
|
||||
|
||||
// Initialize GPU - CPU thread syncing, this gives us a deterministic way to start the GPU thread.
|
||||
void FifoManager::Prepare(Core::System& system)
|
||||
void FifoManager::Prepare()
|
||||
{
|
||||
m_event_sync_gpu = system.GetCoreTiming().RegisterEvent("SyncGPUCallback", SyncGPUCallback);
|
||||
m_event_sync_gpu = m_system.GetCoreTiming().RegisterEvent("SyncGPUCallback", SyncGPUCallback);
|
||||
m_syncing_suspended = true;
|
||||
}
|
||||
} // namespace Fifo
|
||||
|
|
|
@ -41,19 +41,19 @@ enum class SyncGPUReason
|
|||
class FifoManager final
|
||||
{
|
||||
public:
|
||||
FifoManager();
|
||||
explicit FifoManager(Core::System& system);
|
||||
FifoManager(const FifoManager& other) = delete;
|
||||
FifoManager(FifoManager&& other) = delete;
|
||||
FifoManager& operator=(const FifoManager& other) = delete;
|
||||
FifoManager& operator=(FifoManager&& other) = delete;
|
||||
~FifoManager();
|
||||
|
||||
void Init(Core::System& system);
|
||||
void Init();
|
||||
void Shutdown();
|
||||
void Prepare(Core::System& system); // Must be called from the CPU thread.
|
||||
void Prepare(); // Must be called from the CPU thread.
|
||||
void DoState(PointerWrap& f);
|
||||
void PauseAndLock(Core::System& system, bool doLock, bool unpauseOnUnlock);
|
||||
void UpdateWantDeterminism(Core::System& system, bool want);
|
||||
void PauseAndLock(bool do_lock, bool unpause_on_unlock);
|
||||
void UpdateWantDeterminism(bool want);
|
||||
bool UseDeterministicGPUThread() const { return m_use_deterministic_gpu_thread; }
|
||||
bool UseSyncGPU() const { return m_config_sync_gpu; }
|
||||
|
||||
|
@ -62,25 +62,25 @@ public:
|
|||
|
||||
// In single core mode, this runs the GPU for a single slice.
|
||||
// In dual core mode, this synchronizes with the GPU thread.
|
||||
void SyncGPUForRegisterAccess(Core::System& system);
|
||||
void SyncGPUForRegisterAccess();
|
||||
|
||||
void PushFifoAuxBuffer(const void* ptr, size_t size);
|
||||
void* PopFifoAuxBuffer(size_t size);
|
||||
|
||||
void FlushGpu(Core::System& system);
|
||||
void RunGpu(Core::System& system);
|
||||
void FlushGpu();
|
||||
void RunGpu();
|
||||
void GpuMaySleep();
|
||||
void RunGpuLoop(Core::System& system);
|
||||
void ExitGpuLoop(Core::System& system);
|
||||
void RunGpuLoop();
|
||||
void ExitGpuLoop();
|
||||
void EmulatorState(bool running);
|
||||
void ResetVideoBuffer();
|
||||
|
||||
private:
|
||||
void RefreshConfig();
|
||||
void ReadDataFromFifo(Core::System& system, u32 readPtr);
|
||||
void ReadDataFromFifoOnCPU(Core::System& system, u32 readPtr);
|
||||
int RunGpuOnCpu(Core::System& system, int ticks);
|
||||
int WaitForGpuThread(Core::System& system, int ticks);
|
||||
void ReadDataFromFifo(u32 read_ptr);
|
||||
void ReadDataFromFifoOnCPU(u32 read_ptr);
|
||||
int RunGpuOnCpu(int ticks);
|
||||
int WaitForGpuThread(int ticks);
|
||||
static void SyncGPUCallback(Core::System& system, u64 ticks, s64 cyclesLate);
|
||||
|
||||
static constexpr u32 FIFO_SIZE = 2 * 1024 * 1024;
|
||||
|
@ -127,6 +127,8 @@ private:
|
|||
int m_config_sync_gpu_max_distance = 0;
|
||||
int m_config_sync_gpu_min_distance = 0;
|
||||
float m_config_sync_gpu_overclock = 0.0f;
|
||||
|
||||
Core::System& m_system;
|
||||
};
|
||||
|
||||
bool AtBreakpoint(Core::System& system);
|
||||
|
|
|
@ -82,7 +82,7 @@ bool FrameDumper::CheckFrameDumpRenderTexture(u32 target_width, u32 target_heigh
|
|||
m_frame_dump_render_texture.reset();
|
||||
m_frame_dump_render_texture = g_gfx->CreateTexture(
|
||||
TextureConfig(target_width, target_height, 1, 1, 1, AbstractTextureFormat::RGBA8,
|
||||
AbstractTextureFlag_RenderTarget),
|
||||
AbstractTextureFlag_RenderTarget, AbstractTextureType::Texture_2DArray),
|
||||
"Frame dump render texture");
|
||||
if (!m_frame_dump_render_texture)
|
||||
{
|
||||
|
@ -102,9 +102,10 @@ bool FrameDumper::CheckFrameDumpReadbackTexture(u32 target_width, u32 target_hei
|
|||
return true;
|
||||
|
||||
rbtex.reset();
|
||||
rbtex = g_gfx->CreateStagingTexture(
|
||||
StagingTextureType::Readback,
|
||||
TextureConfig(target_width, target_height, 1, 1, 1, AbstractTextureFormat::RGBA8, 0));
|
||||
rbtex = g_gfx->CreateStagingTexture(StagingTextureType::Readback,
|
||||
TextureConfig(target_width, target_height, 1, 1, 1,
|
||||
AbstractTextureFormat::RGBA8, 0,
|
||||
AbstractTextureType::Texture_2DArray));
|
||||
if (!rbtex)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -152,13 +152,15 @@ static u32 CalculateEFBLayers()
|
|||
TextureConfig FramebufferManager::GetEFBColorTextureConfig(u32 width, u32 height)
|
||||
{
|
||||
return TextureConfig(width, height, 1, CalculateEFBLayers(), g_ActiveConfig.iMultisamples,
|
||||
GetEFBColorFormat(), AbstractTextureFlag_RenderTarget);
|
||||
GetEFBColorFormat(), AbstractTextureFlag_RenderTarget,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
}
|
||||
|
||||
TextureConfig FramebufferManager::GetEFBDepthTextureConfig(u32 width, u32 height)
|
||||
{
|
||||
return TextureConfig(width, height, 1, CalculateEFBLayers(), g_ActiveConfig.iMultisamples,
|
||||
GetEFBDepthFormat(), AbstractTextureFlag_RenderTarget);
|
||||
GetEFBDepthFormat(), AbstractTextureFlag_RenderTarget,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
}
|
||||
|
||||
FramebufferState FramebufferManager::GetEFBFramebufferState() const
|
||||
|
@ -254,7 +256,8 @@ bool FramebufferManager::CreateEFBFramebuffer()
|
|||
flags |= AbstractTextureFlag_RenderTarget;
|
||||
m_efb_resolve_color_texture = g_gfx->CreateTexture(
|
||||
TextureConfig(efb_color_texture_config.width, efb_color_texture_config.height, 1,
|
||||
efb_color_texture_config.layers, 1, efb_color_texture_config.format, flags),
|
||||
efb_color_texture_config.layers, 1, efb_color_texture_config.format, flags,
|
||||
AbstractTextureType::Texture_2DArray),
|
||||
"EFB color resolve texture");
|
||||
if (!m_efb_resolve_color_texture)
|
||||
return false;
|
||||
|
@ -274,7 +277,7 @@ bool FramebufferManager::CreateEFBFramebuffer()
|
|||
m_efb_depth_resolve_texture = g_gfx->CreateTexture(
|
||||
TextureConfig(efb_depth_texture_config.width, efb_depth_texture_config.height, 1,
|
||||
efb_depth_texture_config.layers, 1, GetEFBDepthCopyFormat(),
|
||||
AbstractTextureFlag_RenderTarget),
|
||||
AbstractTextureFlag_RenderTarget, AbstractTextureType::Texture_2DArray),
|
||||
"EFB depth resolve texture");
|
||||
if (!m_efb_depth_resolve_texture)
|
||||
return false;
|
||||
|
@ -695,7 +698,8 @@ bool FramebufferManager::CreateReadbackFramebuffer()
|
|||
{
|
||||
const TextureConfig color_config(IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_WIDTH,
|
||||
IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_HEIGHT, 1,
|
||||
1, 1, GetEFBColorFormat(), AbstractTextureFlag_RenderTarget);
|
||||
1, 1, GetEFBColorFormat(), AbstractTextureFlag_RenderTarget,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
m_efb_color_cache.texture = g_gfx->CreateTexture(color_config, "EFB color cache");
|
||||
if (!m_efb_color_cache.texture)
|
||||
return false;
|
||||
|
@ -717,7 +721,8 @@ bool FramebufferManager::CreateReadbackFramebuffer()
|
|||
const TextureConfig depth_config(IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_WIDTH,
|
||||
IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_HEIGHT, 1,
|
||||
1, 1, GetEFBDepthCopyFormat(),
|
||||
AbstractTextureFlag_RenderTarget);
|
||||
AbstractTextureFlag_RenderTarget,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
m_efb_depth_cache.texture = g_gfx->CreateTexture(depth_config, "EFB depth cache");
|
||||
if (!m_efb_depth_cache.texture)
|
||||
return false;
|
||||
|
@ -729,12 +734,14 @@ bool FramebufferManager::CreateReadbackFramebuffer()
|
|||
}
|
||||
|
||||
// Staging texture use the full EFB dimensions, as this is the buffer for the whole cache.
|
||||
m_efb_color_cache.readback_texture = g_gfx->CreateStagingTexture(
|
||||
StagingTextureType::Mutable,
|
||||
TextureConfig(EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, GetEFBColorFormat(), 0));
|
||||
m_efb_color_cache.readback_texture =
|
||||
g_gfx->CreateStagingTexture(StagingTextureType::Mutable,
|
||||
TextureConfig(EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, GetEFBColorFormat(),
|
||||
0, AbstractTextureType::Texture_2DArray));
|
||||
m_efb_depth_cache.readback_texture = g_gfx->CreateStagingTexture(
|
||||
StagingTextureType::Mutable,
|
||||
TextureConfig(EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, GetEFBDepthCopyFormat(), 0));
|
||||
TextureConfig(EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, GetEFBDepthCopyFormat(), 0,
|
||||
AbstractTextureType::Texture_2DArray));
|
||||
if (!m_efb_color_cache.readback_texture || !m_efb_depth_cache.readback_texture)
|
||||
return false;
|
||||
|
||||
|
@ -1116,14 +1123,15 @@ void FramebufferManager::DoSaveState(PointerWrap& p)
|
|||
AbstractTexture* depth_texture = ResolveEFBDepthTexture(m_efb_depth_texture->GetRect(), true);
|
||||
|
||||
// We don't want to save these as rendertarget textures, just the data itself when deserializing.
|
||||
const TextureConfig color_texture_config(color_texture->GetWidth(), color_texture->GetHeight(),
|
||||
color_texture->GetLevels(), color_texture->GetLayers(),
|
||||
1, GetEFBColorFormat(), 0);
|
||||
const TextureConfig color_texture_config(
|
||||
color_texture->GetWidth(), color_texture->GetHeight(), color_texture->GetLevels(),
|
||||
color_texture->GetLayers(), 1, GetEFBColorFormat(), 0, AbstractTextureType::Texture_2DArray);
|
||||
g_texture_cache->SerializeTexture(color_texture, color_texture_config, p);
|
||||
|
||||
const TextureConfig depth_texture_config(depth_texture->GetWidth(), depth_texture->GetHeight(),
|
||||
depth_texture->GetLevels(), depth_texture->GetLayers(),
|
||||
1, GetEFBDepthCopyFormat(), 0);
|
||||
1, GetEFBDepthCopyFormat(), 0,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
g_texture_cache->SerializeTexture(depth_texture, depth_texture_config, p);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace VideoCommon
|
|||
// As pipelines encompass both shader UIDs and render states, changes to either of these should
|
||||
// also increment the pipeline UID version. Incrementing the UID version will cause all UID
|
||||
// caches to be invalidated.
|
||||
constexpr u32 GX_PIPELINE_UID_VERSION = 7; // Last changed in PR 11859
|
||||
constexpr u32 GX_PIPELINE_UID_VERSION = 8; // Last changed in PR 12185
|
||||
|
||||
struct GXPipelineUid
|
||||
{
|
||||
|
|
|
@ -93,7 +93,7 @@ ShaderCode GenerateGeometryShaderCode(APIType api_type, const ShaderHostConfig&
|
|||
|
||||
// uniforms
|
||||
if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
|
||||
out.Write("UBO_BINDING(std140, 3) uniform GSBlock {{\n");
|
||||
out.Write("UBO_BINDING(std140, 4) uniform GSBlock {{\n");
|
||||
else
|
||||
out.Write("cbuffer GSBlock {{\n");
|
||||
|
||||
|
|
|
@ -393,21 +393,15 @@ void CustomPipelineAction::OnTextureCreate(GraphicsModActionData::TextureCreate*
|
|||
return;
|
||||
}
|
||||
|
||||
if (property.m_type == VideoCommon::MaterialProperty::Type::Type_TextureAsset)
|
||||
if (auto* value = std::get_if<std::string>(&property.m_value))
|
||||
{
|
||||
if (property.m_value)
|
||||
auto asset = loader.LoadGameTexture(*value, m_library);
|
||||
if (asset)
|
||||
{
|
||||
if (auto* value = std::get_if<std::string>(&*property.m_value))
|
||||
{
|
||||
auto asset = loader.LoadGameTexture(*value, m_library);
|
||||
if (asset)
|
||||
{
|
||||
const auto loaded_time = asset->GetLastLoadedTime();
|
||||
game_assets.push_back(VideoCommon::CachedAsset<VideoCommon::GameTextureAsset>{
|
||||
std::move(asset), loaded_time});
|
||||
m_texture_code_names.push_back(property.m_code_name);
|
||||
}
|
||||
}
|
||||
const auto loaded_time = asset->GetLastLoadedTime();
|
||||
game_assets.push_back(
|
||||
VideoCommon::CachedAsset<VideoCommon::GameTextureAsset>{std::move(asset), loaded_time});
|
||||
m_texture_code_names.push_back(property.m_code_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -423,7 +417,7 @@ void CustomPipelineAction::OnTextureCreate(GraphicsModActionData::TextureCreate*
|
|||
auto data = game_texture.m_asset->GetData();
|
||||
if (data)
|
||||
{
|
||||
if (data->m_slices.empty() || data->m_slices[0].m_levels.empty())
|
||||
if (data->m_texture.m_slices.empty() || data->m_texture.m_slices[0].m_levels.empty())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
|
@ -431,15 +425,16 @@ void CustomPipelineAction::OnTextureCreate(GraphicsModActionData::TextureCreate*
|
|||
create->texture_name, game_texture.m_asset->GetAssetId());
|
||||
m_valid = false;
|
||||
}
|
||||
else if (create->texture_width != data->m_slices[0].m_levels[0].width ||
|
||||
create->texture_height != data->m_slices[0].m_levels[0].height)
|
||||
else if (create->texture_width != data->m_texture.m_slices[0].m_levels[0].width ||
|
||||
create->texture_height != data->m_texture.m_slices[0].m_levels[0].height)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Custom pipeline for texture '{}' has asset '{}' that does not match "
|
||||
"the width/height of the texture loaded. Texture {}x{} vs asset {}x{}",
|
||||
create->texture_name, game_texture.m_asset->GetAssetId(),
|
||||
create->texture_width, create->texture_height,
|
||||
data->m_slices[0].m_levels[0].width, data->m_slices[0].m_levels[0].height);
|
||||
data->m_texture.m_slices[0].m_levels[0].width,
|
||||
data->m_texture.m_slices[0].m_levels[0].height);
|
||||
m_valid = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,8 +95,8 @@ void CustomShaderCache::AsyncCreatePipeline(const VideoCommon::GXPipelineUid& ui
|
|||
PipelineWorkItem(CustomShaderCache* shader_cache, const VideoCommon::GXPipelineUid& uid,
|
||||
const CustomShaderInstance& custom_shaders, PipelineIterator iterator,
|
||||
const AbstractPipelineConfig& pipeline_config)
|
||||
: m_shader_cache(shader_cache), m_uid(uid), m_iterator(iterator),
|
||||
m_custom_shaders(custom_shaders), m_config(pipeline_config)
|
||||
: m_shader_cache(shader_cache), m_uid(uid), m_iterator(iterator), m_config(pipeline_config),
|
||||
m_custom_shaders(custom_shaders)
|
||||
{
|
||||
SetStagesReady();
|
||||
}
|
||||
|
@ -179,8 +179,8 @@ void CustomShaderCache::AsyncCreatePipeline(const VideoCommon::GXUberPipelineUid
|
|||
PipelineWorkItem(CustomShaderCache* shader_cache, const VideoCommon::GXUberPipelineUid& uid,
|
||||
const CustomShaderInstance& custom_shaders, UberPipelineIterator iterator,
|
||||
const AbstractPipelineConfig& pipeline_config)
|
||||
: m_shader_cache(shader_cache), m_uid(uid), m_iterator(iterator),
|
||||
m_custom_shaders(custom_shaders), m_config(pipeline_config)
|
||||
: m_shader_cache(shader_cache), m_uid(uid), m_iterator(iterator), m_config(pipeline_config),
|
||||
m_custom_shaders(custom_shaders)
|
||||
{
|
||||
SetStagesReady();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
|
@ -20,6 +21,7 @@ struct DrawStarted
|
|||
u32 texture_unit;
|
||||
bool* skip;
|
||||
std::optional<CustomPixelShader>* custom_pixel_shader;
|
||||
std::span<u8>* material_uniform_buffer;
|
||||
};
|
||||
|
||||
struct EFB
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionFactory.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
#include "VideoCommon/Constants.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/MoveAction.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.h"
|
||||
|
@ -32,7 +35,16 @@ std::unique_ptr<GraphicsModAction> Create(std::string_view name, const picojson:
|
|||
}
|
||||
else if (name == "custom_pipeline")
|
||||
{
|
||||
#ifdef ANDROID
|
||||
// Custom shaders currently need more than 8 pixel samplers
|
||||
// to be used with textures, rather than make things complicated
|
||||
// just disable the feature for Android which has issues
|
||||
ERROR_LOG_FMT(VIDEO, "Android needs more than 8 pixel samplers to function, {} provided",
|
||||
VideoCommon::MAX_PIXEL_SHADER_SAMPLERS);
|
||||
return nullptr;
|
||||
#else
|
||||
return CustomPipelineAction::Create(json_data, std::move(library));
|
||||
#endif
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
|
|
@ -130,8 +130,8 @@ void HiresTexture::Update()
|
|||
{
|
||||
// Since this is just a texture (single file) the mapper doesn't really matter
|
||||
// just provide a string
|
||||
s_file_library->SetAssetIDMapData(
|
||||
filename, std::map<std::string, std::filesystem::path>{{"", StringToPath(path)}});
|
||||
s_file_library->SetAssetIDMapData(filename, std::map<std::string, std::filesystem::path>{
|
||||
{"texture", StringToPath(path)}});
|
||||
|
||||
if (g_ActiveConfig.bCacheHiresTextures)
|
||||
{
|
||||
|
|
|
@ -214,43 +214,44 @@ void GenerateCustomLightingHeaderDetails(ShaderCode* out, u32 enablelighting, u3
|
|||
out->Write("\tint light_count;\n");
|
||||
}
|
||||
|
||||
static void GenerateLighting(ShaderCode* out, const LightingUidData& uid_data, int index,
|
||||
int litchan_index, u32 channel_index, u32 custom_light_index,
|
||||
bool alpha)
|
||||
{
|
||||
const auto attnfunc =
|
||||
static_cast<AttenuationFunc>((uid_data.attnfunc >> (2 * litchan_index)) & 0x3);
|
||||
|
||||
const std::string_view light_type = alpha ? "alpha" : "color";
|
||||
const std::string name = fmt::format("lights_chan{}_{}", channel_index, light_type);
|
||||
|
||||
out->Write("\t{{\n");
|
||||
out->Write("\t\tcustom_data.{}[{}].direction = " LIGHT_DIR ".xyz;\n", name, custom_light_index,
|
||||
LIGHT_DIR_PARAMS(index));
|
||||
out->Write("\t\tcustom_data.{}[{}].position = " LIGHT_POS ".xyz;\n", name, custom_light_index,
|
||||
LIGHT_POS_PARAMS(index));
|
||||
out->Write("\t\tcustom_data.{}[{}].cosatt = " LIGHT_COSATT ";\n", name, custom_light_index,
|
||||
LIGHT_COSATT_PARAMS(index));
|
||||
out->Write("\t\tcustom_data.{}[{}].distatt = " LIGHT_DISTATT ";\n", name, custom_light_index,
|
||||
LIGHT_DISTATT_PARAMS(index));
|
||||
out->Write("\t\tcustom_data.{}[{}].attenuation_type = {};\n", name, custom_light_index,
|
||||
static_cast<u32>(attnfunc));
|
||||
if (alpha)
|
||||
{
|
||||
out->Write("\t\tcustom_data.{}[{}].color = float3(" LIGHT_COL
|
||||
") / float3(255.0, 255.0, 255.0);\n",
|
||||
name, custom_light_index, LIGHT_COL_PARAMS(index, alpha ? "a" : "rgb"));
|
||||
}
|
||||
else
|
||||
{
|
||||
out->Write("\t\tcustom_data.{}[{}].color = " LIGHT_COL " / float3(255.0, 255.0, 255.0);\n",
|
||||
name, custom_light_index, LIGHT_COL_PARAMS(index, alpha ? "a" : "rgb"));
|
||||
}
|
||||
out->Write("\t}}\n");
|
||||
}
|
||||
|
||||
void GenerateCustomLightingImplementation(ShaderCode* out, const LightingUidData& uid_data,
|
||||
std::string_view in_color_name)
|
||||
{
|
||||
auto generate_lighting = [](ShaderCode* out, const LightingUidData& uid_data, int index,
|
||||
int litchan_index, u32 channel_index, u32 custom_light_index,
|
||||
bool alpha) {
|
||||
const auto attnfunc =
|
||||
static_cast<AttenuationFunc>((uid_data.attnfunc >> (2 * litchan_index)) & 0x3);
|
||||
|
||||
const std::string_view light_type = alpha ? "alpha" : "color";
|
||||
const std::string name = fmt::format("lights_chan{}_{}", channel_index, light_type);
|
||||
|
||||
out->Write("\t{{\n");
|
||||
out->Write("\t\tcustom_data.{}[{}].direction = " LIGHT_DIR ".xyz;\n", name, custom_light_index,
|
||||
LIGHT_DIR_PARAMS(index));
|
||||
out->Write("\t\tcustom_data.{}[{}].position = " LIGHT_POS ".xyz;\n", name, custom_light_index,
|
||||
LIGHT_POS_PARAMS(index));
|
||||
out->Write("\t\tcustom_data.{}[{}].cosatt = " LIGHT_COSATT ";\n", name, custom_light_index,
|
||||
LIGHT_COSATT_PARAMS(index));
|
||||
out->Write("\t\tcustom_data.{}[{}].distatt = " LIGHT_DISTATT ";\n", name, custom_light_index,
|
||||
LIGHT_DISTATT_PARAMS(index));
|
||||
out->Write("\t\tcustom_data.{}[{}].attenuation_type = {};\n", name, custom_light_index,
|
||||
static_cast<u32>(attnfunc));
|
||||
if (alpha)
|
||||
{
|
||||
out->Write("\t\tcustom_data.{}[{}].color = float3(" LIGHT_COL
|
||||
") / float3(255.0, 255.0, 255.0);\n",
|
||||
name, custom_light_index, LIGHT_COL_PARAMS(index, alpha ? "a" : "rgb"));
|
||||
}
|
||||
else
|
||||
{
|
||||
out->Write("\t\tcustom_data.{}[{}].color = " LIGHT_COL " / float3(255.0, 255.0, 255.0);\n",
|
||||
name, custom_light_index, LIGHT_COL_PARAMS(index, alpha ? "a" : "rgb"));
|
||||
}
|
||||
out->Write("\t}}\n");
|
||||
};
|
||||
|
||||
for (u32 i = 0; i < 8; i++)
|
||||
{
|
||||
for (u32 channel_index = 0; channel_index < NUM_XF_COLOR_CHANNELS; channel_index++)
|
||||
|
@ -330,7 +331,7 @@ void GenerateCustomLightingImplementation(ShaderCode* out, const LightingUidData
|
|||
{
|
||||
if ((uid_data.light_mask & (1 << (i + 8 * j))) != 0)
|
||||
{
|
||||
generate_lighting(out, uid_data, i, j, j, light_count, false);
|
||||
GenerateLighting(out, uid_data, i, j, j, light_count, false);
|
||||
light_count++;
|
||||
}
|
||||
}
|
||||
|
@ -344,7 +345,7 @@ void GenerateCustomLightingImplementation(ShaderCode* out, const LightingUidData
|
|||
{
|
||||
if ((uid_data.light_mask & (1 << (i + 8 * (j + 2)))) != 0)
|
||||
{
|
||||
generate_lighting(out, uid_data, i, j + 2, j, light_count, true);
|
||||
GenerateLighting(out, uid_data, i, j + 2, j, light_count, true);
|
||||
light_count++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,10 @@
|
|||
|
||||
#include "Core/Config/MainSettings.h"
|
||||
|
||||
#include "VideoCommon/AbstractGfx.h"
|
||||
#include "VideoCommon/AbstractTexture.h"
|
||||
#include "VideoCommon/TextureConfig.h"
|
||||
|
||||
namespace OSD
|
||||
{
|
||||
constexpr float LEFT_MARGIN = 10.0f; // Pixels to the left of OSD messages.
|
||||
|
@ -32,8 +36,8 @@ static std::atomic<int> s_obscured_pixels_top = 0;
|
|||
struct Message
|
||||
{
|
||||
Message() = default;
|
||||
Message(std::string text_, u32 duration_, u32 color_)
|
||||
: text(std::move(text_)), duration(duration_), color(color_)
|
||||
Message(std::string text_, u32 duration_, u32 color_, std::unique_ptr<Icon> icon_ = nullptr)
|
||||
: text(std::move(text_)), duration(duration_), color(color_), icon(std::move(icon_))
|
||||
{
|
||||
timer.Start();
|
||||
}
|
||||
|
@ -42,7 +46,10 @@ struct Message
|
|||
Common::Timer timer;
|
||||
u32 duration = 0;
|
||||
bool ever_drawn = false;
|
||||
bool should_discard = false;
|
||||
u32 color = 0;
|
||||
std::unique_ptr<Icon> icon;
|
||||
std::unique_ptr<AbstractTexture> texture;
|
||||
};
|
||||
static std::multimap<MessageType, Message> s_messages;
|
||||
static std::mutex s_messages_mutex;
|
||||
|
@ -77,6 +84,34 @@ static float DrawMessage(int index, Message& msg, const ImVec2& position, int ti
|
|||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav |
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing))
|
||||
{
|
||||
if (msg.icon)
|
||||
{
|
||||
if (!msg.texture)
|
||||
{
|
||||
const u32 width = msg.icon->width;
|
||||
const u32 height = msg.icon->height;
|
||||
TextureConfig tex_config(width, height, 1, 1, 1, AbstractTextureFormat::RGBA8, 0,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
msg.texture = g_gfx->CreateTexture(tex_config);
|
||||
if (msg.texture)
|
||||
{
|
||||
msg.texture->Load(0, width, height, width, msg.icon->rgba_data.data(),
|
||||
sizeof(u32) * width * height);
|
||||
}
|
||||
else
|
||||
{
|
||||
// don't try again next time
|
||||
msg.icon.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (msg.texture)
|
||||
{
|
||||
ImGui::Image(msg.texture.get(), ImVec2(static_cast<float>(msg.icon->width),
|
||||
static_cast<float>(msg.icon->height)));
|
||||
}
|
||||
}
|
||||
|
||||
// Use %s in case message contains %.
|
||||
ImGui::TextColored(ARGBToImVec4(msg.color), "%s", msg.text.c_str());
|
||||
window_height =
|
||||
|
@ -91,17 +126,25 @@ static float DrawMessage(int index, Message& msg, const ImVec2& position, int ti
|
|||
return window_height;
|
||||
}
|
||||
|
||||
void AddTypedMessage(MessageType type, std::string message, u32 ms, u32 argb)
|
||||
void AddTypedMessage(MessageType type, std::string message, u32 ms, u32 argb,
|
||||
std::unique_ptr<Icon> icon)
|
||||
{
|
||||
std::lock_guard lock{s_messages_mutex};
|
||||
s_messages.erase(type);
|
||||
s_messages.emplace(type, Message(std::move(message), ms, argb));
|
||||
|
||||
// A message may hold a reference to a texture that can only be destroyed on the video thread, so
|
||||
// only mark the old typed message (if any) for removal. It will be discarded on the next call to
|
||||
// DrawMessages().
|
||||
auto range = s_messages.equal_range(type);
|
||||
for (auto it = range.first; it != range.second; ++it)
|
||||
it->second.should_discard = true;
|
||||
|
||||
s_messages.emplace(type, Message(std::move(message), ms, argb, std::move(icon)));
|
||||
}
|
||||
|
||||
void AddMessage(std::string message, u32 ms, u32 argb)
|
||||
void AddMessage(std::string message, u32 ms, u32 argb, std::unique_ptr<Icon> icon)
|
||||
{
|
||||
std::lock_guard lock{s_messages_mutex};
|
||||
s_messages.emplace(MessageType::Typeless, Message(std::move(message), ms, argb));
|
||||
s_messages.emplace(MessageType::Typeless, Message(std::move(message), ms, argb, std::move(icon)));
|
||||
}
|
||||
|
||||
void DrawMessages()
|
||||
|
@ -117,6 +160,12 @@ void DrawMessages()
|
|||
for (auto it = s_messages.begin(); it != s_messages.end();)
|
||||
{
|
||||
Message& msg = it->second;
|
||||
if (msg.should_discard)
|
||||
{
|
||||
it = s_messages.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
const s64 time_left = msg.TimeRemaining();
|
||||
|
||||
// Make sure we draw them at least once if they were printed with 0ms,
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
|
@ -35,10 +37,18 @@ constexpr u32 NORMAL = 5000;
|
|||
constexpr u32 VERY_LONG = 10000;
|
||||
}; // namespace Duration
|
||||
|
||||
struct Icon
|
||||
{
|
||||
std::vector<u8> rgba_data;
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
}; // struct Icon
|
||||
|
||||
// On-screen message display (colored yellow by default)
|
||||
void AddMessage(std::string message, u32 ms = Duration::SHORT, u32 argb = Color::YELLOW);
|
||||
void AddMessage(std::string message, u32 ms = Duration::SHORT, u32 argb = Color::YELLOW,
|
||||
std::unique_ptr<Icon> icon = nullptr);
|
||||
void AddTypedMessage(MessageType type, std::string message, u32 ms = Duration::SHORT,
|
||||
u32 argb = Color::YELLOW);
|
||||
u32 argb = Color::YELLOW, std::unique_ptr<Icon> icon = nullptr);
|
||||
|
||||
// Draw the current messages on the screen. Only call once per frame.
|
||||
void DrawMessages();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "Common/Profiler.h"
|
||||
#include "Common/Timer.h"
|
||||
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Config/NetplaySettings.h"
|
||||
#include "Core/Movie.h"
|
||||
|
@ -76,7 +77,8 @@ bool OnScreenUI::Initialize(u32 width, u32 height, float scale)
|
|||
io.Fonts->GetTexDataAsRGBA32(&font_tex_pixels, &font_tex_width, &font_tex_height);
|
||||
|
||||
TextureConfig font_tex_config(font_tex_width, font_tex_height, 1, 1, 1,
|
||||
AbstractTextureFormat::RGBA8, 0);
|
||||
AbstractTextureFormat::RGBA8, 0,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
std::unique_ptr<AbstractTexture> font_tex =
|
||||
g_gfx->CreateTexture(font_tex_config, "ImGui font texture");
|
||||
if (!font_tex)
|
||||
|
@ -326,6 +328,67 @@ void OnScreenUI::DrawDebugText()
|
|||
ImGui::TextUnformatted(profile_output.c_str());
|
||||
}
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
void OnScreenUI::DrawChallenges()
|
||||
{
|
||||
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
|
||||
const auto& challenge_icons = AchievementManager::GetInstance().GetChallengeIcons();
|
||||
if (challenge_icons.empty())
|
||||
return;
|
||||
|
||||
const std::string window_name = "Challenges";
|
||||
|
||||
u32 sum_of_icon_heights = 0;
|
||||
u32 max_icon_width = 0;
|
||||
for (const auto& [name, icon] : challenge_icons)
|
||||
{
|
||||
// These *should* all be the same square size but you never know.
|
||||
if (icon->width > max_icon_width)
|
||||
max_icon_width = icon->width;
|
||||
sum_of_icon_heights += icon->height;
|
||||
}
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(ImGui::GetIO().DisplaySize.x - 20.f * m_backbuffer_scale - max_icon_width,
|
||||
ImGui::GetIO().DisplaySize.y - 20.f * m_backbuffer_scale - sum_of_icon_heights));
|
||||
ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f));
|
||||
if (ImGui::Begin(window_name.c_str(), nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav |
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing))
|
||||
{
|
||||
for (const auto& [name, icon] : challenge_icons)
|
||||
{
|
||||
if (m_challenge_texture_map.find(name) != m_challenge_texture_map.end())
|
||||
continue;
|
||||
const u32 width = icon->width;
|
||||
const u32 height = icon->height;
|
||||
TextureConfig tex_config(width, height, 1, 1, 1, AbstractTextureFormat::RGBA8, 0,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
auto res = m_challenge_texture_map.insert_or_assign(name, g_gfx->CreateTexture(tex_config));
|
||||
res.first->second->Load(0, width, height, width, icon->rgba_data.data(),
|
||||
sizeof(u32) * width * height);
|
||||
}
|
||||
for (auto& [name, texture] : m_challenge_texture_map)
|
||||
{
|
||||
auto icon_itr = challenge_icons.find(name);
|
||||
if (icon_itr == challenge_icons.end())
|
||||
{
|
||||
m_challenge_texture_map.erase(name);
|
||||
continue;
|
||||
}
|
||||
if (texture)
|
||||
{
|
||||
ImGui::Image(texture.get(), ImVec2(static_cast<float>(icon_itr->second->width),
|
||||
static_cast<float>(icon_itr->second->height)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
void OnScreenUI::Finalize()
|
||||
{
|
||||
auto lock = GetImGuiLock();
|
||||
|
@ -333,6 +396,9 @@ void OnScreenUI::Finalize()
|
|||
g_perf_metrics.DrawImGuiStats(m_backbuffer_scale);
|
||||
DrawDebugText();
|
||||
OSD::DrawMessages();
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
DrawChallenges();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
ImGui::Render();
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,9 @@ public:
|
|||
|
||||
private:
|
||||
void DrawDebugText();
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
void DrawChallenges();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
// ImGui resources.
|
||||
std::unique_ptr<NativeVertexFormat> m_imgui_vertex_format;
|
||||
|
@ -74,6 +77,10 @@ private:
|
|||
u32 m_backbuffer_height = 1;
|
||||
float m_backbuffer_scale = 1.0;
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
std::map<std::string, std::unique_ptr<AbstractTexture>, std::less<>> m_challenge_texture_map;
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
bool m_ready = false;
|
||||
};
|
||||
} // namespace VideoCommon
|
||||
|
|
|
@ -27,8 +27,8 @@
|
|||
#include "VideoCommon/Statistics.h"
|
||||
#include "VideoCommon/VertexLoaderBase.h"
|
||||
#include "VideoCommon/VertexLoaderManager.h"
|
||||
#include "VideoCommon/VertexShaderManager.h"
|
||||
#include "VideoCommon/XFMemory.h"
|
||||
#include "VideoCommon/XFStateManager.h"
|
||||
#include "VideoCommon/XFStructs.h"
|
||||
|
||||
namespace OpcodeDecoder
|
||||
|
@ -60,13 +60,13 @@ public:
|
|||
{
|
||||
VertexLoaderManager::g_needs_cp_xf_consistency_check = true;
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.GetVertexShaderManager().SetTexMatrixChangedA(value);
|
||||
system.GetXFStateManager().SetTexMatrixChangedA(value);
|
||||
}
|
||||
else if (sub_command == MATINDEX_B)
|
||||
{
|
||||
VertexLoaderManager::g_needs_cp_xf_consistency_check = true;
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.GetVertexShaderManager().SetTexMatrixChangedB(value);
|
||||
system.GetXFStateManager().SetTexMatrixChangedB(value);
|
||||
}
|
||||
else if (sub_command == VCD_LO || sub_command == VCD_HI)
|
||||
{
|
||||
|
@ -220,7 +220,7 @@ public:
|
|||
else
|
||||
{
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.GetCommandProcessor().HandleUnknownOpcode(system, opcode, data, is_preprocess);
|
||||
system.GetCommandProcessor().HandleUnknownOpcode(opcode, data, is_preprocess);
|
||||
m_cycles += 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,7 +159,7 @@ static DOLPHIN_FORCE_INLINE u32 RunCommand(const u8* data, u32 available, T& cal
|
|||
const u16 base_address = cmd2 & 0xffff;
|
||||
|
||||
const u16 stream_size_temp = cmd2 >> 16;
|
||||
ASSERT(stream_size_temp < 16);
|
||||
ASSERT_MSG(VIDEO, stream_size_temp < 16, "cmd2 = 0x{:08X}", cmd2);
|
||||
const u8 stream_size = (stream_size_temp & 0xf) + 1;
|
||||
|
||||
if (available < u32(5 + stream_size * 4))
|
||||
|
|
|
@ -30,6 +30,10 @@ enum
|
|||
INT_CAUSE_PE_FINISH = 0x400, // GP Finished
|
||||
};
|
||||
|
||||
PixelEngineManager::PixelEngineManager(Core::System& system) : m_system{system}
|
||||
{
|
||||
}
|
||||
|
||||
void PixelEngineManager::DoState(PointerWrap& p)
|
||||
{
|
||||
p.Do(m_z_conf);
|
||||
|
@ -49,7 +53,7 @@ void PixelEngineManager::DoState(PointerWrap& p)
|
|||
p.Do(m_signal_finish_interrupt);
|
||||
}
|
||||
|
||||
void PixelEngineManager::Init(Core::System& system)
|
||||
void PixelEngineManager::Init()
|
||||
{
|
||||
m_control.hex = 0;
|
||||
m_z_conf.hex = 0;
|
||||
|
@ -68,7 +72,7 @@ void PixelEngineManager::Init(Core::System& system)
|
|||
m_signal_finish_interrupt = false;
|
||||
|
||||
m_event_type_set_token_finish =
|
||||
system.GetCoreTiming().RegisterEvent("SetTokenFinish", SetTokenFinish_OnMainThread_Static);
|
||||
m_system.GetCoreTiming().RegisterEvent("SetTokenFinish", SetTokenFinish_OnMainThread_Static);
|
||||
}
|
||||
|
||||
void PixelEngineManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
|
@ -156,8 +160,7 @@ void PixelEngineManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
|||
|
||||
void PixelEngineManager::UpdateInterrupts()
|
||||
{
|
||||
auto& system = Core::System::GetInstance();
|
||||
auto& processor_interface = system.GetProcessorInterface();
|
||||
auto& processor_interface = m_system.GetProcessorInterface();
|
||||
|
||||
// check if there is a token-interrupt
|
||||
processor_interface.SetInterrupt(INT_CAUSE_PE_TOKEN,
|
||||
|
@ -171,13 +174,12 @@ void PixelEngineManager::UpdateInterrupts()
|
|||
void PixelEngineManager::SetTokenFinish_OnMainThread_Static(Core::System& system, u64 userdata,
|
||||
s64 cycles_late)
|
||||
{
|
||||
system.GetPixelEngine().SetTokenFinish_OnMainThread(system, userdata, cycles_late);
|
||||
system.GetPixelEngine().SetTokenFinish_OnMainThread(userdata, cycles_late);
|
||||
}
|
||||
|
||||
void PixelEngineManager::SetTokenFinish_OnMainThread(Core::System& system, u64 userdata,
|
||||
s64 cycles_late)
|
||||
void PixelEngineManager::SetTokenFinish_OnMainThread(u64 userdata, s64 cycles_late)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_token_finish_mutex);
|
||||
std::unique_lock lk(m_token_finish_mutex);
|
||||
m_event_raised = false;
|
||||
|
||||
m_token = m_token_pending;
|
||||
|
@ -202,7 +204,7 @@ void PixelEngineManager::SetTokenFinish_OnMainThread(Core::System& system, u64 u
|
|||
// Raise the event handler above on the CPU thread.
|
||||
// m_token_finish_mutex must be locked.
|
||||
// THIS IS EXECUTED FROM VIDEO THREAD
|
||||
void PixelEngineManager::RaiseEvent(Core::System& system, int cycles_into_future)
|
||||
void PixelEngineManager::RaiseEvent(int cycles_into_future)
|
||||
{
|
||||
if (m_event_raised)
|
||||
return;
|
||||
|
@ -211,7 +213,7 @@ void PixelEngineManager::RaiseEvent(Core::System& system, int cycles_into_future
|
|||
|
||||
CoreTiming::FromThread from = CoreTiming::FromThread::NON_CPU;
|
||||
s64 cycles = 0; // we don't care about timings for dual core mode.
|
||||
if (!system.IsDualCoreMode() || system.GetFifo().UseDeterministicGPUThread())
|
||||
if (!m_system.IsDualCoreMode() || m_system.GetFifo().UseDeterministicGPUThread())
|
||||
{
|
||||
from = CoreTiming::FromThread::CPU;
|
||||
|
||||
|
@ -219,35 +221,34 @@ void PixelEngineManager::RaiseEvent(Core::System& system, int cycles_into_future
|
|||
// games time to setup any interrupt state
|
||||
cycles = std::max(500, cycles_into_future);
|
||||
}
|
||||
system.GetCoreTiming().ScheduleEvent(cycles, m_event_type_set_token_finish, 0, from);
|
||||
m_system.GetCoreTiming().ScheduleEvent(cycles, m_event_type_set_token_finish, 0, from);
|
||||
}
|
||||
|
||||
// SetToken
|
||||
// THIS IS EXECUTED FROM VIDEO THREAD
|
||||
void PixelEngineManager::SetToken(Core::System& system, const u16 token, const bool interrupt,
|
||||
int cycles_into_future)
|
||||
void PixelEngineManager::SetToken(const u16 token, const bool interrupt, int cycles_into_future)
|
||||
{
|
||||
DEBUG_LOG_FMT(PIXELENGINE, "VIDEO Backend raises INT_CAUSE_PE_TOKEN (btw, token: {:04x})", token);
|
||||
|
||||
std::lock_guard<std::mutex> lk(m_token_finish_mutex);
|
||||
std::lock_guard lk(m_token_finish_mutex);
|
||||
|
||||
m_token_pending = token;
|
||||
m_token_interrupt_pending |= interrupt;
|
||||
|
||||
RaiseEvent(system, cycles_into_future);
|
||||
RaiseEvent(cycles_into_future);
|
||||
}
|
||||
|
||||
// SetFinish
|
||||
// THIS IS EXECUTED FROM VIDEO THREAD (BPStructs.cpp) when a new frame has been drawn
|
||||
void PixelEngineManager::SetFinish(Core::System& system, int cycles_into_future)
|
||||
void PixelEngineManager::SetFinish(int cycles_into_future)
|
||||
{
|
||||
DEBUG_LOG_FMT(PIXELENGINE, "VIDEO Set Finish");
|
||||
|
||||
std::lock_guard<std::mutex> lk(m_token_finish_mutex);
|
||||
std::lock_guard lk(m_token_finish_mutex);
|
||||
|
||||
m_finish_interrupt_pending |= true;
|
||||
|
||||
RaiseEvent(system, cycles_into_future);
|
||||
RaiseEvent(cycles_into_future);
|
||||
}
|
||||
|
||||
} // namespace PixelEngine
|
||||
|
|
|
@ -179,20 +179,22 @@ union UPECtrlReg
|
|||
class PixelEngineManager
|
||||
{
|
||||
public:
|
||||
void Init(Core::System& system);
|
||||
explicit PixelEngineManager(Core::System& system);
|
||||
|
||||
void Init();
|
||||
void DoState(PointerWrap& p);
|
||||
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||
|
||||
// gfx backend support
|
||||
void SetToken(Core::System& system, const u16 token, const bool interrupt, int cycle_delay);
|
||||
void SetFinish(Core::System& system, int cycle_delay);
|
||||
void SetToken(const u16 token, const bool interrupt, int cycle_delay);
|
||||
void SetFinish(int cycle_delay);
|
||||
AlphaReadMode GetAlphaReadMode() const { return m_alpha_read.read_mode; }
|
||||
|
||||
private:
|
||||
void RaiseEvent(Core::System& system, int cycles_into_future);
|
||||
void RaiseEvent(int cycles_into_future);
|
||||
void UpdateInterrupts();
|
||||
void SetTokenFinish_OnMainThread(Core::System& system, u64 userdata, s64 cycles_late);
|
||||
void SetTokenFinish_OnMainThread(u64 userdata, s64 cycles_late);
|
||||
|
||||
static void SetTokenFinish_OnMainThread_Static(Core::System& system, u64 userdata,
|
||||
s64 cycles_late);
|
||||
|
@ -216,6 +218,8 @@ private:
|
|||
bool m_signal_finish_interrupt = false;
|
||||
|
||||
CoreTiming::EventType* m_event_type_set_token_finish = nullptr;
|
||||
|
||||
Core::System& m_system;
|
||||
};
|
||||
|
||||
} // namespace PixelEngine
|
||||
|
|
|
@ -345,7 +345,8 @@ void ClearUnusedPixelShaderUidBits(APIType api_type, const ShaderHostConfig& hos
|
|||
}
|
||||
|
||||
void WritePixelShaderCommonHeader(ShaderCode& out, APIType api_type,
|
||||
const ShaderHostConfig& host_config, bool bounding_box)
|
||||
const ShaderHostConfig& host_config, bool bounding_box,
|
||||
const CustomPixelShaderContents& custom_details)
|
||||
{
|
||||
// dot product for integer vectors
|
||||
out.Write("int idot(int3 x, int3 y)\n"
|
||||
|
@ -426,6 +427,14 @@ void WritePixelShaderCommonHeader(ShaderCode& out, APIType api_type,
|
|||
out.Write("}};\n");
|
||||
}
|
||||
|
||||
if (!custom_details.shaders.empty() &&
|
||||
!custom_details.shaders.back().material_uniform_block.empty())
|
||||
{
|
||||
out.Write("UBO_BINDING(std140, 3) uniform CustomShaderBlock {{\n");
|
||||
out.Write("{}", custom_details.shaders.back().material_uniform_block);
|
||||
out.Write("}} custom_uniforms;\n");
|
||||
}
|
||||
|
||||
if (bounding_box)
|
||||
{
|
||||
out.Write("SSBO_BINDING(0) coherent buffer BBox {{\n"
|
||||
|
@ -907,7 +916,7 @@ ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& hos
|
|||
// Stuff that is shared between ubershaders and pixelgen.
|
||||
WriteBitfieldExtractHeader(out, api_type, host_config);
|
||||
|
||||
WritePixelShaderCommonHeader(out, api_type, host_config, uid_data->bounding_box);
|
||||
WritePixelShaderCommonHeader(out, api_type, host_config, uid_data->bounding_box, custom_details);
|
||||
|
||||
// Custom shader details
|
||||
WriteCustomShaderStructDef(&out, uid_data->genMode_numtexgens);
|
||||
|
|
|
@ -165,7 +165,8 @@ ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& hos
|
|||
const pixel_shader_uid_data* uid_data,
|
||||
const CustomPixelShaderContents& custom_details);
|
||||
void WritePixelShaderCommonHeader(ShaderCode& out, APIType api_type,
|
||||
const ShaderHostConfig& host_config, bool bounding_box);
|
||||
const ShaderHostConfig& host_config, bool bounding_box,
|
||||
const CustomPixelShaderContents& custom_details);
|
||||
void ClearUnusedPixelShaderUidBits(APIType api_type, const ShaderHostConfig& host_config,
|
||||
PixelShaderUid* uid);
|
||||
PixelShaderUid GetPixelShaderUid();
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "VideoCommon/ConstantManager.h"
|
||||
|
||||
|
@ -52,6 +54,10 @@ public:
|
|||
PixelShaderConstants constants{};
|
||||
bool dirty = false;
|
||||
|
||||
// Constants for custom shaders
|
||||
std::span<u8> custom_constants;
|
||||
bool custom_constants_dirty = false;
|
||||
|
||||
private:
|
||||
bool m_fog_range_adjusted_changed = false;
|
||||
bool m_viewport_changed = false;
|
||||
|
|
|
@ -39,8 +39,8 @@ static const char s_default_pixel_shader_name[] = "default_pre_post_process";
|
|||
// RGBA16F should have enough quality even if we store colors in gamma space on it.
|
||||
static const AbstractTextureFormat s_intermediary_buffer_format = AbstractTextureFormat::RGBA16F;
|
||||
|
||||
bool LoadShaderFromFile(const std::string& shader, const std::string& sub_dir,
|
||||
std::string& out_code)
|
||||
static bool LoadShaderFromFile(const std::string& shader, const std::string& sub_dir,
|
||||
std::string& out_code)
|
||||
{
|
||||
std::string path = File::GetUserPath(D_SHADERS_IDX) + sub_dir + shader + ".glsl";
|
||||
|
||||
|
@ -530,7 +530,8 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle<int>& dst,
|
|||
{
|
||||
const TextureConfig intermediary_color_texture_config(
|
||||
target_width, target_height, 1, target_layers, src_tex->GetSamples(),
|
||||
s_intermediary_buffer_format, AbstractTextureFlag_RenderTarget);
|
||||
s_intermediary_buffer_format, AbstractTextureFlag_RenderTarget,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
m_intermediary_color_texture = g_gfx->CreateTexture(intermediary_color_texture_config,
|
||||
"Intermediary post process texture");
|
||||
|
||||
|
@ -769,7 +770,7 @@ std::string PostProcessing::GetFooter() const
|
|||
return {};
|
||||
}
|
||||
|
||||
std::string GetVertexShaderBody()
|
||||
static std::string GetVertexShaderBody()
|
||||
{
|
||||
std::ostringstream ss;
|
||||
if (g_ActiveConfig.backend_info.bSupportsGeometryShaders)
|
||||
|
@ -1002,7 +1003,7 @@ bool PostProcessing::CompilePixelShader()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool UseGeometryShaderForPostProcess(bool is_intermediary_buffer)
|
||||
static bool UseGeometryShaderForPostProcess(bool is_intermediary_buffer)
|
||||
{
|
||||
// We only return true on stereo modes that need to copy
|
||||
// both source texture layers into the target texture layers.
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "VideoCommon/Present.h"
|
||||
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Core/Config/GraphicsSettings.h"
|
||||
#include "Core/HW/VideoInterface.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/System.h"
|
||||
|
@ -13,6 +14,7 @@
|
|||
#include "Present.h"
|
||||
#include "VideoCommon/AbstractGfx.h"
|
||||
#include "VideoCommon/FrameDumper.h"
|
||||
#include "VideoCommon/FramebufferManager.h"
|
||||
#include "VideoCommon/OnScreenUI.h"
|
||||
#include "VideoCommon/PostProcessing.h"
|
||||
#include "VideoCommon/Statistics.h"
|
||||
|
@ -28,9 +30,10 @@ static constexpr int VIDEO_ENCODER_LCM = 4;
|
|||
|
||||
namespace VideoCommon
|
||||
{
|
||||
static float AspectToWidescreen(float aspect)
|
||||
// Stretches the native/internal analog resolution aspect ratio from ~4:3 to ~16:9
|
||||
static float SourceAspectRatioToWidescreen(float source_aspect)
|
||||
{
|
||||
return aspect * ((16.0f / 9.0f) / (4.0f / 3.0f));
|
||||
return source_aspect * ((16.0f / 9.0f) / (4.0f / 3.0f));
|
||||
}
|
||||
|
||||
static std::tuple<int, int> FindClosestIntegerResolution(float width, float height,
|
||||
|
@ -201,20 +204,46 @@ void Presenter::ProcessFrameDumping(u64 ticks) const
|
|||
|
||||
void Presenter::SetBackbuffer(int backbuffer_width, int backbuffer_height)
|
||||
{
|
||||
const bool is_first = m_backbuffer_width == 0 && m_backbuffer_height == 0;
|
||||
const bool size_changed =
|
||||
(m_backbuffer_width != backbuffer_width || m_backbuffer_height != backbuffer_height);
|
||||
m_backbuffer_width = backbuffer_width;
|
||||
m_backbuffer_height = backbuffer_height;
|
||||
UpdateDrawRectangle();
|
||||
|
||||
OnBackbufferSet(size_changed, is_first);
|
||||
}
|
||||
|
||||
void Presenter::SetBackbuffer(SurfaceInfo info)
|
||||
{
|
||||
const bool is_first = m_backbuffer_width == 0 && m_backbuffer_height == 0;
|
||||
const bool size_changed =
|
||||
(m_backbuffer_width != (int)info.width || m_backbuffer_height != (int)info.height);
|
||||
m_backbuffer_width = info.width;
|
||||
m_backbuffer_height = info.height;
|
||||
m_backbuffer_scale = info.scale;
|
||||
m_backbuffer_format = info.format;
|
||||
if (m_onscreen_ui)
|
||||
m_onscreen_ui->SetScale(info.scale);
|
||||
|
||||
OnBackbufferSet(size_changed, is_first);
|
||||
}
|
||||
|
||||
void Presenter::OnBackbufferSet(bool size_changed, bool is_first_set)
|
||||
{
|
||||
UpdateDrawRectangle();
|
||||
|
||||
// Automatically update the resolution scale if the window size changed,
|
||||
// or if the game XFB resolution changed.
|
||||
if (size_changed && !is_first_set && g_ActiveConfig.iEFBScale == EFB_SCALE_AUTO_INTEGRAL &&
|
||||
m_auto_resolution_scale != AutoIntegralScale())
|
||||
{
|
||||
g_framebuffer_manager->RecreateEFBFramebuffer();
|
||||
}
|
||||
if (size_changed || is_first_set)
|
||||
{
|
||||
m_auto_resolution_scale = AutoIntegralScale();
|
||||
}
|
||||
}
|
||||
|
||||
void Presenter::ConfigChanged(u32 changed_bits)
|
||||
|
@ -292,15 +321,22 @@ float Presenter::CalculateDrawAspectRatio(bool allow_stretch) const
|
|||
return (static_cast<float>(m_backbuffer_width) / static_cast<float>(m_backbuffer_height));
|
||||
|
||||
auto& vi = Core::System::GetInstance().GetVideoInterface();
|
||||
const float aspect_ratio = vi.GetAspectRatio();
|
||||
const float source_aspect_ratio = vi.GetAspectRatio();
|
||||
|
||||
if (aspect_mode == AspectMode::AnalogWide ||
|
||||
// This will scale up the source ~4:3 resolution to its equivalent ~16:9 resolution
|
||||
if (aspect_mode == AspectMode::ForceWide ||
|
||||
(aspect_mode == AspectMode::Auto && g_widescreen->IsGameWidescreen()))
|
||||
{
|
||||
return AspectToWidescreen(aspect_ratio);
|
||||
return SourceAspectRatioToWidescreen(source_aspect_ratio);
|
||||
}
|
||||
// For the "custom" mode, we force the exact target aspect ratio, without
|
||||
// acknowleding the difference between the source aspect ratio and 4:3.
|
||||
else if (aspect_mode == AspectMode::Custom)
|
||||
{
|
||||
return g_ActiveConfig.GetCustomAspectRatio();
|
||||
}
|
||||
|
||||
return aspect_ratio;
|
||||
return source_aspect_ratio;
|
||||
}
|
||||
|
||||
void Presenter::AdjustRectanglesToFitBounds(MathUtil::Rectangle<int>* target_rect,
|
||||
|
@ -365,45 +401,35 @@ void* Presenter::GetNewSurfaceHandle()
|
|||
|
||||
u32 Presenter::AutoIntegralScale() const
|
||||
{
|
||||
const float efb_aspect_ratio = static_cast<float>(EFB_WIDTH) / EFB_HEIGHT;
|
||||
const float target_aspect_ratio =
|
||||
static_cast<float>(m_target_rectangle.GetWidth()) / m_target_rectangle.GetHeight();
|
||||
|
||||
u32 target_width;
|
||||
u32 target_height;
|
||||
|
||||
// Instead of using the entire window (back buffer) resolution,
|
||||
// find the portion of it that will actually contain the EFB output,
|
||||
// and ignore the portion that will likely have black bars.
|
||||
if (target_aspect_ratio >= efb_aspect_ratio)
|
||||
{
|
||||
target_height = m_target_rectangle.GetHeight();
|
||||
target_width = static_cast<u32>(
|
||||
std::round((static_cast<float>(m_target_rectangle.GetWidth()) / target_aspect_ratio) *
|
||||
efb_aspect_ratio));
|
||||
}
|
||||
// Take the source resolution (XFB) and stretch it on the target aspect ratio.
|
||||
// If the target resolution is larger (on either x or y), we scale the source
|
||||
// by a integer multiplier until it won't have to be scaled up anymore.
|
||||
u32 source_width = m_last_xfb_width;
|
||||
u32 source_height = m_last_xfb_height;
|
||||
const u32 target_width = m_target_rectangle.GetWidth();
|
||||
const u32 target_height = m_target_rectangle.GetHeight();
|
||||
const float source_aspect_ratio = (float)source_width / source_height;
|
||||
const float target_aspect_ratio = (float)target_width / target_height;
|
||||
if (source_aspect_ratio >= target_aspect_ratio)
|
||||
source_width = std::round(source_height * target_aspect_ratio);
|
||||
else
|
||||
{
|
||||
target_width = m_target_rectangle.GetWidth();
|
||||
target_height = static_cast<u32>(
|
||||
std::round((static_cast<float>(m_target_rectangle.GetHeight()) * target_aspect_ratio) /
|
||||
efb_aspect_ratio));
|
||||
}
|
||||
|
||||
// Calculate a scale based on the adjusted window size
|
||||
u32 width = EFB_WIDTH * target_width / m_last_xfb_width;
|
||||
u32 height = EFB_HEIGHT * target_height / m_last_xfb_height;
|
||||
return std::max((width - 1) / EFB_WIDTH + 1, (height - 1) / EFB_HEIGHT + 1);
|
||||
source_height = std::round(source_width / target_aspect_ratio);
|
||||
const u32 width_scale =
|
||||
source_width > 0 ? ((target_width + (source_width - 1)) / source_width) : 1;
|
||||
const u32 height_scale =
|
||||
source_height > 0 ? ((target_height + (source_height - 1)) / source_height) : 1;
|
||||
// Limit to the max to avoid creating textures larger than their max supported resolution.
|
||||
return std::min(std::max(width_scale, height_scale),
|
||||
static_cast<u32>(Config::Get(Config::GFX_MAX_EFB_SCALE)));
|
||||
}
|
||||
|
||||
void Presenter::SetWindowSize(int width, int height)
|
||||
void Presenter::SetSuggestedWindowSize(int width, int height)
|
||||
{
|
||||
// While trying to guess the best window resolution, we can't allow it to use the
|
||||
// "AspectMode::Stretch" setting because that would self influence the output result,
|
||||
// given it would be based on the previous frame resolution
|
||||
const bool allow_stretch = false;
|
||||
const auto [out_width, out_height] =
|
||||
g_presenter->CalculateOutputDimensions(width, height, allow_stretch);
|
||||
const auto [out_width, out_height] = CalculateOutputDimensions(width, height, allow_stretch);
|
||||
|
||||
// Track the last values of width/height to avoid sending a window resize event every frame.
|
||||
if (out_width == m_last_window_request_width && out_height == m_last_window_request_height)
|
||||
|
@ -415,7 +441,7 @@ void Presenter::SetWindowSize(int width, int height)
|
|||
Host_RequestRenderWindowSize(out_width, out_height);
|
||||
}
|
||||
|
||||
// Crop to exactly 16:9 or 4:3 if enabled and not AspectMode::Stretch.
|
||||
// Crop to exact forced aspect ratios if enabled and not AspectMode::Stretch.
|
||||
std::tuple<float, float> Presenter::ApplyStandardAspectCrop(float width, float height,
|
||||
bool allow_stretch) const
|
||||
{
|
||||
|
@ -427,13 +453,28 @@ std::tuple<float, float> Presenter::ApplyStandardAspectCrop(float width, float h
|
|||
if (!g_ActiveConfig.bCrop || aspect_mode == AspectMode::Stretch)
|
||||
return {width, height};
|
||||
|
||||
// Force 4:3 or 16:9 by cropping the image.
|
||||
// Force aspect ratios by cropping the image.
|
||||
const float current_aspect = width / height;
|
||||
const float expected_aspect =
|
||||
(aspect_mode == AspectMode::AnalogWide ||
|
||||
(aspect_mode == AspectMode::Auto && g_widescreen->IsGameWidescreen())) ?
|
||||
(16.0f / 9.0f) :
|
||||
(4.0f / 3.0f);
|
||||
float expected_aspect;
|
||||
switch (aspect_mode)
|
||||
{
|
||||
default:
|
||||
case AspectMode::Auto:
|
||||
expected_aspect = g_widescreen->IsGameWidescreen() ? (16.0f / 9.0f) : (4.0f / 3.0f);
|
||||
break;
|
||||
case AspectMode::ForceWide:
|
||||
expected_aspect = 16.0f / 9.0f;
|
||||
break;
|
||||
case AspectMode::ForceStandard:
|
||||
expected_aspect = 4.0f / 3.0f;
|
||||
break;
|
||||
// There should be no cropping needed in the custom case,
|
||||
// as output should always exactly match the target aspect ratio
|
||||
case AspectMode::Custom:
|
||||
expected_aspect = g_ActiveConfig.GetCustomAspectRatio();
|
||||
break;
|
||||
}
|
||||
|
||||
if (current_aspect > expected_aspect)
|
||||
{
|
||||
// keep height, crop width
|
||||
|
@ -458,11 +499,13 @@ void Presenter::UpdateDrawRectangle()
|
|||
if (g_ActiveConfig.bWidescreenHack)
|
||||
{
|
||||
auto& vi = Core::System::GetInstance().GetVideoInterface();
|
||||
float source_aspect = vi.GetAspectRatio();
|
||||
float source_aspect_ratio = vi.GetAspectRatio();
|
||||
// If the game is meant to be in widescreen (or forced to),
|
||||
// scale the source aspect ratio to it.
|
||||
if (g_widescreen->IsGameWidescreen())
|
||||
source_aspect = AspectToWidescreen(source_aspect);
|
||||
source_aspect_ratio = SourceAspectRatioToWidescreen(source_aspect_ratio);
|
||||
|
||||
const float adjust = source_aspect / draw_aspect_ratio;
|
||||
const float adjust = source_aspect_ratio / draw_aspect_ratio;
|
||||
if (adjust > 1)
|
||||
{
|
||||
// Vert+
|
||||
|
@ -653,7 +696,7 @@ void Presenter::Present()
|
|||
|
||||
// Update the window size based on the frame that was just rendered.
|
||||
// Due to depending on guest state, we need to call this every frame.
|
||||
SetWindowSize(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight());
|
||||
SetSuggestedWindowSize(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -695,7 +738,7 @@ void Presenter::Present()
|
|||
{
|
||||
// Update the window size based on the frame that was just rendered.
|
||||
// Due to depending on guest state, we need to call this every frame.
|
||||
SetWindowSize(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight());
|
||||
SetSuggestedWindowSize(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight());
|
||||
}
|
||||
|
||||
if (m_onscreen_ui)
|
||||
|
@ -737,12 +780,12 @@ void Presenter::DoState(PointerWrap& p)
|
|||
p.Do(m_last_xfb_stride);
|
||||
p.Do(m_last_xfb_height);
|
||||
|
||||
if (p.IsReadMode())
|
||||
// If we're loading and there is a last XFB, re-display it.
|
||||
if (p.IsReadMode() && m_last_xfb_stride != 0)
|
||||
{
|
||||
// This technically counts as the end of the frame
|
||||
AfterFrameEvent::Trigger();
|
||||
|
||||
// re-display the most recent XFB
|
||||
ImmediateSwap(m_last_xfb_addr, m_last_xfb_width, m_last_xfb_stride, m_last_xfb_height,
|
||||
m_last_xfb_ticks);
|
||||
}
|
||||
|
|
|
@ -46,18 +46,20 @@ public:
|
|||
|
||||
void ConfigChanged(u32 changed_bits);
|
||||
|
||||
// Display resolution
|
||||
// Window resolution (display resolution if fullscreen)
|
||||
int GetBackbufferWidth() const { return m_backbuffer_width; }
|
||||
int GetBackbufferHeight() const { return m_backbuffer_height; }
|
||||
float GetBackbufferScale() const { return m_backbuffer_scale; }
|
||||
u32 AutoIntegralScale() const;
|
||||
AbstractTextureFormat GetBackbufferFormat() const { return m_backbuffer_format; }
|
||||
void SetWindowSize(int width, int height);
|
||||
void SetSuggestedWindowSize(int width, int height);
|
||||
void SetBackbuffer(int backbuffer_width, int backbuffer_height);
|
||||
void SetBackbuffer(SurfaceInfo info);
|
||||
void OnBackbufferSet(bool size_changed, bool is_first_set);
|
||||
|
||||
void UpdateDrawRectangle();
|
||||
|
||||
// Returns the target aspect ratio the XFB output should be drawn with.
|
||||
float CalculateDrawAspectRatio(bool allow_stretch = true) const;
|
||||
|
||||
// Crops the target rectangle to the framebuffer dimensions, reducing the size of the source
|
||||
|
@ -103,6 +105,8 @@ private:
|
|||
|
||||
void ProcessFrameDumping(u64 ticks) const;
|
||||
|
||||
void OnBackBufferSizeChanged();
|
||||
|
||||
std::tuple<int, int> CalculateOutputDimensions(int width, int height,
|
||||
bool allow_stretch = true) const;
|
||||
std::tuple<float, float> ApplyStandardAspectCrop(float width, float height,
|
||||
|
@ -126,15 +130,20 @@ private:
|
|||
Common::Flag m_surface_changed;
|
||||
Common::Flag m_surface_resized;
|
||||
|
||||
// The presentation rectangle.
|
||||
// Width and height correspond to the final output resolution.
|
||||
// Offsets imply black borders (if the window aspect ratio doesn't match the game's one).
|
||||
MathUtil::Rectangle<int> m_target_rectangle = {};
|
||||
|
||||
u32 m_auto_resolution_scale = 1;
|
||||
|
||||
RcTcacheEntry m_xfb_entry;
|
||||
MathUtil::Rectangle<int> m_xfb_rect;
|
||||
|
||||
// Tracking of XFB textures so we don't render duplicate frames.
|
||||
u64 m_last_xfb_id = std::numeric_limits<u64>::max();
|
||||
|
||||
// These will be set on the first call to SetWindowSize.
|
||||
// These will be set on the first call to SetSuggestedWindowSize.
|
||||
int m_last_window_request_width = 0;
|
||||
int m_last_window_request_height = 0;
|
||||
|
||||
|
|
|
@ -120,47 +120,45 @@ void BlendingState::Generate(const BPMemory& bp)
|
|||
const bool dstalpha = bp.dstalpha.enable && alphaupdate;
|
||||
usedualsrc = true;
|
||||
|
||||
// The subtract bit has the highest priority
|
||||
if (bp.blendmode.subtract)
|
||||
if (bp.blendmode.blendenable)
|
||||
{
|
||||
blendenable = true;
|
||||
subtractAlpha = subtract = true;
|
||||
srcfactoralpha = srcfactor = SrcBlendFactor::One;
|
||||
dstfactoralpha = dstfactor = DstBlendFactor::One;
|
||||
|
||||
if (dstalpha)
|
||||
if (bp.blendmode.subtract)
|
||||
{
|
||||
subtractAlpha = false;
|
||||
srcfactoralpha = SrcBlendFactor::One;
|
||||
dstfactoralpha = DstBlendFactor::Zero;
|
||||
blendenable = true;
|
||||
subtractAlpha = subtract = true;
|
||||
srcfactoralpha = srcfactor = SrcBlendFactor::One;
|
||||
dstfactoralpha = dstfactor = DstBlendFactor::One;
|
||||
|
||||
if (dstalpha)
|
||||
{
|
||||
subtractAlpha = false;
|
||||
srcfactoralpha = SrcBlendFactor::One;
|
||||
dstfactoralpha = DstBlendFactor::Zero;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
blendenable = true;
|
||||
srcfactor = bp.blendmode.srcfactor;
|
||||
dstfactor = bp.blendmode.dstfactor;
|
||||
if (!target_has_alpha)
|
||||
{
|
||||
// uses ONE instead of DSTALPHA
|
||||
srcfactor = RemoveDstAlphaUsage(srcfactor);
|
||||
dstfactor = RemoveDstAlphaUsage(dstfactor);
|
||||
}
|
||||
// replaces SrcClr with SrcAlpha and DstClr with DstAlpha, it is important to
|
||||
// use the dst function for the src factor and vice versa
|
||||
srcfactoralpha = RemoveDstColorUsage(srcfactor);
|
||||
dstfactoralpha = RemoveSrcColorUsage(dstfactor);
|
||||
|
||||
if (dstalpha)
|
||||
{
|
||||
srcfactoralpha = SrcBlendFactor::One;
|
||||
dstfactoralpha = DstBlendFactor::Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The blendenable bit has the middle priority
|
||||
else if (bp.blendmode.blendenable)
|
||||
{
|
||||
blendenable = true;
|
||||
srcfactor = bp.blendmode.srcfactor;
|
||||
dstfactor = bp.blendmode.dstfactor;
|
||||
if (!target_has_alpha)
|
||||
{
|
||||
// uses ONE instead of DSTALPHA
|
||||
srcfactor = RemoveDstAlphaUsage(srcfactor);
|
||||
dstfactor = RemoveDstAlphaUsage(dstfactor);
|
||||
}
|
||||
// replaces SrcClr with SrcAlpha and DstClr with DstAlpha, it is important to
|
||||
// use the dst function for the src factor and vice versa
|
||||
srcfactoralpha = RemoveDstColorUsage(srcfactor);
|
||||
dstfactoralpha = RemoveSrcColorUsage(dstfactor);
|
||||
|
||||
if (dstalpha)
|
||||
{
|
||||
srcfactoralpha = SrcBlendFactor::One;
|
||||
dstfactoralpha = DstBlendFactor::Zero;
|
||||
}
|
||||
}
|
||||
|
||||
// The logicop bit has the lowest priority
|
||||
else if (bp.blendmode.logicopenable)
|
||||
{
|
||||
if (bp.blendmode.logicmode == LogicOp::NoOp)
|
||||
|
|
|
@ -334,6 +334,7 @@ constexpr std::string_view CUSTOM_PIXELSHADER_COLOR_FUNC = "customShaderColor";
|
|||
struct CustomPixelShader
|
||||
{
|
||||
std::string custom_shader;
|
||||
std::string material_uniform_block;
|
||||
|
||||
bool operator==(const CustomPixelShader& other) const = default;
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#if defined(_M_X86) || defined(_M_X86_64)
|
||||
#if defined(_M_X86_64)
|
||||
#include <pmmintrin.h>
|
||||
#endif
|
||||
|
||||
|
@ -52,6 +52,7 @@
|
|||
#include "VideoCommon/TextureConversionShader.h"
|
||||
#include "VideoCommon/TextureConverterShaderGen.h"
|
||||
#include "VideoCommon/TextureDecoder.h"
|
||||
#include "VideoCommon/TextureUtils.h"
|
||||
#include "VideoCommon/VertexManagerBase.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
@ -414,7 +415,8 @@ void TextureCacheBase::ScaleTextureCacheEntryTo(RcTcacheEntry& entry, u32 new_wi
|
|||
}
|
||||
|
||||
const TextureConfig newconfig(new_width, new_height, 1, entry->GetNumLayers(), 1,
|
||||
AbstractTextureFormat::RGBA8, AbstractTextureFlag_RenderTarget);
|
||||
AbstractTextureFormat::RGBA8, AbstractTextureFlag_RenderTarget,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
std::optional<TexPoolEntry> new_texture = AllocateTexture(newconfig);
|
||||
if (!new_texture)
|
||||
{
|
||||
|
@ -444,7 +446,8 @@ bool TextureCacheBase::CheckReadbackTexture(u32 width, u32 height, AbstractTextu
|
|||
return true;
|
||||
}
|
||||
|
||||
TextureConfig staging_config(std::max(width, 128u), std::max(height, 128u), 1, 1, 1, format, 0);
|
||||
TextureConfig staging_config(std::max(width, 128u), std::max(height, 128u), 1, 1, 1, format, 0,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
m_readback_texture.reset();
|
||||
m_readback_texture = g_gfx->CreateStagingTexture(StagingTextureType::Readback, staging_config);
|
||||
return m_readback_texture != nullptr;
|
||||
|
@ -998,39 +1001,6 @@ RcTcacheEntry TextureCacheBase::DoPartialTextureUpdates(RcTcacheEntry& entry_to_
|
|||
return entry_to_update;
|
||||
}
|
||||
|
||||
void TextureCacheBase::DumpTexture(RcTcacheEntry& entry, std::string basename, unsigned int level,
|
||||
bool is_arbitrary)
|
||||
{
|
||||
std::string szDir = File::GetUserPath(D_DUMPTEXTURES_IDX) + SConfig::GetInstance().GetGameID();
|
||||
|
||||
// make sure that the directory exists
|
||||
if (!File::IsDirectory(szDir))
|
||||
File::CreateDir(szDir);
|
||||
|
||||
if (is_arbitrary)
|
||||
{
|
||||
basename += "_arb";
|
||||
}
|
||||
|
||||
if (level > 0)
|
||||
{
|
||||
if (!g_ActiveConfig.bDumpMipmapTextures)
|
||||
return;
|
||||
basename += fmt::format("_mip{}", level);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!g_ActiveConfig.bDumpBaseTextures)
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string filename = fmt::format("{}/{}.png", szDir, basename);
|
||||
if (File::Exists(filename))
|
||||
return;
|
||||
|
||||
entry->texture->Save(filename, level, Config::Get(Config::GFX_TEXTURE_PNG_COMPRESSION_LEVEL));
|
||||
}
|
||||
|
||||
// Helper for checking if a BPMemory TexMode0 register is set to Point
|
||||
// Filtering modes. This is used to decide whether Anisotropic enhancements
|
||||
// are (mostly) safe in the VideoBackends.
|
||||
|
@ -1606,7 +1576,7 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
|
|||
}
|
||||
|
||||
std::vector<VideoCommon::CachedAsset<VideoCommon::GameTextureAsset>> cached_game_assets;
|
||||
std::vector<std::shared_ptr<VideoCommon::CustomTextureData>> data_for_assets;
|
||||
std::vector<std::shared_ptr<VideoCommon::TextureData>> data_for_assets;
|
||||
bool has_arbitrary_mipmaps = false;
|
||||
bool skip_texture_dump = false;
|
||||
std::shared_ptr<HiresTexture> hires_texture;
|
||||
|
@ -1640,12 +1610,12 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
|
|||
auto data = asset->GetData();
|
||||
if (data)
|
||||
{
|
||||
if (!data->m_slices.empty())
|
||||
if (!data->m_texture.m_slices.empty())
|
||||
{
|
||||
if (!data->m_slices[0].m_levels.empty())
|
||||
if (!data->m_texture.m_slices[0].m_levels.empty())
|
||||
{
|
||||
height = data->m_slices[0].m_levels[0].height;
|
||||
width = data->m_slices[0].m_levels[0].width;
|
||||
height = data->m_texture.m_slices[0].m_levels[0].height;
|
||||
width = data->m_texture.m_slices[0].m_levels[0].width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1660,6 +1630,7 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
|
|||
}
|
||||
}
|
||||
|
||||
data_for_assets.reserve(cached_game_assets.size());
|
||||
for (auto& cached_asset : cached_game_assets)
|
||||
{
|
||||
auto data = cached_asset.m_asset->GetData();
|
||||
|
@ -1667,7 +1638,7 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
|
|||
{
|
||||
if (cached_asset.m_asset->Validate(texture_info.GetRawWidth(), texture_info.GetRawHeight()))
|
||||
{
|
||||
data_for_assets.push_back(std::move(data));
|
||||
data_for_assets.push_back(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1688,7 +1659,7 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
|
|||
RcTcacheEntry TextureCacheBase::CreateTextureEntry(
|
||||
const TextureCreationInfo& creation_info, const TextureInfo& texture_info,
|
||||
const int safety_color_sample_size,
|
||||
std::vector<std::shared_ptr<VideoCommon::CustomTextureData>> assets_data,
|
||||
std::vector<std::shared_ptr<VideoCommon::TextureData>> assets_data,
|
||||
const bool custom_arbitrary_mipmaps, bool skip_texture_dump)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
|
@ -1703,21 +1674,23 @@ RcTcacheEntry TextureCacheBase::CreateTextureEntry(
|
|||
const auto calculate_max_levels = [&]() {
|
||||
const auto max_element = std::max_element(
|
||||
assets_data.begin(), assets_data.end(), [](const auto& lhs, const auto& rhs) {
|
||||
return lhs->m_slices[0].m_levels.size() < rhs->m_slices[0].m_levels.size();
|
||||
return lhs->m_texture.m_slices[0].m_levels.size() <
|
||||
rhs->m_texture.m_slices[0].m_levels.size();
|
||||
});
|
||||
return max_element->get()->m_slices[0].m_levels.size();
|
||||
return (*max_element)->m_texture.m_slices[0].m_levels.size();
|
||||
};
|
||||
const u32 texLevels = no_mips ? 1 : (u32)calculate_max_levels();
|
||||
const auto& first_level = assets_data[0]->m_slices[0].m_levels[0];
|
||||
const auto& first_level = assets_data[0]->m_texture.m_slices[0].m_levels[0];
|
||||
const TextureConfig config(first_level.width, first_level.height, texLevels,
|
||||
static_cast<u32>(assets_data.size()), 1, first_level.format, 0);
|
||||
static_cast<u32>(assets_data.size()), 1, first_level.format, 0,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
entry = AllocateCacheEntry(config);
|
||||
if (!entry) [[unlikely]]
|
||||
return entry;
|
||||
for (u32 data_index = 0; data_index < static_cast<u32>(assets_data.size()); data_index++)
|
||||
{
|
||||
const auto asset = assets_data[data_index];
|
||||
const auto& slice = asset->m_slices[0];
|
||||
const auto& asset = assets_data[data_index];
|
||||
const auto& slice = asset->m_texture.m_slices[0];
|
||||
for (u32 level_index = 0;
|
||||
level_index < std::min(texLevels, static_cast<u32>(slice.m_levels.size()));
|
||||
++level_index)
|
||||
|
@ -1740,7 +1713,8 @@ RcTcacheEntry TextureCacheBase::CreateTextureEntry(
|
|||
const u32 width = texture_info.GetRawWidth();
|
||||
const u32 height = texture_info.GetRawHeight();
|
||||
|
||||
const TextureConfig config(width, height, texLevels, 1, 1, AbstractTextureFormat::RGBA8, 0);
|
||||
const TextureConfig config(width, height, texLevels, 1, 1, AbstractTextureFormat::RGBA8, 0,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
entry = AllocateCacheEntry(config);
|
||||
if (!entry) [[unlikely]]
|
||||
return entry;
|
||||
|
@ -1839,12 +1813,21 @@ RcTcacheEntry TextureCacheBase::CreateTextureEntry(
|
|||
|
||||
entry->has_arbitrary_mips = arbitrary_mip_detector.HasArbitraryMipmaps(dst_buffer);
|
||||
|
||||
if (g_ActiveConfig.bDumpTextures && !skip_texture_dump)
|
||||
if (g_ActiveConfig.bDumpTextures && !skip_texture_dump && texLevels > 0)
|
||||
{
|
||||
const std::string basename = texture_info.CalculateTextureName().GetFullName();
|
||||
for (u32 level = 0; level < texLevels; ++level)
|
||||
if (g_ActiveConfig.bDumpBaseTextures)
|
||||
{
|
||||
DumpTexture(entry, basename, level, entry->has_arbitrary_mips);
|
||||
VideoCommon::TextureUtils::DumpTexture(*entry->texture, basename, 0,
|
||||
entry->has_arbitrary_mips);
|
||||
}
|
||||
if (g_ActiveConfig.bDumpMipmapTextures)
|
||||
{
|
||||
for (u32 level = 1; level < texLevels; ++level)
|
||||
{
|
||||
VideoCommon::TextureUtils::DumpTexture(*entry->texture, basename, level,
|
||||
entry->has_arbitrary_mips);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1917,7 +1900,8 @@ RcTcacheEntry TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height
|
|||
|
||||
// Create a new VRAM texture, and fill it with the data from guest RAM.
|
||||
entry = AllocateCacheEntry(TextureConfig(width, height, 1, 1, 1, AbstractTextureFormat::RGBA8,
|
||||
AbstractTextureFlag_RenderTarget));
|
||||
AbstractTextureFlag_RenderTarget,
|
||||
AbstractTextureType::Texture_2DArray));
|
||||
|
||||
// Compute total texture size. XFB textures aren't tiled, so this is simple.
|
||||
const u32 total_size = height * stride;
|
||||
|
@ -2378,7 +2362,8 @@ void TextureCacheBase::CopyRenderTargetToTexture(
|
|||
{
|
||||
// create the texture
|
||||
const TextureConfig config(scaled_tex_w, scaled_tex_h, 1, g_framebuffer_manager->GetEFBLayers(),
|
||||
1, AbstractTextureFormat::RGBA8, AbstractTextureFlag_RenderTarget);
|
||||
1, AbstractTextureFormat::RGBA8, AbstractTextureFlag_RenderTarget,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
entry = AllocateCacheEntry(config);
|
||||
if (entry)
|
||||
{
|
||||
|
@ -2678,7 +2663,7 @@ void TextureCacheBase::UninitializeXFBMemory(u8* dst, u32 stride, u32 bytes_per_
|
|||
// (Y=1,U=254,V=254) instead of dark green (Y=0,U=0,V=0) in YUV
|
||||
// like is done in the EFB path.
|
||||
|
||||
#if defined(_M_X86) || defined(_M_X86_64)
|
||||
#if defined(_M_X86_64)
|
||||
__m128i sixteenBytes = _mm_set1_epi16((s16)(u16)0xFE01);
|
||||
#endif
|
||||
|
||||
|
@ -2686,7 +2671,7 @@ void TextureCacheBase::UninitializeXFBMemory(u8* dst, u32 stride, u32 bytes_per_
|
|||
{
|
||||
u32 size = bytes_per_row;
|
||||
u8* rowdst = dst;
|
||||
#if defined(_M_X86) || defined(_M_X86_64)
|
||||
#if defined(_M_X86_64)
|
||||
while (size >= 16)
|
||||
{
|
||||
_mm_storeu_si128((__m128i*)rowdst, sixteenBytes);
|
||||
|
@ -2863,7 +2848,8 @@ void TextureCacheBase::ReleaseToPool(TCacheEntry* entry)
|
|||
bool TextureCacheBase::CreateUtilityTextures()
|
||||
{
|
||||
constexpr TextureConfig encoding_texture_config(
|
||||
EFB_WIDTH * 4, 1024, 1, 1, 1, AbstractTextureFormat::BGRA8, AbstractTextureFlag_RenderTarget);
|
||||
EFB_WIDTH * 4, 1024, 1, 1, 1, AbstractTextureFormat::BGRA8, AbstractTextureFlag_RenderTarget,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
m_efb_encoding_texture = g_gfx->CreateTexture(encoding_texture_config, "EFB encoding texture");
|
||||
if (!m_efb_encoding_texture)
|
||||
return false;
|
||||
|
@ -2875,7 +2861,8 @@ bool TextureCacheBase::CreateUtilityTextures()
|
|||
if (g_ActiveConfig.backend_info.bSupportsGPUTextureDecoding)
|
||||
{
|
||||
constexpr TextureConfig decoding_texture_config(
|
||||
1024, 1024, 1, 1, 1, AbstractTextureFormat::RGBA8, AbstractTextureFlag_ComputeImage);
|
||||
1024, 1024, 1, 1, 1, AbstractTextureFormat::RGBA8, AbstractTextureFlag_ComputeImage,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
m_decoding_texture =
|
||||
g_gfx->CreateTexture(decoding_texture_config, "GPU texture decoding texture");
|
||||
if (!m_decoding_texture)
|
||||
|
|
|
@ -349,7 +349,7 @@ private:
|
|||
RcTcacheEntry
|
||||
CreateTextureEntry(const TextureCreationInfo& creation_info, const TextureInfo& texture_info,
|
||||
int safety_color_sample_size,
|
||||
std::vector<std::shared_ptr<VideoCommon::CustomTextureData>> assets_data,
|
||||
std::vector<std::shared_ptr<VideoCommon::TextureData>> assets_data,
|
||||
bool custom_arbitrary_mipmaps, bool skip_texture_dump);
|
||||
|
||||
RcTcacheEntry GetXFBFromCache(u32 address, u32 width, u32 height, u32 stride);
|
||||
|
@ -362,8 +362,6 @@ private:
|
|||
TLUTFormat tlutfmt);
|
||||
void StitchXFBCopy(RcTcacheEntry& entry_to_update);
|
||||
|
||||
void DumpTexture(RcTcacheEntry& entry, std::string basename, unsigned int level,
|
||||
bool is_arbitrary);
|
||||
void CheckTempSize(size_t required_size);
|
||||
|
||||
RcTcacheEntry AllocateCacheEntry(const TextureConfig& config);
|
||||
|
|
|
@ -39,16 +39,22 @@ enum AbstractTextureFlag : u32
|
|||
{
|
||||
AbstractTextureFlag_RenderTarget = (1 << 0), // Texture is used as a framebuffer.
|
||||
AbstractTextureFlag_ComputeImage = (1 << 1), // Texture is used as a compute image.
|
||||
AbstractTextureFlag_CubeMap = (1 << 2), // Texture is used as a cube map.
|
||||
};
|
||||
|
||||
enum class AbstractTextureType
|
||||
{
|
||||
Texture_2DArray, // Used as a 2D texture array
|
||||
Texture_2D, // Used as a normal 2D texture
|
||||
Texture_CubeMap, // Used as a cube map texture
|
||||
};
|
||||
|
||||
struct TextureConfig
|
||||
{
|
||||
constexpr TextureConfig() = default;
|
||||
constexpr TextureConfig(u32 width_, u32 height_, u32 levels_, u32 layers_, u32 samples_,
|
||||
AbstractTextureFormat format_, u32 flags_)
|
||||
AbstractTextureFormat format_, u32 flags_, AbstractTextureType type_)
|
||||
: width(width_), height(height_), levels(levels_), layers(layers_), samples(samples_),
|
||||
format(format_), flags(flags_)
|
||||
format(format_), flags(flags_), type(type_)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -62,7 +68,6 @@ struct TextureConfig
|
|||
bool IsMultisampled() const { return samples > 1; }
|
||||
bool IsRenderTarget() const { return (flags & AbstractTextureFlag_RenderTarget) != 0; }
|
||||
bool IsComputeImage() const { return (flags & AbstractTextureFlag_ComputeImage) != 0; }
|
||||
bool IsCubeMap() const { return (flags & AbstractTextureFlag_CubeMap) != 0; }
|
||||
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
|
@ -71,6 +76,7 @@ struct TextureConfig
|
|||
u32 samples = 1;
|
||||
AbstractTextureFormat format = AbstractTextureFormat::RGBA8;
|
||||
u32 flags = 0;
|
||||
AbstractTextureType type = AbstractTextureType::Texture_2DArray;
|
||||
};
|
||||
|
||||
namespace std
|
||||
|
|
41
Source/Core/VideoCommon/TextureUtils.cpp
Normal file
41
Source/Core/VideoCommon/TextureUtils.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2023 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/TextureUtils.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Core/Config/GraphicsSettings.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
|
||||
#include "VideoCommon/AbstractTexture.h"
|
||||
|
||||
namespace VideoCommon::TextureUtils
|
||||
{
|
||||
void DumpTexture(const ::AbstractTexture& texture, std::string basename, u32 level,
|
||||
bool is_arbitrary)
|
||||
{
|
||||
std::string szDir = File::GetUserPath(D_DUMPTEXTURES_IDX) + SConfig::GetInstance().GetGameID();
|
||||
|
||||
// make sure that the directory exists
|
||||
if (!File::IsDirectory(szDir))
|
||||
File::CreateDir(szDir);
|
||||
|
||||
if (is_arbitrary)
|
||||
{
|
||||
basename += "_arb";
|
||||
}
|
||||
|
||||
if (level > 0)
|
||||
{
|
||||
basename += fmt::format("_mip{}", level);
|
||||
}
|
||||
|
||||
const std::string filename = fmt::format("{}/{}.png", szDir, basename);
|
||||
if (File::Exists(filename))
|
||||
return;
|
||||
|
||||
texture.Save(filename, level, Config::Get(Config::GFX_TEXTURE_PNG_COMPRESSION_LEVEL));
|
||||
}
|
||||
} // namespace VideoCommon::TextureUtils
|
16
Source/Core/VideoCommon/TextureUtils.h
Normal file
16
Source/Core/VideoCommon/TextureUtils.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2023 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class AbstractTexture;
|
||||
|
||||
namespace VideoCommon::TextureUtils
|
||||
{
|
||||
void DumpTexture(const ::AbstractTexture& texture, std::string basename, u32 level,
|
||||
bool is_arbitrary);
|
||||
}
|
|
@ -330,7 +330,7 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
|
|||
|
||||
out.Write("// {}\n", *uid_data);
|
||||
WriteBitfieldExtractHeader(out, api_type, host_config);
|
||||
WritePixelShaderCommonHeader(out, api_type, host_config, bounding_box);
|
||||
WritePixelShaderCommonHeader(out, api_type, host_config, bounding_box, custom_details);
|
||||
WriteCustomShaderStructDef(&out, numTexgen);
|
||||
for (std::size_t i = 0; i < custom_details.shaders.size(); i++)
|
||||
{
|
||||
|
|
|
@ -52,7 +52,7 @@ ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config
|
|||
|
||||
if (vertex_loader)
|
||||
{
|
||||
out.Write("UBO_BINDING(std140, 3) uniform GSBlock {{\n");
|
||||
out.Write("UBO_BINDING(std140, 4) uniform GSBlock {{\n");
|
||||
out.Write("{}", s_geometry_shader_uniforms);
|
||||
out.Write("}};\n");
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ SSBO_BINDING(1) readonly restrict buffer Vertices {{
|
|||
// D3D12 uses a root constant for this uniform, since it changes with every draw.
|
||||
// D3D11 doesn't currently support dynamic vertex loader, and we'll have to figure something
|
||||
// out for it if we want to support it in the future.
|
||||
out.Write("UBO_BINDING(std140, 4) uniform DX_Constants {{\n"
|
||||
out.Write("UBO_BINDING(std140, 5) uniform DX_Constants {{\n"
|
||||
" uint base_vertex;\n"
|
||||
"}};\n\n"
|
||||
"uint GetVertexBaseOffset(uint vertex_id) {{\n"
|
||||
|
@ -101,7 +101,7 @@ SSBO_BINDING(1) readonly restrict buffer Vertices {{
|
|||
out.Write(R"(
|
||||
uint4 load_input_uint4_ubyte4(uint vtx_offset, uint attr_offset) {{
|
||||
uint value = vertex_buffer[vtx_offset + attr_offset];
|
||||
return uint4(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, value >> 24);
|
||||
return uint4(value & 0xffu, (value >> 8) & 0xffu, (value >> 16) & 0xffu, value >> 24);
|
||||
}}
|
||||
|
||||
float4 load_input_float4_ubyte4(uint vtx_offset, uint attr_offset) {{
|
||||
|
|
|
@ -77,9 +77,6 @@ VertexLoader::VertexLoader(const TVtxDesc& vtx_desc, const VAT& vtx_attr)
|
|||
|
||||
void VertexLoader::CompileVertexTranslator()
|
||||
{
|
||||
// Reset pipeline
|
||||
m_numPipelineStages = 0;
|
||||
|
||||
// Position in pc vertex format.
|
||||
int nat_offset = 0;
|
||||
|
||||
|
@ -149,9 +146,16 @@ void VertexLoader::CompileVertexTranslator()
|
|||
VertexLoader_Color::GetFunction(m_VtxDesc.low.Color[i], m_VtxAttr.GetColorFormat(i));
|
||||
|
||||
if (pFunc != nullptr)
|
||||
{
|
||||
WriteCall(pFunc);
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(m_VtxDesc.low.Color[i] == VertexComponentFormat::NotPresent);
|
||||
// Keep colIndex in sync if color 0 is absent but color 1 is present
|
||||
if (i == 0 && m_VtxDesc.low.Color[1] != VertexComponentFormat::NotPresent)
|
||||
WriteCall(VertexLoader_Color::GetDummyFunction());
|
||||
}
|
||||
|
||||
if (m_VtxDesc.low.Color[i] != VertexComponentFormat::NotPresent)
|
||||
{
|
||||
|
@ -213,12 +217,13 @@ void VertexLoader::CompileVertexTranslator()
|
|||
{
|
||||
// if there's more tex coords later, have to write a dummy call
|
||||
bool has_more = false;
|
||||
for (size_t j = 0; j < m_VtxDesc.high.TexCoord.Size(); ++j)
|
||||
for (size_t j = i + 1; j < m_VtxDesc.high.TexCoord.Size(); ++j)
|
||||
{
|
||||
if (m_VtxDesc.high.TexCoord[j] != VertexComponentFormat::NotPresent)
|
||||
{
|
||||
has_more = true;
|
||||
WriteCall(VertexLoader_TextCoord::GetDummyFunction()); // important to get indices right!
|
||||
// Keep tcIndex in sync so that the correct array is used later
|
||||
WriteCall(VertexLoader_TextCoord::GetDummyFunction());
|
||||
break;
|
||||
}
|
||||
else if (m_VtxDesc.low.TexMatIdx[j])
|
||||
|
@ -245,7 +250,7 @@ void VertexLoader::CompileVertexTranslator()
|
|||
|
||||
void VertexLoader::WriteCall(TPipelineFunction func)
|
||||
{
|
||||
m_PipelineStages[m_numPipelineStages++] = func;
|
||||
m_PipelineStages.push_back(func);
|
||||
}
|
||||
|
||||
int VertexLoader::RunVertices(const u8* src, u8* dst, int count)
|
||||
|
@ -261,8 +266,8 @@ int VertexLoader::RunVertices(const u8* src, u8* dst, int count)
|
|||
m_tcIndex = 0;
|
||||
m_colIndex = 0;
|
||||
m_texmtxwrite = m_texmtxread = 0;
|
||||
for (int i = 0; i < m_numPipelineStages; i++)
|
||||
m_PipelineStages[i](this);
|
||||
for (TPipelineFunction& func : m_PipelineStages)
|
||||
func(this);
|
||||
PRIM_LOG("\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/SmallVector.h"
|
||||
#include "VideoCommon/VertexLoaderBase.h"
|
||||
|
||||
class VertexLoader;
|
||||
|
@ -38,8 +39,11 @@ public:
|
|||
|
||||
private:
|
||||
// Pipeline.
|
||||
TPipelineFunction m_PipelineStages[64]; // TODO - figure out real max. it's lower.
|
||||
int m_numPipelineStages;
|
||||
// 1 pos matrix + 8 texture matrices + 1 position + 1 normal or normal/binormal/tangent
|
||||
// + 2 colors + 8 texture coordinates or dummy texture coordinates + 8 texture matrices
|
||||
// merged into texture coordinates + 1 skip gives a maximum of 30
|
||||
// (Tested by VertexLoaderTest.LargeFloatVertexSpeed)
|
||||
Common::SmallVector<TPipelineFunction, 30> m_PipelineStages;
|
||||
|
||||
void CompileVertexTranslator();
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ VertexLoaderARM64::VertexLoaderARM64(const TVtxDesc& vtx_desc, const VAT& vtx_at
|
|||
const Common::ScopedJITPageWriteAndNoExecute enable_jit_page_writes;
|
||||
ClearCodeSpace();
|
||||
GenerateVertexLoader();
|
||||
WriteProtect();
|
||||
WriteProtect(true);
|
||||
}
|
||||
|
||||
// Returns the register to use as the base and an offset from that register.
|
||||
|
@ -82,8 +82,8 @@ std::pair<Arm64Gen::ARM64Reg, u32> VertexLoaderARM64::GetVertexAddr(CPArray arra
|
|||
if (array == CPArray::Position)
|
||||
{
|
||||
EOR(scratch2_reg, scratch1_reg,
|
||||
attribute == VertexComponentFormat::Index8 ? LogicalImm(0xFF, 32) :
|
||||
LogicalImm(0xFFFF, 32));
|
||||
attribute == VertexComponentFormat::Index8 ? LogicalImm(0xFF, GPRSize::B32) :
|
||||
LogicalImm(0xFFFF, GPRSize::B32));
|
||||
m_skip_vertex = CBZ(scratch2_reg);
|
||||
}
|
||||
|
||||
|
@ -202,7 +202,7 @@ void VertexLoaderARM64::ReadColor(VertexComponentFormat attribute, ColorFormat f
|
|||
LDUR(scratch2_reg, reg, offset);
|
||||
|
||||
if (format != ColorFormat::RGBA8888)
|
||||
ORR(scratch2_reg, scratch2_reg, LogicalImm(0xFF000000, 32));
|
||||
ORR(scratch2_reg, scratch2_reg, LogicalImm(0xFF000000, GPRSize::B32));
|
||||
STR(IndexType::Unsigned, scratch2_reg, dst_reg, m_dst_ofs);
|
||||
load_bytes = format == ColorFormat::RGB888 ? 3 : 4;
|
||||
break;
|
||||
|
@ -215,7 +215,7 @@ void VertexLoaderARM64::ReadColor(VertexComponentFormat attribute, ColorFormat f
|
|||
REV16(scratch3_reg, scratch3_reg);
|
||||
|
||||
// B
|
||||
AND(scratch2_reg, scratch3_reg, LogicalImm(0x1F, 32));
|
||||
AND(scratch2_reg, scratch3_reg, LogicalImm(0x1F, GPRSize::B32));
|
||||
ORR(scratch2_reg, ARM64Reg::WSP, scratch2_reg, ArithOption(scratch2_reg, ShiftType::LSL, 3));
|
||||
ORR(scratch2_reg, scratch2_reg, scratch2_reg, ArithOption(scratch2_reg, ShiftType::LSR, 5));
|
||||
ORR(scratch1_reg, ARM64Reg::WSP, scratch2_reg, ArithOption(scratch2_reg, ShiftType::LSL, 16));
|
||||
|
@ -232,7 +232,7 @@ void VertexLoaderARM64::ReadColor(VertexComponentFormat attribute, ColorFormat f
|
|||
ORR(scratch1_reg, scratch1_reg, scratch2_reg, ArithOption(scratch2_reg, ShiftType::LSR, 2));
|
||||
|
||||
// A
|
||||
ORR(scratch1_reg, scratch1_reg, LogicalImm(0xFF000000, 32));
|
||||
ORR(scratch1_reg, scratch1_reg, LogicalImm(0xFF000000, GPRSize::B32));
|
||||
|
||||
STR(IndexType::Unsigned, scratch1_reg, dst_reg, m_dst_ofs);
|
||||
load_bytes = 2;
|
||||
|
@ -248,7 +248,7 @@ void VertexLoaderARM64::ReadColor(VertexComponentFormat attribute, ColorFormat f
|
|||
UBFM(scratch1_reg, scratch3_reg, 4, 7);
|
||||
|
||||
// G
|
||||
AND(scratch2_reg, scratch3_reg, LogicalImm(0xF, 32));
|
||||
AND(scratch2_reg, scratch3_reg, LogicalImm(0xF, GPRSize::B32));
|
||||
ORR(scratch1_reg, scratch1_reg, scratch2_reg, ArithOption(scratch2_reg, ShiftType::LSL, 8));
|
||||
|
||||
// B
|
||||
|
@ -356,7 +356,7 @@ void VertexLoaderARM64::GenerateVertexLoader()
|
|||
if (m_VtxDesc.low.PosMatIdx)
|
||||
{
|
||||
LDRB(IndexType::Unsigned, scratch1_reg, src_reg, m_src_ofs);
|
||||
AND(scratch1_reg, scratch1_reg, LogicalImm(0x3F, 32));
|
||||
AND(scratch1_reg, scratch1_reg, LogicalImm(0x3F, GPRSize::B32));
|
||||
STR(IndexType::Unsigned, scratch1_reg, dst_reg, m_dst_ofs);
|
||||
|
||||
// Z-Freeze
|
||||
|
|
|
@ -49,7 +49,7 @@ VertexLoaderX64::VertexLoaderX64(const TVtxDesc& vtx_desc, const VAT& vtx_att)
|
|||
AllocCodeSpace(4096);
|
||||
ClearCodeSpace();
|
||||
GenerateVertexLoader();
|
||||
WriteProtect();
|
||||
WriteProtect(true);
|
||||
|
||||
Common::JitRegister::Register(region, GetCodePtr(), "VertexLoaderX64\nVtx desc: \n{}\nVAT:\n{}",
|
||||
vtx_desc, vtx_att);
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
void Color_Read_Dummy(VertexLoader* loader)
|
||||
{
|
||||
loader->m_colIndex++;
|
||||
}
|
||||
|
||||
constexpr u32 alpha_mask = 0xFF000000;
|
||||
|
||||
void SetCol(VertexLoader* loader, u32 val)
|
||||
|
@ -201,3 +206,8 @@ TPipelineFunction VertexLoader_Color::GetFunction(VertexComponentFormat type, Co
|
|||
}
|
||||
return s_table_read_color[type][format];
|
||||
}
|
||||
|
||||
TPipelineFunction VertexLoader_Color::GetDummyFunction()
|
||||
{
|
||||
return Color_Read_Dummy;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,9 @@ public:
|
|||
|
||||
static TPipelineFunction GetFunction(VertexComponentFormat type, ColorFormat format);
|
||||
|
||||
// It is important to synchronize colIndex, or else the wrong color array will be used
|
||||
static TPipelineFunction GetDummyFunction();
|
||||
|
||||
private:
|
||||
template <typename T, auto last_member>
|
||||
using EnumMap = typename Common::EnumMap<T, last_member>;
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
static TPipelineFunction GetFunction(VertexComponentFormat type, ComponentFormat format,
|
||||
TexComponentCount elements);
|
||||
|
||||
// It is important to synchronize tcIndex.
|
||||
// It is important to synchronize tcIndex, or else the wrong texture coordinate array will be used
|
||||
static TPipelineFunction GetDummyFunction();
|
||||
|
||||
private:
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "VideoCommon/VideoCommon.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
#include "VideoCommon/XFMemory.h"
|
||||
#include "VideoCommon/XFStateManager.h"
|
||||
|
||||
std::unique_ptr<VertexManagerBase> g_vertex_manager;
|
||||
|
||||
|
@ -90,8 +91,9 @@ static bool IsAnamorphicProjection(const Projection::Raw& projection, const View
|
|||
const VideoConfig& config)
|
||||
{
|
||||
// If ratio between our projection and viewport aspect ratios is similar to 16:9 / 4:3
|
||||
// we have an anamorphic projection. This value can be overridden
|
||||
// by a GameINI.
|
||||
// we have an anamorphic projection. This value can be overridden by a GameINI.
|
||||
// Game cheats that change the aspect ratio to natively unsupported ones
|
||||
// won't be automatically recognized here.
|
||||
|
||||
return std::abs(CalculateProjectionViewportRatio(projection, viewport) -
|
||||
config.widescreen_heuristic_widescreen_ratio) <
|
||||
|
@ -539,6 +541,7 @@ void VertexManagerBase::Flush()
|
|||
auto& pixel_shader_manager = system.GetPixelShaderManager();
|
||||
auto& geometry_shader_manager = system.GetGeometryShaderManager();
|
||||
auto& vertex_shader_manager = system.GetVertexShaderManager();
|
||||
auto& xf_state_manager = system.GetXFStateManager();
|
||||
|
||||
if (g_ActiveConfig.bGraphicMods)
|
||||
{
|
||||
|
@ -578,7 +581,7 @@ void VertexManagerBase::Flush()
|
|||
}
|
||||
}
|
||||
}
|
||||
vertex_shader_manager.SetConstants(texture_names);
|
||||
vertex_shader_manager.SetConstants(texture_names, xf_state_manager);
|
||||
if (!bpmem.genMode.zfreeze)
|
||||
{
|
||||
// Must be done after VertexShaderManager::SetConstants()
|
||||
|
@ -595,12 +598,14 @@ void VertexManagerBase::Flush()
|
|||
CustomPixelShaderContents custom_pixel_shader_contents;
|
||||
std::optional<CustomPixelShader> custom_pixel_shader;
|
||||
std::vector<std::string> custom_pixel_texture_names;
|
||||
for (int i = 0; i < texture_names.size(); i++)
|
||||
std::span<u8> custom_pixel_shader_uniforms;
|
||||
for (size_t i = 0; i < texture_names.size(); i++)
|
||||
{
|
||||
const std::string& texture_name = texture_names[i];
|
||||
const u32 texture_unit = texture_units[i];
|
||||
bool skip = false;
|
||||
GraphicsModActionData::DrawStarted draw_started{texture_unit, &skip, &custom_pixel_shader};
|
||||
GraphicsModActionData::DrawStarted draw_started{texture_unit, &skip, &custom_pixel_shader,
|
||||
&custom_pixel_shader_uniforms};
|
||||
for (const auto& action : g_graphics_mod_manager->GetDrawStartedActions(texture_name))
|
||||
{
|
||||
action->OnDrawStarted(&draw_started);
|
||||
|
@ -644,6 +649,12 @@ void VertexManagerBase::Flush()
|
|||
// Now we can upload uniforms, as nothing else will override them.
|
||||
geometry_shader_manager.SetConstants(m_current_primitive_type);
|
||||
pixel_shader_manager.SetConstants();
|
||||
if (!custom_pixel_shader_uniforms.empty() &&
|
||||
pixel_shader_manager.custom_constants.data() != custom_pixel_shader_uniforms.data())
|
||||
{
|
||||
pixel_shader_manager.custom_constants_dirty = true;
|
||||
}
|
||||
pixel_shader_manager.custom_constants = custom_pixel_shader_uniforms;
|
||||
UploadUniforms();
|
||||
|
||||
// Update the pipeline, or compile one if needed.
|
||||
|
@ -1052,7 +1063,8 @@ void VertexManagerBase::OnEndFrame()
|
|||
return;
|
||||
|
||||
// In order to reduce CPU readback latency, we want to kick a command buffer roughly halfway
|
||||
// between the draw counters that invoked the readback, or every 250 draws, whichever is smaller.
|
||||
// between the draw counters that invoked the readback, or every 250 draws, whichever is
|
||||
// smaller.
|
||||
if (g_ActiveConfig.iCommandBufferExecuteInterval > 0)
|
||||
{
|
||||
u32 last_draw_counter = 0;
|
||||
|
|
|
@ -96,14 +96,14 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
|
|||
|
||||
if (uid_data->vs_expand != VSExpand::None)
|
||||
{
|
||||
out.Write("UBO_BINDING(std140, 3) uniform GSBlock {{\n");
|
||||
out.Write("UBO_BINDING(std140, 4) uniform GSBlock {{\n");
|
||||
out.Write("{}", s_geometry_shader_uniforms);
|
||||
out.Write("}};\n");
|
||||
|
||||
if (api_type == APIType::D3D)
|
||||
{
|
||||
// D3D doesn't include the base vertex in SV_VertexID
|
||||
out.Write("UBO_BINDING(std140, 4) uniform DX_Constants {{\n"
|
||||
out.Write("UBO_BINDING(std140, 5) uniform DX_Constants {{\n"
|
||||
" uint base_vertex;\n"
|
||||
"}};\n\n");
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
|
|||
// Can't use float3, etc because we want 4-byte alignment
|
||||
out.Write(
|
||||
"uint4 unpack_ubyte4(uint value) {{\n"
|
||||
" return uint4(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, value >> 24);\n"
|
||||
" return uint4(value & 0xffu, (value >> 8) & 0xffu, (value >> 16) & 0xffu, value >> 24);\n"
|
||||
"}}\n\n"
|
||||
"struct InputData {{\n");
|
||||
if (uid_data->components & VB_HAS_POSMTXIDX)
|
||||
|
@ -271,7 +271,7 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
|
|||
if (api_type == APIType::D3D)
|
||||
out.Write("uint vertex_id = (gl_VertexID >> 2) + base_vertex;\n");
|
||||
else
|
||||
out.Write("uint vertex_id = gl_VertexID >> 2;\n");
|
||||
out.Write("uint vertex_id = uint(gl_VertexID) >> 2u;\n");
|
||||
out.Write("InputData i = input_buffer[vertex_id];\n"
|
||||
"{}",
|
||||
input_extract.GetBuffer());
|
||||
|
@ -524,9 +524,9 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
|
|||
out.Write("// Line expansion\n"
|
||||
"uint other_id = vertex_id;\n"
|
||||
"if (is_bottom) {{\n"
|
||||
" other_id -= 1;\n"
|
||||
" other_id -= 1u;\n"
|
||||
"}} else {{\n"
|
||||
" other_id += 1;\n"
|
||||
" other_id += 1u;\n"
|
||||
"}}\n"
|
||||
"InputData other = input_buffer[other_id];\n");
|
||||
if (uid_data->position_has_3_elems)
|
||||
|
|
|
@ -30,24 +30,13 @@
|
|||
#include "VideoCommon/VideoCommon.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
#include "VideoCommon/XFMemory.h"
|
||||
#include "VideoCommon/XFStateManager.h"
|
||||
|
||||
void VertexShaderManager::Init()
|
||||
{
|
||||
// Initialize state tracking variables
|
||||
m_minmax_transform_matrices_changed.fill(-1);
|
||||
m_minmax_normal_matrices_changed.fill(-1);
|
||||
m_minmax_post_transform_matrices_changed.fill(-1);
|
||||
m_minmax_lights_changed.fill(-1);
|
||||
m_materials_changed = BitSet32(0);
|
||||
m_tex_matrices_changed.fill(false);
|
||||
m_pos_normal_matrix_changed = false;
|
||||
m_projection_changed = true;
|
||||
m_viewport_changed = false;
|
||||
m_tex_mtx_info_changed = false;
|
||||
m_lighting_config_changed = false;
|
||||
m_projection_graphics_mod_change = false;
|
||||
|
||||
std::memset(static_cast<void*>(&xfmem), 0, sizeof(xfmem));
|
||||
constants = {};
|
||||
|
||||
// TODO: should these go inside ResetView()?
|
||||
|
@ -57,15 +46,6 @@ void VertexShaderManager::Init()
|
|||
dirty = true;
|
||||
}
|
||||
|
||||
void VertexShaderManager::Dirty()
|
||||
{
|
||||
// This function is called after a savestate is loaded.
|
||||
// Any constants that can changed based on settings should be re-calculated
|
||||
m_projection_changed = true;
|
||||
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
Common::Matrix44 VertexShaderManager::LoadProjectionMatrix()
|
||||
{
|
||||
const auto& rawProjection = xfmem.projection.rawProjection;
|
||||
|
@ -147,11 +127,11 @@ Common::Matrix44 VertexShaderManager::LoadProjectionMatrix()
|
|||
return corrected_matrix;
|
||||
}
|
||||
|
||||
void VertexShaderManager::SetProjectionMatrix()
|
||||
void VertexShaderManager::SetProjectionMatrix(XFStateManager& xf_state_manager)
|
||||
{
|
||||
if (m_projection_changed || g_freelook_camera.GetController()->IsDirty())
|
||||
if (xf_state_manager.DidProjectionChange() || g_freelook_camera.GetController()->IsDirty())
|
||||
{
|
||||
m_projection_changed = false;
|
||||
xf_state_manager.ResetProjection();
|
||||
auto corrected_matrix = LoadProjectionMatrix();
|
||||
memcpy(constants.projection.data(), corrected_matrix.data.data(), 4 * sizeof(float4));
|
||||
}
|
||||
|
@ -178,7 +158,8 @@ bool VertexShaderManager::UseVertexDepthRange()
|
|||
|
||||
// Syncs the shader constant buffers with xfmem
|
||||
// TODO: A cleaner way to control the matrices without making a mess in the parameters field
|
||||
void VertexShaderManager::SetConstants(const std::vector<std::string>& textures)
|
||||
void VertexShaderManager::SetConstants(const std::vector<std::string>& textures,
|
||||
XFStateManager& xf_state_manager)
|
||||
{
|
||||
if (constants.missing_color_hex != g_ActiveConfig.iMissingColorValue)
|
||||
{
|
||||
|
@ -192,44 +173,50 @@ void VertexShaderManager::SetConstants(const std::vector<std::string>& textures)
|
|||
dirty = true;
|
||||
}
|
||||
|
||||
if (m_minmax_transform_matrices_changed[0] >= 0)
|
||||
const auto per_vertex_transform_matrix_changes =
|
||||
xf_state_manager.GetPerVertexTransformMatrixChanges();
|
||||
if (per_vertex_transform_matrix_changes[0] >= 0)
|
||||
{
|
||||
int startn = m_minmax_transform_matrices_changed[0] / 4;
|
||||
int endn = (m_minmax_transform_matrices_changed[1] + 3) / 4;
|
||||
int startn = per_vertex_transform_matrix_changes[0] / 4;
|
||||
int endn = (per_vertex_transform_matrix_changes[1] + 3) / 4;
|
||||
memcpy(constants.transformmatrices[startn].data(), &xfmem.posMatrices[startn * 4],
|
||||
(endn - startn) * sizeof(float4));
|
||||
dirty = true;
|
||||
m_minmax_transform_matrices_changed[0] = m_minmax_transform_matrices_changed[1] = -1;
|
||||
xf_state_manager.ResetPerVertexTransformMatrixChanges();
|
||||
}
|
||||
|
||||
if (m_minmax_normal_matrices_changed[0] >= 0)
|
||||
const auto per_vertex_normal_matrices_changed =
|
||||
xf_state_manager.GetPerVertexNormalMatrixChanges();
|
||||
if (per_vertex_normal_matrices_changed[0] >= 0)
|
||||
{
|
||||
int startn = m_minmax_normal_matrices_changed[0] / 3;
|
||||
int endn = (m_minmax_normal_matrices_changed[1] + 2) / 3;
|
||||
int startn = per_vertex_normal_matrices_changed[0] / 3;
|
||||
int endn = (per_vertex_normal_matrices_changed[1] + 2) / 3;
|
||||
for (int i = startn; i < endn; i++)
|
||||
{
|
||||
memcpy(constants.normalmatrices[i].data(), &xfmem.normalMatrices[3 * i], 12);
|
||||
}
|
||||
dirty = true;
|
||||
m_minmax_normal_matrices_changed[0] = m_minmax_normal_matrices_changed[1] = -1;
|
||||
xf_state_manager.ResetPerVertexNormalMatrixChanges();
|
||||
}
|
||||
|
||||
if (m_minmax_post_transform_matrices_changed[0] >= 0)
|
||||
const auto post_transform_matrices_changed = xf_state_manager.GetPostTransformMatrixChanges();
|
||||
if (post_transform_matrices_changed[0] >= 0)
|
||||
{
|
||||
int startn = m_minmax_post_transform_matrices_changed[0] / 4;
|
||||
int endn = (m_minmax_post_transform_matrices_changed[1] + 3) / 4;
|
||||
int startn = post_transform_matrices_changed[0] / 4;
|
||||
int endn = (post_transform_matrices_changed[1] + 3) / 4;
|
||||
memcpy(constants.posttransformmatrices[startn].data(), &xfmem.postMatrices[startn * 4],
|
||||
(endn - startn) * sizeof(float4));
|
||||
dirty = true;
|
||||
m_minmax_post_transform_matrices_changed[0] = m_minmax_post_transform_matrices_changed[1] = -1;
|
||||
xf_state_manager.ResetPostTransformMatrixChanges();
|
||||
}
|
||||
|
||||
if (m_minmax_lights_changed[0] >= 0)
|
||||
const auto light_changes = xf_state_manager.GetLightsChanged();
|
||||
if (light_changes[0] >= 0)
|
||||
{
|
||||
// TODO: Outdated comment
|
||||
// lights don't have a 1 to 1 mapping, the color component needs to be converted to 4 floats
|
||||
int istart = m_minmax_lights_changed[0] / 0x10;
|
||||
int iend = (m_minmax_lights_changed[1] + 15) / 0x10;
|
||||
const int istart = light_changes[0] / 0x10;
|
||||
const int iend = (light_changes[1] + 15) / 0x10;
|
||||
|
||||
for (int i = istart; i < iend; ++i)
|
||||
{
|
||||
|
@ -282,10 +269,10 @@ void VertexShaderManager::SetConstants(const std::vector<std::string>& textures)
|
|||
}
|
||||
dirty = true;
|
||||
|
||||
m_minmax_lights_changed[0] = m_minmax_lights_changed[1] = -1;
|
||||
xf_state_manager.ResetLightsChanged();
|
||||
}
|
||||
|
||||
for (int i : m_materials_changed)
|
||||
for (int i : xf_state_manager.GetMaterialChanges())
|
||||
{
|
||||
u32 data = i >= 2 ? xfmem.matColor[i - 2] : xfmem.ambColor[i];
|
||||
constants.materials[i][0] = (data >> 24) & 0xFF;
|
||||
|
@ -294,12 +281,11 @@ void VertexShaderManager::SetConstants(const std::vector<std::string>& textures)
|
|||
constants.materials[i][3] = data & 0xFF;
|
||||
dirty = true;
|
||||
}
|
||||
m_materials_changed = BitSet32(0);
|
||||
xf_state_manager.ResetMaterialChanges();
|
||||
|
||||
if (m_pos_normal_matrix_changed)
|
||||
if (xf_state_manager.DidPosNormalChange())
|
||||
{
|
||||
m_pos_normal_matrix_changed = false;
|
||||
|
||||
xf_state_manager.ResetPosNormalChange();
|
||||
const float* pos = &xfmem.posMatrices[g_main_cp_state.matrix_index_a.PosNormalMtxIdx * 4];
|
||||
const float* norm =
|
||||
&xfmem.normalMatrices[3 * (g_main_cp_state.matrix_index_a.PosNormalMtxIdx & 31)];
|
||||
|
@ -311,9 +297,9 @@ void VertexShaderManager::SetConstants(const std::vector<std::string>& textures)
|
|||
dirty = true;
|
||||
}
|
||||
|
||||
if (m_tex_matrices_changed[0])
|
||||
if (xf_state_manager.DidTexMatrixAChange())
|
||||
{
|
||||
m_tex_matrices_changed[0] = false;
|
||||
xf_state_manager.ResetTexMatrixAChange();
|
||||
const std::array<const float*, 4> pos_matrix_ptrs{
|
||||
&xfmem.posMatrices[g_main_cp_state.matrix_index_a.Tex0MtxIdx * 4],
|
||||
&xfmem.posMatrices[g_main_cp_state.matrix_index_a.Tex1MtxIdx * 4],
|
||||
|
@ -328,9 +314,9 @@ void VertexShaderManager::SetConstants(const std::vector<std::string>& textures)
|
|||
dirty = true;
|
||||
}
|
||||
|
||||
if (m_tex_matrices_changed[1])
|
||||
if (xf_state_manager.DidTexMatrixBChange())
|
||||
{
|
||||
m_tex_matrices_changed[1] = false;
|
||||
xf_state_manager.ResetTexMatrixBChange();
|
||||
const std::array<const float*, 4> pos_matrix_ptrs{
|
||||
&xfmem.posMatrices[g_main_cp_state.matrix_index_b.Tex4MtxIdx * 4],
|
||||
&xfmem.posMatrices[g_main_cp_state.matrix_index_b.Tex5MtxIdx * 4],
|
||||
|
@ -345,9 +331,9 @@ void VertexShaderManager::SetConstants(const std::vector<std::string>& textures)
|
|||
dirty = true;
|
||||
}
|
||||
|
||||
if (m_viewport_changed)
|
||||
if (xf_state_manager.DidViewportChange())
|
||||
{
|
||||
m_viewport_changed = false;
|
||||
xf_state_manager.ResetViewportChange();
|
||||
|
||||
// The console GPU places the pixel center at 7/12 unless antialiasing
|
||||
// is enabled, while D3D and OpenGL place it at 0.5. See the comment
|
||||
|
@ -421,10 +407,10 @@ void VertexShaderManager::SetConstants(const std::vector<std::string>& textures)
|
|||
}
|
||||
}
|
||||
|
||||
if (m_projection_changed || g_freelook_camera.GetController()->IsDirty() ||
|
||||
if (xf_state_manager.DidProjectionChange() || g_freelook_camera.GetController()->IsDirty() ||
|
||||
!projection_actions.empty() || m_projection_graphics_mod_change)
|
||||
{
|
||||
m_projection_changed = false;
|
||||
xf_state_manager.ResetProjection();
|
||||
m_projection_graphics_mod_change = !projection_actions.empty();
|
||||
|
||||
auto corrected_matrix = LoadProjectionMatrix();
|
||||
|
@ -436,13 +422,12 @@ void VertexShaderManager::SetConstants(const std::vector<std::string>& textures)
|
|||
}
|
||||
|
||||
memcpy(constants.projection.data(), corrected_matrix.data.data(), 4 * sizeof(float4));
|
||||
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (m_tex_mtx_info_changed)
|
||||
if (xf_state_manager.DidTexMatrixInfoChange())
|
||||
{
|
||||
m_tex_mtx_info_changed = false;
|
||||
xf_state_manager.ResetTexMatrixInfoChange();
|
||||
constants.xfmem_dualTexInfo = xfmem.dualTexTrans.enabled;
|
||||
for (size_t i = 0; i < std::size(xfmem.texMtxInfo); i++)
|
||||
constants.xfmem_pack1[i][0] = xfmem.texMtxInfo[i].hex;
|
||||
|
@ -452,9 +437,9 @@ void VertexShaderManager::SetConstants(const std::vector<std::string>& textures)
|
|||
dirty = true;
|
||||
}
|
||||
|
||||
if (m_lighting_config_changed)
|
||||
if (xf_state_manager.DidLightingConfigChange())
|
||||
{
|
||||
m_lighting_config_changed = false;
|
||||
xf_state_manager.ResetLightingConfigChange();
|
||||
|
||||
for (size_t i = 0; i < 2; i++)
|
||||
{
|
||||
|
@ -466,173 +451,6 @@ void VertexShaderManager::SetConstants(const std::vector<std::string>& textures)
|
|||
}
|
||||
}
|
||||
|
||||
void VertexShaderManager::InvalidateXFRange(int start, int end)
|
||||
{
|
||||
if (((u32)start >= (u32)g_main_cp_state.matrix_index_a.PosNormalMtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_a.PosNormalMtxIdx * 4 + 12) ||
|
||||
((u32)start >=
|
||||
XFMEM_NORMALMATRICES + ((u32)g_main_cp_state.matrix_index_a.PosNormalMtxIdx & 31) * 3 &&
|
||||
(u32)start < XFMEM_NORMALMATRICES +
|
||||
((u32)g_main_cp_state.matrix_index_a.PosNormalMtxIdx & 31) * 3 + 9))
|
||||
{
|
||||
m_pos_normal_matrix_changed = true;
|
||||
}
|
||||
|
||||
if (((u32)start >= (u32)g_main_cp_state.matrix_index_a.Tex0MtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_a.Tex0MtxIdx * 4 + 12) ||
|
||||
((u32)start >= (u32)g_main_cp_state.matrix_index_a.Tex1MtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_a.Tex1MtxIdx * 4 + 12) ||
|
||||
((u32)start >= (u32)g_main_cp_state.matrix_index_a.Tex2MtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_a.Tex2MtxIdx * 4 + 12) ||
|
||||
((u32)start >= (u32)g_main_cp_state.matrix_index_a.Tex3MtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_a.Tex3MtxIdx * 4 + 12))
|
||||
{
|
||||
m_tex_matrices_changed[0] = true;
|
||||
}
|
||||
|
||||
if (((u32)start >= (u32)g_main_cp_state.matrix_index_b.Tex4MtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_b.Tex4MtxIdx * 4 + 12) ||
|
||||
((u32)start >= (u32)g_main_cp_state.matrix_index_b.Tex5MtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_b.Tex5MtxIdx * 4 + 12) ||
|
||||
((u32)start >= (u32)g_main_cp_state.matrix_index_b.Tex6MtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_b.Tex6MtxIdx * 4 + 12) ||
|
||||
((u32)start >= (u32)g_main_cp_state.matrix_index_b.Tex7MtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_b.Tex7MtxIdx * 4 + 12))
|
||||
{
|
||||
m_tex_matrices_changed[1] = true;
|
||||
}
|
||||
|
||||
if (start < XFMEM_POSMATRICES_END)
|
||||
{
|
||||
if (m_minmax_transform_matrices_changed[0] == -1)
|
||||
{
|
||||
m_minmax_transform_matrices_changed[0] = start;
|
||||
m_minmax_transform_matrices_changed[1] =
|
||||
end > XFMEM_POSMATRICES_END ? XFMEM_POSMATRICES_END : end;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_minmax_transform_matrices_changed[0] > start)
|
||||
m_minmax_transform_matrices_changed[0] = start;
|
||||
|
||||
if (m_minmax_transform_matrices_changed[1] < end)
|
||||
m_minmax_transform_matrices_changed[1] =
|
||||
end > XFMEM_POSMATRICES_END ? XFMEM_POSMATRICES_END : end;
|
||||
}
|
||||
}
|
||||
|
||||
if (start < XFMEM_NORMALMATRICES_END && end > XFMEM_NORMALMATRICES)
|
||||
{
|
||||
int _start = start < XFMEM_NORMALMATRICES ? 0 : start - XFMEM_NORMALMATRICES;
|
||||
int _end = end < XFMEM_NORMALMATRICES_END ? end - XFMEM_NORMALMATRICES :
|
||||
XFMEM_NORMALMATRICES_END - XFMEM_NORMALMATRICES;
|
||||
|
||||
if (m_minmax_normal_matrices_changed[0] == -1)
|
||||
{
|
||||
m_minmax_normal_matrices_changed[0] = _start;
|
||||
m_minmax_normal_matrices_changed[1] = _end;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_minmax_normal_matrices_changed[0] > _start)
|
||||
m_minmax_normal_matrices_changed[0] = _start;
|
||||
|
||||
if (m_minmax_normal_matrices_changed[1] < _end)
|
||||
m_minmax_normal_matrices_changed[1] = _end;
|
||||
}
|
||||
}
|
||||
|
||||
if (start < XFMEM_POSTMATRICES_END && end > XFMEM_POSTMATRICES)
|
||||
{
|
||||
int _start = start < XFMEM_POSTMATRICES ? XFMEM_POSTMATRICES : start - XFMEM_POSTMATRICES;
|
||||
int _end = end < XFMEM_POSTMATRICES_END ? end - XFMEM_POSTMATRICES :
|
||||
XFMEM_POSTMATRICES_END - XFMEM_POSTMATRICES;
|
||||
|
||||
if (m_minmax_post_transform_matrices_changed[0] == -1)
|
||||
{
|
||||
m_minmax_post_transform_matrices_changed[0] = _start;
|
||||
m_minmax_post_transform_matrices_changed[1] = _end;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_minmax_post_transform_matrices_changed[0] > _start)
|
||||
m_minmax_post_transform_matrices_changed[0] = _start;
|
||||
|
||||
if (m_minmax_post_transform_matrices_changed[1] < _end)
|
||||
m_minmax_post_transform_matrices_changed[1] = _end;
|
||||
}
|
||||
}
|
||||
|
||||
if (start < XFMEM_LIGHTS_END && end > XFMEM_LIGHTS)
|
||||
{
|
||||
int _start = start < XFMEM_LIGHTS ? XFMEM_LIGHTS : start - XFMEM_LIGHTS;
|
||||
int _end = end < XFMEM_LIGHTS_END ? end - XFMEM_LIGHTS : XFMEM_LIGHTS_END - XFMEM_LIGHTS;
|
||||
|
||||
if (m_minmax_lights_changed[0] == -1)
|
||||
{
|
||||
m_minmax_lights_changed[0] = _start;
|
||||
m_minmax_lights_changed[1] = _end;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_minmax_lights_changed[0] > _start)
|
||||
m_minmax_lights_changed[0] = _start;
|
||||
|
||||
if (m_minmax_lights_changed[1] < _end)
|
||||
m_minmax_lights_changed[1] = _end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VertexShaderManager::SetTexMatrixChangedA(u32 Value)
|
||||
{
|
||||
if (g_main_cp_state.matrix_index_a.Hex != Value)
|
||||
{
|
||||
g_vertex_manager->Flush();
|
||||
if (g_main_cp_state.matrix_index_a.PosNormalMtxIdx != (Value & 0x3f))
|
||||
m_pos_normal_matrix_changed = true;
|
||||
m_tex_matrices_changed[0] = true;
|
||||
g_main_cp_state.matrix_index_a.Hex = Value;
|
||||
}
|
||||
}
|
||||
|
||||
void VertexShaderManager::SetTexMatrixChangedB(u32 Value)
|
||||
{
|
||||
if (g_main_cp_state.matrix_index_b.Hex != Value)
|
||||
{
|
||||
g_vertex_manager->Flush();
|
||||
m_tex_matrices_changed[1] = true;
|
||||
g_main_cp_state.matrix_index_b.Hex = Value;
|
||||
}
|
||||
}
|
||||
|
||||
void VertexShaderManager::SetViewportChanged()
|
||||
{
|
||||
m_viewport_changed = true;
|
||||
}
|
||||
|
||||
void VertexShaderManager::SetProjectionChanged()
|
||||
{
|
||||
m_projection_changed = true;
|
||||
}
|
||||
|
||||
void VertexShaderManager::SetMaterialColorChanged(int index)
|
||||
{
|
||||
m_materials_changed[index] = true;
|
||||
}
|
||||
|
||||
void VertexShaderManager::SetTexMatrixInfoChanged(int index)
|
||||
{
|
||||
// TODO: Should we track this with more precision, like which indices changed?
|
||||
// The whole vertex constants are probably going to be uploaded regardless.
|
||||
m_tex_mtx_info_changed = true;
|
||||
}
|
||||
|
||||
void VertexShaderManager::SetLightingConfigChanged()
|
||||
{
|
||||
m_lighting_config_changed = true;
|
||||
}
|
||||
|
||||
void VertexShaderManager::TransformToClipSpace(const float* data, float* out, u32 MtxIdx)
|
||||
{
|
||||
const float* world_matrix = &xfmem.posMatrices[(MtxIdx & 0x3f) * 4];
|
||||
|
@ -662,23 +480,10 @@ void VertexShaderManager::DoState(PointerWrap& p)
|
|||
p.Do(m_viewport_correction);
|
||||
g_freelook_camera.DoState(p);
|
||||
|
||||
p.DoArray(m_minmax_transform_matrices_changed);
|
||||
p.DoArray(m_minmax_normal_matrices_changed);
|
||||
p.DoArray(m_minmax_post_transform_matrices_changed);
|
||||
p.DoArray(m_minmax_lights_changed);
|
||||
|
||||
p.Do(m_materials_changed);
|
||||
p.DoArray(m_tex_matrices_changed);
|
||||
p.Do(m_pos_normal_matrix_changed);
|
||||
p.Do(m_projection_changed);
|
||||
p.Do(m_viewport_changed);
|
||||
p.Do(m_tex_mtx_info_changed);
|
||||
p.Do(m_lighting_config_changed);
|
||||
|
||||
p.Do(constants);
|
||||
|
||||
if (p.IsReadMode())
|
||||
{
|
||||
Dirty();
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,28 +15,18 @@
|
|||
|
||||
class PointerWrap;
|
||||
struct PortableVertexDeclaration;
|
||||
class XFStateManager;
|
||||
|
||||
// The non-API dependent parts.
|
||||
class alignas(16) VertexShaderManager
|
||||
{
|
||||
public:
|
||||
void Init();
|
||||
void Dirty();
|
||||
void DoState(PointerWrap& p);
|
||||
|
||||
// constant management
|
||||
void SetProjectionMatrix();
|
||||
void SetConstants(const std::vector<std::string>& textures);
|
||||
|
||||
void InvalidateXFRange(int start, int end);
|
||||
void SetTexMatrixChangedA(u32 value);
|
||||
void SetTexMatrixChangedB(u32 value);
|
||||
void SetViewportChanged();
|
||||
void SetProjectionChanged();
|
||||
void SetMaterialColorChanged(int index);
|
||||
|
||||
void SetTexMatrixInfoChanged(int index);
|
||||
void SetLightingConfigChanged();
|
||||
void SetProjectionMatrix(XFStateManager& xf_state_manager);
|
||||
void SetConstants(const std::vector<std::string>& textures, XFStateManager& xf_state_manager);
|
||||
|
||||
// data: 3 floats representing the X, Y and Z vertex model coordinates and the posmatrix index.
|
||||
// out: 4 floats which will be initialized with the corresponding clip space coordinates
|
||||
|
@ -92,18 +82,7 @@ private:
|
|||
alignas(16) std::array<float, 16> m_projection_matrix;
|
||||
|
||||
// track changes
|
||||
std::array<bool, 2> m_tex_matrices_changed{};
|
||||
bool m_pos_normal_matrix_changed = false;
|
||||
bool m_projection_changed = false;
|
||||
bool m_viewport_changed = false;
|
||||
bool m_tex_mtx_info_changed = false;
|
||||
bool m_lighting_config_changed = false;
|
||||
bool m_projection_graphics_mod_change = false;
|
||||
BitSet32 m_materials_changed;
|
||||
std::array<int, 2> m_minmax_transform_matrices_changed{};
|
||||
std::array<int, 2> m_minmax_normal_matrices_changed{};
|
||||
std::array<int, 2> m_minmax_post_transform_matrices_changed{};
|
||||
std::array<int, 2> m_minmax_lights_changed{};
|
||||
|
||||
Common::Matrix44 m_viewport_correction{};
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
#include "VideoCommon/VideoConfig.h"
|
||||
#include "VideoCommon/VideoState.h"
|
||||
#include "VideoCommon/Widescreen.h"
|
||||
#include "VideoCommon/XFStateManager.h"
|
||||
|
||||
VideoBackendBase* g_video_backend = nullptr;
|
||||
|
||||
|
@ -92,7 +93,7 @@ std::string VideoBackendBase::BadShaderFilename(const char* shader_stage, int co
|
|||
void VideoBackendBase::Video_ExitLoop()
|
||||
{
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.GetFifo().ExitGpuLoop(system);
|
||||
system.GetFifo().ExitGpuLoop();
|
||||
}
|
||||
|
||||
// Run from the CPU thread (from VideoInterface.cpp)
|
||||
|
@ -377,14 +378,15 @@ bool VideoBackendBase::InitializeShared(std::unique_ptr<AbstractGfx> gfx,
|
|||
|
||||
auto& system = Core::System::GetInstance();
|
||||
auto& command_processor = system.GetCommandProcessor();
|
||||
command_processor.Init(system);
|
||||
system.GetFifo().Init(system);
|
||||
system.GetPixelEngine().Init(system);
|
||||
command_processor.Init();
|
||||
system.GetFifo().Init();
|
||||
system.GetPixelEngine().Init();
|
||||
BPInit();
|
||||
VertexLoaderManager::Init();
|
||||
system.GetVertexShaderManager().Init();
|
||||
system.GetGeometryShaderManager().Init();
|
||||
system.GetPixelShaderManager().Init();
|
||||
system.GetXFStateManager().Init();
|
||||
TMEM::Init();
|
||||
|
||||
g_Config.VerifyValidity();
|
||||
|
|
|
@ -67,13 +67,13 @@ void VideoConfig::Refresh()
|
|||
|
||||
const bool lock_gpu_thread = Core::IsRunningAndStarted();
|
||||
if (lock_gpu_thread)
|
||||
system.GetFifo().PauseAndLock(system, true, false);
|
||||
system.GetFifo().PauseAndLock(true, false);
|
||||
|
||||
g_Config.Refresh();
|
||||
g_Config.VerifyValidity();
|
||||
|
||||
if (lock_gpu_thread)
|
||||
system.GetFifo().PauseAndLock(system, false, true);
|
||||
system.GetFifo().PauseAndLock(false, true);
|
||||
});
|
||||
s_has_registered_callback = true;
|
||||
}
|
||||
|
@ -85,6 +85,8 @@ void VideoConfig::Refresh()
|
|||
|
||||
bWidescreenHack = Config::Get(Config::GFX_WIDESCREEN_HACK);
|
||||
aspect_mode = Config::Get(Config::GFX_ASPECT_RATIO);
|
||||
custom_aspect_width = Config::Get(Config::GFX_CUSTOM_ASPECT_RATIO_WIDTH);
|
||||
custom_aspect_height = Config::Get(Config::GFX_CUSTOM_ASPECT_RATIO_HEIGHT);
|
||||
suggested_aspect_mode = Config::Get(Config::GFX_SUGGESTED_ASPECT_RATIO);
|
||||
widescreen_heuristic_transition_threshold =
|
||||
Config::Get(Config::GFX_WIDESCREEN_HEURISTIC_TRANSITION_THRESHOLD);
|
||||
|
@ -288,6 +290,7 @@ void CheckForConfigChanges()
|
|||
const u32 old_game_mod_changes =
|
||||
g_ActiveConfig.graphics_mod_config ? g_ActiveConfig.graphics_mod_config->GetChangeCount() : 0;
|
||||
const bool old_graphics_mods_enabled = g_ActiveConfig.bGraphicMods;
|
||||
const AspectMode old_aspect_mode = g_ActiveConfig.aspect_mode;
|
||||
const AspectMode old_suggested_aspect_mode = g_ActiveConfig.suggested_aspect_mode;
|
||||
const bool old_widescreen_hack = g_ActiveConfig.bWidescreenHack;
|
||||
const auto old_post_processing_shader = g_ActiveConfig.sPostProcessingShader;
|
||||
|
@ -337,6 +340,8 @@ void CheckForConfigChanges()
|
|||
changed_bits |= CONFIG_CHANGE_BIT_BBOX;
|
||||
if (old_efb_scale != g_ActiveConfig.iEFBScale)
|
||||
changed_bits |= CONFIG_CHANGE_BIT_TARGET_SIZE;
|
||||
if (old_aspect_mode != g_ActiveConfig.aspect_mode)
|
||||
changed_bits |= CONFIG_CHANGE_BIT_ASPECT_RATIO;
|
||||
if (old_suggested_aspect_mode != g_ActiveConfig.suggested_aspect_mode)
|
||||
changed_bits |= CONFIG_CHANGE_BIT_ASPECT_RATIO;
|
||||
if (old_widescreen_hack != g_ActiveConfig.bWidescreenHack)
|
||||
|
@ -370,6 +375,7 @@ void CheckForConfigChanges()
|
|||
if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES))
|
||||
{
|
||||
OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL);
|
||||
g_gfx->WaitForGPUIdle();
|
||||
g_vertex_manager->InvalidatePipelineObject();
|
||||
g_vertex_manager->NotifyCustomShaderCacheOfHostChange(new_host_config);
|
||||
g_shader_cache->SetHostConfig(new_host_config);
|
||||
|
|
|
@ -21,10 +21,11 @@ constexpr int EFB_SCALE_AUTO_INTEGRAL = 0;
|
|||
|
||||
enum class AspectMode : int
|
||||
{
|
||||
Auto,
|
||||
AnalogWide,
|
||||
Analog,
|
||||
Auto, // 4:3 or 16:9
|
||||
ForceWide, // 16:9
|
||||
ForceStandard, // 4:3
|
||||
Stretch,
|
||||
Custom,
|
||||
};
|
||||
|
||||
enum class StereoMode : int
|
||||
|
@ -105,6 +106,8 @@ struct VideoConfig final
|
|||
bool bVSyncActive = false;
|
||||
bool bWidescreenHack = false;
|
||||
AspectMode aspect_mode{};
|
||||
int custom_aspect_width = 1;
|
||||
int custom_aspect_height = 1;
|
||||
AspectMode suggested_aspect_mode{};
|
||||
u32 widescreen_heuristic_transition_threshold = 0;
|
||||
float widescreen_heuristic_aspect_ratio_slop = 0.f;
|
||||
|
@ -142,8 +145,9 @@ struct VideoConfig final
|
|||
float fSDRDisplayCustomGamma = 2.2f;
|
||||
|
||||
// HDR:
|
||||
// 200 is a good default value that matches the brightness of many SDR screens
|
||||
float fHDRPaperWhiteNits = 200.f;
|
||||
// 203 is a good default value that matches the brightness of many SDR screens.
|
||||
// It's also the value recommended by the ITU.
|
||||
float fHDRPaperWhiteNits = 203.f;
|
||||
} color_correction;
|
||||
|
||||
// Information
|
||||
|
@ -365,6 +369,8 @@ struct VideoConfig final
|
|||
bool UsingUberShaders() const;
|
||||
u32 GetShaderCompilerThreads() const;
|
||||
u32 GetShaderPrecompilerThreads() const;
|
||||
|
||||
float GetCustomAspectRatio() const { return (float)custom_aspect_width / custom_aspect_height; }
|
||||
};
|
||||
|
||||
extern VideoConfig g_Config;
|
||||
|
|
|
@ -81,3 +81,6 @@ using BeforePresentEvent = Common::HookableEvent<"BeforePresent", PresentInfo&>;
|
|||
// An event that is triggered after a frame is presented.
|
||||
// The exact timing of this event depends on backend/driver support.
|
||||
using AfterPresentEvent = Common::HookableEvent<"AfterPresent", PresentInfo&>;
|
||||
|
||||
// An end of frame event that runs on the CPU thread
|
||||
using VIEndFieldEvent = Common::HookableEvent<"VIEndField">;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "VideoCommon/VertexShaderManager.h"
|
||||
#include "VideoCommon/Widescreen.h"
|
||||
#include "VideoCommon/XFMemory.h"
|
||||
#include "VideoCommon/XFStateManager.h"
|
||||
|
||||
void VideoCommon_DoState(PointerWrap& p)
|
||||
{
|
||||
|
@ -105,6 +106,9 @@ void VideoCommon_DoState(PointerWrap& p)
|
|||
g_widescreen->DoState(p);
|
||||
p.DoMarker("Widescreen");
|
||||
|
||||
system.GetXFStateManager().DoState(p);
|
||||
p.DoMarker("XFStateManager");
|
||||
|
||||
// Refresh state.
|
||||
if (p.IsReadMode())
|
||||
{
|
||||
|
|
|
@ -36,23 +36,24 @@ void WidescreenManager::Update()
|
|||
m_is_game_widescreen = Config::Get(Config::SYSCONF_WIDESCREEN);
|
||||
|
||||
// suggested_aspect_mode overrides SYSCONF_WIDESCREEN
|
||||
if (g_ActiveConfig.suggested_aspect_mode == AspectMode::Analog)
|
||||
if (g_ActiveConfig.suggested_aspect_mode == AspectMode::ForceStandard)
|
||||
m_is_game_widescreen = false;
|
||||
else if (g_ActiveConfig.suggested_aspect_mode == AspectMode::AnalogWide)
|
||||
else if (g_ActiveConfig.suggested_aspect_mode == AspectMode::ForceWide)
|
||||
m_is_game_widescreen = true;
|
||||
|
||||
// If widescreen hack is disabled override game's AR if UI is set to 4:3 or 16:9.
|
||||
if (!g_ActiveConfig.bWidescreenHack)
|
||||
{
|
||||
const auto aspect_mode = g_ActiveConfig.aspect_mode;
|
||||
if (aspect_mode == AspectMode::Analog)
|
||||
if (aspect_mode == AspectMode::ForceStandard)
|
||||
m_is_game_widescreen = false;
|
||||
else if (aspect_mode == AspectMode::AnalogWide)
|
||||
else if (aspect_mode == AspectMode::ForceWide)
|
||||
m_is_game_widescreen = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Heuristic to detect if a GameCube game is in 16:9 anamorphic widescreen mode.
|
||||
// Cheats that change the game aspect ratio to natively unsupported ones won't be recognized here.
|
||||
void WidescreenManager::UpdateWidescreenHeuristic()
|
||||
{
|
||||
const auto flush_statistics = g_vertex_manager->ResetFlushAspectRatioCount();
|
||||
|
@ -63,9 +64,10 @@ void WidescreenManager::UpdateWidescreenHeuristic()
|
|||
|
||||
Update();
|
||||
|
||||
// If widescreen hack isn't active and aspect_mode (UI) is 4:3 or 16:9 don't use heuristic.
|
||||
if (!g_ActiveConfig.bWidescreenHack && (g_ActiveConfig.aspect_mode == AspectMode::Analog ||
|
||||
g_ActiveConfig.aspect_mode == AspectMode::AnalogWide))
|
||||
// If widescreen hack isn't active and aspect_mode (user setting)
|
||||
// is set to a forced aspect ratio, don't use heuristic.
|
||||
if (!g_ActiveConfig.bWidescreenHack && (g_ActiveConfig.aspect_mode == AspectMode::ForceStandard ||
|
||||
g_ActiveConfig.aspect_mode == AspectMode::ForceWide))
|
||||
return;
|
||||
|
||||
// Modify the threshold based on which aspect ratio we're already using:
|
||||
|
|
|
@ -11,11 +11,14 @@
|
|||
class PointerWrap;
|
||||
|
||||
// This class is responsible for tracking the game's aspect ratio.
|
||||
// This exclusively supports 4:3 or 16:9 detection by default.
|
||||
class WidescreenManager
|
||||
{
|
||||
public:
|
||||
WidescreenManager();
|
||||
|
||||
// Just a helper to tell whether the game seems to be running in widescreen,
|
||||
// or if it's being forced to.
|
||||
bool IsGameWidescreen() const { return m_is_game_widescreen; }
|
||||
|
||||
void DoState(PointerWrap& p);
|
||||
|
|
279
Source/Core/VideoCommon/XFStateManager.cpp
Normal file
279
Source/Core/VideoCommon/XFStateManager.cpp
Normal file
|
@ -0,0 +1,279 @@
|
|||
// Copyright 2023 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/XFStateManager.h"
|
||||
|
||||
#include "Common/ChunkFile.h"
|
||||
|
||||
#include "VideoCommon/FramebufferManager.h"
|
||||
#include "VideoCommon/VertexManagerBase.h"
|
||||
#include "VideoCommon/XFMemory.h"
|
||||
|
||||
void XFStateManager::Init()
|
||||
{
|
||||
// Initialize state tracking variables
|
||||
ResetTexMatrixAChange();
|
||||
ResetTexMatrixBChange();
|
||||
ResetPosNormalChange();
|
||||
ResetProjection();
|
||||
ResetViewportChange();
|
||||
ResetTexMatrixInfoChange();
|
||||
ResetLightingConfigChange();
|
||||
ResetLightsChanged();
|
||||
ResetMaterialChanges();
|
||||
ResetPerVertexTransformMatrixChanges();
|
||||
ResetPerVertexNormalMatrixChanges();
|
||||
ResetPostTransformMatrixChanges();
|
||||
|
||||
std::memset(static_cast<void*>(&xfmem), 0, sizeof(xfmem));
|
||||
}
|
||||
|
||||
void XFStateManager::DoState(PointerWrap& p)
|
||||
{
|
||||
p.DoArray(m_minmax_transform_matrices_changed);
|
||||
p.DoArray(m_minmax_normal_matrices_changed);
|
||||
p.DoArray(m_minmax_post_transform_matrices_changed);
|
||||
p.DoArray(m_minmax_lights_changed);
|
||||
|
||||
p.Do(m_materials_changed);
|
||||
p.DoArray(m_tex_matrices_changed);
|
||||
p.Do(m_pos_normal_matrix_changed);
|
||||
p.Do(m_projection_changed);
|
||||
p.Do(m_viewport_changed);
|
||||
p.Do(m_tex_mtx_info_changed);
|
||||
p.Do(m_lighting_config_changed);
|
||||
|
||||
if (p.IsReadMode())
|
||||
{
|
||||
// This is called after a savestate is loaded.
|
||||
// Any constants that can changed based on settings should be re-calculated
|
||||
m_projection_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void XFStateManager::InvalidateXFRange(int start, int end)
|
||||
{
|
||||
if (((u32)start >= (u32)g_main_cp_state.matrix_index_a.PosNormalMtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_a.PosNormalMtxIdx * 4 + 12) ||
|
||||
((u32)start >=
|
||||
XFMEM_NORMALMATRICES + ((u32)g_main_cp_state.matrix_index_a.PosNormalMtxIdx & 31) * 3 &&
|
||||
(u32)start < XFMEM_NORMALMATRICES +
|
||||
((u32)g_main_cp_state.matrix_index_a.PosNormalMtxIdx & 31) * 3 + 9))
|
||||
{
|
||||
m_pos_normal_matrix_changed = true;
|
||||
}
|
||||
|
||||
if (((u32)start >= (u32)g_main_cp_state.matrix_index_a.Tex0MtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_a.Tex0MtxIdx * 4 + 12) ||
|
||||
((u32)start >= (u32)g_main_cp_state.matrix_index_a.Tex1MtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_a.Tex1MtxIdx * 4 + 12) ||
|
||||
((u32)start >= (u32)g_main_cp_state.matrix_index_a.Tex2MtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_a.Tex2MtxIdx * 4 + 12) ||
|
||||
((u32)start >= (u32)g_main_cp_state.matrix_index_a.Tex3MtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_a.Tex3MtxIdx * 4 + 12))
|
||||
{
|
||||
m_tex_matrices_changed[0] = true;
|
||||
}
|
||||
|
||||
if (((u32)start >= (u32)g_main_cp_state.matrix_index_b.Tex4MtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_b.Tex4MtxIdx * 4 + 12) ||
|
||||
((u32)start >= (u32)g_main_cp_state.matrix_index_b.Tex5MtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_b.Tex5MtxIdx * 4 + 12) ||
|
||||
((u32)start >= (u32)g_main_cp_state.matrix_index_b.Tex6MtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_b.Tex6MtxIdx * 4 + 12) ||
|
||||
((u32)start >= (u32)g_main_cp_state.matrix_index_b.Tex7MtxIdx * 4 &&
|
||||
(u32)start < (u32)g_main_cp_state.matrix_index_b.Tex7MtxIdx * 4 + 12))
|
||||
{
|
||||
m_tex_matrices_changed[1] = true;
|
||||
}
|
||||
|
||||
if (start < XFMEM_POSMATRICES_END)
|
||||
{
|
||||
if (m_minmax_transform_matrices_changed[0] == -1)
|
||||
{
|
||||
m_minmax_transform_matrices_changed[0] = start;
|
||||
m_minmax_transform_matrices_changed[1] =
|
||||
end > XFMEM_POSMATRICES_END ? XFMEM_POSMATRICES_END : end;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_minmax_transform_matrices_changed[0] > start)
|
||||
m_minmax_transform_matrices_changed[0] = start;
|
||||
|
||||
if (m_minmax_transform_matrices_changed[1] < end)
|
||||
m_minmax_transform_matrices_changed[1] =
|
||||
end > XFMEM_POSMATRICES_END ? XFMEM_POSMATRICES_END : end;
|
||||
}
|
||||
}
|
||||
|
||||
if (start < XFMEM_NORMALMATRICES_END && end > XFMEM_NORMALMATRICES)
|
||||
{
|
||||
int _start = start < XFMEM_NORMALMATRICES ? 0 : start - XFMEM_NORMALMATRICES;
|
||||
int _end = end < XFMEM_NORMALMATRICES_END ? end - XFMEM_NORMALMATRICES :
|
||||
XFMEM_NORMALMATRICES_END - XFMEM_NORMALMATRICES;
|
||||
|
||||
if (m_minmax_normal_matrices_changed[0] == -1)
|
||||
{
|
||||
m_minmax_normal_matrices_changed[0] = _start;
|
||||
m_minmax_normal_matrices_changed[1] = _end;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_minmax_normal_matrices_changed[0] > _start)
|
||||
m_minmax_normal_matrices_changed[0] = _start;
|
||||
|
||||
if (m_minmax_normal_matrices_changed[1] < _end)
|
||||
m_minmax_normal_matrices_changed[1] = _end;
|
||||
}
|
||||
}
|
||||
|
||||
if (start < XFMEM_POSTMATRICES_END && end > XFMEM_POSTMATRICES)
|
||||
{
|
||||
int _start = start < XFMEM_POSTMATRICES ? XFMEM_POSTMATRICES : start - XFMEM_POSTMATRICES;
|
||||
int _end = end < XFMEM_POSTMATRICES_END ? end - XFMEM_POSTMATRICES :
|
||||
XFMEM_POSTMATRICES_END - XFMEM_POSTMATRICES;
|
||||
|
||||
if (m_minmax_post_transform_matrices_changed[0] == -1)
|
||||
{
|
||||
m_minmax_post_transform_matrices_changed[0] = _start;
|
||||
m_minmax_post_transform_matrices_changed[1] = _end;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_minmax_post_transform_matrices_changed[0] > _start)
|
||||
m_minmax_post_transform_matrices_changed[0] = _start;
|
||||
|
||||
if (m_minmax_post_transform_matrices_changed[1] < _end)
|
||||
m_minmax_post_transform_matrices_changed[1] = _end;
|
||||
}
|
||||
}
|
||||
|
||||
if (start < XFMEM_LIGHTS_END && end > XFMEM_LIGHTS)
|
||||
{
|
||||
int _start = start < XFMEM_LIGHTS ? XFMEM_LIGHTS : start - XFMEM_LIGHTS;
|
||||
int _end = end < XFMEM_LIGHTS_END ? end - XFMEM_LIGHTS : XFMEM_LIGHTS_END - XFMEM_LIGHTS;
|
||||
|
||||
if (m_minmax_lights_changed[0] == -1)
|
||||
{
|
||||
m_minmax_lights_changed[0] = _start;
|
||||
m_minmax_lights_changed[1] = _end;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_minmax_lights_changed[0] > _start)
|
||||
m_minmax_lights_changed[0] = _start;
|
||||
|
||||
if (m_minmax_lights_changed[1] < _end)
|
||||
m_minmax_lights_changed[1] = _end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XFStateManager::SetTexMatrixChangedA(u32 Value)
|
||||
{
|
||||
if (g_main_cp_state.matrix_index_a.Hex != Value)
|
||||
{
|
||||
g_vertex_manager->Flush();
|
||||
if (g_main_cp_state.matrix_index_a.PosNormalMtxIdx != (Value & 0x3f))
|
||||
m_pos_normal_matrix_changed = true;
|
||||
m_tex_matrices_changed[0] = true;
|
||||
g_main_cp_state.matrix_index_a.Hex = Value;
|
||||
}
|
||||
}
|
||||
|
||||
void XFStateManager::ResetTexMatrixAChange()
|
||||
{
|
||||
m_tex_matrices_changed[0] = false;
|
||||
}
|
||||
|
||||
void XFStateManager::SetTexMatrixChangedB(u32 Value)
|
||||
{
|
||||
if (g_main_cp_state.matrix_index_b.Hex != Value)
|
||||
{
|
||||
g_vertex_manager->Flush();
|
||||
m_tex_matrices_changed[1] = true;
|
||||
g_main_cp_state.matrix_index_b.Hex = Value;
|
||||
}
|
||||
}
|
||||
|
||||
void XFStateManager::ResetTexMatrixBChange()
|
||||
{
|
||||
m_tex_matrices_changed[1] = false;
|
||||
}
|
||||
|
||||
void XFStateManager::ResetPosNormalChange()
|
||||
{
|
||||
m_pos_normal_matrix_changed = false;
|
||||
}
|
||||
|
||||
void XFStateManager::SetProjectionChanged()
|
||||
{
|
||||
m_projection_changed = true;
|
||||
}
|
||||
|
||||
void XFStateManager::ResetProjection()
|
||||
{
|
||||
m_projection_changed = false;
|
||||
}
|
||||
|
||||
void XFStateManager::SetViewportChanged()
|
||||
{
|
||||
m_viewport_changed = true;
|
||||
}
|
||||
|
||||
void XFStateManager::ResetViewportChange()
|
||||
{
|
||||
m_viewport_changed = false;
|
||||
}
|
||||
|
||||
void XFStateManager::SetTexMatrixInfoChanged(int index)
|
||||
{
|
||||
// TODO: Should we track this with more precision, like which indices changed?
|
||||
// The whole vertex constants are probably going to be uploaded regardless.
|
||||
m_tex_mtx_info_changed = true;
|
||||
}
|
||||
|
||||
void XFStateManager::ResetTexMatrixInfoChange()
|
||||
{
|
||||
m_tex_mtx_info_changed = false;
|
||||
}
|
||||
|
||||
void XFStateManager::SetLightingConfigChanged()
|
||||
{
|
||||
m_lighting_config_changed = true;
|
||||
}
|
||||
|
||||
void XFStateManager::ResetLightingConfigChange()
|
||||
{
|
||||
m_lighting_config_changed = false;
|
||||
}
|
||||
|
||||
void XFStateManager::ResetLightsChanged()
|
||||
{
|
||||
m_minmax_lights_changed.fill(-1);
|
||||
}
|
||||
|
||||
void XFStateManager::SetMaterialColorChanged(int index)
|
||||
{
|
||||
m_materials_changed[index] = true;
|
||||
}
|
||||
|
||||
void XFStateManager::ResetMaterialChanges()
|
||||
{
|
||||
m_materials_changed = BitSet32(0);
|
||||
}
|
||||
|
||||
void XFStateManager::ResetPerVertexTransformMatrixChanges()
|
||||
{
|
||||
m_minmax_transform_matrices_changed.fill(-1);
|
||||
}
|
||||
|
||||
void XFStateManager::ResetPerVertexNormalMatrixChanges()
|
||||
{
|
||||
m_minmax_normal_matrices_changed.fill(-1);
|
||||
}
|
||||
|
||||
void XFStateManager::ResetPostTransformMatrixChanges()
|
||||
{
|
||||
m_minmax_post_transform_matrices_changed.fill(-1);
|
||||
}
|
87
Source/Core/VideoCommon/XFStateManager.h
Normal file
87
Source/Core/VideoCommon/XFStateManager.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2023 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "Common/BitSet.h"
|
||||
|
||||
class PointerWrap;
|
||||
|
||||
// This class manages how XF state changes over
|
||||
// a period of time (typically a single draw call)
|
||||
class XFStateManager
|
||||
{
|
||||
public:
|
||||
void Init();
|
||||
void DoState(PointerWrap& p);
|
||||
|
||||
void InvalidateXFRange(int start, int end);
|
||||
|
||||
void SetTexMatrixChangedA(u32 value);
|
||||
bool DidTexMatrixAChange() const { return m_tex_matrices_changed[0]; }
|
||||
void ResetTexMatrixAChange();
|
||||
|
||||
void SetTexMatrixChangedB(u32 value);
|
||||
bool DidTexMatrixBChange() const { return m_tex_matrices_changed[1]; }
|
||||
void ResetTexMatrixBChange();
|
||||
|
||||
bool DidPosNormalChange() const { return m_pos_normal_matrix_changed; }
|
||||
void ResetPosNormalChange();
|
||||
|
||||
void SetProjectionChanged();
|
||||
bool DidProjectionChange() const { return m_projection_changed; }
|
||||
void ResetProjection();
|
||||
|
||||
void SetViewportChanged();
|
||||
bool DidViewportChange() const { return m_viewport_changed; }
|
||||
void ResetViewportChange();
|
||||
|
||||
void SetTexMatrixInfoChanged(int index);
|
||||
bool DidTexMatrixInfoChange() const { return m_tex_mtx_info_changed; }
|
||||
void ResetTexMatrixInfoChange();
|
||||
|
||||
void SetLightingConfigChanged();
|
||||
bool DidLightingConfigChange() const { return m_lighting_config_changed; }
|
||||
void ResetLightingConfigChange();
|
||||
|
||||
const std::array<int, 2>& GetLightsChanged() const { return m_minmax_lights_changed; }
|
||||
void ResetLightsChanged();
|
||||
|
||||
void SetMaterialColorChanged(int index);
|
||||
const BitSet32& GetMaterialChanges() const { return m_materials_changed; }
|
||||
void ResetMaterialChanges();
|
||||
|
||||
const std::array<int, 2>& GetPerVertexTransformMatrixChanges() const
|
||||
{
|
||||
return m_minmax_transform_matrices_changed;
|
||||
}
|
||||
void ResetPerVertexTransformMatrixChanges();
|
||||
|
||||
const std::array<int, 2>& GetPerVertexNormalMatrixChanges() const
|
||||
{
|
||||
return m_minmax_normal_matrices_changed;
|
||||
}
|
||||
void ResetPerVertexNormalMatrixChanges();
|
||||
|
||||
const std::array<int, 2>& GetPostTransformMatrixChanges() const
|
||||
{
|
||||
return m_minmax_post_transform_matrices_changed;
|
||||
}
|
||||
void ResetPostTransformMatrixChanges();
|
||||
|
||||
private:
|
||||
// track changes
|
||||
std::array<bool, 2> m_tex_matrices_changed{};
|
||||
bool m_pos_normal_matrix_changed = false;
|
||||
bool m_projection_changed = false;
|
||||
bool m_viewport_changed = false;
|
||||
bool m_tex_mtx_info_changed = false;
|
||||
bool m_lighting_config_changed = false;
|
||||
BitSet32 m_materials_changed;
|
||||
std::array<int, 2> m_minmax_transform_matrices_changed{};
|
||||
std::array<int, 2> m_minmax_normal_matrices_changed{};
|
||||
std::array<int, 2> m_minmax_post_transform_matrices_changed{};
|
||||
std::array<int, 2> m_minmax_lights_changed{};
|
||||
};
|
|
@ -18,18 +18,17 @@
|
|||
#include "VideoCommon/PixelShaderManager.h"
|
||||
#include "VideoCommon/VertexLoaderManager.h"
|
||||
#include "VideoCommon/VertexManagerBase.h"
|
||||
#include "VideoCommon/VertexShaderManager.h"
|
||||
#include "VideoCommon/XFMemory.h"
|
||||
#include "VideoCommon/XFStateManager.h"
|
||||
|
||||
static void XFMemWritten(VertexShaderManager& vertex_shader_manager, u32 transferSize,
|
||||
u32 baseAddress)
|
||||
static void XFMemWritten(XFStateManager& xf_state_manager, u32 transferSize, u32 baseAddress)
|
||||
{
|
||||
g_vertex_manager->Flush();
|
||||
vertex_shader_manager.InvalidateXFRange(baseAddress, baseAddress + transferSize);
|
||||
xf_state_manager.InvalidateXFRange(baseAddress, baseAddress + transferSize);
|
||||
}
|
||||
|
||||
static void XFRegWritten(Core::System& system, VertexShaderManager& vertex_shader_manager,
|
||||
u32 address, u32 value)
|
||||
static void XFRegWritten(Core::System& system, XFStateManager& xf_state_manager, u32 address,
|
||||
u32 value)
|
||||
{
|
||||
if (address >= XFMEM_REGISTERS_START && address < XFMEM_REGISTERS_END)
|
||||
{
|
||||
|
@ -63,7 +62,7 @@ static void XFRegWritten(Core::System& system, VertexShaderManager& vertex_shade
|
|||
case XFMEM_SETNUMCHAN:
|
||||
if (xfmem.numChan.numColorChans != (value & 3))
|
||||
g_vertex_manager->Flush();
|
||||
vertex_shader_manager.SetLightingConfigChanged();
|
||||
xf_state_manager.SetLightingConfigChanged();
|
||||
break;
|
||||
|
||||
case XFMEM_SETCHAN0_AMBCOLOR: // Channel Ambient Color
|
||||
|
@ -73,7 +72,7 @@ static void XFRegWritten(Core::System& system, VertexShaderManager& vertex_shade
|
|||
if (xfmem.ambColor[chan] != value)
|
||||
{
|
||||
g_vertex_manager->Flush();
|
||||
vertex_shader_manager.SetMaterialColorChanged(chan);
|
||||
xf_state_manager.SetMaterialColorChanged(chan);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -85,7 +84,7 @@ static void XFRegWritten(Core::System& system, VertexShaderManager& vertex_shade
|
|||
if (xfmem.matColor[chan] != value)
|
||||
{
|
||||
g_vertex_manager->Flush();
|
||||
vertex_shader_manager.SetMaterialColorChanged(chan + 2);
|
||||
xf_state_manager.SetMaterialColorChanged(chan + 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -96,21 +95,21 @@ static void XFRegWritten(Core::System& system, VertexShaderManager& vertex_shade
|
|||
case XFMEM_SETCHAN1_ALPHA:
|
||||
if (((u32*)&xfmem)[address] != (value & 0x7fff))
|
||||
g_vertex_manager->Flush();
|
||||
vertex_shader_manager.SetLightingConfigChanged();
|
||||
xf_state_manager.SetLightingConfigChanged();
|
||||
break;
|
||||
|
||||
case XFMEM_DUALTEX:
|
||||
if (xfmem.dualTexTrans.enabled != bool(value & 1))
|
||||
g_vertex_manager->Flush();
|
||||
vertex_shader_manager.SetTexMatrixInfoChanged(-1);
|
||||
xf_state_manager.SetTexMatrixInfoChanged(-1);
|
||||
break;
|
||||
|
||||
case XFMEM_SETMATRIXINDA:
|
||||
vertex_shader_manager.SetTexMatrixChangedA(value);
|
||||
xf_state_manager.SetTexMatrixChangedA(value);
|
||||
VertexLoaderManager::g_needs_cp_xf_consistency_check = true;
|
||||
break;
|
||||
case XFMEM_SETMATRIXINDB:
|
||||
vertex_shader_manager.SetTexMatrixChangedB(value);
|
||||
xf_state_manager.SetTexMatrixChangedB(value);
|
||||
VertexLoaderManager::g_needs_cp_xf_consistency_check = true;
|
||||
break;
|
||||
|
||||
|
@ -121,7 +120,7 @@ static void XFRegWritten(Core::System& system, VertexShaderManager& vertex_shade
|
|||
case XFMEM_SETVIEWPORT + 4:
|
||||
case XFMEM_SETVIEWPORT + 5:
|
||||
g_vertex_manager->Flush();
|
||||
vertex_shader_manager.SetViewportChanged();
|
||||
xf_state_manager.SetViewportChanged();
|
||||
system.GetPixelShaderManager().SetViewportChanged();
|
||||
system.GetGeometryShaderManager().SetViewportChanged();
|
||||
break;
|
||||
|
@ -134,7 +133,7 @@ static void XFRegWritten(Core::System& system, VertexShaderManager& vertex_shade
|
|||
case XFMEM_SETPROJECTION + 5:
|
||||
case XFMEM_SETPROJECTION + 6:
|
||||
g_vertex_manager->Flush();
|
||||
vertex_shader_manager.SetProjectionChanged();
|
||||
xf_state_manager.SetProjectionChanged();
|
||||
system.GetGeometryShaderManager().SetProjectionChanged();
|
||||
break;
|
||||
|
||||
|
@ -152,7 +151,7 @@ static void XFRegWritten(Core::System& system, VertexShaderManager& vertex_shade
|
|||
case XFMEM_SETTEXMTXINFO + 6:
|
||||
case XFMEM_SETTEXMTXINFO + 7:
|
||||
g_vertex_manager->Flush();
|
||||
vertex_shader_manager.SetTexMatrixInfoChanged(address - XFMEM_SETTEXMTXINFO);
|
||||
xf_state_manager.SetTexMatrixInfoChanged(address - XFMEM_SETTEXMTXINFO);
|
||||
break;
|
||||
|
||||
case XFMEM_SETPOSTMTXINFO:
|
||||
|
@ -164,7 +163,7 @@ static void XFRegWritten(Core::System& system, VertexShaderManager& vertex_shade
|
|||
case XFMEM_SETPOSTMTXINFO + 6:
|
||||
case XFMEM_SETPOSTMTXINFO + 7:
|
||||
g_vertex_manager->Flush();
|
||||
vertex_shader_manager.SetTexMatrixInfoChanged(address - XFMEM_SETPOSTMTXINFO);
|
||||
xf_state_manager.SetTexMatrixInfoChanged(address - XFMEM_SETPOSTMTXINFO);
|
||||
break;
|
||||
|
||||
// --------------
|
||||
|
@ -218,7 +217,7 @@ void LoadXFReg(u16 base_address, u8 transfer_size, const u8* data)
|
|||
}
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
auto& vertex_shader_manager = system.GetVertexShaderManager();
|
||||
auto& xf_state_manager = system.GetXFStateManager();
|
||||
|
||||
// write to XF mem
|
||||
if (base_address < XFMEM_REGISTERS_START)
|
||||
|
@ -232,7 +231,7 @@ void LoadXFReg(u16 base_address, u8 transfer_size, const u8* data)
|
|||
base_address = XFMEM_REGISTERS_START;
|
||||
}
|
||||
|
||||
XFMemWritten(vertex_shader_manager, xf_mem_transfer_size, xf_mem_base);
|
||||
XFMemWritten(xf_state_manager, xf_mem_transfer_size, xf_mem_base);
|
||||
for (u32 i = 0; i < xf_mem_transfer_size; i++)
|
||||
{
|
||||
((u32*)&xfmem)[xf_mem_base + i] = Common::swap32(data);
|
||||
|
@ -247,7 +246,7 @@ void LoadXFReg(u16 base_address, u8 transfer_size, const u8* data)
|
|||
{
|
||||
const u32 value = Common::swap32(data);
|
||||
|
||||
XFRegWritten(system, vertex_shader_manager, address, value);
|
||||
XFRegWritten(system, xf_state_manager, address, value);
|
||||
((u32*)&xfmem)[address] = value;
|
||||
|
||||
data += 4;
|
||||
|
@ -275,14 +274,14 @@ void LoadIndexedXF(CPArray array, u32 index, u16 address, u8 size)
|
|||
g_main_cp_state.array_strides[array] * index);
|
||||
}
|
||||
|
||||
auto& vertex_shader_manager = system.GetVertexShaderManager();
|
||||
auto& xf_state_manager = system.GetXFStateManager();
|
||||
bool changed = false;
|
||||
for (u32 i = 0; i < size; ++i)
|
||||
{
|
||||
if (currData[i] != Common::swap32(newData[i]))
|
||||
{
|
||||
changed = true;
|
||||
XFMemWritten(vertex_shader_manager, size, address);
|
||||
XFMemWritten(xf_state_manager, size, address);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue