renderer: Add disable right eye performance hack
This commit is contained in:
@@ -12,6 +12,7 @@ add_library(video_core STATIC
|
||||
gpu.cpp
|
||||
gpu.h
|
||||
gpu_debugger.h
|
||||
gpu_impl.h
|
||||
pica_types.h
|
||||
precompiled_headers.h
|
||||
rasterizer_accelerated.cpp
|
||||
@@ -63,6 +64,8 @@ add_library(video_core STATIC
|
||||
# Needed as a fallback regardless of enabled renderers.
|
||||
renderer_software/sw_blitter.cpp
|
||||
renderer_software/sw_blitter.h
|
||||
right_eye_disabler.cpp
|
||||
right_eye_disabler.h
|
||||
shader/debug_data.h
|
||||
shader/generator/glsl_fs_shader_gen.cpp
|
||||
shader/generator/glsl_fs_shader_gen.h
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/archives.h"
|
||||
#include "common/hacks/hack_manager.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
@@ -11,10 +12,12 @@
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/gpu_debugger.h"
|
||||
#include "video_core/gpu_impl.h"
|
||||
#include "video_core/pica/pica_core.h"
|
||||
#include "video_core/pica/regs_lcd.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_software/sw_blitter.h"
|
||||
#include "video_core/right_eye_disabler.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace VideoCore {
|
||||
@@ -25,32 +28,10 @@ constexpr VAddr VADDR_GPU = 0x1EF00000;
|
||||
MICROPROFILE_DEFINE(GPU_DisplayTransfer, "GPU", "DisplayTransfer", MP_RGB(100, 100, 255));
|
||||
MICROPROFILE_DEFINE(GPU_CmdlistProcessing, "GPU", "Cmdlist Processing", MP_RGB(100, 255, 100));
|
||||
|
||||
struct GPU::Impl {
|
||||
Core::Timing& timing;
|
||||
Core::System& system;
|
||||
Memory::MemorySystem& memory;
|
||||
std::shared_ptr<Pica::DebugContext> debug_context;
|
||||
Pica::PicaCore pica;
|
||||
GraphicsDebugger gpu_debugger;
|
||||
std::unique_ptr<RendererBase> renderer;
|
||||
RasterizerInterface* rasterizer;
|
||||
std::unique_ptr<SwRenderer::SwBlitter> sw_blitter;
|
||||
Core::TimingEventType* vblank_event;
|
||||
Service::GSP::InterruptHandler signal_interrupt;
|
||||
|
||||
explicit Impl(Core::System& system, Frontend::EmuWindow& emu_window,
|
||||
Frontend::EmuWindow* secondary_window)
|
||||
: timing{system.CoreTiming()}, system{system}, memory{system.Memory()},
|
||||
debug_context{Pica::g_debug_context}, pica{memory, debug_context},
|
||||
renderer{VideoCore::CreateRenderer(emu_window, secondary_window, pica, system)},
|
||||
rasterizer{renderer->Rasterizer()},
|
||||
sw_blitter{std::make_unique<SwRenderer::SwBlitter>(memory, rasterizer)} {}
|
||||
~Impl() = default;
|
||||
};
|
||||
|
||||
GPU::GPU(Core::System& system, Frontend::EmuWindow& emu_window,
|
||||
Frontend::EmuWindow* secondary_window)
|
||||
: impl{std::make_unique<Impl>(system, emu_window, secondary_window)} {
|
||||
: right_eye_disabler{std::make_unique<RightEyeDisabler>(*this)},
|
||||
impl{std::make_unique<Impl>(system, emu_window, secondary_window)} {
|
||||
impl->vblank_event = impl->timing.RegisterEvent(
|
||||
"GPU::VBlankCallback",
|
||||
[this](uintptr_t user_data, s64 cycles_late) { VBlankCallback(user_data, cycles_late); });
|
||||
@@ -232,6 +213,7 @@ void GPU::SetBufferSwap(u32 screen_id, const Service::GSP::FrameBufferInfo& info
|
||||
if (screen_id == 0) {
|
||||
MicroProfileFlip();
|
||||
impl->system.perf_stats->EndGameFrame();
|
||||
right_eye_disabler->ReportEndFrame();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,6 +314,26 @@ GraphicsDebugger& GPU::Debugger() {
|
||||
return impl->gpu_debugger;
|
||||
}
|
||||
|
||||
void GPU::ReportLoadingProgramID(u64 program_ID) {
|
||||
auto hack = Common::Hacks::hack_manager.GetHack(
|
||||
Common::Hacks::HackType::ACCURATE_MULTIPLICATION, program_ID);
|
||||
bool use_accurate_mul = Settings::values.shaders_accurate_mul.GetValue();
|
||||
if (hack) {
|
||||
switch (hack->mode) {
|
||||
case Common::Hacks::HackAllowMode::DISALLOW:
|
||||
use_accurate_mul = false;
|
||||
break;
|
||||
case Common::Hacks::HackAllowMode::FORCE:
|
||||
use_accurate_mul = true;
|
||||
break;
|
||||
case Common::Hacks::HackAllowMode::ALLOW:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
impl->rasterizer->SetAccurateMul(use_accurate_mul);
|
||||
}
|
||||
|
||||
void GPU::SubmitCmdList(u32 index) {
|
||||
// Check if a command list was triggered.
|
||||
auto& config = impl->pica.regs.internal.pipeline.command_buffer;
|
||||
@@ -344,7 +346,8 @@ void GPU::SubmitCmdList(u32 index) {
|
||||
// Forward command list processing to the PICA core.
|
||||
const PAddr addr = config.GetPhysicalAddress(index);
|
||||
const u32 size = config.GetSize(index);
|
||||
impl->pica.ProcessCmdList(addr, size);
|
||||
impl->pica.ProcessCmdList(addr, size,
|
||||
!right_eye_disabler->ShouldAllowCmdQueueTrigger(addr, size));
|
||||
config.trigger[index] = 0;
|
||||
}
|
||||
|
||||
@@ -396,8 +399,11 @@ void GPU::MemoryTransfer() {
|
||||
impl->sw_blitter->TextureCopy(config);
|
||||
}
|
||||
} else {
|
||||
if (!impl->rasterizer->AccelerateDisplayTransfer(config)) {
|
||||
impl->sw_blitter->DisplayTransfer(config);
|
||||
if (right_eye_disabler->ShouldAllowDisplayTransfer(config.GetPhysicalInputAddress(),
|
||||
config.input_height)) {
|
||||
if (!impl->rasterizer->AccelerateDisplayTransfer(config)) {
|
||||
impl->sw_blitter->DisplayTransfer(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ constexpr u64 FRAME_TICKS = 4481136ull;
|
||||
|
||||
class GraphicsDebugger;
|
||||
class RendererBase;
|
||||
class RightEyeDisabler;
|
||||
|
||||
/**
|
||||
* The GPU class is the high level interface to the video_core for core services.
|
||||
@@ -92,6 +93,12 @@ public:
|
||||
/// Returns a mutable reference to the GSP command debugger.
|
||||
[[nodiscard]] GraphicsDebugger& Debugger();
|
||||
|
||||
RightEyeDisabler& GetRightEyeDisabler() {
|
||||
return *right_eye_disabler;
|
||||
}
|
||||
|
||||
void ReportLoadingProgramID(u64 program_ID);
|
||||
|
||||
private:
|
||||
void SubmitCmdList(u32 index);
|
||||
|
||||
@@ -105,7 +112,10 @@ private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const u32 file_version);
|
||||
|
||||
std::unique_ptr<RightEyeDisabler> right_eye_disabler;
|
||||
|
||||
private:
|
||||
friend class RightEyeDisabler;
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
|
||||
48
src/video_core/gpu_impl.h
Normal file
48
src/video_core/gpu_impl.h
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Copyright 2024 Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/archives.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/gsp/gsp_gpu.h"
|
||||
#include "core/hle/service/plgldr/plgldr.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/gpu_debugger.h"
|
||||
#include "video_core/gpu_impl.h"
|
||||
#include "video_core/pica/pica_core.h"
|
||||
#include "video_core/pica/regs_lcd.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_software/sw_blitter.h"
|
||||
#include "video_core/right_eye_disabler.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace VideoCore {
|
||||
struct GPU::Impl {
|
||||
Core::Timing& timing;
|
||||
Core::System& system;
|
||||
Memory::MemorySystem& memory;
|
||||
std::shared_ptr<Pica::DebugContext> debug_context;
|
||||
Pica::PicaCore pica;
|
||||
GraphicsDebugger gpu_debugger;
|
||||
std::unique_ptr<RendererBase> renderer;
|
||||
RasterizerInterface* rasterizer;
|
||||
std::unique_ptr<SwRenderer::SwBlitter> sw_blitter;
|
||||
Core::TimingEventType* vblank_event;
|
||||
Service::GSP::InterruptHandler signal_interrupt;
|
||||
|
||||
explicit Impl(Core::System& system, Frontend::EmuWindow& emu_window,
|
||||
Frontend::EmuWindow* secondary_window)
|
||||
: timing{system.CoreTiming()}, system{system}, memory{system.Memory()},
|
||||
debug_context{Pica::g_debug_context}, pica{memory, debug_context},
|
||||
renderer{VideoCore::CreateRenderer(emu_window, secondary_window, pica, system)},
|
||||
rasterizer{renderer->Rasterizer()},
|
||||
sw_blitter{std::make_unique<SwRenderer::SwBlitter>(memory, rasterizer)} {}
|
||||
~Impl() = default;
|
||||
};
|
||||
} // namespace VideoCore
|
||||
@@ -91,7 +91,11 @@ void PicaCore::SetInterruptHandler(Service::GSP::InterruptHandler& signal_interr
|
||||
this->signal_interrupt = signal_interrupt;
|
||||
}
|
||||
|
||||
void PicaCore::ProcessCmdList(PAddr list, u32 size) {
|
||||
void PicaCore::ProcessCmdList(PAddr list, u32 size, bool ignore_list) {
|
||||
if (ignore_list) {
|
||||
signal_interrupt(Service::GSP::InterruptId::P3D);
|
||||
return;
|
||||
}
|
||||
// Initialize command list tracking.
|
||||
const u8* head = memory.GetPhysicalPointer(list);
|
||||
cmd_list.Reset(list, head, size);
|
||||
@@ -610,11 +614,78 @@ void PicaCore::LoadVertices(bool is_indexed) {
|
||||
}
|
||||
}
|
||||
|
||||
PicaCore::RenderPropertiesGuess PicaCore::GuessCmdRenderProperties(PAddr list, u32 size) {
|
||||
// Initialize command list tracking.
|
||||
const u8* head = memory.GetPhysicalPointer(list);
|
||||
cmd_list.Reset(list, head, size);
|
||||
|
||||
constexpr size_t max_iterations = 0x100;
|
||||
|
||||
RenderPropertiesGuess find_info{};
|
||||
|
||||
find_info.vp_height = regs.internal.rasterizer.viewport_size_y.Value();
|
||||
find_info.paddr = regs.internal.framebuffer.framebuffer.color_buffer_address.Value() * 8;
|
||||
|
||||
auto process_write = [this, &find_info](u32 cmd_id, u32 value) {
|
||||
switch (cmd_id) {
|
||||
case PICA_REG_INDEX(rasterizer.viewport_size_y):
|
||||
find_info.vp_height = value;
|
||||
find_info.vp_heigh_found = true;
|
||||
break;
|
||||
case PICA_REG_INDEX(framebuffer.framebuffer.color_buffer_address):
|
||||
find_info.paddr = value * 8;
|
||||
find_info.paddr_found = true;
|
||||
break;
|
||||
[[unlikely]] case PICA_REG_INDEX(pipeline.command_buffer.trigger[0]) :
|
||||
[[unlikely]] case PICA_REG_INDEX(pipeline.command_buffer.trigger[1]) : {
|
||||
const u32 index =
|
||||
static_cast<u32>(cmd_id - PICA_REG_INDEX(pipeline.command_buffer.trigger[0]));
|
||||
const PAddr addr = regs.internal.pipeline.command_buffer.GetPhysicalAddress(index);
|
||||
const u32 size = regs.internal.pipeline.command_buffer.GetSize(index);
|
||||
const u8* head = memory.GetPhysicalPointer(addr);
|
||||
cmd_list.Reset(addr, head, size);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return find_info.vp_heigh_found && find_info.paddr_found;
|
||||
};
|
||||
|
||||
size_t iterations = 0;
|
||||
while (cmd_list.current_index < cmd_list.length && iterations < max_iterations) {
|
||||
// Align read pointer to 8 bytes
|
||||
if (cmd_list.current_index % 2 != 0) {
|
||||
cmd_list.current_index++;
|
||||
}
|
||||
|
||||
// Read the header and the value to write.
|
||||
const u32 value = cmd_list.head[cmd_list.current_index++];
|
||||
const CommandHeader header{cmd_list.head[cmd_list.current_index++]};
|
||||
|
||||
// Write to the requested PICA register.
|
||||
if (process_write(header.cmd_id, value))
|
||||
break;
|
||||
|
||||
// Write any extra paramters as well.
|
||||
for (u32 i = 0; i < header.extra_data_length; ++i) {
|
||||
const u32 cmd = header.cmd_id + (header.group_commands ? i + 1 : 0);
|
||||
const u32 extra_value = cmd_list.head[cmd_list.current_index++];
|
||||
if (process_write(cmd, extra_value))
|
||||
break;
|
||||
}
|
||||
|
||||
iterations++;
|
||||
}
|
||||
|
||||
return find_info;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void PicaCore::CommandList::serialize(Archive& ar, const u32 file_version) {
|
||||
ar & addr;
|
||||
ar & length;
|
||||
ar & current_index;
|
||||
ar& addr;
|
||||
ar& length;
|
||||
ar& current_index;
|
||||
if (Archive::is_loading::value) {
|
||||
const u8* ptr = Core::System::GetInstance().Memory().GetPhysicalPointer(addr);
|
||||
head = reinterpret_cast<const u32*>(ptr);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/gsp/gsp_interrupt.h"
|
||||
#include "video_core/pica/geometry_pipeline.h"
|
||||
#include "video_core/pica/packed_attribute.h"
|
||||
@@ -36,7 +37,7 @@ public:
|
||||
|
||||
void SetInterruptHandler(Service::GSP::InterruptHandler& signal_interrupt);
|
||||
|
||||
void ProcessCmdList(PAddr list, u32 size);
|
||||
void ProcessCmdList(PAddr list, u32 size, bool ignore_list);
|
||||
|
||||
private:
|
||||
void InitializeRegs();
|
||||
@@ -109,10 +110,10 @@ public:
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const u32 file_version) {
|
||||
ar & input_vertex;
|
||||
ar & current_attribute;
|
||||
ar & reset_geometry_pipeline;
|
||||
ar & queue;
|
||||
ar& input_vertex;
|
||||
ar& current_attribute;
|
||||
ar& reset_geometry_pipeline;
|
||||
ar& queue;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -199,7 +200,7 @@ public:
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const u32 file_version) {
|
||||
ar & raw;
|
||||
ar& raw;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -256,21 +257,31 @@ private:
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const u32 file_version) {
|
||||
ar & regs_lcd;
|
||||
ar & regs.reg_array;
|
||||
ar & gs_unit;
|
||||
ar & vs_setup;
|
||||
ar & gs_setup;
|
||||
ar & proctex;
|
||||
ar & lighting;
|
||||
ar & fog;
|
||||
ar & input_default_attributes;
|
||||
ar & immediate;
|
||||
ar & geometry_pipeline;
|
||||
ar & primitive_assembler;
|
||||
ar & cmd_list;
|
||||
ar& regs_lcd;
|
||||
ar& regs.reg_array;
|
||||
ar& gs_unit;
|
||||
ar& vs_setup;
|
||||
ar& gs_setup;
|
||||
ar& proctex;
|
||||
ar& lighting;
|
||||
ar& fog;
|
||||
ar& input_default_attributes;
|
||||
ar& immediate;
|
||||
ar& geometry_pipeline;
|
||||
ar& primitive_assembler;
|
||||
ar& cmd_list;
|
||||
}
|
||||
|
||||
public:
|
||||
struct RenderPropertiesGuess {
|
||||
u32 vp_height;
|
||||
PAddr paddr;
|
||||
bool vp_heigh_found = false;
|
||||
bool paddr_found = false;
|
||||
};
|
||||
|
||||
RenderPropertiesGuess GuessCmdRenderProperties(PAddr list, u32 size);
|
||||
|
||||
private:
|
||||
Memory::MemorySystem& memory;
|
||||
VideoCore::RasterizerInterface* rasterizer;
|
||||
|
||||
@@ -82,5 +82,12 @@ public:
|
||||
[[maybe_unused]] const DiskResourceLoadCallback& callback) {}
|
||||
|
||||
virtual void SyncEntireState() {}
|
||||
|
||||
void SetAccurateMul(bool accurate_mul_) {
|
||||
accurate_mul = accurate_mul_;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool accurate_mul = false;
|
||||
};
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -175,7 +175,7 @@ void RasterizerOpenGL::TickFrame() {
|
||||
|
||||
void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading,
|
||||
const VideoCore::DiskResourceLoadCallback& callback) {
|
||||
shader_manager.LoadDiskCache(stop_loading, callback);
|
||||
shader_manager.LoadDiskCache(stop_loading, callback, accurate_mul);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncFixedState() {
|
||||
@@ -271,7 +271,7 @@ void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset,
|
||||
|
||||
bool RasterizerOpenGL::SetupVertexShader() {
|
||||
MICROPROFILE_SCOPE(OpenGL_VS);
|
||||
return shader_manager.UseProgrammableVertexShader(regs, pica.vs_setup);
|
||||
return shader_manager.UseProgrammableVertexShader(regs, pica.vs_setup, accurate_mul);
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::SetupGeometryShader() {
|
||||
@@ -333,7 +333,7 @@ bool RasterizerOpenGL::AccelerateDrawBatchInternal(bool is_indexed) {
|
||||
SetupVertexArray(buffer_ptr, buffer_offset, vs_input_index_min, vs_input_index_max);
|
||||
vertex_buffer.Unmap(vs_input_size);
|
||||
|
||||
shader_manager.ApplyTo(state);
|
||||
shader_manager.ApplyTo(state, accurate_mul);
|
||||
state.Apply();
|
||||
|
||||
if (is_indexed) {
|
||||
@@ -458,7 +458,7 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
||||
state.draw.vertex_buffer = vertex_buffer.GetHandle();
|
||||
shader_manager.UseTrivialVertexShader();
|
||||
shader_manager.UseTrivialGeometryShader();
|
||||
shader_manager.ApplyTo(state);
|
||||
shader_manager.ApplyTo(state, accurate_mul);
|
||||
state.Apply();
|
||||
|
||||
std::size_t max_vertices = 3 * (VERTEX_BUFFER_SIZE / (3 * sizeof(HardwareVertex)));
|
||||
@@ -970,9 +970,10 @@ void RasterizerOpenGL::SyncAndUploadLUTsLF() {
|
||||
if (fs_uniform_block_data.fog_lut_dirty || invalidate) {
|
||||
std::array<Common::Vec2f, 128> new_data;
|
||||
|
||||
std::transform(
|
||||
pica.fog.lut.begin(), pica.fog.lut.end(), new_data.begin(),
|
||||
[](const auto& entry) { return Common::Vec2f{entry.ToFloat(), entry.DiffToFloat()}; });
|
||||
std::transform(pica.fog.lut.begin(), pica.fog.lut.end(), new_data.begin(),
|
||||
[](const auto& entry) {
|
||||
return Common::Vec2f{entry.ToFloat(), entry.DiffToFloat()};
|
||||
});
|
||||
|
||||
if (new_data != fog_lut_data || invalidate) {
|
||||
fog_lut_data = new_data;
|
||||
|
||||
@@ -82,7 +82,7 @@ static std::set<GLenum> GetSupportedFormats() {
|
||||
}
|
||||
|
||||
static std::tuple<PicaVSConfig, Pica::ShaderSetup> BuildVSConfigFromRaw(
|
||||
const ShaderDiskCacheRaw& raw, const Driver& driver) {
|
||||
const ShaderDiskCacheRaw& raw, const Driver& driver, bool accurate_mul) {
|
||||
Pica::ProgramCode program_code{};
|
||||
Pica::SwizzleData swizzle_data{};
|
||||
std::copy_n(raw.GetProgramCode().begin(), Pica::MAX_PROGRAM_CODE_LENGTH, program_code.begin());
|
||||
@@ -96,7 +96,7 @@ static std::tuple<PicaVSConfig, Pica::ShaderSetup> BuildVSConfigFromRaw(
|
||||
// and care about proper quaternions. Otherwise just use standard vertex+fragment shaders
|
||||
const bool use_geometry_shader = !raw.GetRawShaderConfig().lighting.disable;
|
||||
return {PicaVSConfig{raw.GetRawShaderConfig(), setup, driver.HasClipCullDistance(),
|
||||
use_geometry_shader},
|
||||
use_geometry_shader, accurate_mul},
|
||||
setup};
|
||||
}
|
||||
|
||||
@@ -337,12 +337,14 @@ ShaderProgramManager::ShaderProgramManager(Frontend::EmuWindow& emu_window_, con
|
||||
ShaderProgramManager::~ShaderProgramManager() = default;
|
||||
|
||||
bool ShaderProgramManager::UseProgrammableVertexShader(const Pica::RegsInternal& regs,
|
||||
Pica::ShaderSetup& setup) {
|
||||
Pica::ShaderSetup& setup,
|
||||
bool accurate_mul) {
|
||||
// Enable the geometry-shader only if we are actually doing per-fragment lighting
|
||||
// and care about proper quaternions. Otherwise just use standard vertex+fragment shaders
|
||||
const bool use_geometry_shader = !regs.lighting.disable;
|
||||
|
||||
PicaVSConfig config{regs, setup, driver.HasClipCullDistance(), use_geometry_shader};
|
||||
PicaVSConfig config{regs, setup, driver.HasClipCullDistance(), use_geometry_shader,
|
||||
accurate_mul};
|
||||
auto [handle, result] = impl->programmable_vertex_shaders.Get(config, setup);
|
||||
if (handle == 0)
|
||||
return false;
|
||||
@@ -358,9 +360,8 @@ bool ShaderProgramManager::UseProgrammableVertexShader(const Pica::RegsInternal&
|
||||
const u64 unique_identifier = GetUniqueIdentifier(regs, program_code);
|
||||
const ShaderDiskCacheRaw raw{unique_identifier, ProgramType::VS, regs,
|
||||
std::move(program_code)};
|
||||
const bool sanitize_mul = Settings::values.shaders_accurate_mul.GetValue();
|
||||
disk_cache.SaveRaw(raw);
|
||||
disk_cache.SaveDecompiled(unique_identifier, *result, sanitize_mul);
|
||||
disk_cache.SaveDecompiled(unique_identifier, *result, accurate_mul);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -398,7 +399,7 @@ void ShaderProgramManager::UseFragmentShader(const Pica::RegsInternal& regs,
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderProgramManager::ApplyTo(OpenGLState& state) {
|
||||
void ShaderProgramManager::ApplyTo(OpenGLState& state, bool accurate_mul) {
|
||||
if (impl->separable) {
|
||||
if (driver.HasBug(DriverBug::ShaderStageChangeFreeze)) {
|
||||
glUseProgramStages(
|
||||
@@ -418,15 +419,15 @@ void ShaderProgramManager::ApplyTo(OpenGLState& state) {
|
||||
cached_program.Create(false,
|
||||
std::array{impl->current.vs, impl->current.gs, impl->current.fs});
|
||||
auto& disk_cache = impl->disk_cache;
|
||||
const bool sanitize_mul = Settings::values.shaders_accurate_mul.GetValue();
|
||||
disk_cache.SaveDumpToFile(unique_identifier, cached_program.handle, sanitize_mul);
|
||||
disk_cache.SaveDumpToFile(unique_identifier, cached_program.handle, accurate_mul);
|
||||
}
|
||||
state.draw.shader_program = cached_program.handle;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||
const VideoCore::DiskResourceLoadCallback& callback) {
|
||||
const VideoCore::DiskResourceLoadCallback& callback,
|
||||
bool accurate_mul) {
|
||||
auto& disk_cache = impl->disk_cache;
|
||||
const auto transferable = disk_cache.LoadTransferable();
|
||||
if (!transferable) {
|
||||
@@ -484,9 +485,8 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||
|
||||
if (dump != dump_map.end() && decomp != decompiled_map.end()) {
|
||||
// Only load the vertex shader if its sanitize_mul setting matches
|
||||
const bool sanitize_mul = Settings::values.shaders_accurate_mul.GetValue();
|
||||
if (raw.GetProgramType() == ProgramType::VS &&
|
||||
decomp->second.sanitize_mul != sanitize_mul) {
|
||||
decomp->second.sanitize_mul != accurate_mul) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -502,7 +502,7 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||
// we have both the binary shader and the decompiled, so inject it into the
|
||||
// cache
|
||||
if (raw.GetProgramType() == ProgramType::VS) {
|
||||
auto [conf, setup] = BuildVSConfigFromRaw(raw, driver);
|
||||
auto [conf, setup] = BuildVSConfigFromRaw(raw, driver, accurate_mul);
|
||||
std::scoped_lock lock(mutex);
|
||||
impl->programmable_vertex_shaders.Inject(conf, decomp->second.code,
|
||||
std::move(shader));
|
||||
@@ -541,8 +541,7 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||
const auto decomp{decompiled_map.find(unique_identifier)};
|
||||
|
||||
// Only load the program if its sanitize_mul setting matches
|
||||
const bool sanitize_mul = Settings::values.shaders_accurate_mul.GetValue();
|
||||
if (decomp->second.sanitize_mul != sanitize_mul) {
|
||||
if (decomp->second.sanitize_mul != accurate_mul) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -610,7 +609,7 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||
// Otherwise decompile and build the shader at boot and save the result to the
|
||||
// precompiled file
|
||||
if (raw.GetProgramType() == ProgramType::VS) {
|
||||
auto [conf, setup] = BuildVSConfigFromRaw(raw, driver);
|
||||
auto [conf, setup] = BuildVSConfigFromRaw(raw, driver, accurate_mul);
|
||||
code = GLSL::GenerateVertexShader(setup, conf, impl->separable);
|
||||
OGLShaderStage stage{impl->separable};
|
||||
stage.Create(code.c_str(), GL_VERTEX_SHADER);
|
||||
|
||||
@@ -38,9 +38,10 @@ public:
|
||||
~ShaderProgramManager();
|
||||
|
||||
void LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||
const VideoCore::DiskResourceLoadCallback& callback);
|
||||
const VideoCore::DiskResourceLoadCallback& callback, bool accurate_mul);
|
||||
|
||||
bool UseProgrammableVertexShader(const Pica::RegsInternal& config, Pica::ShaderSetup& setup);
|
||||
bool UseProgrammableVertexShader(const Pica::RegsInternal& config, Pica::ShaderSetup& setup,
|
||||
bool accurate_mul);
|
||||
|
||||
void UseTrivialVertexShader();
|
||||
|
||||
@@ -50,7 +51,7 @@ public:
|
||||
|
||||
void UseFragmentShader(const Pica::RegsInternal& config, const Pica::Shader::UserConfig& user);
|
||||
|
||||
void ApplyTo(OpenGLState& state);
|
||||
void ApplyTo(OpenGLState& state, bool accurate_mul);
|
||||
|
||||
private:
|
||||
Frontend::EmuWindow& emu_window;
|
||||
|
||||
@@ -334,13 +334,14 @@ bool PipelineCache::BindPipeline(const PipelineInfo& info, bool wait_built) {
|
||||
|
||||
bool PipelineCache::UseProgrammableVertexShader(const Pica::RegsInternal& regs,
|
||||
Pica::ShaderSetup& setup,
|
||||
const VertexLayout& layout) {
|
||||
const VertexLayout& layout, bool accurate_mul) {
|
||||
// Enable the geometry-shader only if we are actually doing per-fragment lighting
|
||||
// and care about proper quaternions. Otherwise just use standard vertex+fragment shaders.
|
||||
// We also don't need the geometry shader if we have the barycentric extension.
|
||||
const bool use_geometry_shader = instance.UseGeometryShaders() && !regs.lighting.disable &&
|
||||
!instance.IsFragmentShaderBarycentricSupported();
|
||||
PicaVSConfig config{regs, setup, instance.IsShaderClipDistanceSupported(), use_geometry_shader};
|
||||
PicaVSConfig config{regs, setup, instance.IsShaderClipDistanceSupported(), use_geometry_shader,
|
||||
accurate_mul};
|
||||
|
||||
for (u32 i = 0; i < layout.attribute_count; i++) {
|
||||
const VertexAttribute& attr = layout.attributes[i];
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
|
||||
/// Binds a PICA decompiled vertex shader
|
||||
bool UseProgrammableVertexShader(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup,
|
||||
const VertexLayout& layout);
|
||||
const VertexLayout& layout, bool accurate_mul);
|
||||
|
||||
/// Binds a passthrough vertex shader
|
||||
void UseTrivialVertexShader();
|
||||
|
||||
@@ -330,7 +330,7 @@ void RasterizerVulkan::SetupFixedAttribs() {
|
||||
bool RasterizerVulkan::SetupVertexShader() {
|
||||
MICROPROFILE_SCOPE(Vulkan_VS);
|
||||
return pipeline_cache.UseProgrammableVertexShader(regs, pica.vs_setup,
|
||||
pipeline_info.vertex_layout);
|
||||
pipeline_info.vertex_layout, accurate_mul);
|
||||
}
|
||||
|
||||
bool RasterizerVulkan::SetupGeometryShader() {
|
||||
@@ -963,9 +963,10 @@ void RasterizerVulkan::SyncAndUploadLUTsLF() {
|
||||
if (fs_uniform_block_data.fog_lut_dirty || invalidate) {
|
||||
std::array<Common::Vec2f, 128> new_data;
|
||||
|
||||
std::transform(
|
||||
pica.fog.lut.begin(), pica.fog.lut.end(), new_data.begin(),
|
||||
[](const auto& entry) { return Common::Vec2f{entry.ToFloat(), entry.DiffToFloat()}; });
|
||||
std::transform(pica.fog.lut.begin(), pica.fog.lut.end(), new_data.begin(),
|
||||
[](const auto& entry) {
|
||||
return Common::Vec2f{entry.ToFloat(), entry.DiffToFloat()};
|
||||
});
|
||||
|
||||
if (new_data != fog_lut_data || invalidate) {
|
||||
fog_lut_data = new_data;
|
||||
|
||||
75
src/video_core/right_eye_disabler.cpp
Normal file
75
src/video_core/right_eye_disabler.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright 2024 Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "right_eye_disabler.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/gpu_impl.h"
|
||||
|
||||
namespace VideoCore {
|
||||
bool RightEyeDisabler::ShouldAllowCmdQueueTrigger(PAddr addr, u32 size) {
|
||||
if (!enabled || !enable_for_frame)
|
||||
return true;
|
||||
|
||||
constexpr u32 top_screen_size = 0x00469000;
|
||||
|
||||
if (report_end_frame_pending) {
|
||||
ReportEndFrame();
|
||||
report_end_frame_pending = false;
|
||||
}
|
||||
cmd_queue_trigger_happened = true;
|
||||
|
||||
auto guess = gpu.impl->pica.GuessCmdRenderProperties(addr, size);
|
||||
if (guess.vp_height == top_screen_size && !top_screen_blocked) {
|
||||
if (top_screen_buf == 0) {
|
||||
top_screen_buf = guess.paddr;
|
||||
}
|
||||
top_screen_drawn = true;
|
||||
if (top_screen_transfered) {
|
||||
cmd_trigger_blocked = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
cmd_trigger_blocked = false;
|
||||
return true;
|
||||
}
|
||||
bool RightEyeDisabler::ShouldAllowDisplayTransfer(PAddr src_address, size_t size) {
|
||||
if (!enabled || !enable_for_frame)
|
||||
return true;
|
||||
|
||||
if (size >= 400 && !top_screen_blocked) {
|
||||
if (top_screen_drawn && src_address == top_screen_buf) {
|
||||
top_screen_transfered = true;
|
||||
}
|
||||
|
||||
if (src_address == top_screen_buf && cmd_trigger_blocked) {
|
||||
top_screen_blocked = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd_queue_trigger_happened)
|
||||
display_tranfer_happened = true;
|
||||
return true;
|
||||
}
|
||||
void RightEyeDisabler::ReportEndFrame() {
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
enable_for_frame = Settings::values.disable_right_eye_render.GetValue();
|
||||
|
||||
if (display_tranfer_happened) {
|
||||
top_screen_drawn = false;
|
||||
top_screen_transfered = false;
|
||||
top_screen_blocked = false;
|
||||
cmd_queue_trigger_happened = false;
|
||||
cmd_trigger_blocked = false;
|
||||
display_tranfer_happened = false;
|
||||
top_screen_buf = 0;
|
||||
} else {
|
||||
report_end_frame_pending = true;
|
||||
}
|
||||
}
|
||||
} // namespace VideoCore
|
||||
41
src/video_core/right_eye_disabler.h
Normal file
41
src/video_core/right_eye_disabler.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2024 Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace VideoCore {
|
||||
class GPU;
|
||||
|
||||
class RightEyeDisabler {
|
||||
public:
|
||||
RightEyeDisabler(GPU& gpu) : gpu{gpu} {}
|
||||
|
||||
bool ShouldAllowCmdQueueTrigger(PAddr addr, u32 size);
|
||||
bool ShouldAllowDisplayTransfer(PAddr src_address, size_t size);
|
||||
|
||||
void ReportEndFrame();
|
||||
|
||||
void SetEnabled(bool enable) {
|
||||
enabled = enable;
|
||||
}
|
||||
|
||||
private:
|
||||
bool enabled = true;
|
||||
bool enable_for_frame = true;
|
||||
|
||||
bool top_screen_drawn = false;
|
||||
bool top_screen_transfered = false;
|
||||
bool top_screen_blocked = false;
|
||||
bool cmd_trigger_blocked = false;
|
||||
PAddr top_screen_buf = 0;
|
||||
|
||||
bool cmd_queue_trigger_happened = false;
|
||||
bool display_tranfer_happened = false;
|
||||
bool report_end_frame_pending = false;
|
||||
|
||||
GPU& gpu;
|
||||
};
|
||||
} // namespace VideoCore
|
||||
@@ -37,14 +37,14 @@ void PicaGSConfigState::Init(const Pica::RegsInternal& regs, bool use_clip_plane
|
||||
}
|
||||
|
||||
void PicaVSConfigState::Init(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup,
|
||||
bool use_clip_planes_, bool use_geometry_shader_) {
|
||||
bool use_clip_planes_, bool use_geometry_shader_, bool accurate_mul_) {
|
||||
use_clip_planes = use_clip_planes_;
|
||||
use_geometry_shader = use_geometry_shader_;
|
||||
sanitize_mul = accurate_mul_;
|
||||
|
||||
program_hash = setup.GetProgramCodeHash();
|
||||
swizzle_hash = setup.GetSwizzleDataHash();
|
||||
main_offset = regs.vs.main_offset;
|
||||
sanitize_mul = Settings::values.shaders_accurate_mul.GetValue();
|
||||
|
||||
num_outputs = 0;
|
||||
load_flags.fill(AttribLoadFlags::Float);
|
||||
@@ -60,8 +60,8 @@ void PicaVSConfigState::Init(const Pica::RegsInternal& regs, Pica::ShaderSetup&
|
||||
}
|
||||
|
||||
PicaVSConfig::PicaVSConfig(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup,
|
||||
bool use_clip_planes_, bool use_geometry_shader_) {
|
||||
state.Init(regs, setup, use_clip_planes_, use_geometry_shader_);
|
||||
bool use_clip_planes_, bool use_geometry_shader_, bool accurate_mul_) {
|
||||
state.Init(regs, setup, use_clip_planes_, use_geometry_shader_, accurate_mul_);
|
||||
}
|
||||
|
||||
PicaFixedGSConfig::PicaFixedGSConfig(const Pica::RegsInternal& regs, bool use_clip_planes_) {
|
||||
|
||||
@@ -66,7 +66,7 @@ struct PicaGSConfigState {
|
||||
*/
|
||||
struct PicaVSConfigState {
|
||||
void Init(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup, bool use_clip_planes_,
|
||||
bool use_geometry_shader_);
|
||||
bool use_geometry_shader_, bool accurate_mul_);
|
||||
|
||||
bool use_clip_planes;
|
||||
bool use_geometry_shader;
|
||||
@@ -92,7 +92,7 @@ struct PicaVSConfigState {
|
||||
*/
|
||||
struct PicaVSConfig : Common::HashableStruct<PicaVSConfigState> {
|
||||
explicit PicaVSConfig(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup,
|
||||
bool use_clip_planes_, bool use_geometry_shader_);
|
||||
bool use_clip_planes_, bool use_geometry_shader_, bool accurate_mul_);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user