Macro HLE support
This commit is contained in:
parent
3bab5a5e4a
commit
6ce5f3120b
|
@ -27,6 +27,8 @@ add_library(video_core STATIC
|
||||||
engines/shader_type.h
|
engines/shader_type.h
|
||||||
macro/macro.cpp
|
macro/macro.cpp
|
||||||
macro/macro.h
|
macro/macro.h
|
||||||
|
macro/macro_hle.cpp
|
||||||
|
macro/macro_hle.h
|
||||||
macro/macro_interpreter.cpp
|
macro/macro_interpreter.cpp
|
||||||
macro/macro_interpreter.h
|
macro/macro_interpreter.h
|
||||||
macro/macro_jit_x64.cpp
|
macro/macro_jit_x64.cpp
|
||||||
|
|
|
@ -128,7 +128,7 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters)
|
||||||
((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size());
|
((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size());
|
||||||
|
|
||||||
// Execute the current macro.
|
// Execute the current macro.
|
||||||
macro_engine->Execute(macro_positions[entry], parameters);
|
macro_engine->Execute(*this, macro_positions[entry], parameters);
|
||||||
if (mme_draw.current_mode != MMEDrawMode::Undefined) {
|
if (mme_draw.current_mode != MMEDrawMode::Undefined) {
|
||||||
FlushMMEInlineDraw();
|
FlushMMEInlineDraw();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1418,6 +1418,10 @@ public:
|
||||||
return execute_on;
|
return execute_on;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VideoCore::RasterizerInterface& GetRasterizer() {
|
||||||
|
return rasterizer;
|
||||||
|
}
|
||||||
|
|
||||||
/// Notify a memory write has happened.
|
/// Notify a memory write has happened.
|
||||||
void OnMemoryWrite() {
|
void OnMemoryWrite() {
|
||||||
dirty.flags |= dirty.on_write_stores;
|
dirty.flags |= dirty.on_write_stores;
|
||||||
|
|
|
@ -2,23 +2,37 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <boost/container_hash/hash.hpp>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
#include "video_core/engines/maxwell_3d.h"
|
||||||
#include "video_core/macro/macro.h"
|
#include "video_core/macro/macro.h"
|
||||||
|
#include "video_core/macro/macro_hle.h"
|
||||||
#include "video_core/macro/macro_interpreter.h"
|
#include "video_core/macro/macro_interpreter.h"
|
||||||
#include "video_core/macro/macro_jit_x64.h"
|
#include "video_core/macro/macro_jit_x64.h"
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
|
|
||||||
|
MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d)
|
||||||
|
: hle_macros{std::make_unique<Tegra::HLEMacro>(maxwell3d)} {}
|
||||||
|
|
||||||
|
MacroEngine::~MacroEngine() {}
|
||||||
|
|
||||||
void MacroEngine::AddCode(u32 method, u32 data) {
|
void MacroEngine::AddCode(u32 method, u32 data) {
|
||||||
uploaded_macro_code[method].push_back(data);
|
uploaded_macro_code[method].push_back(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
|
void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method,
|
||||||
|
const std::vector<u32>& parameters) {
|
||||||
auto compiled_macro = macro_cache.find(method);
|
auto compiled_macro = macro_cache.find(method);
|
||||||
if (compiled_macro != macro_cache.end()) {
|
if (compiled_macro != macro_cache.end()) {
|
||||||
compiled_macro->second->Execute(parameters, method);
|
const auto& cache_info = compiled_macro->second;
|
||||||
|
if (cache_info.has_hle_program) {
|
||||||
|
cache_info.hle_program->Execute(parameters, method);
|
||||||
|
} else {
|
||||||
|
cache_info.lle_program->Execute(parameters, method);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Macro not compiled, check if it's uploaded and if so, compile it
|
// Macro not compiled, check if it's uploaded and if so, compile it
|
||||||
auto macro_code = uploaded_macro_code.find(method);
|
auto macro_code = uploaded_macro_code.find(method);
|
||||||
|
@ -26,8 +40,21 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
|
||||||
UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method);
|
UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
macro_cache[method] = Compile(macro_code->second);
|
auto& cache_info = macro_cache[method];
|
||||||
macro_cache[method]->Execute(parameters, method);
|
cache_info.hash = boost::hash_value(macro_code->second);
|
||||||
|
cache_info.lle_program = Compile(macro_code->second);
|
||||||
|
|
||||||
|
auto hle_program = hle_macros->GetHLEProgram(cache_info.hash);
|
||||||
|
if (hle_program.has_value()) {
|
||||||
|
cache_info.has_hle_program = true;
|
||||||
|
cache_info.hle_program = std::move(hle_program.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache_info.has_hle_program) {
|
||||||
|
cache_info.hle_program->Execute(parameters, method);
|
||||||
|
} else {
|
||||||
|
cache_info.lle_program->Execute(parameters, method);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,11 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
|
|
||||||
namespace Engines {
|
namespace Engines {
|
||||||
class Maxwell3D;
|
class Maxwell3D;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Macro {
|
namespace Macro {
|
||||||
constexpr std::size_t NUM_MACRO_REGISTERS = 8;
|
constexpr std::size_t NUM_MACRO_REGISTERS = 8;
|
||||||
enum class Operation : u32 {
|
enum class Operation : u32 {
|
||||||
|
@ -94,6 +96,8 @@ union MethodAddress {
|
||||||
|
|
||||||
} // namespace Macro
|
} // namespace Macro
|
||||||
|
|
||||||
|
class HLEMacro;
|
||||||
|
|
||||||
class CachedMacro {
|
class CachedMacro {
|
||||||
public:
|
public:
|
||||||
virtual ~CachedMacro() = default;
|
virtual ~CachedMacro() = default;
|
||||||
|
@ -107,20 +111,29 @@ public:
|
||||||
|
|
||||||
class MacroEngine {
|
class MacroEngine {
|
||||||
public:
|
public:
|
||||||
virtual ~MacroEngine() = default;
|
MacroEngine(Engines::Maxwell3D& maxwell3d);
|
||||||
|
virtual ~MacroEngine();
|
||||||
|
|
||||||
// Store the uploaded macro code to compile them when they're called.
|
// Store the uploaded macro code to compile them when they're called.
|
||||||
void AddCode(u32 method, u32 data);
|
void AddCode(u32 method, u32 data);
|
||||||
|
|
||||||
// Compiles the macro if its not in the cache, and executes the compiled macro
|
// Compiles the macro if its not in the cache, and executes the compiled macro
|
||||||
void Execute(u32 method, const std::vector<u32>& parameters);
|
void Execute(Engines::Maxwell3D& maxwell3d, u32 method, const std::vector<u32>& parameters);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual std::unique_ptr<CachedMacro> Compile(const std::vector<u32>& code) = 0;
|
virtual std::unique_ptr<CachedMacro> Compile(const std::vector<u32>& code) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<u32, std::unique_ptr<CachedMacro>> macro_cache;
|
struct CacheInfo {
|
||||||
|
std::unique_ptr<CachedMacro> lle_program{};
|
||||||
|
std::unique_ptr<CachedMacro> hle_program{};
|
||||||
|
u64 hash{};
|
||||||
|
bool has_hle_program{};
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<u32, CacheInfo> macro_cache;
|
||||||
std::unordered_map<u32, std::vector<u32>> uploaded_macro_code;
|
std::unordered_map<u32, std::vector<u32>> uploaded_macro_code;
|
||||||
|
std::unique_ptr<HLEMacro> hle_macros;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d);
|
std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d);
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
// Copyright 2020 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include "video_core/engines/maxwell_3d.h"
|
||||||
|
#include "video_core/macro/macro_hle.h"
|
||||||
|
#include "video_core/rasterizer_interface.h"
|
||||||
|
|
||||||
|
namespace Tegra {
|
||||||
|
|
||||||
|
// HLE'd functions
|
||||||
|
static void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d,
|
||||||
|
const std::vector<u32>& parameters) {
|
||||||
|
const u32 instance_count = parameters[2] & maxwell3d.GetRegisterValue(0xD1B);
|
||||||
|
|
||||||
|
maxwell3d.regs.draw.topology.Assign(
|
||||||
|
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] &
|
||||||
|
~(0x3ffffff << 26)));
|
||||||
|
maxwell3d.regs.vb_base_instance = parameters[5];
|
||||||
|
maxwell3d.mme_draw.instance_count = instance_count;
|
||||||
|
maxwell3d.regs.vb_element_base = parameters[3];
|
||||||
|
maxwell3d.regs.index_array.count = parameters[1];
|
||||||
|
maxwell3d.regs.index_array.first = parameters[4];
|
||||||
|
|
||||||
|
if (maxwell3d.ShouldExecute()) {
|
||||||
|
maxwell3d.GetRasterizer().Draw(true, true);
|
||||||
|
}
|
||||||
|
maxwell3d.regs.index_array.count = 0;
|
||||||
|
maxwell3d.mme_draw.instance_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d,
|
||||||
|
const std::vector<u32>& parameters) {
|
||||||
|
const u32 count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
|
||||||
|
|
||||||
|
maxwell3d.regs.vertex_buffer.first = parameters[3];
|
||||||
|
maxwell3d.regs.vertex_buffer.count = parameters[1];
|
||||||
|
maxwell3d.regs.vb_base_instance = parameters[4];
|
||||||
|
maxwell3d.regs.draw.topology.Assign(
|
||||||
|
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
|
||||||
|
maxwell3d.mme_draw.instance_count = count;
|
||||||
|
|
||||||
|
if (maxwell3d.ShouldExecute()) {
|
||||||
|
maxwell3d.GetRasterizer().Draw(false, true);
|
||||||
|
}
|
||||||
|
maxwell3d.regs.vertex_buffer.count = 0;
|
||||||
|
maxwell3d.mme_draw.instance_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d,
|
||||||
|
const std::vector<u32>& parameters) {
|
||||||
|
const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
|
||||||
|
const u32 element_base = parameters[4];
|
||||||
|
const u32 base_instance = parameters[5];
|
||||||
|
maxwell3d.regs.index_array.first = parameters[3];
|
||||||
|
maxwell3d.regs.reg_array[0x446] = element_base; // vertex id base?
|
||||||
|
maxwell3d.regs.index_array.count = parameters[1];
|
||||||
|
maxwell3d.regs.vb_element_base = element_base;
|
||||||
|
maxwell3d.regs.vb_base_instance = base_instance;
|
||||||
|
maxwell3d.regs.const_buffer.cb_pos = 0x640;
|
||||||
|
maxwell3d.mme_draw.instance_count = instance_count;
|
||||||
|
maxwell3d.regs.const_buffer.cb_data[0] = element_base;
|
||||||
|
maxwell3d.regs.const_buffer.cb_data[1] = base_instance;
|
||||||
|
maxwell3d.regs.draw.topology.Assign(
|
||||||
|
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
|
||||||
|
if (maxwell3d.ShouldExecute()) {
|
||||||
|
maxwell3d.GetRasterizer().Draw(true, true);
|
||||||
|
}
|
||||||
|
maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base?
|
||||||
|
maxwell3d.regs.index_array.count = 0;
|
||||||
|
maxwell3d.regs.vb_element_base = 0x0;
|
||||||
|
maxwell3d.regs.vb_base_instance = 0x0;
|
||||||
|
maxwell3d.regs.const_buffer.cb_pos = 0x640;
|
||||||
|
maxwell3d.regs.const_buffer.cb_data[0] = 0;
|
||||||
|
maxwell3d.regs.const_buffer.cb_data[1] = 0;
|
||||||
|
maxwell3d.mme_draw.instance_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::unordered_map<u64, HLEFunction> hle_funcs{
|
||||||
|
{0x771BB18C62444DA0, &HLE_771BB18C62444DA0},
|
||||||
|
{0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD},
|
||||||
|
{0x0217920100488FF7, &HLE_0217920100488FF7},
|
||||||
|
};
|
||||||
|
|
||||||
|
HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {}
|
||||||
|
HLEMacro::~HLEMacro() = default;
|
||||||
|
|
||||||
|
std::optional<std::unique_ptr<CachedMacro>> HLEMacro::GetHLEProgram(u64 hash) const {
|
||||||
|
auto it = hle_funcs.find(hash);
|
||||||
|
if (it != hle_funcs.end()) {
|
||||||
|
return std::make_unique<HLEMacroImpl>(maxwell3d, it->second);
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HLEMacroImpl::~HLEMacroImpl() = default;
|
||||||
|
|
||||||
|
HLEMacroImpl::HLEMacroImpl(Engines::Maxwell3D& maxwell3d, HLEFunction func)
|
||||||
|
: maxwell3d(maxwell3d), func(func) {}
|
||||||
|
|
||||||
|
void HLEMacroImpl::Execute(const std::vector<u32>& parameters, u32 method) {
|
||||||
|
func(maxwell3d, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Tegra
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2020 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/macro/macro.h"
|
||||||
|
|
||||||
|
namespace Tegra {
|
||||||
|
|
||||||
|
namespace Engines {
|
||||||
|
class Maxwell3D;
|
||||||
|
}
|
||||||
|
|
||||||
|
using HLEFunction = void (*)(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters);
|
||||||
|
|
||||||
|
class HLEMacro {
|
||||||
|
public:
|
||||||
|
HLEMacro(Engines::Maxwell3D& maxwell3d);
|
||||||
|
~HLEMacro();
|
||||||
|
std::optional<std::unique_ptr<CachedMacro>> GetHLEProgram(u64 hash) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Engines::Maxwell3D& maxwell3d;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HLEMacroImpl : public CachedMacro {
|
||||||
|
public:
|
||||||
|
explicit HLEMacroImpl(Engines::Maxwell3D& maxwell3d, HLEFunction func);
|
||||||
|
~HLEMacroImpl();
|
||||||
|
|
||||||
|
void Execute(const std::vector<u32>& parameters, u32 method) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Engines::Maxwell3D& maxwell3d;
|
||||||
|
HLEFunction func;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Tegra
|
|
@ -11,7 +11,8 @@
|
||||||
MICROPROFILE_DEFINE(MacroInterp, "GPU", "Execute macro interpreter", MP_RGB(128, 128, 192));
|
MICROPROFILE_DEFINE(MacroInterp, "GPU", "Execute macro interpreter", MP_RGB(128, 128, 192));
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {}
|
MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d)
|
||||||
|
: MacroEngine::MacroEngine(maxwell3d), maxwell3d(maxwell3d) {}
|
||||||
|
|
||||||
std::unique_ptr<CachedMacro> MacroInterpreter::Compile(const std::vector<u32>& code) {
|
std::unique_ptr<CachedMacro> MacroInterpreter::Compile(const std::vector<u32>& code) {
|
||||||
return std::make_unique<MacroInterpreterImpl>(maxwell3d, code);
|
return std::make_unique<MacroInterpreterImpl>(maxwell3d, code);
|
||||||
|
|
|
@ -28,7 +28,8 @@ static const std::bitset<32> PERSISTENT_REGISTERS = Common::X64::BuildRegSet({
|
||||||
BRANCH_HOLDER,
|
BRANCH_HOLDER,
|
||||||
});
|
});
|
||||||
|
|
||||||
MacroJITx64::MacroJITx64(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {}
|
MacroJITx64::MacroJITx64(Engines::Maxwell3D& maxwell3d)
|
||||||
|
: MacroEngine::MacroEngine(maxwell3d), maxwell3d(maxwell3d) {}
|
||||||
|
|
||||||
std::unique_ptr<CachedMacro> MacroJITx64::Compile(const std::vector<u32>& code) {
|
std::unique_ptr<CachedMacro> MacroJITx64::Compile(const std::vector<u32>& code) {
|
||||||
return std::make_unique<MacroJITx64Impl>(maxwell3d, code);
|
return std::make_unique<MacroJITx64Impl>(maxwell3d, code);
|
||||||
|
|
Reference in New Issue