mirror of
https://github.com/libretro/parallel-gx1.git
synced 2026-11-07 12:37:59 -10:00
The core asks for version 2 of the D3D11 interface and, when it gets it, shares the immediate context with the frontend by taking turns: it holds the context lock around the frame's emulation, savestates, the context reset and destroy and unloading, and releases it around video_refresh and the sync index wait. It keeps one output texture per sync index and names the frame's with set_texture, so a frontend presenting from another thread reads it in place instead of copying it. When the frontend has used the context since the core last held it, everything the core relies on is bound again: the state manager's graphics and compute bindings and the viewport and scissor, which the backend now keeps. Version 1 rebinds the same way after video_refresh.
843 lines
28 KiB
C++
843 lines
28 KiB
C++
// needs to be first
|
|
#ifdef HAS_VULKAN
|
|
#include "VideoBackends/Vulkan/VulkanLoader.h"
|
|
#endif
|
|
#include "DolphinLibretro/Video.h"
|
|
#include "DolphinLibretro/Output.h"
|
|
|
|
#include <cassert>
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <vector>
|
|
|
|
#ifdef _WIN32
|
|
#include "Common/DynamicLibrary.h"
|
|
#include "VideoBackends/D3D/VideoBackend.h"
|
|
#include "VideoBackends/D3D/D3DGfx.h"
|
|
#include "VideoBackends/D3D/D3DVertexManager.h"
|
|
#include "VideoBackends/D3D/D3DPerfQuery.h"
|
|
#include "VideoBackends/D3D/D3DBoundingBox.h"
|
|
#include "VideoBackends/D3D12/Common.h"
|
|
#include "VideoBackends/D3D12/VideoBackend.h"
|
|
#include "VideoBackends/D3D12/D3D12Gfx.h"
|
|
#include "VideoBackends/D3D12/D3D12VertexManager.h"
|
|
#include "VideoBackends/D3D12/D3D12PerfQuery.h"
|
|
#include "VideoBackends/D3D12/D3D12BoundingBox.h"
|
|
#endif
|
|
|
|
#ifdef HAS_OPENGL
|
|
#include "Common/GL/GLContext.h"
|
|
#include "Common/GL/GLUtil.h"
|
|
#include "VideoBackends/OGL/OGLGfx.h"
|
|
#endif
|
|
|
|
#include "Common/Logging/Log.h"
|
|
#include "Common/Version.h"
|
|
#include "Core/Config/GraphicsSettings.h"
|
|
#include "Core/Config/MainSettings.h"
|
|
#include "Core/ConfigManager.h"
|
|
#include "Core/Core.h"
|
|
#include "Core/HW/VideoInterface.h"
|
|
#include "Core/Host.h"
|
|
#include "Core/System.h"
|
|
#include "DolphinLibretro/Common/Options.h"
|
|
#include "DolphinLibretro/VR.h"
|
|
#include "DolphinLibretro/VideoContexts/ContextStatus.h"
|
|
|
|
#include "VideoCommon/AsyncRequests.h"
|
|
#include "VideoCommon/Fifo.h"
|
|
#include "VideoCommon/VertexLoaderManager.h"
|
|
#include "VideoCommon/VideoBackendBase.h"
|
|
#include "VideoCommon/VideoCommon.h"
|
|
#include "VideoCommon/VideoConfig.h"
|
|
|
|
#ifdef HAS_VULKAN
|
|
#include "VideoBackends/Vulkan/VulkanContext.h"
|
|
#include "VideoBackends/Vulkan/ObjectCache.h"
|
|
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
|
#include "VideoBackends/Vulkan/StateTracker.h"
|
|
#include "VideoBackends/Vulkan/VKGfx.h"
|
|
#include "VideoBackends/Vulkan/VKVertexManager.h"
|
|
#include "VideoBackends/Vulkan/VKPerfQuery.h"
|
|
#include "VideoBackends/Vulkan/VKBoundingBox.h"
|
|
#endif
|
|
|
|
namespace Libretro
|
|
{
|
|
extern std::vector<std::string> g_normalized_game_paths;
|
|
namespace Video
|
|
{
|
|
#ifdef _WIN32
|
|
static Common::DynamicLibrary d3d11_library;
|
|
static Common::DynamicLibrary d3d12_library;
|
|
/* Direct3D 11 version 2: the interface, and the levels of its context
|
|
* lock this thread holds */
|
|
static retro_hw_render_interface_d3d11* s_d3d11_v2;
|
|
static unsigned s_d3d11_depth;
|
|
#endif
|
|
|
|
int GetAdjustedBaseWidth()
|
|
{
|
|
// VR packs both eyes side by side, each at the full width.
|
|
return VR::IsEnabled() ? EFB_WIDTH * 2 : EFB_WIDTH;
|
|
}
|
|
|
|
int GetAdjustedBaseHeight()
|
|
{
|
|
const bool crop_overscan = Libretro::Options::GetCached<bool>(
|
|
Libretro::Options::gfx_settings::CROP_OVERSCAN);
|
|
|
|
if (crop_overscan)
|
|
{
|
|
// A 480-line signal (native NTSC, or a PAL game that switched to PAL60/EuRGB60 at runtime)
|
|
// should report 480 so the frontend / CRT switchres picks a 480i@60 mode instead of stretching
|
|
// a 576-line PAL mode down to ~52 Hz. Detect the 60 Hz output from the current refresh rate,
|
|
// since the cartridge region stays PAL even after the in-game 60 Hz switch.
|
|
const bool is_480_line_output =
|
|
retro_get_region() == RETRO_REGION_NTSC ||
|
|
Core::System::GetInstance().GetVideoInterface().GetTargetRefreshRate() > 55.0;
|
|
|
|
return is_480_line_output ? 480 : 576;
|
|
}
|
|
|
|
return EFB_HEIGHT;
|
|
}
|
|
|
|
void Init()
|
|
{
|
|
DEBUG_LOG_FMT(VIDEO, "Video - Init");
|
|
|
|
std::string renderer = Libretro::Options::GetCached<std::string>(
|
|
Libretro::Options::gfx_settings::RENDERER);
|
|
|
|
if (renderer == "Hardware")
|
|
{
|
|
retro_hw_context_type preferred = RETRO_HW_CONTEXT_NONE;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER, &preferred))
|
|
{
|
|
switch (preferred)
|
|
{
|
|
#ifdef HAS_OPENGL
|
|
case RETRO_HW_CONTEXT_OPENGLES_VERSION:
|
|
if (SetHWRender(preferred, 3, 2))
|
|
return;
|
|
if (SetHWRender(preferred, 3, 1))
|
|
return;
|
|
break;
|
|
#endif
|
|
default:
|
|
if (SetHWRender(preferred))
|
|
return;
|
|
}
|
|
}
|
|
#if defined(HAS_OPENGL) && !defined(ANDROID) && !defined(__WEBOS__)
|
|
if (SetHWRender(RETRO_HW_CONTEXT_OPENGL_CORE))
|
|
return;
|
|
if (SetHWRender(RETRO_HW_CONTEXT_OPENGL))
|
|
return;
|
|
#endif
|
|
#ifdef HAS_OPENGL
|
|
if (SetHWRender(RETRO_HW_CONTEXT_OPENGLES_VERSION, 3, 2))
|
|
return;
|
|
if (SetHWRender(RETRO_HW_CONTEXT_OPENGLES_VERSION, 3, 1))
|
|
return;
|
|
if (SetHWRender(RETRO_HW_CONTEXT_OPENGLES3))
|
|
return;
|
|
#endif
|
|
#ifdef _WIN32
|
|
if (SetHWRender(RETRO_HW_CONTEXT_D3D12))
|
|
return;
|
|
if (SetHWRender(RETRO_HW_CONTEXT_D3D11))
|
|
return;
|
|
#endif
|
|
#ifdef HAS_VULKAN
|
|
if (SetHWRender(RETRO_HW_CONTEXT_VULKAN))
|
|
return;
|
|
#endif
|
|
}
|
|
hw_render.context_type = RETRO_HW_CONTEXT_NONE;
|
|
if (renderer == "Software")
|
|
Config::SetBase(Config::MAIN_GFX_BACKEND, "Software Renderer");
|
|
else
|
|
Config::SetBase(Config::MAIN_GFX_BACKEND, "Null");
|
|
}
|
|
|
|
void D3D11Lock(void)
|
|
{
|
|
#ifdef _WIN32
|
|
bool used;
|
|
if (!s_d3d11_v2)
|
|
return;
|
|
used = s_d3d11_v2->lock_context(s_d3d11_v2->handle);
|
|
/* the frontend had the context: everything bound goes back */
|
|
if (s_d3d11_depth++ == 0 && used && g_gfx && DX11::D3D::stateman)
|
|
static_cast<DX11::Gfx*>(g_gfx.get())->RestoreContext();
|
|
#endif
|
|
}
|
|
|
|
void D3D11Unlock(void)
|
|
{
|
|
#ifdef _WIN32
|
|
if (!s_d3d11_v2 || !s_d3d11_depth)
|
|
return;
|
|
s_d3d11_depth--;
|
|
s_d3d11_v2->unlock_context(s_d3d11_v2->handle);
|
|
#endif
|
|
}
|
|
|
|
unsigned D3D11Suspend(void)
|
|
{
|
|
unsigned depth = 0;
|
|
#ifdef _WIN32
|
|
depth = s_d3d11_depth;
|
|
while (s_d3d11_depth)
|
|
D3D11Unlock();
|
|
#endif
|
|
return depth;
|
|
}
|
|
|
|
void D3D11Resume(unsigned depth)
|
|
{
|
|
while (depth--)
|
|
D3D11Lock();
|
|
}
|
|
|
|
bool SetHWRender(retro_hw_context_type type, const int version_major, const int version_minor)
|
|
{
|
|
DEBUG_LOG_FMT(VIDEO, "Video - SetHWRender!");
|
|
|
|
hw_render.context_type = type;
|
|
hw_render.context_reset = ContextReset;
|
|
hw_render.context_destroy = ContextDestroy;
|
|
hw_render.bottom_left_origin = true;
|
|
bool success = false;
|
|
|
|
switch (type)
|
|
{
|
|
#ifdef HAS_OPENGL
|
|
case RETRO_HW_CONTEXT_OPENGL_CORE:
|
|
// minimum requirements to run is opengl 3.3 (RA will try to use highest version available anyway)
|
|
hw_render.version_major = (version_major != -1) ? version_major : 3;
|
|
hw_render.version_minor = (version_minor != -1) ? version_minor : 3;
|
|
if (environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render))
|
|
{
|
|
Config::SetBase(Config::MAIN_GFX_BACKEND, "OGL");
|
|
success = true;
|
|
} else {
|
|
WARN_LOG_FMT(VIDEO, "Video - SetHWRender - failed to set hw renderer for OpenGL Core");
|
|
}
|
|
break;
|
|
case RETRO_HW_CONTEXT_OPENGLES_VERSION:
|
|
case RETRO_HW_CONTEXT_OPENGLES3:
|
|
case RETRO_HW_CONTEXT_OPENGL:
|
|
{
|
|
// when using RETRO_HW_CONTEXT_OPENGL you can't set version above 3.0 (RA will try to use highest version available anyway)
|
|
// dolphin support OpenGL ES 3.0 too (no support for 2.0) so we are good
|
|
hw_render.version_major = (version_major != -1) ? version_major : 3;
|
|
hw_render.version_minor = (version_minor != -1) ? version_minor : 0;
|
|
|
|
std::string api_name =
|
|
(type == RETRO_HW_CONTEXT_OPENGLES3 || type == RETRO_HW_CONTEXT_OPENGLES_VERSION)
|
|
? "OpenGL ES " + std::to_string(hw_render.version_major) + "." +
|
|
std::to_string(hw_render.version_minor)
|
|
: "OpenGL " + std::to_string(hw_render.version_major) + "." +
|
|
std::to_string(hw_render.version_minor);
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render))
|
|
{
|
|
// Shared context is required with "gl" video driver
|
|
if (!environ_cb(RETRO_ENVIRONMENT_SET_HW_SHARED_CONTEXT, nullptr)) {
|
|
WARN_LOG_FMT(VIDEO, "Video - SetHWRender - unable to set shared context for {}", api_name);
|
|
}
|
|
INFO_LOG_FMT(VIDEO, "Video - SetHWRender - using {}", api_name);
|
|
Config::SetBase(Config::MAIN_GFX_BACKEND, "OGL");
|
|
success = true;
|
|
}
|
|
else
|
|
{
|
|
WARN_LOG_FMT(VIDEO, "Video - SetHWRender - failed to set hw renderer for {}", api_name);
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
#ifdef _WIN32
|
|
case RETRO_HW_CONTEXT_D3D11:
|
|
hw_render.version_major = 11;
|
|
hw_render.version_minor = 0;
|
|
if (environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render))
|
|
{
|
|
/* version 2 of the interface, when the frontend has it: the frame
|
|
* is read where it is drawn, from whichever thread presents */
|
|
static const struct retro_hw_render_context_negotiation_interface_d3d11 negotiation = {
|
|
RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_D3D11,
|
|
RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_D3D11_VERSION,
|
|
RETRO_HW_RENDER_INTERFACE_D3D11_VERSION_2};
|
|
struct retro_hw_render_context_negotiation_interface probe;
|
|
probe.interface_type = RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_D3D11;
|
|
probe.interface_version = 0;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_SUPPORT,
|
|
&probe) &&
|
|
probe.interface_version >= 1)
|
|
{
|
|
environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE,
|
|
(void*)&negotiation);
|
|
}
|
|
Config::SetBase(Config::MAIN_GFX_BACKEND, "D3D");
|
|
success = true;
|
|
}
|
|
break;
|
|
case RETRO_HW_CONTEXT_D3D12:
|
|
hw_render.version_major = 12;
|
|
hw_render.version_minor = 0;
|
|
if (environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render))
|
|
{
|
|
/* version 2 of the interface, when the frontend has it: the frame
|
|
* is read where it is drawn, not copied */
|
|
static const struct retro_hw_render_context_negotiation_interface_d3d12 negotiation = {
|
|
RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_D3D12,
|
|
RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_D3D12_VERSION,
|
|
RETRO_HW_RENDER_INTERFACE_D3D12_VERSION_2};
|
|
struct retro_hw_render_context_negotiation_interface probe;
|
|
probe.interface_type = RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_D3D12;
|
|
probe.interface_version = 0;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_SUPPORT,
|
|
&probe) &&
|
|
probe.interface_version >= 1)
|
|
{
|
|
environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE,
|
|
(void*)&negotiation);
|
|
}
|
|
Config::SetBase(Config::MAIN_GFX_BACKEND, "D3D12");
|
|
success = true;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef HAS_VULKAN
|
|
case RETRO_HW_CONTEXT_VULKAN:
|
|
hw_render.version_major = VK_API_VERSION_1_0;
|
|
hw_render.version_minor = 0;
|
|
if (environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render))
|
|
{
|
|
static const struct retro_hw_render_context_negotiation_interface_vulkan iface = {
|
|
RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN,
|
|
RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION,
|
|
Vk::GetApplicationInfo,
|
|
Vk::CreateDevice,
|
|
NULL, // destroy_device
|
|
#ifdef __APPLE__
|
|
Vk::CreateInstance, // create_instance (v2 API)
|
|
#else
|
|
NULL, // create_instance
|
|
#endif
|
|
Vk::CreateDevice2, // create_device2 (v2 API)
|
|
};
|
|
environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE, (void*)&iface);
|
|
|
|
Config::SetBase(Config::MAIN_GFX_BACKEND, "Vulkan");
|
|
success = true;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
/* The Direct3D 11 backend on the frontend's device, with the context
|
|
* lock held. */
|
|
static void ContextResetD3D11(retro_hw_render_interface_d3d11* d3d, const WindowSystemInfo& wsi,
|
|
int efbScale)
|
|
{
|
|
DX11::D3D::device = d3d->device;
|
|
DX11::D3D::context = d3d->context;
|
|
DX11::D3D::feature_level = d3d->featureLevel;
|
|
|
|
if (!d3d11_library.IsOpen() && !d3d11_library.Open("d3d11.dll"))
|
|
{
|
|
d3d11_library.Close();
|
|
ERROR_LOG_FMT(VIDEO, "Failed to load D3D11 Libraries");
|
|
return;
|
|
}
|
|
|
|
if (!D3DCommon::LoadLibraries())
|
|
{
|
|
ERROR_LOG_FMT(VIDEO, "Failed to load the dxgi library");
|
|
return;
|
|
}
|
|
|
|
if (FAILED(DX11::D3D::device.As(&DX11::D3D::device1)))
|
|
{
|
|
WARN_LOG_FMT(VIDEO, "Missing Direct3D 11.1 support. Logical operations will not be supported.");
|
|
g_backend_info.bSupportsLogicOp = false;
|
|
}
|
|
|
|
DX11::D3D::stateman = std::make_unique<DX11::D3D::StateManager>();
|
|
|
|
DX11::VideoBackend* d3d11 = static_cast<DX11::VideoBackend*>(g_video_backend);
|
|
|
|
if (!d3d11)
|
|
{
|
|
WARN_LOG_FMT(VIDEO, "D3D11 backend is null");
|
|
return;
|
|
}
|
|
|
|
d3d11->FillD3DBackendInfo();
|
|
UpdateActiveConfig();
|
|
|
|
std::unique_ptr<DX11Output> output = std::make_unique<DX11Output>(
|
|
s_d3d11_v2, GetAdjustedBaseWidth() * efbScale, GetAdjustedBaseHeight() * efbScale);
|
|
if (!output->Create())
|
|
return;
|
|
|
|
auto gfx = std::make_unique<DX11::Gfx>(std::move(output), wsi.render_surface_scale);
|
|
auto vertex_manager = std::make_unique<DX11::VertexManager>();
|
|
auto perf_query = std::make_unique<DX11::PerfQuery>();
|
|
auto bounding_box = std::make_unique<DX11::D3DBoundingBox>();
|
|
|
|
if(d3d11->InitializeShared(std::move(gfx), std::move(vertex_manager), std::move(perf_query),
|
|
std::move(bounding_box)))
|
|
g_context_status.MarkInitialized();
|
|
}
|
|
#endif
|
|
|
|
void ContextReset(void)
|
|
{
|
|
DEBUG_LOG_FMT(VIDEO, "Video - ContextReset!");
|
|
|
|
g_context_status.MarkReset();
|
|
|
|
#ifdef HAS_VULKAN
|
|
if (hw_render.context_type == RETRO_HW_CONTEXT_VULKAN)
|
|
{
|
|
retro_hw_render_interface* vulkan;
|
|
if (!Libretro::environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE, (void**)&vulkan) ||
|
|
!vulkan)
|
|
{
|
|
ERROR_LOG_FMT(VIDEO, "Failed to get HW rendering interface!");
|
|
return;
|
|
}
|
|
if (vulkan->interface_version != RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION)
|
|
{
|
|
ERROR_LOG_FMT(VIDEO, "HW render interface mismatch, expected {}, got {}",
|
|
RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION, vulkan->interface_version);
|
|
return;
|
|
}
|
|
Vk::SetHWRenderInterface(vulkan);
|
|
}
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
if (hw_render.context_type == RETRO_HW_CONTEXT_D3D11)
|
|
{
|
|
WindowSystemInfo wsi(WindowSystemType::Libretro, nullptr, nullptr, nullptr);
|
|
int efbScale = Libretro::Options::GetCached<int>(
|
|
Libretro::Options::gfx_settings::EFB_SCALE, 1);
|
|
wsi.render_surface_scale = efbScale;
|
|
g_video_backend->PrepareWindow(wsi);
|
|
|
|
retro_hw_render_interface_d3d11* d3d;
|
|
if (!Libretro::environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE, (void**)&d3d) || !d3d)
|
|
{
|
|
ERROR_LOG_FMT(VIDEO, "Failed to get HW rendering interface!");
|
|
return;
|
|
}
|
|
if (d3d->interface_version != RETRO_HW_RENDER_INTERFACE_D3D11_VERSION &&
|
|
d3d->interface_version != RETRO_HW_RENDER_INTERFACE_D3D11_VERSION_2)
|
|
{
|
|
ERROR_LOG_FMT(VIDEO, "HW render interface mismatch, expected {} or {}, got {}!",
|
|
RETRO_HW_RENDER_INTERFACE_D3D11_VERSION,
|
|
RETRO_HW_RENDER_INTERFACE_D3D11_VERSION_2, d3d->interface_version);
|
|
return;
|
|
}
|
|
s_d3d11_v2 = d3d->interface_version >= RETRO_HW_RENDER_INTERFACE_D3D11_VERSION_2 ? d3d : nullptr;
|
|
s_d3d11_depth = 0;
|
|
D3D11Lock();
|
|
ContextResetD3D11(d3d, wsi, efbScale);
|
|
D3D11Unlock();
|
|
return;
|
|
}
|
|
|
|
if (hw_render.context_type == RETRO_HW_CONTEXT_D3D12)
|
|
{
|
|
WindowSystemInfo wsi(WindowSystemType::Libretro, nullptr, nullptr, nullptr);
|
|
int efbScale = Libretro::Options::GetCached<int>(
|
|
Libretro::Options::gfx_settings::EFB_SCALE, 1);
|
|
wsi.render_surface_scale = efbScale;
|
|
|
|
retro_hw_render_interface_d3d12* d3d12;
|
|
if (!Libretro::environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE, (void**)&d3d12) || !d3d12)
|
|
{
|
|
ERROR_LOG_FMT(VIDEO, "Failed to get HW rendering interface!");
|
|
return;
|
|
}
|
|
|
|
if (d3d12->interface_version != RETRO_HW_RENDER_INTERFACE_D3D12_VERSION &&
|
|
d3d12->interface_version != RETRO_HW_RENDER_INTERFACE_D3D12_VERSION_2)
|
|
{
|
|
ERROR_LOG_FMT(VIDEO, "HW render interface mismatch, expected {} or {}, got {}!",
|
|
RETRO_HW_RENDER_INTERFACE_D3D12_VERSION,
|
|
RETRO_HW_RENDER_INTERFACE_D3D12_VERSION_2, d3d12->interface_version);
|
|
return;
|
|
}
|
|
|
|
if (!d3d12_library.IsOpen() && !d3d12_library.Open("d3d12.dll"))
|
|
{
|
|
d3d12_library.Close();
|
|
ERROR_LOG_FMT(VIDEO, "Failed to load D3D12 Libraries");
|
|
return;
|
|
}
|
|
|
|
if (!D3DCommon::LoadLibraries())
|
|
{
|
|
ERROR_LOG_FMT(VIDEO, "Failed to load the dxgi library");
|
|
return;
|
|
}
|
|
|
|
// Create D3D12 context with RetroArch's device and queue
|
|
ID3D12Device* ra_device = static_cast<ID3D12Device*>(d3d12->device);
|
|
ID3D12CommandQueue* ra_queue = static_cast<ID3D12CommandQueue*>(d3d12->queue);
|
|
|
|
if (!DX12::DXContext::CreateWithExternalDevice(ra_device, ra_queue))
|
|
{
|
|
ERROR_LOG_FMT(VIDEO, "Failed to create D3D12 context with external device");
|
|
return;
|
|
}
|
|
|
|
Microsoft::WRL::ComPtr<ID3D12Device1> device1;
|
|
if (FAILED(DX12::g_dx_context->GetDevice()->QueryInterface(IID_PPV_ARGS(&device1))))
|
|
{
|
|
WARN_LOG_FMT(VIDEO,
|
|
"Missing Direct3D 12.1+ support. Logical operations will not be supported.");
|
|
g_backend_info.bSupportsLogicOp = false;
|
|
}
|
|
|
|
if (!DX12::g_dx_context->CreateGlobalResources())
|
|
{
|
|
ERROR_LOG_FMT(VIDEO, "Failed to create D3D12 global resources");
|
|
DX12::DXContext::Destroy();
|
|
return;
|
|
}
|
|
|
|
UpdateActiveConfig();
|
|
|
|
std::unique_ptr<DX12Output> output = std::make_unique<DX12Output>(
|
|
d3d12, GetAdjustedBaseWidth() * efbScale, GetAdjustedBaseHeight() * efbScale);
|
|
if (!output->Create())
|
|
{
|
|
DX12::DXContext::Destroy();
|
|
return;
|
|
}
|
|
|
|
auto gfx = std::make_unique<DX12::Gfx>(std::move(output), wsi.render_surface_scale);
|
|
auto vertex_manager = std::make_unique<DX12::VertexManager>();
|
|
auto perf_query = std::make_unique<DX12::PerfQuery>();
|
|
auto bounding_box = std::make_unique<DX12::D3D12BoundingBox>();
|
|
|
|
if(!g_video_backend->InitializeShared(std::move(gfx), std::move(vertex_manager),
|
|
std::move(perf_query), std::move(bounding_box)))
|
|
{
|
|
ERROR_LOG_FMT(VIDEO, "Failed to initialize shared components");
|
|
DX12::DXContext::Destroy();
|
|
return;
|
|
}
|
|
|
|
g_context_status.MarkInitialized();
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
if(Video_InitializeBackend())
|
|
g_context_status.MarkInitialized();
|
|
}
|
|
|
|
bool Video_InitializeBackend()
|
|
{
|
|
WindowSystemInfo wsi = {};
|
|
wsi.type = WindowSystemType::Libretro;
|
|
int efbScale = Libretro::Options::GetCached<int>(
|
|
Libretro::Options::gfx_settings::EFB_SCALE, 1);
|
|
wsi.render_surface_scale = efbScale;
|
|
|
|
g_video_backend->PrepareWindow(wsi);
|
|
|
|
#ifdef HAS_VULKAN
|
|
if (hw_render.context_type == RETRO_HW_CONTEXT_VULKAN)
|
|
{
|
|
// Since VulkanContext maintains a copy of the device features and properties, we can use this
|
|
// to initialize the backend information, so that we don't need to enumerate everything again.
|
|
Vulkan::VulkanContext::PopulateBackendInfoFeatures(&g_backend_info, Vulkan::g_vulkan_context->GetPhysicalDevice(),
|
|
Vulkan::g_vulkan_context->GetDeviceInfo());
|
|
Vulkan::VulkanContext::PopulateBackendInfoMultisampleModes(
|
|
&g_backend_info, Vulkan::g_vulkan_context->GetPhysicalDevice(), Vulkan::g_vulkan_context->GetDeviceInfo());
|
|
UpdateActiveConfig();
|
|
|
|
// Remaining classes are also dependent on object cache.
|
|
Vulkan::g_object_cache = std::make_unique<Vulkan::ObjectCache>();
|
|
if (!Vulkan::g_object_cache->Initialize())
|
|
{
|
|
PanicAlertFmt("Failed to initialize Vulkan object cache.");
|
|
return false;
|
|
}
|
|
|
|
// Create command buffers. We do this separately because the other classes depend on it.
|
|
Vulkan::g_command_buffer_mgr = std::make_unique<Vulkan::CommandBufferManager>(g_Config.bBackendMultithreading);
|
|
if (!Vulkan::g_command_buffer_mgr->Initialize())
|
|
{
|
|
PanicAlertFmt("Failed to create Vulkan command buffers");
|
|
return false;
|
|
}
|
|
|
|
if (!Vulkan::StateTracker::CreateInstance())
|
|
{
|
|
PanicAlertFmt("Failed to create state tracker");
|
|
return false;
|
|
}
|
|
|
|
/* where the picture goes: the frontend's sync images, sized as the
|
|
* frame the frontend was told of */
|
|
std::unique_ptr<VKOutput> output = std::make_unique<VKOutput>(
|
|
Vk::GetHWRenderInterface(), GetAdjustedBaseWidth() * efbScale,
|
|
GetAdjustedBaseHeight() * efbScale);
|
|
if (!output->Create())
|
|
return false;
|
|
|
|
auto gfx = std::make_unique<Vulkan::VKGfx>(std::move(output), wsi.render_surface_scale);
|
|
auto vertex_manager = std::make_unique<Vulkan::VertexManager>();
|
|
auto perf_query = std::make_unique<Vulkan::PerfQuery>();
|
|
auto bounding_box = std::make_unique<Vulkan::VKBoundingBox>();
|
|
|
|
return g_video_backend->InitializeShared(std::move(gfx), std::move(vertex_manager), std::move(perf_query),
|
|
std::move(bounding_box));
|
|
}
|
|
#endif
|
|
|
|
if(!g_video_backend) {
|
|
WARN_LOG_FMT(VIDEO, "Video - g_video_backend - No supported renderer found");
|
|
return false;
|
|
}
|
|
|
|
// this calls InitializeGLExtensions, FillBackendInfo and InitializeShared internally
|
|
return g_video_backend->Initialize(wsi);
|
|
}
|
|
|
|
void ContextDestroy(void)
|
|
{
|
|
DEBUG_LOG_FMT(VIDEO, "Video - ContextDestroy!");
|
|
|
|
g_context_status.MarkDestroyed();
|
|
|
|
/* the backend's teardown uses the context */
|
|
D3D11Lock();
|
|
|
|
#ifdef HAS_OPENGL
|
|
if (g_gfx &&
|
|
Config::Get(Config::MAIN_GFX_BACKEND) == "OGL")
|
|
{
|
|
static_cast<OGL::OGLGfx*>(g_gfx.get())->SetSystemFrameBuffer(0);
|
|
}
|
|
#endif
|
|
|
|
if (g_video_backend)
|
|
{
|
|
g_video_backend->Shutdown();
|
|
}
|
|
|
|
switch (hw_render.context_type)
|
|
{
|
|
case RETRO_HW_CONTEXT_D3D12:
|
|
#ifdef _WIN32
|
|
DX12::DXContext::Destroy();
|
|
D3DCommon::UnloadLibraries();
|
|
d3d12_library.Close();
|
|
#endif
|
|
break;
|
|
case RETRO_HW_CONTEXT_D3D11:
|
|
#ifdef _WIN32
|
|
if (DX11::D3D::context)
|
|
{
|
|
DX11::D3D::context->ClearState();
|
|
DX11::D3D::context->Flush();
|
|
}
|
|
|
|
DX11::D3D::context.Reset();
|
|
DX11::D3D::device1.Reset();
|
|
|
|
DX11::D3D::device = nullptr;
|
|
DX11::D3D::device1 = nullptr;
|
|
DX11::D3D::context = nullptr;
|
|
DX11::D3D::stateman.reset();
|
|
D3DCommon::UnloadLibraries();
|
|
d3d11_library.Close();
|
|
#endif
|
|
break;
|
|
case RETRO_HW_CONTEXT_VULKAN:
|
|
#ifdef HAS_VULKAN
|
|
Vk::SetHWRenderInterface(nullptr);
|
|
#endif
|
|
break;
|
|
case RETRO_HW_CONTEXT_OPENGL:
|
|
case RETRO_HW_CONTEXT_OPENGL_CORE:
|
|
case RETRO_HW_CONTEXT_OPENGLES3:
|
|
break;
|
|
default:
|
|
case RETRO_HW_CONTEXT_NONE:
|
|
break;
|
|
}
|
|
D3D11Unlock();
|
|
#ifdef _WIN32
|
|
s_d3d11_v2 = nullptr;
|
|
s_d3d11_depth = 0;
|
|
#endif
|
|
}
|
|
#ifdef HAS_VULKAN
|
|
namespace Vk
|
|
{
|
|
const VkApplicationInfo* GetApplicationInfo(void)
|
|
{
|
|
static VkApplicationInfo app_info{VK_STRUCTURE_TYPE_APPLICATION_INFO};
|
|
app_info.pApplicationName = "Dolphin-Emu";
|
|
app_info.applicationVersion = 5; // TODO: extract from Common::scm_desc_str
|
|
app_info.pEngineName = "Dolphin-Emu";
|
|
app_info.engineVersion = 2;
|
|
app_info.apiVersion = VK_API_VERSION_1_0;
|
|
return &app_info;
|
|
}
|
|
|
|
#ifdef __APPLE__
|
|
VkInstance CreateInstance(PFN_vkGetInstanceProcAddr get_instance_proc_addr,
|
|
const VkApplicationInfo* app,
|
|
retro_vulkan_create_instance_wrapper_t create_instance_wrapper,
|
|
void* opaque)
|
|
{
|
|
VkInstanceCreateInfo create_info = {};
|
|
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
|
create_info.pApplicationInfo = app;
|
|
|
|
std::vector<const char*> extensions;
|
|
extensions.push_back(VK_EXT_LAYER_SETTINGS_EXTENSION_NAME);
|
|
|
|
create_info.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
|
|
create_info.ppEnabledExtensionNames = extensions.data();
|
|
|
|
|
|
static VkBool32 disable_arg_buffers = VK_FALSE;
|
|
|
|
VkLayerSettingEXT layer_setting = {};
|
|
layer_setting.pLayerName = "MoltenVK";
|
|
layer_setting.pSettingName = "MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS";
|
|
layer_setting.type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
|
|
layer_setting.valueCount = 1;
|
|
layer_setting.pValues = &disable_arg_buffers;
|
|
|
|
VkLayerSettingsCreateInfoEXT layer_settings = {};
|
|
layer_settings.sType = VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT;
|
|
layer_settings.settingCount = 1;
|
|
layer_settings.pSettings = &layer_setting;
|
|
|
|
create_info.pNext = &layer_settings;
|
|
|
|
return create_instance_wrapper(opaque, &create_info);
|
|
}
|
|
#endif
|
|
|
|
static bool CreateDeviceInternal(
|
|
retro_vulkan_context* context, VkInstance instance, VkPhysicalDevice gpu, VkSurfaceKHR surface,
|
|
PFN_vkGetInstanceProcAddr get_instance_proc_addr, const char** required_device_extensions,
|
|
unsigned num_required_device_extensions, const char** required_device_layers,
|
|
unsigned num_required_device_layers, const VkPhysicalDeviceFeatures* required_features,
|
|
retro_vulkan_create_device_wrapper_t create_device_wrapper, void* opaque)
|
|
{
|
|
assert(g_video_backend->GetConfigName() == "Vulkan");
|
|
|
|
Vulkan::LoadVulkanLibrary();
|
|
|
|
Init(instance, gpu, get_instance_proc_addr, required_device_extensions,
|
|
num_required_device_extensions, required_device_layers, num_required_device_layers,
|
|
required_features, create_device_wrapper, opaque);
|
|
|
|
if (!Vulkan::LoadVulkanInstanceFunctions(instance))
|
|
{
|
|
ERROR_LOG_FMT(VIDEO, "Failed to load Vulkan instance functions.");
|
|
Vulkan::UnloadVulkanLibrary();
|
|
return false;
|
|
}
|
|
|
|
Vulkan::VulkanContext::GPUList gpu_list = Vulkan::VulkanContext::EnumerateGPUs(instance);
|
|
if (gpu_list.empty())
|
|
{
|
|
ERROR_LOG_FMT(VIDEO, "No Vulkan physical devices available.");
|
|
Vulkan::UnloadVulkanLibrary();
|
|
return false;
|
|
}
|
|
|
|
Vulkan::VulkanContext::PopulateBackendInfo(&g_backend_info);
|
|
Vulkan::VulkanContext::PopulateBackendInfoAdapters(&g_backend_info, gpu_list);
|
|
|
|
if (gpu == VK_NULL_HANDLE)
|
|
gpu = gpu_list[0];
|
|
Vulkan::g_vulkan_context =
|
|
Vulkan::VulkanContext::Create(instance, gpu, surface, false, false, VK_API_VERSION_1_0);
|
|
if (!Vulkan::g_vulkan_context)
|
|
{
|
|
ERROR_LOG_FMT(VIDEO, "Failed to create Vulkan device");
|
|
Vulkan::UnloadVulkanLibrary();
|
|
return false;
|
|
}
|
|
|
|
context->gpu = Vulkan::g_vulkan_context->GetPhysicalDevice();
|
|
context->device = Vulkan::g_vulkan_context->GetDevice();
|
|
context->queue = Vulkan::g_vulkan_context->GetGraphicsQueue();
|
|
context->queue_family_index = Vulkan::g_vulkan_context->GetGraphicsQueueFamilyIndex();
|
|
/* the frontend presents; the queue the surface takes may be another
|
|
* family's */
|
|
context->presentation_queue = context->queue;
|
|
context->presentation_queue_family_index = context->queue_family_index;
|
|
if (Vulkan::g_vulkan_context->GetPresentQueue() != VK_NULL_HANDLE)
|
|
{
|
|
context->presentation_queue = Vulkan::g_vulkan_context->GetPresentQueue();
|
|
context->presentation_queue_family_index =
|
|
Vulkan::g_vulkan_context->GetPresentQueueFamilyIndex();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CreateDevice(retro_vulkan_context* context, VkInstance instance, VkPhysicalDevice gpu,
|
|
VkSurfaceKHR surface, PFN_vkGetInstanceProcAddr get_instance_proc_addr,
|
|
const char** required_device_extensions, unsigned num_required_device_extensions,
|
|
const char** required_device_layers, unsigned num_required_device_layers,
|
|
const VkPhysicalDeviceFeatures* required_features)
|
|
{
|
|
return CreateDeviceInternal(context, instance, gpu, surface, get_instance_proc_addr,
|
|
required_device_extensions, num_required_device_extensions,
|
|
required_device_layers, num_required_device_layers, required_features,
|
|
nullptr, nullptr);
|
|
}
|
|
|
|
bool CreateDevice2(retro_vulkan_context* context, VkInstance instance, VkPhysicalDevice gpu,
|
|
VkSurfaceKHR surface, PFN_vkGetInstanceProcAddr get_instance_proc_addr,
|
|
retro_vulkan_create_device_wrapper_t create_device_wrapper, void* opaque)
|
|
{
|
|
if (!create_device_wrapper)
|
|
{
|
|
ERROR_LOG_FMT(VIDEO, "Vulkan negotiation v2 requires a device creation wrapper.");
|
|
return false;
|
|
}
|
|
|
|
return CreateDeviceInternal(context, instance, gpu, surface, get_instance_proc_addr, nullptr, 0,
|
|
nullptr, 0, nullptr, create_device_wrapper, opaque);
|
|
}
|
|
} // namespace Vk
|
|
#endif
|
|
|
|
} // namespace Video
|
|
} // namespace Libretro
|
|
|
|
void retro_set_video_refresh(retro_video_refresh_t cb)
|
|
{
|
|
Libretro::Video::video_cb = cb;
|
|
}
|