9 Commits

Author SHA1 Message Date
Duckey77 fa107635c9 Attempt to create Metal Renderer 2023-03-08 11:41:15 -07:00
C.W. Betts e58e557efc Hide more symbols from included libraries. 2023-03-03 15:55:12 -07:00
C.W. Betts bc2f72c49e Merge branch 'moreMetalAttempt' of https://github.com/OpenEmu/dolphin into moreMetalAttempt
Also bump the version to match the Dolphin version.
2023-03-03 15:02:34 -07:00
C.W. Betts 1798ae9e27 Well, it almost links... 2023-03-03 14:25:49 -07:00
Duckey77 19d945f88b update OGLfx compatfile (still need to find and make modifications for OE) 2023-03-03 14:21:37 -07:00
Duckey77 dcbbd8d589 Add missing files and externals for build 2023-03-03 14:20:49 -07:00
C.W. Betts 37704af83d Adding missing classes to the Xcode project. 2023-03-03 02:49:09 -07:00
C.W. Betts 630e35efd9 More recent Dolphin pokes
TODO:
* include Speex library.
* Find a way to generate glsl_intrinsic_header.h
* Build SPIRV-Cross
2023-03-03 02:32:37 -07:00
C.W. Betts 513e5e9a9b Metal attempt 1.
Currently doesn't build.
2023-03-02 21:06:03 -07:00
15 changed files with 4218 additions and 2404 deletions
+29 -28
View File
@@ -13,6 +13,7 @@
#include "Core/ConfigManager.h"
// This shouldn't be a global, at least not here.
extern std::unique_ptr<SoundStream> g_sound_stream;
std::unique_ptr<SoundStream> g_sound_stream;
static bool s_audio_dump_start = false;
@@ -23,42 +24,42 @@ namespace AudioCommon
static const int AUDIO_VOLUME_MIN = 0;
static const int AUDIO_VOLUME_MAX = 100;
void InitSoundStream()
void InitSoundStream(Core::System& system)
{
g_sound_stream = std::make_unique<OpenEmuAudioStream>();
if (!g_sound_stream->Init())
{
WARN_LOG(AUDIO, "Could not initialize backend");
WARN_LOG_FMT(AUDIO, "Could not initialize backend");
g_sound_stream = std::make_unique<NullSound>();
}
UpdateSoundStream();
SetSoundStreamRunning(true);
UpdateSoundStream(system);
SetSoundStreamRunning(system, true);
}
void PostInitSoundStream()
void PostInitSoundStream(Core::System& system)
{
// This needs to be called after AudioInterface::Init and SerialInterface::Init (for GBA devices)
// where input sample rates are set
UpdateSoundStream();
SetSoundStreamRunning(true);
UpdateSoundStream(system);
SetSoundStreamRunning(system, true);
if (Config::Get(Config::MAIN_DUMP_AUDIO) && !s_audio_dump_start)
StartAudioDump();
StartAudioDump(system);
}
void ShutdownSoundStream()
void ShutdownSoundStream(Core::System& system)
{
INFO_LOG(AUDIO, "Shutting down sound stream");
INFO_LOG_FMT(AUDIO, "Shutting down sound stream");
if (Config::Get(Config::MAIN_DUMP_AUDIO) && s_audio_dump_start)
StopAudioDump();
StopAudioDump(system);
SetSoundStreamRunning(false);
SetSoundStreamRunning(system, false);
g_sound_stream.reset();
INFO_LOG(AUDIO, "Done shutting down sound stream");
INFO_LOG_FMT(AUDIO, "Done shutting down sound stream");
}
std::string GetDefaultSoundBackend()
@@ -97,7 +98,7 @@ DPL2Quality GetDefaultDPL2Quality()
return false;
}
void UpdateSoundStream()
void UpdateSoundStream(Core::System& system)
{
if (g_sound_stream)
{
@@ -106,7 +107,7 @@ DPL2Quality GetDefaultDPL2Quality()
}
}
void SetSoundStreamRunning(bool running)
void SetSoundStreamRunning(Core::System& system, bool running)
{
if (!g_sound_stream)
return;
@@ -118,20 +119,20 @@ DPL2Quality GetDefaultDPL2Quality()
if (g_sound_stream->SetRunning(running))
return;
if (running)
ERROR_LOG(AUDIO, "Error starting stream.");
ERROR_LOG_FMT(AUDIO, "Error starting stream.");
else
ERROR_LOG(AUDIO, "Error stopping stream.");
ERROR_LOG_FMT(AUDIO, "Error stopping stream.");
}
void SendAIBuffer(const short* samples, unsigned int num_samples)
void SendAIBuffer(Core::System& system, const short* samples, unsigned int num_samples)
{
if (!g_sound_stream)
return;
if (Config::Get(Config::MAIN_DUMP_AUDIO) && !s_audio_dump_start)
StartAudioDump();
StartAudioDump(system);
else if (!Config::Get(Config::MAIN_DUMP_AUDIO) && s_audio_dump_start)
StopAudioDump();
StopAudioDump(system);
Mixer* pMixer = g_sound_stream->GetMixer();
@@ -141,7 +142,7 @@ DPL2Quality GetDefaultDPL2Quality()
}
}
void StartAudioDump()
void StartAudioDump(Core::System& system)
{
std::string audio_file_name_dtk = File::GetUserPath(D_DUMPAUDIO_IDX) + "dtkdump.wav";
std::string audio_file_name_dsp = File::GetUserPath(D_DUMPAUDIO_IDX) + "dspdump.wav";
@@ -152,7 +153,7 @@ DPL2Quality GetDefaultDPL2Quality()
s_audio_dump_start = true;
}
void StopAudioDump()
void StopAudioDump(Core::System& system)
{
if (!g_sound_stream)
return;
@@ -161,7 +162,7 @@ DPL2Quality GetDefaultDPL2Quality()
s_audio_dump_start = false;
}
void IncreaseVolume(unsigned short offset)
void IncreaseVolume(Core::System& system, unsigned short offset)
{
Config::SetBaseOrCurrent(Config::MAIN_AUDIO_MUTED, false);
int currentVolume = Config::Get(Config::MAIN_AUDIO_VOLUME);
@@ -169,10 +170,10 @@ DPL2Quality GetDefaultDPL2Quality()
if (currentVolume > AUDIO_VOLUME_MAX)
currentVolume = AUDIO_VOLUME_MAX;
Config::SetBaseOrCurrent(Config::MAIN_AUDIO_VOLUME, currentVolume);
UpdateSoundStream();
UpdateSoundStream(system);
}
void DecreaseVolume(unsigned short offset)
void DecreaseVolume(Core::System& system, unsigned short offset)
{
Config::SetBaseOrCurrent(Config::MAIN_AUDIO_MUTED, false);
int currentVolume = Config::Get(Config::MAIN_AUDIO_VOLUME);
@@ -180,13 +181,13 @@ DPL2Quality GetDefaultDPL2Quality()
if (currentVolume < AUDIO_VOLUME_MIN)
currentVolume = AUDIO_VOLUME_MIN;
Config::SetBaseOrCurrent(Config::MAIN_AUDIO_VOLUME, currentVolume);
UpdateSoundStream();
UpdateSoundStream(system);
}
void ToggleMuteVolume()
void ToggleMuteVolume(Core::System& system)
{
bool isMuted = Config::Get(Config::MAIN_AUDIO_MUTED);
Config::SetBaseOrCurrent(Config::MAIN_AUDIO_MUTED, !isMuted);
UpdateSoundStream();
UpdateSoundStream(system);
}
} // namespace AudioCommon
+400 -477
View File
File diff suppressed because it is too large Load Diff
+11 -73
View File
@@ -16,7 +16,6 @@
#include "AudioCommon/AudioCommon.h"
#include "Common/Assert.h"
#include "Common/CDUtils.h"
#include "Common/CommonPaths.h"
#include "Common/CommonTypes.h"
#include "Common/Config/Config.h"
@@ -34,6 +33,7 @@
#include "Core/Config/SYSCONFSettings.h"
#include "Core/ConfigLoaders/GameConfigLoader.h"
#include "Core/Core.h"
#include "Core/System.h"
#include "Core/FifoPlayer/FifoDataFile.h"
#include "Core/HLE/HLE.h"
#include "Core/HW/DVD/DVDInterface.h"
@@ -169,23 +169,13 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri
m_title_description = title_database.Describe(m_gametdb_id, language);
NOTICE_LOG_FMT(CORE, "Active title: {}", m_title_description);
Host_TitleChanged();
if (Core::IsRunning())
{
Core::UpdateTitle();
}
Config::AddLayer(ConfigLoaders::GenerateGlobalGameConfigLoader(game_id, revision));
Config::AddLayer(ConfigLoaders::GenerateLocalGameConfigLoader(game_id, revision));
if (Core::IsRunning())
{
// TODO: have a callback mechanism for title changes?
if (!g_symbolDB.IsEmpty())
{
g_symbolDB.Clear();
Host_NotifyMapLoaded();
}
CBoot::LoadMapFromFilename();
HLE::Reload();
PatchEngine::Reload();
HiresTexture::Update();
}
}
void SConfig::LoadDefaults()
@@ -198,53 +188,6 @@ void SConfig::LoadDefaults()
ResetRunningGameMetadata();
}
// The reason we need this function is because some memory card code
// expects to get a non-NTSC-K region even if we're emulating an NTSC-K Wii.
DiscIO::Region SConfig::ToGameCubeRegion(DiscIO::Region region)
{
if (region != DiscIO::Region::NTSC_K)
return region;
// GameCube has no NTSC-K region. No choice of replacement value is completely
// non-arbitrary, but let's go with NTSC-J since Korean GameCubes are NTSC-J.
return DiscIO::Region::NTSC_J;
}
const char* SConfig::GetDirectoryForRegion(DiscIO::Region region)
{
if (region == DiscIO::Region::Unknown)
region = ToGameCubeRegion(GetFallbackRegion());
switch (region)
{
case DiscIO::Region::NTSC_J:
return JAP_DIR;
case DiscIO::Region::NTSC_U:
return USA_DIR;
case DiscIO::Region::PAL:
return EUR_DIR;
case DiscIO::Region::NTSC_K:
ASSERT_MSG(BOOT, false, "NTSC-K is not a valid GameCube region");
return JAP_DIR; // See ToGameCubeRegion
default:
ASSERT_MSG(BOOT, false, "Default case should not be reached");
return EUR_DIR;
}
}
std::string SConfig::GetBootROMPath(const std::string& region_directory) const
{
const std::string path =
File::GetUserPath(D_GCUSER_IDX) + DIR_SEP + region_directory + DIR_SEP GC_IPL;
if (!File::Exists(path))
return File::GetSysDirectory() + GC_SYS_DIR + DIR_SEP + region_directory + DIR_SEP GC_IPL;
return path;
}
struct SetGameMetadata
{
SetGameMetadata(SConfig* config_, DiscIO::Region* region_) : config(config_), region(region_) {}
@@ -346,21 +289,16 @@ bool SConfig::SetPathsAndGameMetadata(const BootParameters& boot)
return false;
if (m_region == DiscIO::Region::Unknown)
m_region = GetFallbackRegion();
m_region = Config::Get(Config::MAIN_FALLBACK_REGION);
// Set up paths
const std::string region_dir = GetDirectoryForRegion(ToGameCubeRegion(m_region));
const std::string region_dir = Config::GetDirectoryForRegion(Config::ToGameCubeRegion(m_region));
m_strSRAM = File::GetUserPath(F_GCSRAM_IDX);
m_strBootROM = GetBootROMPath(region_dir);
m_strBootROM = Config::GetBootROMPath(region_dir);
return true;
}
DiscIO::Region SConfig::GetFallbackRegion()
{
return Config::Get(Config::MAIN_FALLBACK_REGION);
}
DiscIO::Language SConfig::GetCurrentLanguage(bool wii) const
{
DiscIO::Language language;
@@ -450,7 +388,7 @@ IniFile SConfig::LoadGameIni(const std::string& id, std::optional<u16> revision)
return game_ini;
}
void SConfig::OnNewTitleLoad()
void SConfig::OnNewTitleLoad(const Core::CPUThreadGuard &guard)
{
if (!Core::IsRunning())
return;
@@ -460,8 +398,8 @@ void SConfig::OnNewTitleLoad()
g_symbolDB.Clear();
Host_NotifyMapLoaded();
}
CBoot::LoadMapFromFilename();
HLE::Reload();
CBoot::LoadMapFromFilename(guard);
HLE::Reload(Core::System::GetInstance());
PatchEngine::Reload();
HiresTexture::Update();
}
+3 -9
View File
@@ -82,7 +82,7 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi)
// nothing needed
#endif
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
ciface::DualShockUDPClient::Init();
m_input_backends.emplace_back(ciface::DualShockUDPClient::CreateInputBackend(this));
#endif
//OpenEmu initalize OpenEmu Input
@@ -173,9 +173,6 @@ void ControllerInterface::RefreshDevices(RefreshReason reason)
#ifdef CIFACE_USE_PIPES
ciface::Pipes::PopulateDevices();
#endif
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
ciface::DualShockUDPClient::PopulateDevices();
#endif
WiimoteReal::ProcessWiimotePool();
@@ -228,9 +225,6 @@ void ControllerInterface::Shutdown()
#ifdef CIFACE_USE_EVDEV
ciface::evdev::Shutdown();
#endif
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
ciface::DualShockUDPClient::DeInit();
#endif
}
bool ControllerInterface::AddDevice(std::shared_ptr<ciface::Core::Device> device)
@@ -265,7 +259,7 @@ bool ControllerInterface::AddDevice(std::shared_ptr<ciface::Core::Device> device
device->SetId(id);
}
NOTICE_LOG(SERIALINTERFACE, "Added device: %s", device->GetQualifiedName().c_str());
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "Added device: {}", device->GetQualifiedName());
m_devices.emplace_back(std::move(device));
}
@@ -281,7 +275,7 @@ void ControllerInterface::RemoveDevice(std::function<bool(const ciface::Core::De
auto it = std::remove_if(m_devices.begin(), m_devices.end(), [&callback](const auto& dev) {
if (callback(dev.get()))
{
NOTICE_LOG(SERIALINTERFACE, "Removed device: %s", dev->GetQualifiedName().c_str());
NOTICE_LOG_FMT(SERIALINTERFACE, "Removed device: {}", dev->GetQualifiedName());
return true;
}
return false;
+524
View File
@@ -0,0 +1,524 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoBackends/Metal/MTLGfx.h"
#include "VideoBackends/Metal/MTLBoundingBox.h"
#include "VideoBackends/Metal/MTLObjectCache.h"
#include "VideoBackends/Metal/MTLPipeline.h"
#include "VideoBackends/Metal/MTLStateTracker.h"
#include "VideoBackends/Metal/MTLTexture.h"
#include "VideoBackends/Metal/MTLUtil.h"
#include "VideoBackends/Metal/MTLVertexFormat.h"
#include "VideoBackends/Metal/MTLVertexManager.h"
#include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/Present.h"
#include "VideoCommon/VideoBackendBase.h"
#import "DolphinGameCore.h"
#include <fstream>
Metal::Gfx::Gfx(MRCOwned<CAMetalLayer*> layer) : m_layer(std::move(layer))
{
UpdateActiveConfig();
[m_layer setDisplaySyncEnabled:g_ActiveConfig.bVSyncActive];
SetupSurface();
g_state_tracker->FlushEncoders();
}
Metal::Gfx::~Gfx() = default;
bool Metal::Gfx::IsHeadless() const
{
return m_layer == nullptr;
}
// MARK: Texture Creation
std::unique_ptr<AbstractTexture> Metal::Gfx::CreateTexture(const TextureConfig& config,
std::string_view name)
{
@autoreleasepool
{
MRCOwned<MTLTextureDescriptor*> desc = MRCTransfer([MTLTextureDescriptor new]);
[desc setTextureType:config.samples > 1 ? MTLTextureType2DMultisampleArray :
MTLTextureType2DArray];
[desc setPixelFormat:Util::FromAbstract(config.format)];
[desc setWidth:config.width];
[desc setHeight:config.height];
[desc setMipmapLevelCount:config.levels];
[desc setArrayLength:config.layers];
[desc setSampleCount:config.samples];
[desc setStorageMode:MTLStorageModePrivate];
MTLTextureUsage usage = MTLTextureUsageShaderRead;
if (config.IsRenderTarget())
usage |= MTLTextureUsageRenderTarget;
if (config.IsComputeImage())
usage |= MTLTextureUsageShaderWrite;
[desc setUsage:usage];
id<MTLTexture> texture = [g_device newTextureWithDescriptor:desc];
if (!texture)
return nullptr;
if (name.empty())
[texture setLabel:[NSString stringWithFormat:@"Texture %d", m_texture_counter++]];
else
[texture setLabel:MRCTransfer([[NSString alloc] initWithBytes:name.data()
length:name.size()
encoding:NSUTF8StringEncoding])];
return std::make_unique<Texture>(MRCTransfer(texture), config);
}
}
std::unique_ptr<AbstractStagingTexture>
Metal::Gfx::CreateStagingTexture(StagingTextureType type, const TextureConfig& config)
{
@autoreleasepool
{
const size_t stride = config.GetStride();
const size_t buffer_size = stride * static_cast<size_t>(config.height);
MTLResourceOptions options = MTLStorageModeShared;
if (type == StagingTextureType::Upload)
options |= MTLResourceCPUCacheModeWriteCombined;
id<MTLBuffer> buffer = [g_device newBufferWithLength:buffer_size options:options];
if (!buffer)
return nullptr;
[buffer
setLabel:[NSString stringWithFormat:@"Staging Texture %d", m_staging_texture_counter++]];
return std::make_unique<StagingTexture>(MRCTransfer(buffer), type, config);
}
}
std::unique_ptr<AbstractFramebuffer>
Metal::Gfx::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment)
{
AbstractTexture* const either_attachment = color_attachment ? color_attachment : depth_attachment;
return std::make_unique<Framebuffer>(
color_attachment, depth_attachment, either_attachment->GetWidth(),
either_attachment->GetHeight(), either_attachment->GetLayers(),
either_attachment->GetSamples());
}
// MARK: Pipeline Creation
std::unique_ptr<AbstractShader> Metal::Gfx::CreateShaderFromSource(ShaderStage stage,
std::string_view source,
std::string_view name)
{
std::optional<std::string> msl = Util::TranslateShaderToMSL(stage, source);
if (!msl.has_value())
{
PanicAlertFmt("Failed to convert shader {} to MSL", name);
return nullptr;
}
return CreateShaderFromMSL(stage, std::move(*msl), source, name);
}
std::unique_ptr<AbstractShader> Metal::Gfx::CreateShaderFromBinary(ShaderStage stage,
const void* data, size_t length,
std::string_view name)
{
return CreateShaderFromMSL(stage, std::string(static_cast<const char*>(data), length), {}, name);
}
// clang-format off
static const char* StageFilename(ShaderStage stage)
{
switch (stage)
{
case ShaderStage::Vertex: return "vs";
case ShaderStage::Geometry: return "gs";
case ShaderStage::Pixel: return "ps";
case ShaderStage::Compute: return "cs";
}
}
static NSString* GenericShaderName(ShaderStage stage)
{
switch (stage)
{
case ShaderStage::Vertex: return @"Vertex shader %d";
case ShaderStage::Geometry: return @"Geometry shader %d";
case ShaderStage::Pixel: return @"Pixel shader %d";
case ShaderStage::Compute: return @"Compute shader %d";
}
}
// clang-format on
std::unique_ptr<AbstractShader> Metal::Gfx::CreateShaderFromMSL(ShaderStage stage, std::string msl,
std::string_view glsl,
std::string_view name)
{
@autoreleasepool
{
NSError* err = nullptr;
auto DumpBadShader = [&](std::string_view msg) {
static int counter = 0;
std::string filename = VideoBackendBase::BadShaderFilename(StageFilename(stage), counter++);
std::ofstream stream(filename);
if (stream.good())
{
stream << msl << std::endl;
stream << "/*" << std::endl;
stream << msg << std::endl;
stream << "Error:" << std::endl;
stream << [[err localizedDescription] UTF8String] << std::endl;
if (!glsl.empty())
{
stream << "Original GLSL:" << std::endl;
stream << glsl << std::endl;
}
else
{
stream << "Shader was created with cached MSL so no GLSL is available." << std::endl;
}
}
stream << std::endl;
stream << "Dolphin Version: " << Common::GetScmRevStr() << std::endl;
stream << "Video Backend: " << g_video_backend->GetDisplayName() << std::endl;
stream << "*/" << std::endl;
stream.close();
PanicAlertFmt("{} (written to {})\n", msg, filename);
};
auto lib = MRCTransfer([g_device newLibraryWithSource:[NSString stringWithUTF8String:msl.data()]
options:nil
error:&err]);
if (err)
{
DumpBadShader(fmt::format("Failed to compile {}", name));
return nullptr;
}
auto fn = MRCTransfer([lib newFunctionWithName:@"main0"]);
if (!fn)
{
DumpBadShader(fmt::format("Shader {} is missing its main0 function", name));
return nullptr;
}
if (!name.empty())
[fn setLabel:MRCTransfer([[NSString alloc] initWithBytes:name.data()
length:name.size()
encoding:NSUTF8StringEncoding])];
else
[fn setLabel:[NSString stringWithFormat:GenericShaderName(stage),
m_shader_counter[static_cast<u32>(stage)]++]];
[lib setLabel:[fn label]];
if (stage == ShaderStage::Compute)
{
MTLComputePipelineReflection* reflection = nullptr;
auto desc = [MTLComputePipelineDescriptor new];
[desc setComputeFunction:fn];
[desc setLabel:[fn label]];
MRCOwned<id<MTLComputePipelineState>> pipeline =
MRCTransfer([g_device newComputePipelineStateWithDescriptor:desc
options:MTLPipelineOptionArgumentInfo
reflection:&reflection
error:&err]);
if (err)
{
DumpBadShader(fmt::format("Failed to compile compute pipeline {}", name));
return nullptr;
}
return std::make_unique<ComputePipeline>(stage, reflection, std::move(msl), std::move(fn),
std::move(pipeline));
}
return std::make_unique<Shader>(stage, std::move(msl), std::move(fn));
}
}
std::unique_ptr<NativeVertexFormat>
Metal::Gfx::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl)
{
@autoreleasepool
{
return std::make_unique<VertexFormat>(vtx_decl);
}
}
std::unique_ptr<AbstractPipeline> Metal::Gfx::CreatePipeline(const AbstractPipelineConfig& config,
const void* cache_data,
size_t cache_data_length)
{
return g_object_cache->CreatePipeline(config);
}
void Metal::Gfx::Flush()
{
@autoreleasepool
{
g_state_tracker->FlushEncoders();
}
}
void Metal::Gfx::WaitForGPUIdle()
{
@autoreleasepool
{
g_state_tracker->FlushEncoders();
g_state_tracker->WaitForFlushedEncoders();
}
}
void Metal::Gfx::OnConfigChanged(u32 bits)
{
AbstractGfx::OnConfigChanged(bits);
if (bits & CONFIG_CHANGE_BIT_VSYNC)
[m_layer setDisplaySyncEnabled:g_ActiveConfig.bVSyncActive];
if (bits & CONFIG_CHANGE_BIT_ANISOTROPY)
{
g_object_cache->ReloadSamplers();
g_state_tracker->ReloadSamplers();
}
}
void Metal::Gfx::ClearRegion(const MathUtil::Rectangle<int>& target_rc, bool color_enable,
bool alpha_enable, bool z_enable, u32 color, u32 z)
{
u32 framebuffer_width = m_current_framebuffer->GetWidth();
u32 framebuffer_height = m_current_framebuffer->GetHeight();
// All Metal render passes are fullscreen, so we can only run a fast clear if the target is too
if (target_rc == MathUtil::Rectangle<int>(0, 0, framebuffer_width, framebuffer_height))
{
// Determine whether the EFB has an alpha channel. If it doesn't, we can clear the alpha
// channel to 0xFF. This hopefully allows us to use the fast path in most cases.
if (bpmem.zcontrol.pixel_format == PixelFormat::RGB565_Z16 ||
bpmem.zcontrol.pixel_format == PixelFormat::RGB8_Z24 ||
bpmem.zcontrol.pixel_format == PixelFormat::Z24)
{
// Force alpha writes, and clear the alpha channel. This is different from the other backends,
// where the existing values of the alpha channel are preserved.
alpha_enable = true;
color &= 0x00FFFFFF;
}
bool c_ok = (color_enable && alpha_enable) ||
g_state_tracker->GetCurrentFramebuffer()->GetColorFormat() ==
AbstractTextureFormat::Undefined;
bool z_ok = z_enable || g_state_tracker->GetCurrentFramebuffer()->GetDepthFormat() ==
AbstractTextureFormat::Undefined;
if (c_ok && z_ok)
{
@autoreleasepool
{
// clang-format off
MTLClearColor clear_color = MTLClearColorMake(
static_cast<double>((color >> 16) & 0xFF) / 255.0,
static_cast<double>((color >> 8) & 0xFF) / 255.0,
static_cast<double>((color >> 0) & 0xFF) / 255.0,
static_cast<double>((color >> 24) & 0xFF) / 255.0);
// clang-format on
float z_normalized = static_cast<float>(z & 0xFFFFFF) / 16777216.0f;
if (!g_Config.backend_info.bSupportsReversedDepthRange)
z_normalized = 1.f - z_normalized;
g_state_tracker->BeginClearRenderPass(clear_color, z_normalized);
return;
}
}
}
g_state_tracker->EnableEncoderLabel(false);
AbstractGfx::ClearRegion(target_rc, color_enable, alpha_enable, z_enable, color, z);
g_state_tracker->EnableEncoderLabel(true);
}
void Metal::Gfx::SetPipeline(const AbstractPipeline* pipeline)
{
g_state_tracker->SetPipeline(static_cast<const Pipeline*>(pipeline));
}
void Metal::Gfx::SetFramebuffer(AbstractFramebuffer* framebuffer)
{
// Shouldn't be bound as a texture.
if (AbstractTexture* color = framebuffer->GetColorAttachment())
g_state_tracker->UnbindTexture(static_cast<Texture*>(color)->GetMTLTexture());
if (AbstractTexture* depth = framebuffer->GetDepthAttachment())
g_state_tracker->UnbindTexture(static_cast<Texture*>(depth)->GetMTLTexture());
m_current_framebuffer = framebuffer;
g_state_tracker->SetCurrentFramebuffer(static_cast<Framebuffer*>(framebuffer));
}
void Metal::Gfx::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer)
{
@autoreleasepool
{
SetFramebuffer(framebuffer);
g_state_tracker->BeginRenderPass(MTLLoadActionDontCare);
}
}
void Metal::Gfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer,
const ClearColor& color_value, float depth_value)
{
@autoreleasepool
{
SetFramebuffer(framebuffer);
MTLClearColor color =
MTLClearColorMake(color_value[0], color_value[1], color_value[2], color_value[3]);
g_state_tracker->BeginClearRenderPass(color, depth_value);
}
}
void Metal::Gfx::SetScissorRect(const MathUtil::Rectangle<int>& rc)
{
g_state_tracker->SetScissor(rc);
}
void Metal::Gfx::SetTexture(u32 index, const AbstractTexture* texture)
{
g_state_tracker->SetTexture(
index, texture ? static_cast<const Texture*>(texture)->GetMTLTexture() : nullptr);
}
void Metal::Gfx::SetSamplerState(u32 index, const SamplerState& state)
{
g_state_tracker->SetSampler(index, state);
}
void Metal::Gfx::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write)
{
g_state_tracker->SetComputeTexture(static_cast<const Texture*>(texture));
}
void Metal::Gfx::UnbindTexture(const AbstractTexture* texture)
{
g_state_tracker->UnbindTexture(static_cast<const Texture*>(texture)->GetMTLTexture());
}
void Metal::Gfx::SetViewport(float x, float y, float width, float height, float near_depth,
float far_depth)
{
g_state_tracker->SetViewport(x, y, width, height, near_depth, far_depth);
}
void Metal::Gfx::Draw(u32 base_vertex, u32 num_vertices)
{
@autoreleasepool
{
g_state_tracker->Draw(base_vertex, num_vertices);
}
}
void Metal::Gfx::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex)
{
@autoreleasepool
{
g_state_tracker->DrawIndexed(base_index, num_indices, base_vertex);
}
}
void Metal::Gfx::DispatchComputeShader(const AbstractShader* shader, //
u32 groupsize_x, u32 groupsize_y, u32 groupsize_z,
u32 groups_x, u32 groups_y, u32 groups_z)
{
@autoreleasepool
{
g_state_tracker->SetPipeline(static_cast<const ComputePipeline*>(shader));
g_state_tracker->DispatchComputeShader(groupsize_x, groupsize_y, groupsize_z, //
groups_x, groups_y, groups_z);
}
}
void Metal::Gfx::BindBackbuffer(const ClearColor& clear_color)
{
@autoreleasepool
{
CheckForSurfaceChange();
CheckForSurfaceResize();
m_drawable = MRCRetain([m_layer nextDrawable]);
m_bb_texture->SetMTLTexture(MRCRetain([m_drawable texture]));
SetAndClearFramebuffer(m_backbuffer.get(), clear_color);
}
}
void Metal::Gfx::PresentBackbuffer()
{
@autoreleasepool
{
g_state_tracker->EndRenderPass();
if (m_drawable)
{
//OpenEmu Blit to GameCoore Texture for Rendering
id<MTLBlitCommandEncoder> blitCommandEncoder = [g_state_tracker->GetRenderCmdBuf() blitCommandEncoder];
if (@available(macOS 10.15, *)) {
[blitCommandEncoder copyFromTexture:[m_drawable texture] toTexture:id<MTLTexture>([_current metalTexture])];
} else {
// Fallback on earlier versions
// TODO: Add pre 10.15 metal blit
}
[blitCommandEncoder endEncoding];
// PresentDrawable refuses to allow Dolphin to present faster than the display's refresh rate
// when windowed (or fullscreen with vsync enabled, but that's more understandable).
// On the other hand, it helps Xcode's GPU captures start and stop on frame boundaries
// which is convenient. Put it here as a default-off config, which we can override in Xcode.
// It also seems to improve frame pacing, so enable it by default with vsync
if (g_ActiveConfig.iUsePresentDrawable == TriState::On ||
(g_ActiveConfig.iUsePresentDrawable == TriState::Auto && g_ActiveConfig.bVSyncActive))
[g_state_tracker->GetRenderCmdBuf() presentDrawable:m_drawable];
else
[g_state_tracker->GetRenderCmdBuf()
addScheduledHandler:[drawable = std::move(m_drawable)](id) { [drawable present]; }];
m_bb_texture->SetMTLTexture(nullptr);
m_drawable = nullptr;
}
g_state_tracker->FlushEncoders();
}
}
void Metal::Gfx::CheckForSurfaceChange()
{
if (!g_presenter->SurfaceChangedTestAndClear())
return;
m_layer = MRCRetain(static_cast<CAMetalLayer*>(g_presenter->GetNewSurfaceHandle()));
SetupSurface();
}
void Metal::Gfx::CheckForSurfaceResize()
{
if (!g_presenter->SurfaceResizedTestAndClear())
return;
SetupSurface();
}
void Metal::Gfx::SetupSurface()
{
auto info = GetSurfaceInfo();
[m_layer setDrawableSize:{static_cast<double>(info.width), static_cast<double>(info.height)}];
TextureConfig cfg(info.width, info.height, 1, 1, 1, info.format,
AbstractTextureFlag_RenderTarget);
m_bb_texture = std::make_unique<Texture>(nullptr, cfg);
m_backbuffer = std::make_unique<Framebuffer>(m_bb_texture.get(), nullptr, //
info.width, info.height, 1, 1);
if (g_presenter)
g_presenter->SetBackbuffer(info);
}
SurfaceInfo Metal::Gfx::GetSurfaceInfo() const
{
if (!m_layer) // Headless
return {};
CGSize size = [m_layer bounds].size;
const float scale = [m_layer contentsScale];
return {static_cast<u32>(size.width * scale), static_cast<u32>(size.height * scale), scale,
Util::ToAbstract([m_layer pixelFormat])};
}
+160
View File
@@ -0,0 +1,160 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoBackends/Metal/VideoBackend.h"
// This must be included before we use any TARGET_OS_* macros.
#include <TargetConditionals.h>
#if TARGET_OS_OSX
#include <AppKit/AppKit.h>
#endif
#include <Metal/Metal.h>
#include <QuartzCore/QuartzCore.h>
#include "Common/Common.h"
#include "Common/MsgHandler.h"
#include "VideoBackends/Metal/MTLBoundingBox.h"
#include "VideoBackends/Metal/MTLGfx.h"
#include "VideoBackends/Metal/MTLObjectCache.h"
#include "VideoBackends/Metal/MTLPerfQuery.h"
#include "VideoBackends/Metal/MTLStateTracker.h"
#include "VideoBackends/Metal/MTLUtil.h"
#include "VideoBackends/Metal/MTLVertexManager.h"
#include "VideoCommon/AbstractGfx.h"
#include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/VideoCommon.h"
#include "VideoCommon/VideoConfig.h"
#import "DolphinGameCore.h"
std::string Metal::VideoBackend::GetName() const
{
return NAME;
}
std::string Metal::VideoBackend::GetDisplayName() const
{
// i18n: Apple's Metal graphics API (https://developer.apple.com/metal/)
return _trans("Metal");
}
std::optional<std::string> Metal::VideoBackend::GetWarningMessage() const
{
// if (Util::GetAdapterList().empty())
// {
// return _trans("No Metal-compatible GPUs were found. "
// "Use the OpenGL backend or upgrade your computer/GPU");
// }
return std::nullopt;
}
static bool WindowSystemTypeSupportsMetal(WindowSystemType type)
{
// switch (type)
// {
// case WindowSystemType::MacOS:
// case WindowSystemType::Headless:
return true;
// default:
// return false;
// }
}
bool Metal::VideoBackend::Initialize(const WindowSystemInfo& wsi)
{
@autoreleasepool
{
const bool surface_ok = wsi.type == WindowSystemType::Headless || wsi.render_surface;
if (!WindowSystemTypeSupportsMetal(wsi.type) || !surface_ok)
{
PanicAlertFmt("Bad WindowSystemInfo for Metal renderer.");
return false;
}
Util::PopulateBackendInfo(&g_Config);
std::vector<MRCOwned<id<MTLDevice>>> adapters;
adapters.push_back(MRCTransfer([_current metalDevice]));
Util::PopulateBackendInfoAdapters(&g_Config, adapters);
if (!adapters.empty())
{
// Use the selected adapter, or the first to fill features.
size_t index = static_cast<size_t>(g_Config.iAdapter);
if (index >= adapters.size())
index = 0;
Util::PopulateBackendInfoFeatures(&g_Config, adapters[index]);
}
// Since we haven't called InitializeShared yet, iAdapter may be out of range,
// so we have to check it ourselves.
size_t selected_adapter_index = static_cast<size_t>(g_Config.iAdapter);
if (selected_adapter_index >= adapters.size())
{
WARN_LOG_FMT(VIDEO, "Metal adapter index out of range, selecting default adapter.");
selected_adapter_index = 0;
}
MRCOwned<id<MTLDevice>> adapter = MRCOwned<id<MTLDevice>>(MRCRetain([_current metalDevice]));
Util::PopulateBackendInfoFeatures(&g_Config, adapter);
UpdateActiveConfig();
MRCOwned<CAMetalLayer*> layer = MRCRetain(static_cast<CAMetalLayer*>(wsi.render_surface));
//MRCOwned<CAMetalLayer*> layer = MRCRetain([CAMetalLayer layer]);
layer.Get().framebufferOnly=NO;
[layer setDevice:adapter];
if (Util::ToAbstract([layer pixelFormat]) == AbstractTextureFormat::Undefined)
[layer setPixelFormat:MTLPixelFormatBGRA8Unorm];
ObjectCache::Initialize(std::move(adapter));
g_state_tracker = std::make_unique<StateTracker>();
return InitializeShared(
std::make_unique<Metal::Gfx>(std::move(layer)), std::make_unique<Metal::VertexManager>(),
std::make_unique<Metal::PerfQuery>(), std::make_unique<Metal::BoundingBox>());
}
}
void Metal::VideoBackend::Shutdown()
{
ShutdownShared();
g_state_tracker.reset();
ObjectCache::Shutdown();
}
void Metal::VideoBackend::InitBackendInfo()
{
@autoreleasepool
{
Util::PopulateBackendInfo(&g_Config);
std::vector<MRCOwned<id<MTLDevice>>> adapters;
adapters.push_back(MRCTransfer([_current metalDevice]));
Util::PopulateBackendInfoAdapters(&g_Config, adapters);
if (!adapters.empty())
{
// Use the selected adapter, or the first to fill features.
size_t index = static_cast<size_t>(g_Config.iAdapter);
if (index >= adapters.size())
index = 0;
Util::PopulateBackendInfoFeatures(&g_Config, adapters[index]);
}
}
}
void Metal::VideoBackend::PrepareWindow(WindowSystemInfo& wsi)
{
//#if TARGET_OS_OSX
// if (wsi.type != WindowSystemType::MacOS)
// return;
// NSView* view = static_cast<NSView*>(wsi.render_surface);
CAMetalLayer* layer = [CAMetalLayer layer];
// [view setWantsLayer:YES];
// [view setLayer:layer];
wsi.render_surface = layer;
//#endif
}
+731
View File
@@ -0,0 +1,731 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoBackends/OGL/OGLGfx.h"
#include "Common/GL/GLContext.h"
#include "Common/GL/GLExtensions/GLExtensions.h"
#include "Common/Logging/LogManager.h"
#include "Core/Config/GraphicsSettings.h"
#include "VideoBackends/OGL/OGLConfig.h"
#include "VideoBackends/OGL/OGLPipeline.h"
#include "VideoBackends/OGL/OGLShader.h"
#include "VideoBackends/OGL/OGLTexture.h"
#include "VideoBackends/OGL/ProgramShaderCache.h"
#include "VideoBackends/OGL/SamplerCache.h"
#include "VideoCommon/AsyncShaderCompiler.h"
#include "VideoCommon/DriverDetails.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/Present.h"
#include "VideoCommon/VideoConfig.h"
#include <string_view>
namespace OGL
{
VideoConfig g_ogl_config;
static void APIENTRY ErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei length, const char* message, const void* userParam)
{
const char* s_source;
const char* s_type;
// Performance - DualCore driver performance warning:
// DualCore application thread syncing with server thread
if (id == 0x200b0)
return;
switch (source)
{
case GL_DEBUG_SOURCE_API_ARB:
s_source = "API";
break;
case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB:
s_source = "Window System";
break;
case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB:
s_source = "Shader Compiler";
break;
case GL_DEBUG_SOURCE_THIRD_PARTY_ARB:
s_source = "Third Party";
break;
case GL_DEBUG_SOURCE_APPLICATION_ARB:
s_source = "Application";
break;
case GL_DEBUG_SOURCE_OTHER_ARB:
s_source = "Other";
break;
default:
s_source = "Unknown";
break;
}
switch (type)
{
case GL_DEBUG_TYPE_ERROR_ARB:
s_type = "Error";
break;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:
s_type = "Deprecated";
break;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:
s_type = "Undefined";
break;
case GL_DEBUG_TYPE_PORTABILITY_ARB:
s_type = "Portability";
break;
case GL_DEBUG_TYPE_PERFORMANCE_ARB:
s_type = "Performance";
break;
case GL_DEBUG_TYPE_OTHER_ARB:
s_type = "Other";
break;
default:
s_type = "Unknown";
break;
}
switch (severity)
{
case GL_DEBUG_SEVERITY_HIGH_ARB:
ERROR_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message);
break;
case GL_DEBUG_SEVERITY_MEDIUM_ARB:
WARN_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message);
break;
case GL_DEBUG_SEVERITY_LOW_ARB:
DEBUG_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message);
break;
case GL_DEBUG_SEVERITY_NOTIFICATION:
DEBUG_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message);
break;
default:
ERROR_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message);
break;
}
}
// Two small Fallbacks to avoid GL_ARB_ES2_compatibility
static void APIENTRY DepthRangef(GLfloat neardepth, GLfloat fardepth)
{
glDepthRange(neardepth, fardepth);
}
static void APIENTRY ClearDepthf(GLfloat depthval)
{
glClearDepth(depthval);
}
OGLGfx::OGLGfx(std::unique_ptr<GLContext> main_gl_context, float backbuffer_scale)
: m_main_gl_context(std::move(main_gl_context)),
m_current_rasterization_state(RenderState::GetInvalidRasterizationState()),
m_current_depth_state(RenderState::GetInvalidDepthState()),
m_current_blend_state(RenderState::GetInvalidBlendingState()),
m_backbuffer_scale(backbuffer_scale)
{
// Create the window framebuffer.
if (!m_main_gl_context->IsHeadless())
{
m_system_framebuffer = std::make_unique<OGLFramebuffer>(
nullptr, nullptr, AbstractTextureFormat::RGBA8, AbstractTextureFormat::Undefined,
std::max(m_main_gl_context->GetBackBufferWidth(), 1u),
std::max(m_main_gl_context->GetBackBufferHeight(), 1u), 1, 1, g_Config.iRenderFBO);
m_current_framebuffer = m_system_framebuffer.get();
}
if (!m_main_gl_context->IsGLES())
{
// OpenGL 3 doesn't provide GLES like float functions for depth.
// They are in core in OpenGL 4.1, so almost every driver should support them.
// But for the oldest ones, we provide fallbacks to the old double functions.
if (!GLExtensions::Supports("GL_ARB_ES2_compatibility"))
{
glDepthRangef = DepthRangef;
glClearDepthf = ClearDepthf;
}
}
if (!PopulateConfig(m_main_gl_context.get()))
{
// Not all needed extensions are supported, so we have to stop here.
// Else some of the next calls might crash.
return;
}
InitDriverInfo();
// Setup Debug logging
if (g_ogl_config.bSupportsDebug)
{
if (GLExtensions::Supports("GL_KHR_debug"))
{
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, true);
glDebugMessageCallback(ErrorCallback, nullptr);
}
else
{
glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, true);
glDebugMessageCallbackARB(ErrorCallback, nullptr);
}
if (Common::Log::LogManager::GetInstance()->IsEnabled(Common::Log::LogType::HOST_GPU,
Common::Log::LogLevel::LERROR))
{
glEnable(GL_DEBUG_OUTPUT);
}
else
{
glDisable(GL_DEBUG_OUTPUT);
}
}
// Handle VSync on/off
if (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC))
m_main_gl_context->SwapInterval(g_ActiveConfig.bVSyncActive);
if (g_ActiveConfig.backend_info.bSupportsClipControl)
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
if (g_ActiveConfig.backend_info.bSupportsDepthClamp)
{
glEnable(GL_CLIP_DISTANCE0);
glEnable(GL_CLIP_DISTANCE1);
glEnable(GL_DEPTH_CLAMP);
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment
glGenFramebuffers(1, &m_shared_read_framebuffer);
glGenFramebuffers(1, &m_shared_draw_framebuffer);
if (g_ActiveConfig.backend_info.bSupportsPrimitiveRestart)
GLUtil::EnablePrimitiveRestart(m_main_gl_context.get());
UpdateActiveConfig();
}
OGLGfx::~OGLGfx()
{
glDeleteFramebuffers(1, &m_shared_draw_framebuffer);
glDeleteFramebuffers(1, &m_shared_read_framebuffer);
}
bool OGLGfx::IsHeadless() const
{
return m_main_gl_context->IsHeadless();
}
std::unique_ptr<AbstractTexture> OGLGfx::CreateTexture(const TextureConfig& config,
std::string_view name)
{
return std::make_unique<OGLTexture>(config, name);
}
std::unique_ptr<AbstractStagingTexture> OGLGfx::CreateStagingTexture(StagingTextureType type,
const TextureConfig& config)
{
return OGLStagingTexture::Create(type, config);
}
std::unique_ptr<AbstractFramebuffer> OGLGfx::CreateFramebuffer(AbstractTexture* color_attachment,
AbstractTexture* depth_attachment)
{
return OGLFramebuffer::Create(static_cast<OGLTexture*>(color_attachment),
static_cast<OGLTexture*>(depth_attachment));
}
std::unique_ptr<AbstractShader>
OGLGfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::string_view name)
{
return OGLShader::CreateFromSource(stage, source, name);
}
std::unique_ptr<AbstractShader>
OGLGfx::CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length,
[[maybe_unused]] std::string_view name)
{
return nullptr;
}
std::unique_ptr<AbstractPipeline> OGLGfx::CreatePipeline(const AbstractPipelineConfig& config,
const void* cache_data,
size_t cache_data_length)
{
return OGLPipeline::Create(config, cache_data, cache_data_length);
}
void OGLGfx::SetScissorRect(const MathUtil::Rectangle<int>& rc)
{
glScissor(rc.left, rc.top, rc.GetWidth(), rc.GetHeight());
}
void OGLGfx::SetViewport(float x, float y, float width, float height, float near_depth,
float far_depth)
{
if (g_ogl_config.bSupportViewportFloat)
{
glViewportIndexedf(0, x, y, width, height);
}
else
{
auto iceilf = [](float f) { return static_cast<GLint>(std::ceil(f)); };
glViewport(iceilf(x), iceilf(y), iceilf(width), iceilf(height));
}
glDepthRangef(near_depth, far_depth);
}
void OGLGfx::Draw(u32 base_vertex, u32 num_vertices)
{
glDrawArrays(static_cast<const OGLPipeline*>(m_current_pipeline)->GetGLPrimitive(), base_vertex,
num_vertices);
}
void OGLGfx::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex)
{
if (g_ogl_config.bSupportsGLBaseVertex)
{
glDrawElementsBaseVertex(static_cast<const OGLPipeline*>(m_current_pipeline)->GetGLPrimitive(),
num_indices, GL_UNSIGNED_SHORT,
static_cast<u16*>(nullptr) + base_index, base_vertex);
}
else
{
glDrawElements(static_cast<const OGLPipeline*>(m_current_pipeline)->GetGLPrimitive(),
num_indices, GL_UNSIGNED_SHORT, static_cast<u16*>(nullptr) + base_index);
}
}
void OGLGfx::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y,
u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z)
{
glUseProgram(static_cast<const OGLShader*>(shader)->GetGLComputeProgramID());
glDispatchCompute(groups_x, groups_y, groups_z);
// We messed up the program binding, so restore it.
ProgramShaderCache::InvalidateLastProgram();
if (m_current_pipeline)
static_cast<const OGLPipeline*>(m_current_pipeline)->GetProgram()->shader.Bind();
// Barrier to texture can be used for reads.
if (m_bound_image_texture)
glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT);
}
void OGLGfx::SelectLeftBuffer()
{
glDrawBuffer(GL_BACK_LEFT);
}
void OGLGfx::SelectRightBuffer()
{
glDrawBuffer(GL_BACK_RIGHT);
}
void OGLGfx::SelectMainBuffer()
{
glDrawBuffer(GL_BACK);
}
void OGLGfx::SetFramebuffer(AbstractFramebuffer* framebuffer)
{
if (m_current_framebuffer == framebuffer)
return;
glBindFramebuffer(GL_FRAMEBUFFER, static_cast<OGLFramebuffer*>(framebuffer)->GetFBO());
m_current_framebuffer = framebuffer;
}
void OGLGfx::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer)
{
// EXT_discard_framebuffer could be used here to save bandwidth on tilers.
SetFramebuffer(framebuffer);
}
void OGLGfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const ClearColor& color_value,
float depth_value)
{
SetFramebuffer(framebuffer);
glDisable(GL_SCISSOR_TEST);
GLbitfield clear_mask = 0;
if (framebuffer->HasColorBuffer())
{
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glClearColor(color_value[0], color_value[1], color_value[2], color_value[3]);
clear_mask |= GL_COLOR_BUFFER_BIT;
}
if (framebuffer->HasDepthBuffer())
{
glDepthMask(GL_TRUE);
glClearDepthf(depth_value);
clear_mask |= GL_DEPTH_BUFFER_BIT;
}
glClear(clear_mask);
glEnable(GL_SCISSOR_TEST);
// Restore color/depth mask.
if (framebuffer->HasColorBuffer())
{
glColorMask(m_current_blend_state.colorupdate, m_current_blend_state.colorupdate,
m_current_blend_state.colorupdate, m_current_blend_state.alphaupdate);
}
if (framebuffer->HasDepthBuffer())
glDepthMask(m_current_depth_state.updateenable);
}
void OGLGfx::ClearRegion(const MathUtil::Rectangle<int>& target_rc, bool colorEnable,
bool alphaEnable, bool zEnable, u32 color, u32 z)
{
u32 clear_mask = 0;
if (colorEnable || alphaEnable)
{
glColorMask(colorEnable, colorEnable, colorEnable, alphaEnable);
glClearColor(float((color >> 16) & 0xFF) / 255.0f, float((color >> 8) & 0xFF) / 255.0f,
float((color >> 0) & 0xFF) / 255.0f, float((color >> 24) & 0xFF) / 255.0f);
clear_mask = GL_COLOR_BUFFER_BIT;
}
if (zEnable)
{
glDepthMask(zEnable ? GL_TRUE : GL_FALSE);
glClearDepthf(float(z & 0xFFFFFF) / 16777216.0f);
clear_mask |= GL_DEPTH_BUFFER_BIT;
}
// Update rect for clearing the picture
// glColorMask/glDepthMask/glScissor affect glClear (glViewport does not)
g_gfx->SetScissorRect(target_rc);
glClear(clear_mask);
// Restore color/depth mask.
if (colorEnable || alphaEnable)
{
glColorMask(m_current_blend_state.colorupdate, m_current_blend_state.colorupdate,
m_current_blend_state.colorupdate, m_current_blend_state.alphaupdate);
}
if (zEnable)
glDepthMask(m_current_depth_state.updateenable);
}
void OGLGfx::BindBackbuffer(const ClearColor& clear_color)
{
CheckForSurfaceChange();
CheckForSurfaceResize();
SetAndClearFramebuffer(m_system_framebuffer.get(), clear_color);
}
void OGLGfx::PresentBackbuffer()
{
if (g_ogl_config.bSupportsDebug)
{
if (Common::Log::LogManager::GetInstance()->IsEnabled(Common::Log::LogType::HOST_GPU,
Common::Log::LogLevel::LERROR))
{
glEnable(GL_DEBUG_OUTPUT);
}
else
{
glDisable(GL_DEBUG_OUTPUT);
}
}
// Swap the back and front buffers, presenting the image.
m_main_gl_context->Swap();
}
void OGLGfx::OnConfigChanged(u32 bits)
{
AbstractGfx::OnConfigChanged(bits);
if (bits & CONFIG_CHANGE_BIT_VSYNC && !DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC))
m_main_gl_context->SwapInterval(g_ActiveConfig.bVSyncActive);
if (bits & CONFIG_CHANGE_BIT_ANISOTROPY)
g_sampler_cache->Clear();
}
void OGLGfx::Flush()
{
// ensure all commands are sent to the GPU.
// Otherwise the driver could batch several frames together.
glFlush();
}
void OGLGfx::WaitForGPUIdle()
{
glFinish();
}
void OGLGfx::CheckForSurfaceChange()
{
if (!g_presenter->SurfaceChangedTestAndClear())
return;
m_main_gl_context->UpdateSurface(g_presenter->GetNewSurfaceHandle());
u32 width = m_main_gl_context->GetBackBufferWidth();
u32 height = m_main_gl_context->GetBackBufferHeight();
// With a surface change, the window likely has new dimensions.
g_presenter->SetBackbuffer(width, height);
m_system_framebuffer->UpdateDimensions(width, height);
}
void OGLGfx::CheckForSurfaceResize()
{
if (!g_presenter->SurfaceResizedTestAndClear())
return;
m_main_gl_context->Update();
u32 width = m_main_gl_context->GetBackBufferWidth();
u32 height = m_main_gl_context->GetBackBufferHeight();
g_presenter->SetBackbuffer(width, height);
m_system_framebuffer->UpdateDimensions(width, height);
}
void OGLGfx::BeginUtilityDrawing()
{
AbstractGfx::BeginUtilityDrawing();
if (g_ActiveConfig.backend_info.bSupportsDepthClamp)
{
glDisable(GL_CLIP_DISTANCE0);
glDisable(GL_CLIP_DISTANCE1);
}
}
void OGLGfx::EndUtilityDrawing()
{
AbstractGfx::EndUtilityDrawing();
if (g_ActiveConfig.backend_info.bSupportsDepthClamp)
{
glEnable(GL_CLIP_DISTANCE0);
glEnable(GL_CLIP_DISTANCE1);
}
}
void OGLGfx::ApplyRasterizationState(const RasterizationState state)
{
if (m_current_rasterization_state == state)
return;
// none, ccw, cw, ccw
if (state.cullmode != CullMode::None)
{
// TODO: GX_CULL_ALL not supported, yet!
glEnable(GL_CULL_FACE);
glFrontFace(state.cullmode == CullMode::Front ? GL_CCW : GL_CW);
}
else
{
glDisable(GL_CULL_FACE);
}
m_current_rasterization_state = state;
}
void OGLGfx::ApplyDepthState(const DepthState state)
{
if (m_current_depth_state == state)
return;
const GLenum glCmpFuncs[8] = {GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL,
GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS};
if (state.testenable)
{
glEnable(GL_DEPTH_TEST);
glDepthMask(state.updateenable ? GL_TRUE : GL_FALSE);
glDepthFunc(glCmpFuncs[u32(state.func.Value())]);
}
else
{
// if the test is disabled write is disabled too
// TODO: When PE performance metrics are being emulated via occlusion queries, we should
// (probably?) enable depth test with depth function ALWAYS here
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
}
m_current_depth_state = state;
}
void OGLGfx::ApplyBlendingState(const BlendingState state)
{
if (m_current_blend_state == state)
return;
bool useDualSource = state.usedualsrc;
const GLenum src_factors[8] = {GL_ZERO,
GL_ONE,
GL_DST_COLOR,
GL_ONE_MINUS_DST_COLOR,
useDualSource ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA,
useDualSource ? GL_ONE_MINUS_SRC1_ALPHA :
(GLenum)GL_ONE_MINUS_SRC_ALPHA,
GL_DST_ALPHA,
GL_ONE_MINUS_DST_ALPHA};
const GLenum dst_factors[8] = {GL_ZERO,
GL_ONE,
GL_SRC_COLOR,
GL_ONE_MINUS_SRC_COLOR,
useDualSource ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA,
useDualSource ? GL_ONE_MINUS_SRC1_ALPHA :
(GLenum)GL_ONE_MINUS_SRC_ALPHA,
GL_DST_ALPHA,
GL_ONE_MINUS_DST_ALPHA};
if (state.blendenable)
glEnable(GL_BLEND);
else
glDisable(GL_BLEND);
// Always call glBlendEquationSeparate and glBlendFuncSeparate, even when
// GL_BLEND is disabled, as a workaround for some bugs (possibly graphics
// driver issues?). See https://bugs.dolphin-emu.org/issues/10120 : "Sonic
// Adventure 2 Battle: graphics crash when loading first Dark level"
GLenum equation = state.subtract ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD;
GLenum equationAlpha = state.subtractAlpha ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD;
glBlendEquationSeparate(equation, equationAlpha);
glBlendFuncSeparate(src_factors[u32(state.srcfactor.Value())],
dst_factors[u32(state.dstfactor.Value())],
src_factors[u32(state.srcfactoralpha.Value())],
dst_factors[u32(state.dstfactoralpha.Value())]);
const GLenum logic_op_codes[16] = {
GL_CLEAR, GL_AND, GL_AND_REVERSE, GL_COPY, GL_AND_INVERTED, GL_NOOP,
GL_XOR, GL_OR, GL_NOR, GL_EQUIV, GL_INVERT, GL_OR_REVERSE,
GL_COPY_INVERTED, GL_OR_INVERTED, GL_NAND, GL_SET};
// Logic ops aren't available in GLES3
if (!IsGLES())
{
if (state.logicopenable)
{
glEnable(GL_COLOR_LOGIC_OP);
glLogicOp(logic_op_codes[u32(state.logicmode.Value())]);
}
else
{
glDisable(GL_COLOR_LOGIC_OP);
}
}
glColorMask(state.colorupdate, state.colorupdate, state.colorupdate, state.alphaupdate);
m_current_blend_state = state;
}
void OGLGfx::SetPipeline(const AbstractPipeline* pipeline)
{
if (m_current_pipeline == pipeline)
return;
if (pipeline)
{
ApplyRasterizationState(static_cast<const OGLPipeline*>(pipeline)->GetRasterizationState());
ApplyDepthState(static_cast<const OGLPipeline*>(pipeline)->GetDepthState());
ApplyBlendingState(static_cast<const OGLPipeline*>(pipeline)->GetBlendingState());
ProgramShaderCache::BindVertexFormat(
static_cast<const OGLPipeline*>(pipeline)->GetVertexFormat());
static_cast<const OGLPipeline*>(pipeline)->GetProgram()->shader.Bind();
}
else
{
ProgramShaderCache::InvalidateLastProgram();
glUseProgram(0);
}
m_current_pipeline = pipeline;
}
void OGLGfx::SetTexture(u32 index, const AbstractTexture* texture)
{
const OGLTexture* gl_texture = static_cast<const OGLTexture*>(texture);
if (m_bound_textures[index] == gl_texture)
return;
glActiveTexture(GL_TEXTURE0 + index);
if (gl_texture)
glBindTexture(gl_texture->GetGLTarget(), gl_texture->GetGLTextureId());
else
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
m_bound_textures[index] = gl_texture;
}
void OGLGfx::SetSamplerState(u32 index, const SamplerState& state)
{
g_sampler_cache->SetSamplerState(index, state);
}
void OGLGfx::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write)
{
if (m_bound_image_texture == texture)
return;
if (texture)
{
const GLenum access = read ? (write ? GL_READ_WRITE : GL_READ_ONLY) : GL_WRITE_ONLY;
glBindImageTexture(0, static_cast<OGLTexture*>(texture)->GetGLTextureId(), 0, GL_TRUE, 0,
access, static_cast<OGLTexture*>(texture)->GetGLFormatForImageTexture());
}
else
{
glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
}
m_bound_image_texture = texture;
}
void OGLGfx::UnbindTexture(const AbstractTexture* texture)
{
for (size_t i = 0; i < m_bound_textures.size(); i++)
{
if (m_bound_textures[i] != texture)
continue;
glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + i));
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
m_bound_textures[i] = nullptr;
}
if (m_bound_image_texture == texture)
{
glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
m_bound_image_texture = nullptr;
}
}
std::unique_ptr<VideoCommon::AsyncShaderCompiler> OGLGfx::CreateAsyncShaderCompiler()
{
return std::make_unique<SharedContextAsyncShaderCompiler>();
}
bool OGLGfx::IsGLES() const
{
return m_main_gl_context->IsGLES();
}
void OGLGfx::BindSharedReadFramebuffer()
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_shared_read_framebuffer);
}
void OGLGfx::BindSharedDrawFramebuffer()
{
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_shared_draw_framebuffer);
}
void OGLGfx::RestoreFramebufferBinding()
{
glBindFramebuffer(
GL_FRAMEBUFFER,
m_current_framebuffer ? static_cast<OGLFramebuffer*>(m_current_framebuffer)->GetFBO() : 0);
}
SurfaceInfo OGLGfx::GetSurfaceInfo() const
{
return {std::max(m_main_gl_context->GetBackBufferWidth(), 1u),
std::max(m_main_gl_context->GetBackBufferHeight(), 1u), m_backbuffer_scale,
AbstractTextureFormat::RGBA8};
}
} // namespace OGL
File diff suppressed because it is too large Load Diff
+153 -88
View File
@@ -16,6 +16,7 @@
#include <vector>
#include "Common/CommonTypes.h"
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h"
#include "VideoCommon/VideoCommon.h"
enum class APIType;
@@ -54,6 +55,35 @@ enum class ShaderCompilationMode : int
AsynchronousSkipRendering
};
enum class TextureFilteringMode : int
{
Default,
Nearest,
Linear,
};
enum class TriState : int
{
Off,
On,
Auto
};
// Bitmask containing information about which configuration has changed for the backend.
enum ConfigChangeBits : u32
{
CONFIG_CHANGE_BIT_HOST_CONFIG = (1 << 0),
CONFIG_CHANGE_BIT_MULTISAMPLES = (1 << 1),
CONFIG_CHANGE_BIT_STEREO_MODE = (1 << 2),
CONFIG_CHANGE_BIT_TARGET_SIZE = (1 << 3),
CONFIG_CHANGE_BIT_ANISOTROPY = (1 << 4),
CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING = (1 << 5),
CONFIG_CHANGE_BIT_VSYNC = (1 << 6),
CONFIG_CHANGE_BIT_BBOX = (1 << 7),
CONFIG_CHANGE_BIT_ASPECT_RATIO = (1 << 8),
CONFIG_CHANGE_BIT_POST_PROCESSING_SHADER = (1 << 9),
};
// NEVER inherit from this class.
struct VideoConfig final
{
@@ -62,35 +92,43 @@ struct VideoConfig final
void VerifyValidity();
// General
bool bVSync;
bool bVSyncActive;
bool bWidescreenHack;
AspectMode aspect_mode;
AspectMode suggested_aspect_mode;
bool bCrop; // Aspect ratio controls.
bool bShaderCache;
bool bVSync = false;
bool bVSyncActive = false;
bool bWidescreenHack = false;
AspectMode aspect_mode{};
AspectMode suggested_aspect_mode{};
bool bCrop = false; // Aspect ratio controls.
bool bShaderCache = false;
// Enhancements
u32 iMultisamples;
bool bSSAA;
int iEFBScale;
bool bForceFiltering;
int iMaxAnisotropy;
u32 iMultisamples = 0;
bool bSSAA = false;
int iEFBScale = 0;
TextureFilteringMode texture_filtering_mode = TextureFilteringMode::Default;
int iMaxAnisotropy = 0;
std::string sPostProcessingShader;
bool bForceTrueColor;
bool bDisableCopyFilter;
bool bArbitraryMipmapDetection;
float fArbitraryMipmapDetectionThreshold;
bool bForceTrueColor = false;
bool bDisableCopyFilter = false;
bool bArbitraryMipmapDetection = false;
float fArbitraryMipmapDetectionThreshold = 0;
// Information
bool bShowFPS;
bool bShowNetPlayPing;
bool bShowNetPlayMessages;
bool bOverlayStats;
bool bOverlayProjStats;
bool bTexFmtOverlayEnable;
bool bTexFmtOverlayCenter;
bool bLogRenderTimeToFile;
bool bShowFPS = false;
bool bShowFTimes = false;
bool bShowVPS = false;
bool bShowVTimes = false;
bool bShowGraphs = false;
bool bShowSpeed = false;
bool bShowSpeedColors = false;
int iPerfSampleUSec = 0;
bool bShowNetPlayPing = false;
bool bShowNetPlayMessages = false;
bool bOverlayStats = false;
bool bOverlayProjStats = false;
bool bOverlayScissorStats = false;
bool bTexFmtOverlayEnable = false;
bool bTexFmtOverlayCenter = false;
bool bLogRenderTimeToFile = false;
// Render
bool bWireFrame;
@@ -100,96 +138,101 @@ struct VideoConfig final
int iRenderFBO = 0;
// Utility
bool bDumpTextures;
bool bDumpMipmapTextures;
bool bDumpBaseTextures;
bool bHiresTextures;
bool bCacheHiresTextures;
bool bDumpEFBTarget;
bool bDumpXFBTarget;
bool bDumpFramesAsImages;
bool bUseFFV1;
bool bDumpTextures = false;
bool bDumpMipmapTextures = false;
bool bDumpBaseTextures = false;
bool bHiresTextures = false;
bool bCacheHiresTextures = false;
bool bDumpEFBTarget = false;
bool bDumpXFBTarget = false;
bool bDumpFramesAsImages = false;
bool bUseFFV1 = false;
std::string sDumpCodec;
std::string sDumpPixelFormat;
std::string sDumpEncoder;
std::string sDumpFormat;
std::string sDumpPath;
bool bInternalResolutionFrameDumps;
bool bBorderlessFullscreen;
bool bEnableGPUTextureDecoding;
int iBitrateKbps;
bool bInternalResolutionFrameDumps = false;
bool bBorderlessFullscreen = false;
bool bEnableGPUTextureDecoding = false;
bool bPreferVSForLinePointExpansion = false;
int iBitrateKbps = 0;
bool bGraphicMods = false;
std::optional<GraphicsModGroupConfig> graphics_mod_config;
// Hacks
bool bEFBAccessEnable;
bool bEFBAccessDeferInvalidation;
bool bPerfQueriesEnable;
bool bBBoxEnable;
bool bForceProgressive;
bool bEFBAccessEnable = false;
bool bEFBAccessDeferInvalidation = false;
bool bPerfQueriesEnable = false;
bool bBBoxEnable = false;
bool bForceProgressive = false;
bool bCPUCull = false;
bool bEFBEmulateFormatChanges;
bool bSkipEFBCopyToRam;
bool bSkipXFBCopyToRam;
bool bDisableCopyToVRAM;
bool bDeferEFBCopies;
bool bImmediateXFB;
bool bSkipPresentingDuplicateXFBs;
bool bCopyEFBScaled;
int iSafeTextureCache_ColorSamples;
float fAspectRatioHackW, fAspectRatioHackH;
bool bEnablePixelLighting;
bool bFastDepthCalc;
bool bVertexRounding;
int iEFBAccessTileSize;
u32 iMissingColorValue;
bool bFastTextureSampling;
int iLog; // CONF_ bits
int iSaveTargetId; // TODO: Should be dropped
bool bEFBEmulateFormatChanges = false;
bool bSkipEFBCopyToRam = false;
bool bSkipXFBCopyToRam = false;
bool bDisableCopyToVRAM = false;
bool bDeferEFBCopies = false;
bool bImmediateXFB = false;
bool bSkipPresentingDuplicateXFBs = false;
bool bCopyEFBScaled = false;
int iSafeTextureCache_ColorSamples = 0;
float fAspectRatioHackW = 1; // Initial value needed for the first frame
float fAspectRatioHackH = 1;
bool bEnablePixelLighting = false;
bool bFastDepthCalc = false;
bool bVertexRounding = false;
bool bVISkip = false;
int iEFBAccessTileSize = 0;
int iSaveTargetId = 0; // TODO: Should be dropped
u32 iMissingColorValue = 0;
bool bFastTextureSampling = false;
#ifdef __APPLE__
bool bNoMipmapping = false; // Used by macOS fifoci to work around an M1 bug
#endif
// Stereoscopy
StereoMode stereo_mode;
int iStereoDepth;
int iStereoConvergence;
int iStereoConvergencePercentage;
bool bStereoSwapEyes;
bool bStereoEFBMonoDepth;
int iStereoDepthPercentage;
StereoMode stereo_mode{};
int iStereoDepth = 0;
int iStereoConvergence = 0;
int iStereoConvergencePercentage = 0;
bool bStereoSwapEyes = false;
bool bStereoEFBMonoDepth = false;
int iStereoDepthPercentage = 0;
// D3D only config, mostly to be merged into the above
int iAdapter;
int iAdapter = 0;
// VideoSW Debugging
int drawStart;
int drawEnd;
bool bZComploc;
bool bZFreeze;
bool bDumpObjects;
bool bDumpTevStages;
bool bDumpTevTextureFetches;
// Metal only config
TriState iManuallyUploadBuffers = TriState::Auto;
TriState iUsePresentDrawable = TriState::Auto;
// Enable API validation layers, currently only supported with Vulkan.
bool bEnableValidationLayer;
bool bEnableValidationLayer = false;
// Multithreaded submission, currently only supported with Vulkan.
bool bBackendMultithreading;
bool bBackendMultithreading = true;
// Early command buffer execution interval in number of draws.
// Currently only supported with Vulkan.
int iCommandBufferExecuteInterval;
int iCommandBufferExecuteInterval = 0;
// Shader compilation settings.
bool bWaitForShadersBeforeStarting;
ShaderCompilationMode iShaderCompilationMode;
bool bWaitForShadersBeforeStarting = false;
ShaderCompilationMode iShaderCompilationMode{};
// Number of shader compiler threads.
// 0 disables background compilation.
// -1 uses an automatic number based on the CPU threads.
int iShaderCompilerThreads;
int iShaderPrecompilerThreads;
int iShaderCompilerThreads = 0;
int iShaderPrecompilerThreads = 0;
// Static config per API
// TODO: Move this out of VideoConfig
struct
{
APIType api_type = APIType::Nothing;
std::string DisplayName;
std::vector<std::string> Adapters; // for D3D
std::vector<u32> AAModes;
@@ -199,11 +242,11 @@ struct VideoConfig final
u32 MaxTextureSize = 16384;
bool bUsesLowerLeftOrigin = false;
bool bUsesExplictQuadBuffering = false;
bool bSupportsExclusiveFullscreen = false;
bool bSupportsDualSourceBlend = false;
bool bSupportsPrimitiveRestart = false;
bool bSupportsOversizedViewports = false;
bool bSupportsGeometryShaders = false;
bool bSupportsComputeShaders = false;
bool bSupports3DVision = false;
@@ -238,9 +281,21 @@ struct VideoConfig final
bool bSupportsTextureQueryLevels = false;
bool bSupportsLodBiasInSampler = false;
bool bSupportsSettingObjectNames = false;
bool bSupportsPartialMultisampleResolve = false;
bool bSupportsDynamicVertexLoader = false;
bool bSupportsVSLinePointExpand = false;
bool bSupportsGLLayerInFS = true;
} backend_info;
// Utility
bool UseVSForLinePointExpand() const
{
if (!backend_info.bSupportsVSLinePointExpand)
return false;
if (!backend_info.bSupportsGeometryShaders)
return true;
return bPreferVSForLinePointExpansion;
}
bool MultisamplingEnabled() const { return iMultisamples > 1; }
bool ExclusiveFullscreenEnabled() const
{
@@ -251,15 +306,25 @@ struct VideoConfig final
return backend_info.bSupportsGPUTextureDecoding && bEnableGPUTextureDecoding;
}
bool UseVertexRounding() const { return bVertexRounding && iEFBScale != 1; }
bool ManualTextureSamplingWithHiResTextures() const
bool ManualTextureSamplingWithCustomTextureSizes() const
{
// Hi-res textures (including hi-res EFB copies, but not native-resolution EFB copies at higher
// internal resolutions) breaks the wrapping logic used by manual texture sampling.
// If manual texture sampling is disabled, we don't need to do anything.
if (bFastTextureSampling)
return false;
// Hi-res textures break the wrapping logic used by manual texture sampling, as a texture's
// size won't match the size the game sets.
if (bHiresTextures)
return true;
// Hi-res EFB copies (but not native-resolution EFB copies at higher internal resolutions)
// also result in different texture sizes that need special handling.
if (iEFBScale != 1 && bCopyEFBScaled)
return true;
return bHiresTextures;
// Stereoscopic 3D changes the number of layers some textures have (EFB copies have 2 layers,
// while game textures still have 1), meaning bounds checks need to be added.
if (stereo_mode != StereoMode::Off)
return true;
// Otherwise, manual texture sampling can use the sizes games specify directly.
return false;
}
bool UsingUberShaders() const;
u32 GetShaderCompilerThreads() const;
+1 -1
View File
@@ -1,5 +1,5 @@
/* Default visibility */
#define DEFAULT_VISIBILITY __attribute__((visibility("default")))
#define DEFAULT_VISIBILITY __attribute__((visibility("hidden")))
/* Start with debug message logging enabled */
#undef ENABLE_DEBUG_LOGGING
+1 -1
View File
@@ -281,7 +281,7 @@
/* Define to 1 or 0, depending whether the compiler supports simple visibility
declarations. */
#define HAVE_VISIBILITY 1
#define HAVE_VISIBILITY 0
/* Define to 1 if you have the `wcwidth' function. */
#define HAVE_WCWIDTH 1
+18 -5
View File
@@ -51,6 +51,7 @@
#include "Core/Config/WiimoteSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/System.h"
#include "Core/Host.h"
#include "Core/HW/CPU.h"
#include "Core/HW/Wiimote.h"
@@ -132,7 +133,7 @@ void DolHost::Init(std::string supportDirectoryPath, std::string cpath)
Config::SetBase(Config::MAIN_SHOW_FRAME_COUNT, false);
//Video
Config::SetBase(Config::MAIN_GFX_BACKEND, "OGL");
Config::SetBase(Config::MAIN_GFX_BACKEND, "MTL");
VideoBackendBase::ActivateBackend(Config::Get(Config::MAIN_GFX_BACKEND));
//Set the Sound
@@ -249,7 +250,7 @@ void DolHost::Pause(bool flag)
void DolHost::RequestStop()
{
Core::SetState(Core::State::Running);
ProcessorInterface::PowerButton_Tap();
Core::System::GetInstance().GetProcessorInterface().PowerButton_Tap();
Core::Stop();
while (CPU::GetState() != CPU::State::PowerDown)
@@ -261,7 +262,7 @@ void DolHost::RequestStop()
void DolHost::Reset()
{
ProcessorInterface::ResetButton_Tap();
Core::System::GetInstance().GetProcessorInterface().ResetButton_Tap();
}
void DolHost::UpdateFrame()
@@ -296,7 +297,7 @@ void DolHost::SetBackBufferSize(int width, int height) {
void DolHost::SetVolume(float value)
{
Config::SetBaseOrCurrent(Config::MAIN_AUDIO_VOLUME, value * 100);
AudioCommon::UpdateSoundStream();
AudioCommon::UpdateSoundStream(Core::System::GetInstance());
}
# pragma mark - Save states
@@ -460,7 +461,9 @@ void DolHost::SetCheat(std::string code, std::string type, bool enabled)
if(!exists)
arcodes.push_back(arcode);
ActionReplay::RunAllActive();
// TODO: move to a better place
Core::CPUThreadGuard guard;
ActionReplay::RunAllActive(guard);
}
# pragma mark - Controls
@@ -633,3 +636,13 @@ void Host_ShowVideoConfig(void*, const std::string&) {}
void Host_YieldToUI() {}
void Host_TitleChanged() {}
void Host_UpdateProgressDialog(const char* caption, int position, int total) {}
void Host_UpdateDiscordClientID(const std::string& client_id) {}
bool Host_UpdateDiscordPresenceRaw(const std::string& details, const std::string& state,
const std::string& large_image_key,
const std::string& large_image_text,
const std::string& small_image_key,
const std::string& small_image_text,
const int64_t start_timestamp,
const int64_t end_timestamp, const int party_size,
const int party_max) { return false; }
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -182,7 +182,7 @@ extern std::unique_ptr<SoundStream> g_sound_stream;
# pragma mark - Video
- (OEGameCoreRendering)gameCoreRendering
{
return OEGameCoreRenderingOpenGL3Video;
return OEGameCoreRenderingMetal2Video;
}
- (BOOL)hasAlternateRenderingThread
@@ -226,7 +226,7 @@ extern std::unique_ptr<SoundStream> g_sound_stream;
- (GLenum)pixelType
{
return GL_UNSIGNED_BYTE;
return GL_UNSIGNED_INT_8_8_8_8_REV;
}
- (GLenum)internalPixelFormat
+1 -1
Submodule dolphin updated: a4445fa1b0...a93b5a4a35