mirror of
https://github.com/stenzek/duckstation.git
synced 2024-11-23 05:49:43 +00:00
GPUDevice: Extract swap chain to separate class
This commit is contained in:
parent
c6055affbf
commit
eb46142ee7
@ -4341,10 +4341,11 @@ void FullscreenUI::DrawDisplaySettingsPage()
|
|||||||
options.emplace_back(FSUI_STR("Borderless Fullscreen"), strvalue.has_value() && strvalue->empty());
|
options.emplace_back(FSUI_STR("Borderless Fullscreen"), strvalue.has_value() && strvalue->empty());
|
||||||
if (selected_adapter)
|
if (selected_adapter)
|
||||||
{
|
{
|
||||||
for (const std::string& mode : selected_adapter->fullscreen_modes)
|
for (const GPUDevice::ExclusiveFullscreenMode& mode : selected_adapter->fullscreen_modes)
|
||||||
{
|
{
|
||||||
const bool checked = (strvalue.has_value() && strvalue.value() == mode);
|
const TinyString mode_str = mode.ToString();
|
||||||
options.emplace_back(mode, checked);
|
const bool checked = (strvalue.has_value() && strvalue.value() == mode_str);
|
||||||
|
options.emplace_back(std::string(mode_str.view()), checked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1145,8 +1145,16 @@ void GPU::UpdateCommandTickEvent()
|
|||||||
void GPU::ConvertScreenCoordinatesToDisplayCoordinates(float window_x, float window_y, float* display_x,
|
void GPU::ConvertScreenCoordinatesToDisplayCoordinates(float window_x, float window_y, float* display_x,
|
||||||
float* display_y) const
|
float* display_y) const
|
||||||
{
|
{
|
||||||
|
if (!g_gpu_device->HasMainSwapChain()) [[unlikely]]
|
||||||
|
{
|
||||||
|
*display_x = 0.0f;
|
||||||
|
*display_y = 0.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
GSVector4i display_rc, draw_rc;
|
GSVector4i display_rc, draw_rc;
|
||||||
CalculateDrawRect(g_gpu_device->GetWindowWidth(), g_gpu_device->GetWindowHeight(), true, true, &display_rc, &draw_rc);
|
CalculateDrawRect(g_gpu_device->GetMainSwapChain()->GetWidth(), g_gpu_device->GetMainSwapChain()->GetHeight(), true,
|
||||||
|
true, &display_rc, &draw_rc);
|
||||||
|
|
||||||
// convert coordinates to active display region, then to full display region
|
// convert coordinates to active display region, then to full display region
|
||||||
const float scaled_display_x =
|
const float scaled_display_x =
|
||||||
@ -1644,7 +1652,8 @@ bool GPU::CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_sm
|
|||||||
if (display)
|
if (display)
|
||||||
{
|
{
|
||||||
plconfig.layout = GPUPipeline::Layout::SingleTextureAndPushConstants;
|
plconfig.layout = GPUPipeline::Layout::SingleTextureAndPushConstants;
|
||||||
plconfig.SetTargetFormats(g_gpu_device->HasSurface() ? g_gpu_device->GetWindowFormat() : GPUTexture::Format::RGBA8);
|
plconfig.SetTargetFormats(g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetFormat() :
|
||||||
|
GPUTexture::Format::RGBA8);
|
||||||
|
|
||||||
std::string vs = shadergen.GenerateDisplayVertexShader();
|
std::string vs = shadergen.GenerateDisplayVertexShader();
|
||||||
std::string fs;
|
std::string fs;
|
||||||
@ -1839,10 +1848,13 @@ GPUDevice::PresentResult GPU::PresentDisplay()
|
|||||||
{
|
{
|
||||||
FlushRender();
|
FlushRender();
|
||||||
|
|
||||||
|
if (!g_gpu_device->HasMainSwapChain())
|
||||||
|
return GPUDevice::PresentResult::SkipPresent;
|
||||||
|
|
||||||
GSVector4i display_rect;
|
GSVector4i display_rect;
|
||||||
GSVector4i draw_rect;
|
GSVector4i draw_rect;
|
||||||
CalculateDrawRect(g_gpu_device->GetWindowWidth(), g_gpu_device->GetWindowHeight(), !g_settings.debugging.show_vram,
|
CalculateDrawRect(g_gpu_device->GetMainSwapChain()->GetWidth(), g_gpu_device->GetMainSwapChain()->GetHeight(),
|
||||||
true, &display_rect, &draw_rect);
|
!g_settings.debugging.show_vram, true, &display_rect, &draw_rect);
|
||||||
return RenderDisplay(nullptr, display_rect, draw_rect, !g_settings.debugging.show_vram);
|
return RenderDisplay(nullptr, display_rect, draw_rect, !g_settings.debugging.show_vram);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1887,13 +1899,12 @@ GPUDevice::PresentResult GPU::RenderDisplay(GPUTexture* target, const GSVector4i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const GPUTexture::Format hdformat = target ? target->GetFormat() : g_gpu_device->GetWindowFormat();
|
const GPUTexture::Format hdformat = target ? target->GetFormat() : g_gpu_device->GetMainSwapChain()->GetFormat();
|
||||||
const u32 target_width = target ? target->GetWidth() : g_gpu_device->GetWindowWidth();
|
const u32 target_width = target ? target->GetWidth() : g_gpu_device->GetMainSwapChain()->GetWidth();
|
||||||
const u32 target_height = target ? target->GetHeight() : g_gpu_device->GetWindowHeight();
|
const u32 target_height = target ? target->GetHeight() : g_gpu_device->GetMainSwapChain()->GetHeight();
|
||||||
const bool really_postfx =
|
const bool really_postfx = (postfx && PostProcessing::DisplayChain.IsActive() && g_gpu_device->HasMainSwapChain() &&
|
||||||
(postfx && PostProcessing::DisplayChain.IsActive() && !g_gpu_device->GetWindowInfo().IsSurfaceless() &&
|
hdformat != GPUTexture::Format::Unknown && target_width > 0 && target_height > 0 &&
|
||||||
hdformat != GPUTexture::Format::Unknown && target_width > 0 && target_height > 0 &&
|
PostProcessing::DisplayChain.CheckTargets(hdformat, target_width, target_height));
|
||||||
PostProcessing::DisplayChain.CheckTargets(hdformat, target_width, target_height));
|
|
||||||
const GSVector4i real_draw_rect =
|
const GSVector4i real_draw_rect =
|
||||||
g_gpu_device->UsesLowerLeftOrigin() ? GPUDevice::FlipToLowerLeft(draw_rect, target_height) : draw_rect;
|
g_gpu_device->UsesLowerLeftOrigin() ? GPUDevice::FlipToLowerLeft(draw_rect, target_height) : draw_rect;
|
||||||
if (really_postfx)
|
if (really_postfx)
|
||||||
@ -1904,9 +1915,15 @@ GPUDevice::PresentResult GPU::RenderDisplay(GPUTexture* target, const GSVector4i
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (target)
|
if (target)
|
||||||
|
{
|
||||||
g_gpu_device->SetRenderTarget(target);
|
g_gpu_device->SetRenderTarget(target);
|
||||||
else if (const GPUDevice::PresentResult pres = g_gpu_device->BeginPresent(); pres != GPUDevice::PresentResult::OK)
|
}
|
||||||
return pres;
|
else
|
||||||
|
{
|
||||||
|
const GPUDevice::PresentResult pres = g_gpu_device->BeginPresent(g_gpu_device->GetMainSwapChain());
|
||||||
|
if (pres != GPUDevice::PresentResult::OK)
|
||||||
|
return pres;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (display_texture)
|
if (display_texture)
|
||||||
@ -2559,7 +2576,7 @@ bool GPU::RenderScreenshotToBuffer(u32 width, u32 height, const GSVector4i displ
|
|||||||
GPUTexture::Format* out_format)
|
GPUTexture::Format* out_format)
|
||||||
{
|
{
|
||||||
const GPUTexture::Format hdformat =
|
const GPUTexture::Format hdformat =
|
||||||
g_gpu_device->HasSurface() ? g_gpu_device->GetWindowFormat() : GPUTexture::Format::RGBA8;
|
g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetFormat() : GPUTexture::Format::RGBA8;
|
||||||
|
|
||||||
auto render_texture =
|
auto render_texture =
|
||||||
g_gpu_device->FetchAutoRecycleTexture(width, height, 1, 1, 1, GPUTexture::Type::RenderTarget, hdformat);
|
g_gpu_device->FetchAutoRecycleTexture(width, height, 1, 1, 1, GPUTexture::Type::RenderTarget, hdformat);
|
||||||
@ -2605,8 +2622,8 @@ bool GPU::RenderScreenshotToBuffer(u32 width, u32 height, const GSVector4i displ
|
|||||||
void GPU::CalculateScreenshotSize(DisplayScreenshotMode mode, u32* width, u32* height, GSVector4i* display_rect,
|
void GPU::CalculateScreenshotSize(DisplayScreenshotMode mode, u32* width, u32* height, GSVector4i* display_rect,
|
||||||
GSVector4i* draw_rect) const
|
GSVector4i* draw_rect) const
|
||||||
{
|
{
|
||||||
*width = g_gpu_device->GetWindowWidth();
|
*width = g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetWidth() : 1;
|
||||||
*height = g_gpu_device->GetWindowHeight();
|
*height = g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetHeight() : 1;
|
||||||
CalculateDrawRect(*width, *height, true, !g_settings.debugging.show_vram, display_rect, draw_rect);
|
CalculateDrawRect(*width, *height, true, !g_settings.debugging.show_vram, display_rect, draw_rect);
|
||||||
|
|
||||||
const bool internal_resolution = (mode != DisplayScreenshotMode::ScreenResolution || g_settings.debugging.show_vram);
|
const bool internal_resolution = (mode != DisplayScreenshotMode::ScreenResolution || g_settings.debugging.show_vram);
|
||||||
|
@ -525,7 +525,7 @@ void GPU_HW::UpdateSettings(const Settings& old_settings)
|
|||||||
// When using very high upscaling, it's possible that we don't have enough VRAM for two sets of buffers.
|
// When using very high upscaling, it's possible that we don't have enough VRAM for two sets of buffers.
|
||||||
// Purge the pool, and idle the GPU so that all video memory is freed prior to creating the new buffers.
|
// Purge the pool, and idle the GPU so that all video memory is freed prior to creating the new buffers.
|
||||||
g_gpu_device->PurgeTexturePool();
|
g_gpu_device->PurgeTexturePool();
|
||||||
g_gpu_device->ExecuteAndWaitForGPUIdle();
|
g_gpu_device->WaitForGPUIdle();
|
||||||
|
|
||||||
if (!CreateBuffers())
|
if (!CreateBuffers())
|
||||||
Panic("Failed to recreate buffers.");
|
Panic("Failed to recreate buffers.");
|
||||||
@ -680,7 +680,7 @@ u32 GPU_HW::CalculateResolutionScale() const
|
|||||||
{
|
{
|
||||||
// Auto scaling.
|
// Auto scaling.
|
||||||
if (m_crtc_state.display_width == 0 || m_crtc_state.display_height == 0 || m_crtc_state.display_vram_width == 0 ||
|
if (m_crtc_state.display_width == 0 || m_crtc_state.display_height == 0 || m_crtc_state.display_vram_width == 0 ||
|
||||||
m_crtc_state.display_vram_height == 0 || m_GPUSTAT.display_disable)
|
m_crtc_state.display_vram_height == 0 || m_GPUSTAT.display_disable || !g_gpu_device->HasMainSwapChain())
|
||||||
{
|
{
|
||||||
// When the system is starting and all borders crop is enabled, the registers are zero, and
|
// When the system is starting and all borders crop is enabled, the registers are zero, and
|
||||||
// display_height therefore is also zero. Keep the existing resolution until it updates.
|
// display_height therefore is also zero. Keep the existing resolution until it updates.
|
||||||
@ -689,8 +689,8 @@ u32 GPU_HW::CalculateResolutionScale() const
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
GSVector4i display_rect, draw_rect;
|
GSVector4i display_rect, draw_rect;
|
||||||
CalculateDrawRect(g_gpu_device->GetWindowWidth(), g_gpu_device->GetWindowHeight(), true, true, &display_rect,
|
CalculateDrawRect(g_gpu_device->GetMainSwapChain()->GetWidth(), g_gpu_device->GetMainSwapChain()->GetHeight(),
|
||||||
&draw_rect);
|
true, true, &display_rect, &draw_rect);
|
||||||
|
|
||||||
// We use the draw rect to determine scaling. This way we match the resolution as best we can, regardless of the
|
// We use the draw rect to determine scaling. This way we match the resolution as best we can, regardless of the
|
||||||
// anamorphic aspect ratio.
|
// anamorphic aspect ratio.
|
||||||
|
@ -243,14 +243,14 @@ void GTE::UpdateAspectRatio()
|
|||||||
{
|
{
|
||||||
case DisplayAspectRatio::MatchWindow:
|
case DisplayAspectRatio::MatchWindow:
|
||||||
{
|
{
|
||||||
if (!g_gpu_device)
|
if (!g_gpu_device || !g_gpu_device->HasMainSwapChain())
|
||||||
{
|
{
|
||||||
s_config.aspect_ratio = DisplayAspectRatio::R4_3;
|
s_config.aspect_ratio = DisplayAspectRatio::R4_3;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
num = g_gpu_device->GetWindowWidth();
|
num = g_gpu_device->GetMainSwapChain()->GetWidth();
|
||||||
denom = g_gpu_device->GetWindowHeight();
|
denom = g_gpu_device->GetMainSwapChain()->GetHeight();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -274,13 +274,19 @@ std::string Host::GetHTTPUserAgent()
|
|||||||
return fmt::format("DuckStation for {} ({}) {}", TARGET_OS_STR, CPU_ARCH_STR, g_scm_tag_str);
|
return fmt::format("DuckStation for {} ({}) {}", TARGET_OS_STR, CPU_ARCH_STR, g_scm_tag_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Host::CreateGPUDevice(RenderAPI api, Error* error)
|
bool Host::CreateGPUDevice(RenderAPI api, bool fullscreen, Error* error)
|
||||||
{
|
{
|
||||||
DebugAssert(!g_gpu_device);
|
DebugAssert(!g_gpu_device);
|
||||||
|
|
||||||
INFO_LOG("Trying to create a {} GPU device...", GPUDevice::RenderAPIToString(api));
|
INFO_LOG("Trying to create a {} GPU device...", GPUDevice::RenderAPIToString(api));
|
||||||
g_gpu_device = GPUDevice::CreateDeviceForAPI(api);
|
g_gpu_device = GPUDevice::CreateDeviceForAPI(api);
|
||||||
|
|
||||||
|
std::optional<GPUDevice::ExclusiveFullscreenMode> fullscreen_mode;
|
||||||
|
if (fullscreen && g_gpu_device && g_gpu_device->SupportsExclusiveFullscreen())
|
||||||
|
{
|
||||||
|
fullscreen_mode =
|
||||||
|
GPUDevice::ExclusiveFullscreenMode::Parse(Host::GetTinyStringSettingValue("GPU", "FullscreenMode", ""));
|
||||||
|
}
|
||||||
std::optional<bool> exclusive_fullscreen_control;
|
std::optional<bool> exclusive_fullscreen_control;
|
||||||
if (g_settings.display_exclusive_fullscreen_control != DisplayExclusiveFullscreenControl::Automatic)
|
if (g_settings.display_exclusive_fullscreen_control != DisplayExclusiveFullscreenControl::Automatic)
|
||||||
{
|
{
|
||||||
@ -300,18 +306,30 @@ bool Host::CreateGPUDevice(RenderAPI api, Error* error)
|
|||||||
if (g_settings.gpu_disable_raster_order_views)
|
if (g_settings.gpu_disable_raster_order_views)
|
||||||
disabled_features |= GPUDevice::FEATURE_MASK_RASTER_ORDER_VIEWS;
|
disabled_features |= GPUDevice::FEATURE_MASK_RASTER_ORDER_VIEWS;
|
||||||
|
|
||||||
|
// Don't dump shaders on debug builds for Android, users will complain about storage...
|
||||||
|
#if !defined(__ANDROID__) || defined(_DEBUG)
|
||||||
|
const std::string_view shader_dump_directory(EmuFolders::DataRoot);
|
||||||
|
#else
|
||||||
|
const std::string_view shader_dump_directory;
|
||||||
|
#endif
|
||||||
|
|
||||||
Error create_error;
|
Error create_error;
|
||||||
if (!g_gpu_device || !g_gpu_device->Create(
|
std::optional<WindowInfo> wi;
|
||||||
g_settings.gpu_adapter,
|
if (!g_gpu_device ||
|
||||||
g_settings.gpu_disable_shader_cache ? std::string_view() : std::string_view(EmuFolders::Cache),
|
!(wi = Host::AcquireRenderWindow(api, fullscreen, fullscreen_mode.has_value(), &create_error)).has_value() ||
|
||||||
SHADER_CACHE_VERSION, g_settings.gpu_use_debug_device, System::GetEffectiveVSyncMode(),
|
!g_gpu_device->Create(
|
||||||
System::ShouldAllowPresentThrottle(), exclusive_fullscreen_control,
|
g_settings.gpu_adapter, static_cast<GPUDevice::FeatureMask>(disabled_features), shader_dump_directory,
|
||||||
static_cast<GPUDevice::FeatureMask>(disabled_features), &create_error))
|
g_settings.gpu_disable_shader_cache ? std::string_view() : std::string_view(EmuFolders::Cache),
|
||||||
|
SHADER_CACHE_VERSION, g_settings.gpu_use_debug_device, wi.value(), System::GetEffectiveVSyncMode(),
|
||||||
|
System::ShouldAllowPresentThrottle(), fullscreen_mode.has_value() ? &fullscreen_mode.value() : nullptr,
|
||||||
|
exclusive_fullscreen_control, &create_error))
|
||||||
{
|
{
|
||||||
ERROR_LOG("Failed to create GPU device: {}", create_error.GetDescription());
|
ERROR_LOG("Failed to create GPU device: {}", create_error.GetDescription());
|
||||||
if (g_gpu_device)
|
if (g_gpu_device)
|
||||||
g_gpu_device->Destroy();
|
g_gpu_device->Destroy();
|
||||||
g_gpu_device.reset();
|
g_gpu_device.reset();
|
||||||
|
if (wi.has_value())
|
||||||
|
Host::ReleaseRenderWindow();
|
||||||
|
|
||||||
Error::SetStringFmt(
|
Error::SetStringFmt(
|
||||||
error,
|
error,
|
||||||
@ -327,27 +345,52 @@ bool Host::CreateGPUDevice(RenderAPI api, Error* error)
|
|||||||
Error::SetStringFmt(error, "Failed to initialize ImGuiManager: {}", create_error.GetDescription());
|
Error::SetStringFmt(error, "Failed to initialize ImGuiManager: {}", create_error.GetDescription());
|
||||||
g_gpu_device->Destroy();
|
g_gpu_device->Destroy();
|
||||||
g_gpu_device.reset();
|
g_gpu_device.reset();
|
||||||
|
Host::ReleaseRenderWindow();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
InputManager::SetDisplayWindowSize(static_cast<float>(g_gpu_device->GetWindowWidth()),
|
InputManager::SetDisplayWindowSize(ImGuiManager::GetWindowWidth(), ImGuiManager::GetWindowHeight());
|
||||||
static_cast<float>(g_gpu_device->GetWindowHeight()));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::UpdateDisplayWindow()
|
void Host::UpdateDisplayWindow(bool fullscreen)
|
||||||
{
|
{
|
||||||
if (!g_gpu_device)
|
if (!g_gpu_device)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!g_gpu_device->UpdateWindow())
|
const GPUVSyncMode vsync_mode =
|
||||||
|
g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetVSyncMode() : GPUVSyncMode::Disabled;
|
||||||
|
const bool allow_present_throttle =
|
||||||
|
g_gpu_device->HasMainSwapChain() && g_gpu_device->GetMainSwapChain()->IsPresentThrottleAllowed();
|
||||||
|
std::optional<GPUDevice::ExclusiveFullscreenMode> fullscreen_mode;
|
||||||
|
if (fullscreen && g_gpu_device->SupportsExclusiveFullscreen())
|
||||||
{
|
{
|
||||||
Host::ReportErrorAsync("Error", "Failed to change window after update. The log may contain more information.");
|
fullscreen_mode =
|
||||||
|
GPUDevice::ExclusiveFullscreenMode::Parse(Host::GetTinyStringSettingValue("GPU", "FullscreenMode", ""));
|
||||||
|
}
|
||||||
|
std::optional<bool> exclusive_fullscreen_control;
|
||||||
|
if (g_settings.display_exclusive_fullscreen_control != DisplayExclusiveFullscreenControl::Automatic)
|
||||||
|
{
|
||||||
|
exclusive_fullscreen_control =
|
||||||
|
(g_settings.display_exclusive_fullscreen_control == DisplayExclusiveFullscreenControl::Allowed);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_gpu_device->DestroyMainSwapChain();
|
||||||
|
|
||||||
|
Error error;
|
||||||
|
std::optional<WindowInfo> wi;
|
||||||
|
if (!(wi = Host::AcquireRenderWindow(g_gpu_device->GetRenderAPI(), fullscreen, fullscreen_mode.has_value(), &error))
|
||||||
|
.has_value() ||
|
||||||
|
!g_gpu_device->RecreateMainSwapChain(wi.value(), vsync_mode, allow_present_throttle,
|
||||||
|
fullscreen_mode.has_value() ? &fullscreen_mode.value() : nullptr,
|
||||||
|
exclusive_fullscreen_control, &error))
|
||||||
|
{
|
||||||
|
Host::ReportFatalError("Failed to change window after update", error.GetDescription());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float f_width = static_cast<float>(g_gpu_device->GetWindowWidth());
|
const float f_width = static_cast<float>(g_gpu_device->GetMainSwapChain()->GetWidth());
|
||||||
const float f_height = static_cast<float>(g_gpu_device->GetWindowHeight());
|
const float f_height = static_cast<float>(g_gpu_device->GetMainSwapChain()->GetHeight());
|
||||||
ImGuiManager::WindowResized(f_width, f_height);
|
ImGuiManager::WindowResized(f_width, f_height);
|
||||||
InputManager::SetDisplayWindowSize(f_width, f_height);
|
InputManager::SetDisplayWindowSize(f_width, f_height);
|
||||||
System::HostDisplayResized();
|
System::HostDisplayResized();
|
||||||
@ -365,15 +408,21 @@ void Host::UpdateDisplayWindow()
|
|||||||
|
|
||||||
void Host::ResizeDisplayWindow(s32 width, s32 height, float scale)
|
void Host::ResizeDisplayWindow(s32 width, s32 height, float scale)
|
||||||
{
|
{
|
||||||
if (!g_gpu_device)
|
if (!g_gpu_device || !g_gpu_device->HasMainSwapChain())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DEV_LOG("Display window resized to {}x{}", width, height);
|
DEV_LOG("Display window resized to {}x{}", width, height);
|
||||||
|
|
||||||
g_gpu_device->ResizeWindow(width, height, scale);
|
Error error;
|
||||||
|
if (!g_gpu_device->GetMainSwapChain()->ResizeBuffers(width, height, scale, &error))
|
||||||
|
{
|
||||||
|
ERROR_LOG("Failed to resize main swap chain: {}", error.GetDescription());
|
||||||
|
UpdateDisplayWindow(Host::IsFullscreen());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const float f_width = static_cast<float>(g_gpu_device->GetWindowWidth());
|
const float f_width = static_cast<float>(g_gpu_device->GetMainSwapChain()->GetWidth());
|
||||||
const float f_height = static_cast<float>(g_gpu_device->GetWindowHeight());
|
const float f_height = static_cast<float>(g_gpu_device->GetMainSwapChain()->GetHeight());
|
||||||
ImGuiManager::WindowResized(f_width, f_height);
|
ImGuiManager::WindowResized(f_width, f_height);
|
||||||
InputManager::SetDisplayWindowSize(f_width, f_height);
|
InputManager::SetDisplayWindowSize(f_width, f_height);
|
||||||
|
|
||||||
@ -404,4 +453,6 @@ void Host::ReleaseGPUDevice()
|
|||||||
INFO_LOG("Destroying {} GPU device...", GPUDevice::RenderAPIToString(g_gpu_device->GetRenderAPI()));
|
INFO_LOG("Destroying {} GPU device...", GPUDevice::RenderAPIToString(g_gpu_device->GetRenderAPI()));
|
||||||
g_gpu_device->Destroy();
|
g_gpu_device->Destroy();
|
||||||
g_gpu_device.reset();
|
g_gpu_device.reset();
|
||||||
|
|
||||||
|
Host::ReleaseRenderWindow();
|
||||||
}
|
}
|
||||||
|
@ -82,11 +82,25 @@ void DisplayLoadingScreen(const char* message, int progress_min = -1, int progre
|
|||||||
/// Safely executes a function on the VM thread.
|
/// Safely executes a function on the VM thread.
|
||||||
void RunOnCPUThread(std::function<void()> function, bool block = false);
|
void RunOnCPUThread(std::function<void()> function, bool block = false);
|
||||||
|
|
||||||
|
/// Called when the core is creating a render device.
|
||||||
|
/// This could also be fullscreen transition.
|
||||||
|
std::optional<WindowInfo> AcquireRenderWindow(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen,
|
||||||
|
Error* error);
|
||||||
|
|
||||||
|
/// Called when the core is finished with a render window.
|
||||||
|
void ReleaseRenderWindow();
|
||||||
|
|
||||||
|
/// Returns true if the hosting application is currently fullscreen.
|
||||||
|
bool IsFullscreen();
|
||||||
|
|
||||||
|
/// Alters fullscreen state of hosting application.
|
||||||
|
void SetFullscreen(bool enabled);
|
||||||
|
|
||||||
/// Attempts to create the rendering device backend.
|
/// Attempts to create the rendering device backend.
|
||||||
bool CreateGPUDevice(RenderAPI api, Error* error);
|
bool CreateGPUDevice(RenderAPI api, bool fullscreen, Error* error);
|
||||||
|
|
||||||
/// Handles fullscreen transitions and such.
|
/// Handles fullscreen transitions and such.
|
||||||
void UpdateDisplayWindow();
|
void UpdateDisplayWindow(bool fullscreen);
|
||||||
|
|
||||||
/// Called when the window is resized.
|
/// Called when the window is resized.
|
||||||
void ResizeDisplayWindow(s32 width, s32 height, float scale);
|
void ResizeDisplayWindow(s32 width, s32 height, float scale);
|
||||||
|
@ -84,7 +84,7 @@ static std::tuple<float, float> GetMinMax(std::span<const float> values)
|
|||||||
void Host::DisplayLoadingScreen(const char* message, int progress_min /*= -1*/, int progress_max /*= -1*/,
|
void Host::DisplayLoadingScreen(const char* message, int progress_min /*= -1*/, int progress_max /*= -1*/,
|
||||||
int progress_value /*= -1*/)
|
int progress_value /*= -1*/)
|
||||||
{
|
{
|
||||||
if (!g_gpu_device)
|
if (!g_gpu_device || !g_gpu_device->HasMainSwapChain())
|
||||||
{
|
{
|
||||||
INFO_LOG("{}: {}/{}", message, progress_value, progress_max);
|
INFO_LOG("{}: {}/{}", message, progress_value, progress_max);
|
||||||
return;
|
return;
|
||||||
@ -160,10 +160,11 @@ void Host::DisplayLoadingScreen(const char* message, int progress_min /*= -1*/,
|
|||||||
|
|
||||||
// TODO: Glass effect or something.
|
// TODO: Glass effect or something.
|
||||||
|
|
||||||
if (g_gpu_device->BeginPresent() == GPUDevice::PresentResult::OK)
|
GPUSwapChain* swap_chain = g_gpu_device->GetMainSwapChain();
|
||||||
|
if (g_gpu_device->BeginPresent(swap_chain) == GPUDevice::PresentResult::OK)
|
||||||
{
|
{
|
||||||
g_gpu_device->RenderImGui();
|
g_gpu_device->RenderImGui(swap_chain);
|
||||||
g_gpu_device->EndPresent(false);
|
g_gpu_device->EndPresent(swap_chain, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
@ -158,8 +158,6 @@ void Settings::Load(SettingsInterface& si, SettingsInterface& controller_si)
|
|||||||
turbo_speed = si.GetFloatValue("Main", "TurboSpeed", 0.0f);
|
turbo_speed = si.GetFloatValue("Main", "TurboSpeed", 0.0f);
|
||||||
sync_to_host_refresh_rate = si.GetBoolValue("Main", "SyncToHostRefreshRate", false);
|
sync_to_host_refresh_rate = si.GetBoolValue("Main", "SyncToHostRefreshRate", false);
|
||||||
inhibit_screensaver = si.GetBoolValue("Main", "InhibitScreensaver", true);
|
inhibit_screensaver = si.GetBoolValue("Main", "InhibitScreensaver", true);
|
||||||
start_paused = si.GetBoolValue("Main", "StartPaused", false);
|
|
||||||
start_fullscreen = si.GetBoolValue("Main", "StartFullscreen", false);
|
|
||||||
pause_on_focus_loss = si.GetBoolValue("Main", "PauseOnFocusLoss", false);
|
pause_on_focus_loss = si.GetBoolValue("Main", "PauseOnFocusLoss", false);
|
||||||
pause_on_controller_disconnection = si.GetBoolValue("Main", "PauseOnControllerDisconnection", false);
|
pause_on_controller_disconnection = si.GetBoolValue("Main", "PauseOnControllerDisconnection", false);
|
||||||
save_state_on_exit = si.GetBoolValue("Main", "SaveStateOnExit", true);
|
save_state_on_exit = si.GetBoolValue("Main", "SaveStateOnExit", true);
|
||||||
@ -505,8 +503,6 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const
|
|||||||
{
|
{
|
||||||
si.SetBoolValue("Main", "SyncToHostRefreshRate", sync_to_host_refresh_rate);
|
si.SetBoolValue("Main", "SyncToHostRefreshRate", sync_to_host_refresh_rate);
|
||||||
si.SetBoolValue("Main", "InhibitScreensaver", inhibit_screensaver);
|
si.SetBoolValue("Main", "InhibitScreensaver", inhibit_screensaver);
|
||||||
si.SetBoolValue("Main", "StartPaused", start_paused);
|
|
||||||
si.SetBoolValue("Main", "StartFullscreen", start_fullscreen);
|
|
||||||
si.SetBoolValue("Main", "PauseOnFocusLoss", pause_on_focus_loss);
|
si.SetBoolValue("Main", "PauseOnFocusLoss", pause_on_focus_loss);
|
||||||
si.SetBoolValue("Main", "PauseOnControllerDisconnection", pause_on_controller_disconnection);
|
si.SetBoolValue("Main", "PauseOnControllerDisconnection", pause_on_controller_disconnection);
|
||||||
si.SetBoolValue("Main", "SaveStateOnExit", save_state_on_exit);
|
si.SetBoolValue("Main", "SaveStateOnExit", save_state_on_exit);
|
||||||
@ -1657,10 +1653,11 @@ float Settings::GetDisplayAspectRatioValue() const
|
|||||||
{
|
{
|
||||||
case DisplayAspectRatio::MatchWindow:
|
case DisplayAspectRatio::MatchWindow:
|
||||||
{
|
{
|
||||||
if (!g_gpu_device)
|
if (!g_gpu_device || !g_gpu_device->HasMainSwapChain())
|
||||||
return s_display_aspect_ratio_values[static_cast<size_t>(DEFAULT_DISPLAY_ASPECT_RATIO)];
|
return s_display_aspect_ratio_values[static_cast<size_t>(DEFAULT_DISPLAY_ASPECT_RATIO)];
|
||||||
|
|
||||||
return static_cast<float>(g_gpu_device->GetWindowWidth()) / static_cast<float>(g_gpu_device->GetWindowHeight());
|
return static_cast<float>(g_gpu_device->GetMainSwapChain()->GetWidth()) /
|
||||||
|
static_cast<float>(g_gpu_device->GetMainSwapChain()->GetHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
case DisplayAspectRatio::Custom:
|
case DisplayAspectRatio::Custom:
|
||||||
@ -2110,7 +2107,6 @@ std::string EmuFolders::Bios;
|
|||||||
std::string EmuFolders::Cache;
|
std::string EmuFolders::Cache;
|
||||||
std::string EmuFolders::Cheats;
|
std::string EmuFolders::Cheats;
|
||||||
std::string EmuFolders::Covers;
|
std::string EmuFolders::Covers;
|
||||||
std::string EmuFolders::Dumps;
|
|
||||||
std::string EmuFolders::GameIcons;
|
std::string EmuFolders::GameIcons;
|
||||||
std::string EmuFolders::GameSettings;
|
std::string EmuFolders::GameSettings;
|
||||||
std::string EmuFolders::InputProfiles;
|
std::string EmuFolders::InputProfiles;
|
||||||
@ -2130,7 +2126,6 @@ void EmuFolders::SetDefaults()
|
|||||||
Cache = Path::Combine(DataRoot, "cache");
|
Cache = Path::Combine(DataRoot, "cache");
|
||||||
Cheats = Path::Combine(DataRoot, "cheats");
|
Cheats = Path::Combine(DataRoot, "cheats");
|
||||||
Covers = Path::Combine(DataRoot, "covers");
|
Covers = Path::Combine(DataRoot, "covers");
|
||||||
Dumps = Path::Combine(DataRoot, "dump");
|
|
||||||
GameIcons = Path::Combine(DataRoot, "gameicons");
|
GameIcons = Path::Combine(DataRoot, "gameicons");
|
||||||
GameSettings = Path::Combine(DataRoot, "gamesettings");
|
GameSettings = Path::Combine(DataRoot, "gamesettings");
|
||||||
InputProfiles = Path::Combine(DataRoot, "inputprofiles");
|
InputProfiles = Path::Combine(DataRoot, "inputprofiles");
|
||||||
@ -2162,7 +2157,6 @@ void EmuFolders::LoadConfig(SettingsInterface& si)
|
|||||||
Cache = LoadPathFromSettings(si, DataRoot, "Folders", "Cache", "cache");
|
Cache = LoadPathFromSettings(si, DataRoot, "Folders", "Cache", "cache");
|
||||||
Cheats = LoadPathFromSettings(si, DataRoot, "Folders", "Cheats", "cheats");
|
Cheats = LoadPathFromSettings(si, DataRoot, "Folders", "Cheats", "cheats");
|
||||||
Covers = LoadPathFromSettings(si, DataRoot, "Folders", "Covers", "covers");
|
Covers = LoadPathFromSettings(si, DataRoot, "Folders", "Covers", "covers");
|
||||||
Dumps = LoadPathFromSettings(si, DataRoot, "Folders", "Dumps", "dump");
|
|
||||||
GameIcons = LoadPathFromSettings(si, DataRoot, "Folders", "GameIcons", "gameicons");
|
GameIcons = LoadPathFromSettings(si, DataRoot, "Folders", "GameIcons", "gameicons");
|
||||||
GameSettings = LoadPathFromSettings(si, DataRoot, "Folders", "GameSettings", "gamesettings");
|
GameSettings = LoadPathFromSettings(si, DataRoot, "Folders", "GameSettings", "gamesettings");
|
||||||
InputProfiles = LoadPathFromSettings(si, DataRoot, "Folders", "InputProfiles", "inputprofiles");
|
InputProfiles = LoadPathFromSettings(si, DataRoot, "Folders", "InputProfiles", "inputprofiles");
|
||||||
@ -2179,7 +2173,6 @@ void EmuFolders::LoadConfig(SettingsInterface& si)
|
|||||||
DEV_LOG("Cache Directory: {}", Cache);
|
DEV_LOG("Cache Directory: {}", Cache);
|
||||||
DEV_LOG("Cheats Directory: {}", Cheats);
|
DEV_LOG("Cheats Directory: {}", Cheats);
|
||||||
DEV_LOG("Covers Directory: {}", Covers);
|
DEV_LOG("Covers Directory: {}", Covers);
|
||||||
DEV_LOG("Dumps Directory: {}", Dumps);
|
|
||||||
DEV_LOG("Game Icons Directory: {}", GameIcons);
|
DEV_LOG("Game Icons Directory: {}", GameIcons);
|
||||||
DEV_LOG("Game Settings Directory: {}", GameSettings);
|
DEV_LOG("Game Settings Directory: {}", GameSettings);
|
||||||
DEV_LOG("Input Profile Directory: {}", InputProfiles);
|
DEV_LOG("Input Profile Directory: {}", InputProfiles);
|
||||||
@ -2201,7 +2194,6 @@ void EmuFolders::Save(SettingsInterface& si)
|
|||||||
si.SetStringValue("Folders", "Cache", Path::MakeRelative(Cache, DataRoot).c_str());
|
si.SetStringValue("Folders", "Cache", Path::MakeRelative(Cache, DataRoot).c_str());
|
||||||
si.SetStringValue("Folders", "Cheats", Path::MakeRelative(Cheats, DataRoot).c_str());
|
si.SetStringValue("Folders", "Cheats", Path::MakeRelative(Cheats, DataRoot).c_str());
|
||||||
si.SetStringValue("Folders", "Covers", Path::MakeRelative(Covers, DataRoot).c_str());
|
si.SetStringValue("Folders", "Covers", Path::MakeRelative(Covers, DataRoot).c_str());
|
||||||
si.SetStringValue("Folders", "Dumps", Path::MakeRelative(Dumps, DataRoot).c_str());
|
|
||||||
si.SetStringValue("Folders", "GameIcons", Path::MakeRelative(GameIcons, DataRoot).c_str());
|
si.SetStringValue("Folders", "GameIcons", Path::MakeRelative(GameIcons, DataRoot).c_str());
|
||||||
si.SetStringValue("Folders", "GameSettings", Path::MakeRelative(GameSettings, DataRoot).c_str());
|
si.SetStringValue("Folders", "GameSettings", Path::MakeRelative(GameSettings, DataRoot).c_str());
|
||||||
si.SetStringValue("Folders", "InputProfiles", Path::MakeRelative(InputProfiles, DataRoot).c_str());
|
si.SetStringValue("Folders", "InputProfiles", Path::MakeRelative(InputProfiles, DataRoot).c_str());
|
||||||
@ -2242,7 +2234,6 @@ bool EmuFolders::EnsureFoldersExist()
|
|||||||
result = FileSystem::EnsureDirectoryExists(Path::Combine(Cache, "achievement_images").c_str(), false) && result;
|
result = FileSystem::EnsureDirectoryExists(Path::Combine(Cache, "achievement_images").c_str(), false) && result;
|
||||||
result = FileSystem::EnsureDirectoryExists(Cheats.c_str(), false) && result;
|
result = FileSystem::EnsureDirectoryExists(Cheats.c_str(), false) && result;
|
||||||
result = FileSystem::EnsureDirectoryExists(Covers.c_str(), false) && result;
|
result = FileSystem::EnsureDirectoryExists(Covers.c_str(), false) && result;
|
||||||
result = FileSystem::EnsureDirectoryExists(Dumps.c_str(), false) && result;
|
|
||||||
result = FileSystem::EnsureDirectoryExists(GameIcons.c_str(), false) && result;
|
result = FileSystem::EnsureDirectoryExists(GameIcons.c_str(), false) && result;
|
||||||
result = FileSystem::EnsureDirectoryExists(GameSettings.c_str(), false) && result;
|
result = FileSystem::EnsureDirectoryExists(GameSettings.c_str(), false) && result;
|
||||||
result = FileSystem::EnsureDirectoryExists(InputProfiles.c_str(), false) && result;
|
result = FileSystem::EnsureDirectoryExists(InputProfiles.c_str(), false) && result;
|
||||||
|
@ -78,8 +78,6 @@ struct Settings
|
|||||||
float turbo_speed = 0.0f;
|
float turbo_speed = 0.0f;
|
||||||
bool sync_to_host_refresh_rate : 1 = false;
|
bool sync_to_host_refresh_rate : 1 = false;
|
||||||
bool inhibit_screensaver : 1 = true;
|
bool inhibit_screensaver : 1 = true;
|
||||||
bool start_paused : 1 = false;
|
|
||||||
bool start_fullscreen : 1 = false;
|
|
||||||
bool pause_on_focus_loss : 1 = false;
|
bool pause_on_focus_loss : 1 = false;
|
||||||
bool pause_on_controller_disconnection : 1 = false;
|
bool pause_on_controller_disconnection : 1 = false;
|
||||||
bool save_state_on_exit : 1 = true;
|
bool save_state_on_exit : 1 = true;
|
||||||
@ -572,7 +570,6 @@ extern std::string Bios;
|
|||||||
extern std::string Cache;
|
extern std::string Cache;
|
||||||
extern std::string Cheats;
|
extern std::string Cheats;
|
||||||
extern std::string Covers;
|
extern std::string Covers;
|
||||||
extern std::string Dumps;
|
|
||||||
extern std::string GameIcons;
|
extern std::string GameIcons;
|
||||||
extern std::string GameSettings;
|
extern std::string GameSettings;
|
||||||
extern std::string InputProfiles;
|
extern std::string InputProfiles;
|
||||||
|
@ -145,21 +145,26 @@ static bool ReadExecutableFromImage(IsoReader& iso, std::string* out_executable_
|
|||||||
static GameHash GetGameHashFromBuffer(std::string_view exe_name, std::span<const u8> exe_buffer,
|
static GameHash GetGameHashFromBuffer(std::string_view exe_name, std::span<const u8> exe_buffer,
|
||||||
const IsoReader::ISOPrimaryVolumeDescriptor& iso_pvd, u32 track_1_length);
|
const IsoReader::ISOPrimaryVolumeDescriptor& iso_pvd, u32 track_1_length);
|
||||||
|
|
||||||
|
/// Settings that are looked up on demand.
|
||||||
|
static bool ShouldStartFullscreen();
|
||||||
|
static bool ShouldStartPaused();
|
||||||
|
|
||||||
/// Checks for settings changes, std::move() the old settings away for comparing beforehand.
|
/// Checks for settings changes, std::move() the old settings away for comparing beforehand.
|
||||||
static void CheckForSettingsChanges(const Settings& old_settings);
|
static void CheckForSettingsChanges(const Settings& old_settings);
|
||||||
static void WarnAboutUnsafeSettings();
|
static void WarnAboutUnsafeSettings();
|
||||||
static void LogUnsafeSettingsToConsole(const SmallStringBase& messages);
|
static void LogUnsafeSettingsToConsole(const SmallStringBase& messages);
|
||||||
|
|
||||||
static bool Initialize(bool force_software_renderer, Error* error);
|
static bool Initialize(bool force_software_renderer, bool fullscreen, Error* error);
|
||||||
static bool LoadBIOS(Error* error);
|
static bool LoadBIOS(Error* error);
|
||||||
static bool SetBootMode(BootMode new_boot_mode, DiscRegion disc_region, Error* error);
|
static bool SetBootMode(BootMode new_boot_mode, DiscRegion disc_region, Error* error);
|
||||||
static void InternalReset();
|
static void InternalReset();
|
||||||
static void ClearRunningGame();
|
static void ClearRunningGame();
|
||||||
static void DestroySystem();
|
static void DestroySystem();
|
||||||
|
|
||||||
static bool CreateGPU(GPURenderer renderer, bool is_switching, Error* error);
|
static bool CreateGPU(GPURenderer renderer, bool is_switching, bool fullscreen, Error* error);
|
||||||
static bool RecreateGPU(GPURenderer renderer, bool force_recreate_device = false, bool update_display = true);
|
static bool RecreateGPU(GPURenderer renderer, bool force_recreate_device = false, bool update_display = true);
|
||||||
static void HandleHostGPUDeviceLost();
|
static void HandleHostGPUDeviceLost();
|
||||||
|
static void HandleExclusiveFullscreenLost();
|
||||||
|
|
||||||
/// Returns true if boot is being fast forwarded.
|
/// Returns true if boot is being fast forwarded.
|
||||||
static bool IsFastForwardingBoot();
|
static bool IsFastForwardingBoot();
|
||||||
@ -1201,10 +1206,11 @@ bool System::RecreateGPU(GPURenderer renderer, bool force_recreate_device, bool
|
|||||||
{
|
{
|
||||||
PostProcessing::Shutdown();
|
PostProcessing::Shutdown();
|
||||||
Host::ReleaseGPUDevice();
|
Host::ReleaseGPUDevice();
|
||||||
|
Host::ReleaseRenderWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
if (!CreateGPU(renderer, true, &error))
|
if (!CreateGPU(renderer, true, Host::IsFullscreen(), &error))
|
||||||
{
|
{
|
||||||
if (!IsStartupCancelled())
|
if (!IsStartupCancelled())
|
||||||
Host::ReportErrorAsync("Error", error.GetDescription());
|
Host::ReportErrorAsync("Error", error.GetDescription());
|
||||||
@ -1265,6 +1271,12 @@ void System::HandleHostGPUDeviceLost()
|
|||||||
Host::OSD_CRITICAL_ERROR_DURATION);
|
Host::OSD_CRITICAL_ERROR_DURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void System::HandleExclusiveFullscreenLost()
|
||||||
|
{
|
||||||
|
WARNING_LOG("Lost exclusive fullscreen.");
|
||||||
|
Host::SetFullscreen(false);
|
||||||
|
}
|
||||||
|
|
||||||
void System::LoadSettings(bool display_osd_messages)
|
void System::LoadSettings(bool display_osd_messages)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock = Host::GetSettingsLock();
|
std::unique_lock<std::mutex> lock = Host::GetSettingsLock();
|
||||||
@ -1373,6 +1385,9 @@ void System::SetDefaultSettings(SettingsInterface& si)
|
|||||||
|
|
||||||
temp.Save(si, false);
|
temp.Save(si, false);
|
||||||
|
|
||||||
|
si.SetBoolValue("Main", "StartPaused", false);
|
||||||
|
si.SetBoolValue("Main", "StartFullscreen", false);
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(__ANDROID__)
|
#if !defined(_WIN32) && !defined(__ANDROID__)
|
||||||
// On Linux, default the console to whether standard input is currently available.
|
// On Linux, default the console to whether standard input is currently available.
|
||||||
si.SetBoolValue("Logging", "LogToConsole", Log::IsConsoleOutputCurrentlyAvailable());
|
si.SetBoolValue("Logging", "LogToConsole", Log::IsConsoleOutputCurrentlyAvailable());
|
||||||
@ -1793,7 +1808,8 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Component setup.
|
// Component setup.
|
||||||
if (!Initialize(parameters.force_software_renderer, error))
|
if (!Initialize(parameters.force_software_renderer, parameters.override_fullscreen.value_or(ShouldStartFullscreen()),
|
||||||
|
error))
|
||||||
{
|
{
|
||||||
s_boot_mode = System::BootMode::None;
|
s_boot_mode = System::BootMode::None;
|
||||||
s_state = State::Shutdown;
|
s_state = State::Shutdown;
|
||||||
@ -1853,7 +1869,7 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||||||
if (parameters.start_media_capture)
|
if (parameters.start_media_capture)
|
||||||
StartMediaCapture({});
|
StartMediaCapture({});
|
||||||
|
|
||||||
if (g_settings.start_paused || parameters.override_start_paused.value_or(false))
|
if (ShouldStartPaused() || parameters.override_start_paused.value_or(false))
|
||||||
PauseSystem(true);
|
PauseSystem(true);
|
||||||
|
|
||||||
UpdateSpeedLimiterState();
|
UpdateSpeedLimiterState();
|
||||||
@ -1861,7 +1877,7 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::Initialize(bool force_software_renderer, Error* error)
|
bool System::Initialize(bool force_software_renderer, bool fullscreen, Error* error)
|
||||||
{
|
{
|
||||||
g_ticks_per_second = ScaleTicksToOverclock(MASTER_CLOCK);
|
g_ticks_per_second = ScaleTicksToOverclock(MASTER_CLOCK);
|
||||||
s_max_slice_ticks = ScaleTicksToOverclock(MASTER_CLOCK / 10);
|
s_max_slice_ticks = ScaleTicksToOverclock(MASTER_CLOCK / 10);
|
||||||
@ -1911,7 +1927,7 @@ bool System::Initialize(bool force_software_renderer, Error* error)
|
|||||||
Bus::Initialize();
|
Bus::Initialize();
|
||||||
CPU::Initialize();
|
CPU::Initialize();
|
||||||
|
|
||||||
if (!CreateGPU(force_software_renderer ? GPURenderer::Software : g_settings.gpu_renderer, false, error))
|
if (!CreateGPU(force_software_renderer ? GPURenderer::Software : g_settings.gpu_renderer, false, fullscreen, error))
|
||||||
{
|
{
|
||||||
CPU::Shutdown();
|
CPU::Shutdown();
|
||||||
Bus::Shutdown();
|
Bus::Shutdown();
|
||||||
@ -1928,10 +1944,7 @@ bool System::Initialize(bool force_software_renderer, Error* error)
|
|||||||
{
|
{
|
||||||
g_gpu.reset();
|
g_gpu.reset();
|
||||||
if (!s_keep_gpu_device_on_shutdown)
|
if (!s_keep_gpu_device_on_shutdown)
|
||||||
{
|
|
||||||
Host::ReleaseGPUDevice();
|
Host::ReleaseGPUDevice();
|
||||||
Host::ReleaseRenderWindow();
|
|
||||||
}
|
|
||||||
if (g_settings.gpu_pgxp_enable)
|
if (g_settings.gpu_pgxp_enable)
|
||||||
CPU::PGXP::Shutdown();
|
CPU::PGXP::Shutdown();
|
||||||
CPU::Shutdown();
|
CPU::Shutdown();
|
||||||
@ -2018,7 +2031,6 @@ void System::DestroySystem()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Host::ReleaseGPUDevice();
|
Host::ReleaseGPUDevice();
|
||||||
Host::ReleaseRenderWindow();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s_bios_hash = {};
|
s_bios_hash = {};
|
||||||
@ -2189,12 +2201,13 @@ void System::FrameDone()
|
|||||||
const bool is_unique_frame = (s_last_presented_internal_frame_number != s_internal_frame_number);
|
const bool is_unique_frame = (s_last_presented_internal_frame_number != s_internal_frame_number);
|
||||||
s_last_presented_internal_frame_number = s_internal_frame_number;
|
s_last_presented_internal_frame_number = s_internal_frame_number;
|
||||||
|
|
||||||
const bool skip_this_frame = (((s_skip_presenting_duplicate_frames && !is_unique_frame &&
|
const bool skip_this_frame =
|
||||||
s_skipped_frame_count < MAX_SKIPPED_DUPLICATE_FRAME_COUNT) ||
|
(((s_skip_presenting_duplicate_frames && !is_unique_frame &&
|
||||||
(!s_optimal_frame_pacing && current_time > s_next_frame_time &&
|
s_skipped_frame_count < MAX_SKIPPED_DUPLICATE_FRAME_COUNT) ||
|
||||||
s_skipped_frame_count < MAX_SKIPPED_TIMEOUT_FRAME_COUNT) ||
|
(!s_optimal_frame_pacing && current_time > s_next_frame_time &&
|
||||||
g_gpu_device->ShouldSkipPresentingFrame()) &&
|
s_skipped_frame_count < MAX_SKIPPED_TIMEOUT_FRAME_COUNT) ||
|
||||||
!s_syncing_to_host_with_vsync && !IsExecutionInterrupted());
|
(g_gpu_device->HasMainSwapChain() && g_gpu_device->GetMainSwapChain()->ShouldSkipPresentingFrame())) &&
|
||||||
|
!s_syncing_to_host_with_vsync && !IsExecutionInterrupted());
|
||||||
if (!skip_this_frame)
|
if (!skip_this_frame)
|
||||||
{
|
{
|
||||||
s_skipped_frame_count = 0;
|
s_skipped_frame_count = 0;
|
||||||
@ -2211,7 +2224,7 @@ void System::FrameDone()
|
|||||||
const bool do_present = PresentDisplay(true, 0);
|
const bool do_present = PresentDisplay(true, 0);
|
||||||
Throttle(current_time);
|
Throttle(current_time);
|
||||||
if (do_present)
|
if (do_present)
|
||||||
g_gpu_device->SubmitPresent();
|
g_gpu_device->SubmitPresent(g_gpu_device->GetMainSwapChain());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -2373,7 +2386,7 @@ void System::IncrementInternalFrameNumber()
|
|||||||
s_internal_frame_number++;
|
s_internal_frame_number++;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::CreateGPU(GPURenderer renderer, bool is_switching, Error* error)
|
bool System::CreateGPU(GPURenderer renderer, bool is_switching, bool fullscreen, Error* error)
|
||||||
{
|
{
|
||||||
const RenderAPI api = Settings::GetRenderAPIForRenderer(renderer);
|
const RenderAPI api = Settings::GetRenderAPIForRenderer(renderer);
|
||||||
|
|
||||||
@ -2388,7 +2401,7 @@ bool System::CreateGPU(GPURenderer renderer, bool is_switching, Error* error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Host::ReleaseGPUDevice();
|
Host::ReleaseGPUDevice();
|
||||||
if (!Host::CreateGPUDevice(api, error))
|
if (!Host::CreateGPUDevice(api, fullscreen, error))
|
||||||
{
|
{
|
||||||
Host::ReleaseRenderWindow();
|
Host::ReleaseRenderWindow();
|
||||||
return false;
|
return false;
|
||||||
@ -3436,9 +3449,9 @@ void System::UpdateSpeedLimiterState()
|
|||||||
s_syncing_to_host = false;
|
s_syncing_to_host = false;
|
||||||
s_syncing_to_host_with_vsync = false;
|
s_syncing_to_host_with_vsync = false;
|
||||||
|
|
||||||
if (g_settings.sync_to_host_refresh_rate)
|
if (g_settings.sync_to_host_refresh_rate && g_gpu_device->HasMainSwapChain())
|
||||||
{
|
{
|
||||||
const float host_refresh_rate = g_gpu_device->GetWindowInfo().surface_refresh_rate;
|
const float host_refresh_rate = g_gpu_device->GetMainSwapChain()->GetWindowInfo().surface_refresh_rate;
|
||||||
if (host_refresh_rate > 0.0f)
|
if (host_refresh_rate > 0.0f)
|
||||||
{
|
{
|
||||||
const float ratio = host_refresh_rate / System::GetThrottleFrequency();
|
const float ratio = host_refresh_rate / System::GetThrottleFrequency();
|
||||||
@ -3499,14 +3512,23 @@ void System::UpdateDisplayVSync()
|
|||||||
// Avoid flipping vsync on and off by manually throttling when vsync is on.
|
// Avoid flipping vsync on and off by manually throttling when vsync is on.
|
||||||
const GPUVSyncMode vsync_mode = GetEffectiveVSyncMode();
|
const GPUVSyncMode vsync_mode = GetEffectiveVSyncMode();
|
||||||
const bool allow_present_throttle = ShouldAllowPresentThrottle();
|
const bool allow_present_throttle = ShouldAllowPresentThrottle();
|
||||||
if (g_gpu_device->GetVSyncMode() == vsync_mode && g_gpu_device->IsPresentThrottleAllowed() == allow_present_throttle)
|
if (!g_gpu_device->HasMainSwapChain() ||
|
||||||
|
(g_gpu_device->GetMainSwapChain()->GetVSyncMode() == vsync_mode &&
|
||||||
|
g_gpu_device->GetMainSwapChain()->IsPresentThrottleAllowed() == allow_present_throttle))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
VERBOSE_LOG("VSync: {}{}{}", vsync_modes[static_cast<size_t>(vsync_mode)],
|
VERBOSE_LOG("VSync: {}{}{}", vsync_modes[static_cast<size_t>(vsync_mode)],
|
||||||
s_syncing_to_host_with_vsync ? " (for throttling)" : "",
|
s_syncing_to_host_with_vsync ? " (for throttling)" : "",
|
||||||
allow_present_throttle ? " (present throttle allowed)" : "");
|
allow_present_throttle ? " (present throttle allowed)" : "");
|
||||||
|
|
||||||
g_gpu_device->SetVSyncMode(vsync_mode, allow_present_throttle);
|
Error error;
|
||||||
|
if (!g_gpu_device->GetMainSwapChain()->SetVSyncMode(vsync_mode, allow_present_throttle, &error))
|
||||||
|
{
|
||||||
|
ERROR_LOG("Failed to update vsync mode to {}: {}", vsync_modes[static_cast<size_t>(vsync_mode)],
|
||||||
|
error.GetDescription());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GPUVSyncMode System::GetEffectiveVSyncMode()
|
GPUVSyncMode System::GetEffectiveVSyncMode()
|
||||||
@ -4230,6 +4252,16 @@ bool System::SwitchMediaSubImage(u32 index)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool System::ShouldStartFullscreen()
|
||||||
|
{
|
||||||
|
return Host::GetBoolSettingValue("Main", "StartFullscreen", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool System::ShouldStartPaused()
|
||||||
|
{
|
||||||
|
return Host::GetBoolSettingValue("Main", "StartPaused", false);
|
||||||
|
}
|
||||||
|
|
||||||
void System::CheckForSettingsChanges(const Settings& old_settings)
|
void System::CheckForSettingsChanges(const Settings& old_settings)
|
||||||
{
|
{
|
||||||
if (IsValid() &&
|
if (IsValid() &&
|
||||||
@ -5193,7 +5225,7 @@ bool System::StartMediaCapture(std::string path, bool capture_video, bool captur
|
|||||||
u32 capture_height =
|
u32 capture_height =
|
||||||
Host::GetUIntSettingValue("MediaCapture", "VideoHeight", Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_HEIGHT);
|
Host::GetUIntSettingValue("MediaCapture", "VideoHeight", Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_HEIGHT);
|
||||||
const GPUTexture::Format capture_format =
|
const GPUTexture::Format capture_format =
|
||||||
g_gpu_device->HasSurface() ? g_gpu_device->GetWindowFormat() : GPUTexture::Format::RGBA8;
|
g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetFormat() : GPUTexture::Format::RGBA8;
|
||||||
const float fps = System::GetThrottleFrequency();
|
const float fps = System::GetThrottleFrequency();
|
||||||
if (capture_video)
|
if (capture_video)
|
||||||
{
|
{
|
||||||
@ -5640,11 +5672,14 @@ bool System::PresentDisplay(bool explicit_present, u64 present_time)
|
|||||||
ImGuiManager::RenderOverlayWindows();
|
ImGuiManager::RenderOverlayWindows();
|
||||||
ImGuiManager::RenderDebugWindows();
|
ImGuiManager::RenderDebugWindows();
|
||||||
|
|
||||||
const GPUDevice::PresentResult pres = g_gpu ? g_gpu->PresentDisplay() : g_gpu_device->BeginPresent();
|
const GPUDevice::PresentResult pres =
|
||||||
|
g_gpu_device->HasMainSwapChain() ?
|
||||||
|
(g_gpu ? g_gpu->PresentDisplay() : g_gpu_device->BeginPresent(g_gpu_device->GetMainSwapChain())) :
|
||||||
|
GPUDevice::PresentResult::SkipPresent;
|
||||||
if (pres == GPUDevice::PresentResult::OK)
|
if (pres == GPUDevice::PresentResult::OK)
|
||||||
{
|
{
|
||||||
g_gpu_device->RenderImGui();
|
g_gpu_device->RenderImGui(g_gpu_device->GetMainSwapChain());
|
||||||
g_gpu_device->EndPresent(explicit_present, present_time);
|
g_gpu_device->EndPresent(g_gpu_device->GetMainSwapChain(), explicit_present, present_time);
|
||||||
|
|
||||||
if (g_gpu_device->IsGPUTimingEnabled())
|
if (g_gpu_device->IsGPUTimingEnabled())
|
||||||
{
|
{
|
||||||
@ -5656,9 +5691,13 @@ bool System::PresentDisplay(bool explicit_present, u64 present_time)
|
|||||||
{
|
{
|
||||||
if (pres == GPUDevice::PresentResult::DeviceLost) [[unlikely]]
|
if (pres == GPUDevice::PresentResult::DeviceLost) [[unlikely]]
|
||||||
HandleHostGPUDeviceLost();
|
HandleHostGPUDeviceLost();
|
||||||
|
else if (pres == GPUDevice::PresentResult::ExclusiveFullscreenLost)
|
||||||
|
HandleExclusiveFullscreenLost();
|
||||||
|
else
|
||||||
|
g_gpu_device->FlushCommands();
|
||||||
|
|
||||||
// Still need to kick ImGui or it gets cranky.
|
// Still need to kick ImGui or it gets cranky.
|
||||||
ImGui::Render();
|
ImGui::EndFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiManager::NewFrame();
|
ImGuiManager::NewFrame();
|
||||||
|
@ -58,9 +58,9 @@ int DisplayWidget::scaledWindowHeight() const
|
|||||||
static_cast<int>(std::ceil(static_cast<qreal>(height()) * QtUtils::GetDevicePixelRatioForWidget(this))), 1);
|
static_cast<int>(std::ceil(static_cast<qreal>(height()) * QtUtils::GetDevicePixelRatioForWidget(this))), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<WindowInfo> DisplayWidget::getWindowInfo()
|
std::optional<WindowInfo> DisplayWidget::getWindowInfo(Error* error)
|
||||||
{
|
{
|
||||||
std::optional<WindowInfo> ret(QtUtils::GetWindowInfoForWidget(this));
|
std::optional<WindowInfo> ret(QtUtils::GetWindowInfoForWidget(this, error));
|
||||||
if (ret.has_value())
|
if (ret.has_value())
|
||||||
{
|
{
|
||||||
m_last_window_width = ret->surface_width;
|
m_last_window_width = ret->surface_width;
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
#include <QtWidgets/QWidget>
|
#include <QtWidgets/QWidget>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
class Error;
|
||||||
|
|
||||||
class QCloseEvent;
|
class QCloseEvent;
|
||||||
|
|
||||||
class DisplayWidget final : public QWidget
|
class DisplayWidget final : public QWidget
|
||||||
@ -26,7 +28,7 @@ public:
|
|||||||
int scaledWindowWidth() const;
|
int scaledWindowWidth() const;
|
||||||
int scaledWindowHeight() const;
|
int scaledWindowHeight() const;
|
||||||
|
|
||||||
std::optional<WindowInfo> getWindowInfo();
|
std::optional<WindowInfo> getWindowInfo(Error* error);
|
||||||
|
|
||||||
void updateRelativeMode(bool enabled);
|
void updateRelativeMode(bool enabled);
|
||||||
void updateCursor(bool hidden);
|
void updateCursor(bool hidden);
|
||||||
|
@ -852,9 +852,9 @@ void GraphicsSettingsWidget::populateGPUAdaptersAndResolutions(RenderAPI render_
|
|||||||
m_ui.fullscreenMode->addItem(tr("Borderless Fullscreen"), QVariant(QString()));
|
m_ui.fullscreenMode->addItem(tr("Borderless Fullscreen"), QVariant(QString()));
|
||||||
if (current_adapter)
|
if (current_adapter)
|
||||||
{
|
{
|
||||||
for (const std::string& mode_name : current_adapter->fullscreen_modes)
|
for (const GPUDevice::ExclusiveFullscreenMode& mode : current_adapter->fullscreen_modes)
|
||||||
{
|
{
|
||||||
const QString qmodename = QString::fromStdString(mode_name);
|
const QString qmodename = QtUtils::StringViewToQString(mode.ToString());
|
||||||
m_ui.fullscreenMode->addItem(qmodename, QVariant(qmodename));
|
m_ui.fullscreenMode->addItem(qmodename, QVariant(qmodename));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,6 +83,7 @@ static bool s_use_central_widget = false;
|
|||||||
// UI thread VM validity.
|
// UI thread VM validity.
|
||||||
static bool s_system_valid = false;
|
static bool s_system_valid = false;
|
||||||
static bool s_system_paused = false;
|
static bool s_system_paused = false;
|
||||||
|
static std::atomic_uint32_t s_system_locked{false};
|
||||||
static QString s_current_game_title;
|
static QString s_current_game_title;
|
||||||
static QString s_current_game_serial;
|
static QString s_current_game_serial;
|
||||||
static QString s_current_game_path;
|
static QString s_current_game_path;
|
||||||
@ -220,30 +221,25 @@ bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window, bool fullscreen, bool render_to_main,
|
std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool fullscreen, bool render_to_main, bool surfaceless,
|
||||||
bool surfaceless, bool use_main_window_pos)
|
bool use_main_window_pos, Error* error)
|
||||||
{
|
{
|
||||||
DEV_LOG("acquireRenderWindow() recreate={} fullscreen={} render_to_main={} surfaceless={} use_main_window_pos={}",
|
DEV_LOG("acquireRenderWindow() fullscreen={} render_to_main={} surfaceless={} use_main_window_pos={}",
|
||||||
recreate_window ? "true" : "false", fullscreen ? "true" : "false", render_to_main ? "true" : "false",
|
fullscreen ? "true" : "false", render_to_main ? "true" : "false", surfaceless ? "true" : "false",
|
||||||
surfaceless ? "true" : "false", use_main_window_pos ? "true" : "false");
|
use_main_window_pos ? "true" : "false");
|
||||||
|
|
||||||
QWidget* container =
|
QWidget* container =
|
||||||
m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget);
|
m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget);
|
||||||
const bool is_fullscreen = isRenderingFullscreen();
|
const bool is_fullscreen = isRenderingFullscreen();
|
||||||
const bool is_rendering_to_main = isRenderingToMain();
|
const bool is_rendering_to_main = isRenderingToMain();
|
||||||
const bool changing_surfaceless = (!m_display_widget != surfaceless);
|
const bool changing_surfaceless = (!m_display_widget != surfaceless);
|
||||||
if (m_display_created && !recreate_window && fullscreen == is_fullscreen && is_rendering_to_main == render_to_main &&
|
|
||||||
!changing_surfaceless)
|
|
||||||
{
|
|
||||||
return m_display_widget ? m_display_widget->getWindowInfo() : WindowInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip recreating the surface if we're just transitioning between fullscreen and windowed with render-to-main off.
|
// Skip recreating the surface if we're just transitioning between fullscreen and windowed with render-to-main off.
|
||||||
// .. except on Wayland, where everything tends to break if you don't recreate.
|
// .. except on Wayland, where everything tends to break if you don't recreate.
|
||||||
const bool has_container = (m_display_container != nullptr);
|
const bool has_container = (m_display_container != nullptr);
|
||||||
const bool needs_container = DisplayContainer::isNeeded(fullscreen, render_to_main);
|
const bool needs_container = DisplayContainer::isNeeded(fullscreen, render_to_main);
|
||||||
if (m_display_created && !recreate_window && !is_rendering_to_main && !render_to_main &&
|
if (m_display_created && !is_rendering_to_main && !render_to_main && has_container == needs_container &&
|
||||||
has_container == needs_container && !needs_container && !changing_surfaceless)
|
!needs_container && !changing_surfaceless)
|
||||||
{
|
{
|
||||||
DEV_LOG("Toggling to {} without recreating surface", (fullscreen ? "fullscreen" : "windowed"));
|
DEV_LOG("Toggling to {} without recreating surface", (fullscreen ? "fullscreen" : "windowed"));
|
||||||
|
|
||||||
@ -257,12 +253,11 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
container->showNormal();
|
||||||
if (use_main_window_pos)
|
if (use_main_window_pos)
|
||||||
container->setGeometry(geometry());
|
container->setGeometry(geometry());
|
||||||
else
|
else
|
||||||
restoreDisplayWindowGeometryFromConfig();
|
restoreDisplayWindowGeometryFromConfig();
|
||||||
|
|
||||||
container->showNormal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDisplayWidgetCursor();
|
updateDisplayWidgetCursor();
|
||||||
@ -270,7 +265,7 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window,
|
|||||||
updateWindowState();
|
updateWindowState();
|
||||||
|
|
||||||
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||||
return m_display_widget->getWindowInfo();
|
return m_display_widget->getWindowInfo(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyDisplayWidget(surfaceless);
|
destroyDisplayWidget(surfaceless);
|
||||||
@ -282,7 +277,7 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window,
|
|||||||
|
|
||||||
createDisplayWidget(fullscreen, render_to_main, use_main_window_pos);
|
createDisplayWidget(fullscreen, render_to_main, use_main_window_pos);
|
||||||
|
|
||||||
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo();
|
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo(error);
|
||||||
if (!wi.has_value())
|
if (!wi.has_value())
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, tr("Error"), tr("Failed to get window info from widget"));
|
QMessageBox::critical(this, tr("Error"), tr("Failed to get window info from widget"));
|
||||||
@ -2852,11 +2847,13 @@ MainWindow::SystemLock MainWindow::pauseAndLockSystem()
|
|||||||
MainWindow::SystemLock::SystemLock(QWidget* dialog_parent, bool was_paused, bool was_fullscreen)
|
MainWindow::SystemLock::SystemLock(QWidget* dialog_parent, bool was_paused, bool was_fullscreen)
|
||||||
: m_dialog_parent(dialog_parent), m_was_paused(was_paused), m_was_fullscreen(was_fullscreen)
|
: m_dialog_parent(dialog_parent), m_was_paused(was_paused), m_was_fullscreen(was_fullscreen)
|
||||||
{
|
{
|
||||||
|
s_system_locked.fetch_add(1, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::SystemLock::SystemLock(SystemLock&& lock)
|
MainWindow::SystemLock::SystemLock(SystemLock&& lock)
|
||||||
: m_dialog_parent(lock.m_dialog_parent), m_was_paused(lock.m_was_paused), m_was_fullscreen(lock.m_was_fullscreen)
|
: m_dialog_parent(lock.m_dialog_parent), m_was_paused(lock.m_was_paused), m_was_fullscreen(lock.m_was_fullscreen)
|
||||||
{
|
{
|
||||||
|
s_system_locked.fetch_add(1, std::memory_order_release);
|
||||||
lock.m_dialog_parent = nullptr;
|
lock.m_dialog_parent = nullptr;
|
||||||
lock.m_was_paused = true;
|
lock.m_was_paused = true;
|
||||||
lock.m_was_fullscreen = false;
|
lock.m_was_fullscreen = false;
|
||||||
@ -2864,6 +2861,8 @@ MainWindow::SystemLock::SystemLock(SystemLock&& lock)
|
|||||||
|
|
||||||
MainWindow::SystemLock::~SystemLock()
|
MainWindow::SystemLock::~SystemLock()
|
||||||
{
|
{
|
||||||
|
DebugAssert(s_system_locked.load(std::memory_order_relaxed) > 0);
|
||||||
|
s_system_locked.fetch_sub(1, std::memory_order_release);
|
||||||
if (m_was_fullscreen)
|
if (m_was_fullscreen)
|
||||||
g_emu_thread->setFullscreen(true, true);
|
g_emu_thread->setFullscreen(true, true);
|
||||||
if (!m_was_paused)
|
if (!m_was_paused)
|
||||||
@ -2874,4 +2873,9 @@ void MainWindow::SystemLock::cancelResume()
|
|||||||
{
|
{
|
||||||
m_was_paused = true;
|
m_was_paused = true;
|
||||||
m_was_fullscreen = false;
|
m_was_fullscreen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QtHost::IsSystemLocked()
|
||||||
|
{
|
||||||
|
return (s_system_locked.load(std::memory_order_acquire) > 0);
|
||||||
|
}
|
||||||
|
@ -126,8 +126,8 @@ private Q_SLOTS:
|
|||||||
bool confirmMessage(const QString& title, const QString& message);
|
bool confirmMessage(const QString& title, const QString& message);
|
||||||
void onStatusMessage(const QString& message);
|
void onStatusMessage(const QString& message);
|
||||||
|
|
||||||
std::optional<WindowInfo> acquireRenderWindow(bool recreate_window, bool fullscreen, bool render_to_main,
|
std::optional<WindowInfo> acquireRenderWindow(bool fullscreen, bool render_to_main, bool surfaceless,
|
||||||
bool surfaceless, bool use_main_window_pos);
|
bool use_main_window_pos, Error* error);
|
||||||
void displayResizeRequested(qint32 width, qint32 height);
|
void displayResizeRequested(qint32 width, qint32 height);
|
||||||
void releaseRenderWindow();
|
void releaseRenderWindow();
|
||||||
void focusDisplayWidget();
|
void focusDisplayWidget();
|
||||||
|
@ -625,13 +625,6 @@ void EmuThread::loadSettings(SettingsInterface& si)
|
|||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::setInitialState(std::optional<bool> override_fullscreen)
|
|
||||||
{
|
|
||||||
m_is_fullscreen = override_fullscreen.value_or(Host::GetBaseBoolSettingValue("Main", "StartFullscreen", false));
|
|
||||||
m_is_rendering_to_main = shouldRenderToMain();
|
|
||||||
m_is_surfaceless = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuThread::checkForSettingsChanges(const Settings& old_settings)
|
void EmuThread::checkForSettingsChanges(const Settings& old_settings)
|
||||||
{
|
{
|
||||||
if (g_main_window)
|
if (g_main_window)
|
||||||
@ -642,12 +635,16 @@ void EmuThread::checkForSettingsChanges(const Settings& old_settings)
|
|||||||
updatePerformanceCounters();
|
updatePerformanceCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool render_to_main = shouldRenderToMain();
|
// don't mess with fullscreen while locked
|
||||||
if (m_is_rendering_to_main != render_to_main)
|
if (!QtHost::IsSystemLocked())
|
||||||
{
|
{
|
||||||
m_is_rendering_to_main = render_to_main;
|
const bool render_to_main = shouldRenderToMain();
|
||||||
if (g_gpu_device)
|
if (m_is_rendering_to_main != render_to_main && !m_is_fullscreen)
|
||||||
g_gpu_device->UpdateWindow();
|
{
|
||||||
|
m_is_rendering_to_main = render_to_main;
|
||||||
|
if (g_gpu_device)
|
||||||
|
Host::UpdateDisplayWindow(m_is_fullscreen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -780,16 +777,15 @@ void EmuThread::startFullscreenUI()
|
|||||||
// we want settings loaded so we choose the correct renderer
|
// we want settings loaded so we choose the correct renderer
|
||||||
// this also sorts out input sources.
|
// this also sorts out input sources.
|
||||||
System::LoadSettings(false);
|
System::LoadSettings(false);
|
||||||
setInitialState(s_start_fullscreen_ui_fullscreen ? std::optional<bool>(true) : std::optional<bool>());
|
m_is_rendering_to_main = shouldRenderToMain();
|
||||||
m_run_fullscreen_ui = true;
|
m_run_fullscreen_ui = true;
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
if (!Host::CreateGPUDevice(Settings::GetRenderAPIForRenderer(g_settings.gpu_renderer), &error) ||
|
if (!Host::CreateGPUDevice(Settings::GetRenderAPIForRenderer(g_settings.gpu_renderer),
|
||||||
|
s_start_fullscreen_ui_fullscreen, &error) ||
|
||||||
!FullscreenUI::Initialize())
|
!FullscreenUI::Initialize())
|
||||||
{
|
{
|
||||||
Host::ReportErrorAsync("Error", error.GetDescription());
|
Host::ReportErrorAsync("Error", error.GetDescription());
|
||||||
Host::ReleaseGPUDevice();
|
|
||||||
Host::ReleaseRenderWindow();
|
|
||||||
m_run_fullscreen_ui = false;
|
m_run_fullscreen_ui = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -825,7 +821,6 @@ void EmuThread::stopFullscreenUI()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Host::ReleaseGPUDevice();
|
Host::ReleaseGPUDevice();
|
||||||
Host::ReleaseRenderWindow();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::bootSystem(std::shared_ptr<SystemBootParameters> params)
|
void EmuThread::bootSystem(std::shared_ptr<SystemBootParameters> params)
|
||||||
@ -841,7 +836,7 @@ void EmuThread::bootSystem(std::shared_ptr<SystemBootParameters> params)
|
|||||||
if (System::IsValidOrInitializing())
|
if (System::IsValidOrInitializing())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
setInitialState(params->override_fullscreen);
|
m_is_rendering_to_main = shouldRenderToMain();
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
if (!System::BootSystem(std::move(*params), &error))
|
if (!System::BootSystem(std::move(*params), &error))
|
||||||
@ -974,7 +969,7 @@ void EmuThread::setFullscreen(bool fullscreen, bool allow_render_to_main)
|
|||||||
|
|
||||||
m_is_fullscreen = fullscreen;
|
m_is_fullscreen = fullscreen;
|
||||||
m_is_rendering_to_main = allow_render_to_main && shouldRenderToMain();
|
m_is_rendering_to_main = allow_render_to_main && shouldRenderToMain();
|
||||||
Host::UpdateDisplayWindow();
|
Host::UpdateDisplayWindow(fullscreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Host::IsFullscreen()
|
bool Host::IsFullscreen()
|
||||||
@ -984,6 +979,10 @@ bool Host::IsFullscreen()
|
|||||||
|
|
||||||
void Host::SetFullscreen(bool enabled)
|
void Host::SetFullscreen(bool enabled)
|
||||||
{
|
{
|
||||||
|
// don't mess with fullscreen while locked
|
||||||
|
if (QtHost::IsSystemLocked())
|
||||||
|
return;
|
||||||
|
|
||||||
g_emu_thread->setFullscreen(enabled, true);
|
g_emu_thread->setFullscreen(enabled, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -999,7 +998,7 @@ void EmuThread::setSurfaceless(bool surfaceless)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
m_is_surfaceless = surfaceless;
|
m_is_surfaceless = surfaceless;
|
||||||
Host::UpdateDisplayWindow();
|
Host::UpdateDisplayWindow(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::requestDisplaySize(float scale)
|
void EmuThread::requestDisplaySize(float scale)
|
||||||
@ -1016,20 +1015,18 @@ void EmuThread::requestDisplaySize(float scale)
|
|||||||
System::RequestDisplaySize(scale);
|
System::RequestDisplaySize(scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<WindowInfo> EmuThread::acquireRenderWindow(bool recreate_window)
|
std::optional<WindowInfo> EmuThread::acquireRenderWindow(bool fullscreen, bool exclusive_fullscreen, Error* error)
|
||||||
{
|
{
|
||||||
DebugAssert(g_gpu_device);
|
DebugAssert(g_gpu_device);
|
||||||
u32 fs_width, fs_height;
|
|
||||||
float fs_refresh_rate;
|
|
||||||
m_is_exclusive_fullscreen = (m_is_fullscreen && g_gpu_device->SupportsExclusiveFullscreen() &&
|
|
||||||
GPUDevice::GetRequestedExclusiveFullscreenMode(&fs_width, &fs_height, &fs_refresh_rate));
|
|
||||||
|
|
||||||
const bool window_fullscreen = m_is_fullscreen && !m_is_exclusive_fullscreen;
|
m_is_fullscreen = fullscreen;
|
||||||
const bool render_to_main = !m_is_exclusive_fullscreen && !window_fullscreen && m_is_rendering_to_main;
|
|
||||||
|
const bool window_fullscreen = m_is_fullscreen && !exclusive_fullscreen;
|
||||||
|
const bool render_to_main = !fullscreen && m_is_rendering_to_main;
|
||||||
const bool use_main_window_pos = shouldRenderToMain();
|
const bool use_main_window_pos = shouldRenderToMain();
|
||||||
|
|
||||||
return emit onAcquireRenderWindowRequested(recreate_window, window_fullscreen, render_to_main, m_is_surfaceless,
|
return emit onAcquireRenderWindowRequested(window_fullscreen, render_to_main, m_is_surfaceless, use_main_window_pos,
|
||||||
use_main_window_pos);
|
error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::releaseRenderWindow()
|
void EmuThread::releaseRenderWindow()
|
||||||
@ -1811,11 +1808,11 @@ void EmuThread::run()
|
|||||||
|
|
||||||
m_event_loop->processEvents(QEventLoop::AllEvents);
|
m_event_loop->processEvents(QEventLoop::AllEvents);
|
||||||
System::Internal::IdlePollUpdate();
|
System::Internal::IdlePollUpdate();
|
||||||
if (g_gpu_device)
|
if (g_gpu_device && g_gpu_device->HasMainSwapChain())
|
||||||
{
|
{
|
||||||
System::PresentDisplay(false, 0);
|
System::PresentDisplay(false, 0);
|
||||||
if (!g_gpu_device->IsVSyncModeBlocking())
|
if (!g_gpu_device->GetMainSwapChain()->IsVSyncModeBlocking())
|
||||||
g_gpu_device->ThrottlePresentation();
|
g_gpu_device->GetMainSwapChain()->ThrottlePresentation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2005,9 +2002,10 @@ void Host::CommitBaseSettingChanges()
|
|||||||
QtHost::QueueSettingsSave();
|
QtHost::QueueSettingsSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<WindowInfo> Host::AcquireRenderWindow(bool recreate_window)
|
std::optional<WindowInfo> Host::AcquireRenderWindow(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen,
|
||||||
|
Error* error)
|
||||||
{
|
{
|
||||||
return g_emu_thread->acquireRenderWindow(recreate_window);
|
return g_emu_thread->acquireRenderWindow(fullscreen, exclusive_fullscreen, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::ReleaseRenderWindow()
|
void Host::ReleaseRenderWindow()
|
||||||
|
@ -98,7 +98,7 @@ public:
|
|||||||
ALWAYS_INLINE bool isSurfaceless() const { return m_is_surfaceless; }
|
ALWAYS_INLINE bool isSurfaceless() const { return m_is_surfaceless; }
|
||||||
ALWAYS_INLINE bool isRunningFullscreenUI() const { return m_run_fullscreen_ui; }
|
ALWAYS_INLINE bool isRunningFullscreenUI() const { return m_run_fullscreen_ui; }
|
||||||
|
|
||||||
std::optional<WindowInfo> acquireRenderWindow(bool recreate_window);
|
std::optional<WindowInfo> acquireRenderWindow(bool fullscreen, bool exclusive_fullscreen, Error* error);
|
||||||
void connectDisplaySignals(DisplayWidget* widget);
|
void connectDisplaySignals(DisplayWidget* widget);
|
||||||
void releaseRenderWindow();
|
void releaseRenderWindow();
|
||||||
|
|
||||||
@ -135,8 +135,8 @@ Q_SIGNALS:
|
|||||||
void systemPaused();
|
void systemPaused();
|
||||||
void systemResumed();
|
void systemResumed();
|
||||||
void gameListRefreshed();
|
void gameListRefreshed();
|
||||||
std::optional<WindowInfo> onAcquireRenderWindowRequested(bool recreate_window, bool fullscreen, bool render_to_main,
|
std::optional<WindowInfo> onAcquireRenderWindowRequested(bool fullscreen, bool render_to_main, bool surfaceless,
|
||||||
bool surfaceless, bool use_main_window_pos);
|
bool use_main_window_pos, Error* error);
|
||||||
void onResizeRenderWindowRequested(qint32 width, qint32 height);
|
void onResizeRenderWindowRequested(qint32 width, qint32 height);
|
||||||
void onReleaseRenderWindowRequested();
|
void onReleaseRenderWindowRequested();
|
||||||
void focusDisplayWidgetRequested();
|
void focusDisplayWidgetRequested();
|
||||||
@ -220,7 +220,6 @@ private:
|
|||||||
|
|
||||||
void createBackgroundControllerPollTimer();
|
void createBackgroundControllerPollTimer();
|
||||||
void destroyBackgroundControllerPollTimer();
|
void destroyBackgroundControllerPollTimer();
|
||||||
void setInitialState(std::optional<bool> override_fullscreen);
|
|
||||||
void confirmActionIfMemoryCardBusy(const QString& action, bool cancel_resume_on_accept,
|
void confirmActionIfMemoryCardBusy(const QString& action, bool cancel_resume_on_accept,
|
||||||
std::function<void(bool)> callback) const;
|
std::function<void(bool)> callback) const;
|
||||||
|
|
||||||
@ -233,8 +232,6 @@ private:
|
|||||||
bool m_run_fullscreen_ui = false;
|
bool m_run_fullscreen_ui = false;
|
||||||
bool m_is_rendering_to_main = false;
|
bool m_is_rendering_to_main = false;
|
||||||
bool m_is_fullscreen = false;
|
bool m_is_fullscreen = false;
|
||||||
bool m_is_exclusive_fullscreen = false;
|
|
||||||
bool m_lost_exclusive_fullscreen = false;
|
|
||||||
bool m_is_surfaceless = false;
|
bool m_is_surfaceless = false;
|
||||||
bool m_save_state_on_shutdown = false;
|
bool m_save_state_on_shutdown = false;
|
||||||
|
|
||||||
@ -320,6 +317,9 @@ bool ShouldShowDebugOptions();
|
|||||||
bool IsSystemValid();
|
bool IsSystemValid();
|
||||||
bool IsSystemPaused();
|
bool IsSystemPaused();
|
||||||
|
|
||||||
|
/// Returns true if any lock is in place.
|
||||||
|
bool IsSystemLocked();
|
||||||
|
|
||||||
/// Accessors for game information.
|
/// Accessors for game information.
|
||||||
const QString& GetCurrentGameTitle();
|
const QString& GetCurrentGameTitle();
|
||||||
const QString& GetCurrentGameSerial();
|
const QString& GetCurrentGameSerial();
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "core/game_list.h"
|
#include "core/game_list.h"
|
||||||
#include "core/system.h"
|
#include "core/system.h"
|
||||||
|
|
||||||
|
#include "common/error.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
@ -316,7 +317,7 @@ qreal QtUtils::GetDevicePixelRatioForWidget(const QWidget* widget)
|
|||||||
return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast<qreal>(1);
|
return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast<qreal>(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<WindowInfo> QtUtils::GetWindowInfoForWidget(QWidget* widget)
|
std::optional<WindowInfo> QtUtils::GetWindowInfoForWidget(QWidget* widget, Error* error)
|
||||||
{
|
{
|
||||||
WindowInfo wi;
|
WindowInfo wi;
|
||||||
|
|
||||||
@ -344,14 +345,14 @@ std::optional<WindowInfo> QtUtils::GetWindowInfoForWidget(QWidget* widget)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
qCritical() << "Unknown PNI platform " << platform_name;
|
Error::SetStringFmt(error, "Unknown PNI platform {}", platform_name.toStdString());
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const qreal dpr = GetDevicePixelRatioForWidget(widget);
|
const qreal dpr = GetDevicePixelRatioForWidget(widget);
|
||||||
wi.surface_width = static_cast<u32>(static_cast<qreal>(widget->width()) * dpr);
|
wi.surface_width = static_cast<u16>(static_cast<qreal>(widget->width()) * dpr);
|
||||||
wi.surface_height = static_cast<u32>(static_cast<qreal>(widget->height()) * dpr);
|
wi.surface_height = static_cast<u16>(static_cast<qreal>(widget->height()) * dpr);
|
||||||
wi.surface_scale = static_cast<float>(dpr);
|
wi.surface_scale = static_cast<float>(dpr);
|
||||||
|
|
||||||
// Query refresh rate, we need it for sync.
|
// Query refresh rate, we need it for sync.
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
class ByteStream;
|
class Error;
|
||||||
|
|
||||||
class QComboBox;
|
class QComboBox;
|
||||||
class QFrame;
|
class QFrame;
|
||||||
@ -116,7 +116,7 @@ QIcon GetIconForCompatibility(GameDatabase::CompatibilityRating rating);
|
|||||||
qreal GetDevicePixelRatioForWidget(const QWidget* widget);
|
qreal GetDevicePixelRatioForWidget(const QWidget* widget);
|
||||||
|
|
||||||
/// Returns the common window info structure for a Qt widget.
|
/// Returns the common window info structure for a Qt widget.
|
||||||
std::optional<WindowInfo> GetWindowInfoForWidget(QWidget* widget);
|
std::optional<WindowInfo> GetWindowInfoForWidget(QWidget* widget, Error* error = nullptr);
|
||||||
|
|
||||||
/// Saves a window's geometry to configuration. Returns false if the configuration was changed.
|
/// Saves a window's geometry to configuration. Returns false if the configuration was changed.
|
||||||
bool SaveWindowGeometry(std::string_view window_name, QWidget* widget, bool auto_commit_changes = true);
|
bool SaveWindowGeometry(std::string_view window_name, QWidget* widget, bool auto_commit_changes = true);
|
||||||
|
@ -110,7 +110,6 @@ if(ENABLE_OPENGL)
|
|||||||
opengl_context_wgl.cpp
|
opengl_context_wgl.cpp
|
||||||
opengl_context_wgl.h
|
opengl_context_wgl.h
|
||||||
)
|
)
|
||||||
target_link_libraries(util PRIVATE "opengl32.lib")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(LINUX OR BSD OR ANDROID)
|
if(LINUX OR BSD OR ANDROID)
|
||||||
@ -183,6 +182,9 @@ if(NOT ANDROID)
|
|||||||
sdl_input_source.cpp
|
sdl_input_source.cpp
|
||||||
sdl_input_source.h
|
sdl_input_source.h
|
||||||
)
|
)
|
||||||
|
target_compile_definitions(util PUBLIC
|
||||||
|
ENABLE_SDL
|
||||||
|
)
|
||||||
target_link_libraries(util PUBLIC
|
target_link_libraries(util PUBLIC
|
||||||
cubeb
|
cubeb
|
||||||
SDL2::SDL2
|
SDL2::SDL2
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#include <d3dcompiler.h>
|
#include <d3dcompiler.h>
|
||||||
#include <dxgi1_5.h>
|
#include <dxgi1_5.h>
|
||||||
|
|
||||||
LOG_CHANNEL(D3D11Device);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
// We need to synchronize instance creation because of adapter enumeration from the UI thread.
|
// We need to synchronize instance creation because of adapter enumeration from the UI thread.
|
||||||
static std::mutex s_instance_mutex;
|
static std::mutex s_instance_mutex;
|
||||||
@ -45,7 +45,10 @@ void SetD3DDebugObjectName(ID3D11DeviceChild* obj, std::string_view name)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
D3D11Device::D3D11Device() = default;
|
D3D11Device::D3D11Device()
|
||||||
|
{
|
||||||
|
m_render_api = RenderAPI::D3D11;
|
||||||
|
}
|
||||||
|
|
||||||
D3D11Device::~D3D11Device()
|
D3D11Device::~D3D11Device()
|
||||||
{
|
{
|
||||||
@ -53,13 +56,11 @@ D3D11Device::~D3D11Device()
|
|||||||
Assert(!m_device);
|
Assert(!m_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D11Device::HasSurface() const
|
bool D3D11Device::CreateDeviceAndMainSwapChain(std::string_view adapter, FeatureMask disabled_features,
|
||||||
{
|
const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||||
return static_cast<bool>(m_swap_chain);
|
bool allow_present_throttle,
|
||||||
}
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control, Error* error)
|
||||||
bool D3D11Device::CreateDevice(std::string_view adapter, std::optional<bool> exclusive_fullscreen_control,
|
|
||||||
FeatureMask disabled_features, Error* error)
|
|
||||||
{
|
{
|
||||||
std::unique_lock lock(s_instance_mutex);
|
std::unique_lock lock(s_instance_mutex);
|
||||||
|
|
||||||
@ -125,17 +126,14 @@ bool D3D11Device::CreateDevice(std::string_view adapter, std::optional<bool> exc
|
|||||||
INFO_LOG("Max device feature level: {}",
|
INFO_LOG("Max device feature level: {}",
|
||||||
D3DCommon::GetFeatureLevelString(D3DCommon::GetRenderAPIVersionForFeatureLevel(m_max_feature_level)));
|
D3DCommon::GetFeatureLevelString(D3DCommon::GetRenderAPIVersionForFeatureLevel(m_max_feature_level)));
|
||||||
|
|
||||||
BOOL allow_tearing_supported = false;
|
|
||||||
hr = m_dxgi_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported,
|
|
||||||
sizeof(allow_tearing_supported));
|
|
||||||
m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
|
|
||||||
|
|
||||||
SetFeatures(disabled_features);
|
SetFeatures(disabled_features);
|
||||||
|
|
||||||
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain())
|
if (!wi.IsSurfaceless())
|
||||||
{
|
{
|
||||||
Error::SetStringView(error, "Failed to create swap chain");
|
m_main_swap_chain = CreateSwapChain(wi, vsync_mode, allow_present_throttle, exclusive_fullscreen_mode,
|
||||||
return false;
|
exclusive_fullscreen_control, error);
|
||||||
|
if (!m_main_swap_chain)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CreateBuffers())
|
if (!CreateBuffers())
|
||||||
@ -152,6 +150,7 @@ void D3D11Device::DestroyDevice()
|
|||||||
std::unique_lock lock(s_instance_mutex);
|
std::unique_lock lock(s_instance_mutex);
|
||||||
|
|
||||||
DestroyBuffers();
|
DestroyBuffers();
|
||||||
|
m_main_swap_chain.reset();
|
||||||
m_context.Reset();
|
m_context.Reset();
|
||||||
m_device.Reset();
|
m_device.Reset();
|
||||||
}
|
}
|
||||||
@ -160,7 +159,6 @@ void D3D11Device::SetFeatures(FeatureMask disabled_features)
|
|||||||
{
|
{
|
||||||
const D3D_FEATURE_LEVEL feature_level = m_device->GetFeatureLevel();
|
const D3D_FEATURE_LEVEL feature_level = m_device->GetFeatureLevel();
|
||||||
|
|
||||||
m_render_api = RenderAPI::D3D11;
|
|
||||||
m_render_api_version = D3DCommon::GetRenderAPIVersionForFeatureLevel(feature_level);
|
m_render_api_version = D3DCommon::GetRenderAPIVersionForFeatureLevel(feature_level);
|
||||||
m_max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
m_max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
||||||
m_max_multisamples = 1;
|
m_max_multisamples = 1;
|
||||||
@ -202,96 +200,107 @@ void D3D11Device::SetFeatures(FeatureMask disabled_features)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 D3D11Device::GetSwapChainBufferCount() const
|
D3D11SwapChain::D3D11SwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
|
const GPUDevice::ExclusiveFullscreenMode* fullscreen_mode)
|
||||||
|
: GPUSwapChain(wi, vsync_mode, allow_present_throttle)
|
||||||
{
|
{
|
||||||
// With vsync off, we only need two buffers. Same for blocking vsync.
|
if (fullscreen_mode)
|
||||||
// With triple buffering, we need three.
|
InitializeExclusiveFullscreenMode(fullscreen_mode);
|
||||||
return (m_vsync_mode == GPUVSyncMode::Mailbox) ? 3 : 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D11Device::CreateSwapChain()
|
D3D11SwapChain::~D3D11SwapChain()
|
||||||
{
|
{
|
||||||
if (m_window_info.type != WindowInfo::Type::Win32)
|
m_swap_chain_rtv.Reset();
|
||||||
return false;
|
DestroySwapChain();
|
||||||
|
}
|
||||||
|
|
||||||
const DXGI_FORMAT dxgi_format = D3DCommon::GetFormatMapping(s_swap_chain_format).resource_format;
|
bool D3D11SwapChain::InitializeExclusiveFullscreenMode(const GPUDevice::ExclusiveFullscreenMode* mode)
|
||||||
|
{
|
||||||
|
const D3DCommon::DXGIFormatMapping& fm = D3DCommon::GetFormatMapping(s_swap_chain_format);
|
||||||
|
|
||||||
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
|
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
|
||||||
RECT client_rc{};
|
RECT client_rc{};
|
||||||
GetClientRect(window_hwnd, &client_rc);
|
GetClientRect(window_hwnd, &client_rc);
|
||||||
|
|
||||||
DXGI_MODE_DESC fullscreen_mode = {};
|
m_fullscreen_mode = D3DCommon::GetRequestedExclusiveFullscreenModeDesc(
|
||||||
ComPtr<IDXGIOutput> fullscreen_output;
|
D3D11Device::GetDXGIFactory(), client_rc, mode, fm.resource_format, m_fullscreen_output.GetAddressOf());
|
||||||
if (Host::IsFullscreen())
|
return m_fullscreen_mode.has_value();
|
||||||
{
|
}
|
||||||
u32 fullscreen_width, fullscreen_height;
|
|
||||||
float fullscreen_refresh_rate;
|
|
||||||
m_is_exclusive_fullscreen =
|
|
||||||
GetRequestedExclusiveFullscreenMode(&fullscreen_width, &fullscreen_height, &fullscreen_refresh_rate) &&
|
|
||||||
D3DCommon::GetRequestedExclusiveFullscreenModeDesc(m_dxgi_factory.Get(), client_rc, fullscreen_width,
|
|
||||||
fullscreen_height, fullscreen_refresh_rate, dxgi_format,
|
|
||||||
&fullscreen_mode, fullscreen_output.GetAddressOf());
|
|
||||||
|
|
||||||
// Using mailbox-style no-allow-tearing causes tearing in exclusive fullscreen.
|
u32 D3D11SwapChain::GetNewBufferCount(GPUVSyncMode vsync_mode)
|
||||||
if (m_vsync_mode == GPUVSyncMode::Mailbox && m_is_exclusive_fullscreen)
|
{
|
||||||
{
|
// With vsync off, we only need two buffers. Same for blocking vsync.
|
||||||
WARNING_LOG("Using FIFO instead of Mailbox vsync due to exclusive fullscreen.");
|
// With triple buffering, we need three.
|
||||||
m_vsync_mode = GPUVSyncMode::FIFO;
|
return (vsync_mode == GPUVSyncMode::Mailbox) ? 3 : 2;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
bool D3D11SwapChain::CreateSwapChain(Error* error)
|
||||||
|
{
|
||||||
|
const D3DCommon::DXGIFormatMapping& fm = D3DCommon::GetFormatMapping(s_swap_chain_format);
|
||||||
|
|
||||||
|
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
|
||||||
|
RECT client_rc{};
|
||||||
|
GetClientRect(window_hwnd, &client_rc);
|
||||||
|
|
||||||
|
// Using mailbox-style no-allow-tearing causes tearing in exclusive fullscreen.
|
||||||
|
if (IsExclusiveFullscreen() && m_vsync_mode == GPUVSyncMode::Mailbox)
|
||||||
{
|
{
|
||||||
m_is_exclusive_fullscreen = false;
|
WARNING_LOG("Using FIFO instead of Mailbox vsync due to exclusive fullscreen.");
|
||||||
|
m_vsync_mode = GPUVSyncMode::FIFO;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_using_flip_model_swap_chain =
|
m_using_flip_model_swap_chain =
|
||||||
!Host::GetBoolSettingValue("Display", "UseBlitSwapChain", false) || m_is_exclusive_fullscreen;
|
!Host::GetBoolSettingValue("Display", "UseBlitSwapChain", false) || IsExclusiveFullscreen();
|
||||||
|
|
||||||
|
IDXGIFactory5* const dxgi_factory = D3D11Device::GetDXGIFactory();
|
||||||
|
ID3D11Device1* const d3d_device = D3D11Device::GetD3DDevice();
|
||||||
|
|
||||||
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
|
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
|
||||||
swap_chain_desc.Width = static_cast<u32>(client_rc.right - client_rc.left);
|
swap_chain_desc.Width = static_cast<u32>(client_rc.right - client_rc.left);
|
||||||
swap_chain_desc.Height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
swap_chain_desc.Height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
||||||
swap_chain_desc.Format = dxgi_format;
|
swap_chain_desc.Format = fm.resource_format;
|
||||||
swap_chain_desc.SampleDesc.Count = 1;
|
swap_chain_desc.SampleDesc.Count = 1;
|
||||||
swap_chain_desc.BufferCount = GetSwapChainBufferCount();
|
swap_chain_desc.BufferCount = GetNewBufferCount(m_vsync_mode);
|
||||||
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||||
swap_chain_desc.SwapEffect = m_using_flip_model_swap_chain ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
|
swap_chain_desc.SwapEffect = m_using_flip_model_swap_chain ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
|
||||||
|
|
||||||
m_using_allow_tearing = (m_allow_tearing_supported && m_using_flip_model_swap_chain && !m_is_exclusive_fullscreen);
|
|
||||||
if (m_using_allow_tearing)
|
|
||||||
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
|
||||||
|
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
if (m_is_exclusive_fullscreen)
|
if (IsExclusiveFullscreen())
|
||||||
{
|
{
|
||||||
DXGI_SWAP_CHAIN_DESC1 fs_sd_desc = swap_chain_desc;
|
DXGI_SWAP_CHAIN_DESC1 fs_sd_desc = swap_chain_desc;
|
||||||
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
|
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
|
||||||
|
|
||||||
fs_sd_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
fs_sd_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
||||||
fs_sd_desc.Width = fullscreen_mode.Width;
|
fs_sd_desc.Width = m_fullscreen_mode->Width;
|
||||||
fs_sd_desc.Height = fullscreen_mode.Height;
|
fs_sd_desc.Height = m_fullscreen_mode->Height;
|
||||||
fs_desc.RefreshRate = fullscreen_mode.RefreshRate;
|
fs_desc.RefreshRate = m_fullscreen_mode->RefreshRate;
|
||||||
fs_desc.ScanlineOrdering = fullscreen_mode.ScanlineOrdering;
|
fs_desc.ScanlineOrdering = m_fullscreen_mode->ScanlineOrdering;
|
||||||
fs_desc.Scaling = fullscreen_mode.Scaling;
|
fs_desc.Scaling = m_fullscreen_mode->Scaling;
|
||||||
fs_desc.Windowed = FALSE;
|
fs_desc.Windowed = FALSE;
|
||||||
|
|
||||||
VERBOSE_LOG("Creating a {}x{} exclusive fullscreen swap chain", fs_sd_desc.Width, fs_sd_desc.Height);
|
VERBOSE_LOG("Creating a {}x{} exclusive fullscreen swap chain", fs_sd_desc.Width, fs_sd_desc.Height);
|
||||||
hr = m_dxgi_factory->CreateSwapChainForHwnd(m_device.Get(), window_hwnd, &fs_sd_desc, &fs_desc,
|
hr = dxgi_factory->CreateSwapChainForHwnd(d3d_device, window_hwnd, &fs_sd_desc, &fs_desc, m_fullscreen_output.Get(),
|
||||||
fullscreen_output.Get(), m_swap_chain.ReleaseAndGetAddressOf());
|
m_swap_chain.ReleaseAndGetAddressOf());
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
WARNING_LOG("Failed to create fullscreen swap chain, trying windowed.");
|
WARNING_LOG("Failed to create fullscreen swap chain, trying windowed.");
|
||||||
m_is_exclusive_fullscreen = false;
|
m_fullscreen_output.Reset();
|
||||||
m_using_allow_tearing = m_allow_tearing_supported && m_using_flip_model_swap_chain;
|
m_fullscreen_mode.reset();
|
||||||
|
m_using_allow_tearing = (m_using_flip_model_swap_chain && D3DCommon::SupportsAllowTearing(dxgi_factory));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_is_exclusive_fullscreen)
|
if (!IsExclusiveFullscreen())
|
||||||
{
|
{
|
||||||
VERBOSE_LOG("Creating a {}x{} {} windowed swap chain", swap_chain_desc.Width, swap_chain_desc.Height,
|
VERBOSE_LOG("Creating a {}x{} {} windowed swap chain", swap_chain_desc.Width, swap_chain_desc.Height,
|
||||||
m_using_flip_model_swap_chain ? "flip-discard" : "discard");
|
m_using_flip_model_swap_chain ? "flip-discard" : "discard");
|
||||||
hr = m_dxgi_factory->CreateSwapChainForHwnd(m_device.Get(), window_hwnd, &swap_chain_desc, nullptr, nullptr,
|
m_using_allow_tearing = (m_using_flip_model_swap_chain && !IsExclusiveFullscreen() &&
|
||||||
m_swap_chain.ReleaseAndGetAddressOf());
|
D3DCommon::SupportsAllowTearing(D3D11Device::GetDXGIFactory()));
|
||||||
|
if (m_using_allow_tearing)
|
||||||
|
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
||||||
|
hr = dxgi_factory->CreateSwapChainForHwnd(d3d_device, window_hwnd, &swap_chain_desc, nullptr, nullptr,
|
||||||
|
m_swap_chain.ReleaseAndGetAddressOf());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FAILED(hr) && m_using_flip_model_swap_chain)
|
if (FAILED(hr) && m_using_flip_model_swap_chain)
|
||||||
@ -302,11 +311,11 @@ bool D3D11Device::CreateSwapChain()
|
|||||||
m_using_flip_model_swap_chain = false;
|
m_using_flip_model_swap_chain = false;
|
||||||
m_using_allow_tearing = false;
|
m_using_allow_tearing = false;
|
||||||
|
|
||||||
hr = m_dxgi_factory->CreateSwapChainForHwnd(m_device.Get(), window_hwnd, &swap_chain_desc, nullptr, nullptr,
|
hr = dxgi_factory->CreateSwapChainForHwnd(d3d_device, window_hwnd, &swap_chain_desc, nullptr, nullptr,
|
||||||
m_swap_chain.ReleaseAndGetAddressOf());
|
m_swap_chain.ReleaseAndGetAddressOf());
|
||||||
if (FAILED(hr)) [[unlikely]]
|
if (FAILED(hr)) [[unlikely]]
|
||||||
{
|
{
|
||||||
ERROR_LOG("CreateSwapChainForHwnd failed: 0x{:08X}", static_cast<unsigned>(hr));
|
Error::SetHResult(error, "CreateSwapChainForHwnd() failed: ", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -319,25 +328,29 @@ bool D3D11Device::CreateSwapChain()
|
|||||||
WARNING_LOG("MakeWindowAssociation() to disable ALT+ENTER failed");
|
WARNING_LOG("MakeWindowAssociation() to disable ALT+ENTER failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CreateSwapChainRTV())
|
|
||||||
{
|
|
||||||
DestroySwapChain();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render a frame as soon as possible to clear out whatever was previously being displayed.
|
|
||||||
m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), s_clear_color.data());
|
|
||||||
m_swap_chain->Present(0, m_using_allow_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D11Device::CreateSwapChainRTV()
|
void D3D11SwapChain::DestroySwapChain()
|
||||||
|
{
|
||||||
|
if (!m_swap_chain)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// switch out of fullscreen before destroying
|
||||||
|
BOOL is_fullscreen;
|
||||||
|
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen)
|
||||||
|
m_swap_chain->SetFullscreenState(FALSE, nullptr);
|
||||||
|
|
||||||
|
m_swap_chain.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D11SwapChain::CreateRTV(Error* error)
|
||||||
{
|
{
|
||||||
ComPtr<ID3D11Texture2D> backbuffer;
|
ComPtr<ID3D11Texture2D> backbuffer;
|
||||||
HRESULT hr = m_swap_chain->GetBuffer(0, IID_PPV_ARGS(backbuffer.GetAddressOf()));
|
HRESULT hr = m_swap_chain->GetBuffer(0, IID_PPV_ARGS(backbuffer.GetAddressOf()));
|
||||||
if (FAILED(hr)) [[unlikely]]
|
if (FAILED(hr)) [[unlikely]]
|
||||||
{
|
{
|
||||||
ERROR_LOG("GetBuffer for RTV failed: 0x{:08X}", static_cast<unsigned>(hr));
|
Error::SetHResult(error, "GetBuffer() failed: ", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,16 +359,17 @@ bool D3D11Device::CreateSwapChainRTV()
|
|||||||
|
|
||||||
CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D, backbuffer_desc.Format, 0, 0,
|
CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D, backbuffer_desc.Format, 0, 0,
|
||||||
backbuffer_desc.ArraySize);
|
backbuffer_desc.ArraySize);
|
||||||
hr = m_device->CreateRenderTargetView(backbuffer.Get(), &rtv_desc, m_swap_chain_rtv.ReleaseAndGetAddressOf());
|
hr = D3D11Device::GetD3DDevice()->CreateRenderTargetView(backbuffer.Get(), &rtv_desc,
|
||||||
|
m_swap_chain_rtv.ReleaseAndGetAddressOf());
|
||||||
if (FAILED(hr)) [[unlikely]]
|
if (FAILED(hr)) [[unlikely]]
|
||||||
{
|
{
|
||||||
ERROR_LOG("CreateRenderTargetView for swap chain failed: 0x{:08X}", static_cast<unsigned>(hr));
|
Error::SetHResult(error, "CreateRenderTargetView(): ", hr);
|
||||||
m_swap_chain_rtv.Reset();
|
m_swap_chain_rtv.Reset();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_window_info.surface_width = backbuffer_desc.Width;
|
m_window_info.surface_width = static_cast<u16>(backbuffer_desc.Width);
|
||||||
m_window_info.surface_height = backbuffer_desc.Height;
|
m_window_info.surface_height = static_cast<u16>(backbuffer_desc.Height);
|
||||||
m_window_info.surface_format = s_swap_chain_format;
|
m_window_info.surface_format = s_swap_chain_format;
|
||||||
VERBOSE_LOG("Swap chain buffer size: {}x{}", m_window_info.surface_width, m_window_info.surface_height);
|
VERBOSE_LOG("Swap chain buffer size: {}x{}", m_window_info.surface_width, m_window_info.surface_height);
|
||||||
|
|
||||||
@ -374,65 +388,77 @@ bool D3D11Device::CreateSwapChainRTV()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D11Device::DestroySwapChain()
|
bool D3D11SwapChain::ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error)
|
||||||
{
|
{
|
||||||
if (!m_swap_chain)
|
m_window_info.surface_scale = new_scale;
|
||||||
return;
|
if (m_window_info.surface_width == new_width && m_window_info.surface_height == new_height)
|
||||||
|
return true;
|
||||||
m_swap_chain_rtv.Reset();
|
|
||||||
|
|
||||||
// switch out of fullscreen before destroying
|
|
||||||
BOOL is_fullscreen;
|
|
||||||
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen)
|
|
||||||
m_swap_chain->SetFullscreenState(FALSE, nullptr);
|
|
||||||
|
|
||||||
m_swap_chain.Reset();
|
|
||||||
m_is_exclusive_fullscreen = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool D3D11Device::UpdateWindow()
|
|
||||||
{
|
|
||||||
DestroySwapChain();
|
|
||||||
|
|
||||||
if (!AcquireWindow(false))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain())
|
|
||||||
{
|
|
||||||
ERROR_LOG("Failed to create swap chain on updated window");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void D3D11Device::DestroySurface()
|
|
||||||
{
|
|
||||||
DestroySwapChain();
|
|
||||||
}
|
|
||||||
|
|
||||||
void D3D11Device::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
|
||||||
{
|
|
||||||
if (!m_swap_chain || m_is_exclusive_fullscreen)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_window_info.surface_scale = new_window_scale;
|
|
||||||
|
|
||||||
if (m_window_info.surface_width == static_cast<u32>(new_window_width) &&
|
|
||||||
m_window_info.surface_height == static_cast<u32>(new_window_height))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_swap_chain_rtv.Reset();
|
m_swap_chain_rtv.Reset();
|
||||||
|
|
||||||
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN,
|
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN,
|
||||||
m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
|
m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
|
||||||
if (FAILED(hr)) [[unlikely]]
|
if (FAILED(hr)) [[unlikely]]
|
||||||
ERROR_LOG("ResizeBuffers() failed: 0x{:08X}", static_cast<unsigned>(hr));
|
{
|
||||||
|
Error::SetHResult(error, "ResizeBuffers() failed: ", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!CreateSwapChainRTV())
|
return CreateRTV(error);
|
||||||
Panic("Failed to recreate swap chain RTV after resize");
|
}
|
||||||
|
|
||||||
|
bool D3D11SwapChain::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error)
|
||||||
|
{
|
||||||
|
m_allow_present_throttle = allow_present_throttle;
|
||||||
|
|
||||||
|
// Using mailbox-style no-allow-tearing causes tearing in exclusive fullscreen.
|
||||||
|
if (mode == GPUVSyncMode::Mailbox && IsExclusiveFullscreen())
|
||||||
|
{
|
||||||
|
WARNING_LOG("Using FIFO instead of Mailbox vsync due to exclusive fullscreen.");
|
||||||
|
mode = GPUVSyncMode::FIFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_vsync_mode == mode)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const u32 old_buffer_count = GetNewBufferCount(m_vsync_mode);
|
||||||
|
const u32 new_buffer_count = GetNewBufferCount(mode);
|
||||||
|
m_vsync_mode = mode;
|
||||||
|
if (old_buffer_count == new_buffer_count)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Buffer count change => needs recreation.
|
||||||
|
m_swap_chain_rtv.Reset();
|
||||||
|
DestroySwapChain();
|
||||||
|
return CreateSwapChain(error) && CreateRTV(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<GPUSwapChain> D3D11Device::CreateSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||||
|
bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control,
|
||||||
|
Error* error)
|
||||||
|
{
|
||||||
|
std::unique_ptr<D3D11SwapChain> ret;
|
||||||
|
if (wi.type != WindowInfo::Type::Win32)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "Cannot create a swap chain on non-win32 window.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = std::make_unique<D3D11SwapChain>(wi, vsync_mode, allow_present_throttle, exclusive_fullscreen_mode);
|
||||||
|
if (ret->CreateSwapChain(error) && ret->CreateRTV(error))
|
||||||
|
{
|
||||||
|
// Render a frame as soon as possible to clear out whatever was previously being displayed.
|
||||||
|
m_context->ClearRenderTargetView(ret->GetRTV(), s_clear_color.data());
|
||||||
|
ret->GetSwapChain()->Present(0, ret->IsUsingAllowTearing() ? DXGI_PRESENT_ALLOW_TEARING : 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D11Device::SupportsExclusiveFullscreen() const
|
bool D3D11Device::SupportsExclusiveFullscreen() const
|
||||||
@ -471,9 +497,16 @@ std::string D3D11Device::GetDriverInfo() const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D11Device::ExecuteAndWaitForGPUIdle()
|
void D3D11Device::FlushCommands()
|
||||||
{
|
{
|
||||||
m_context->Flush();
|
m_context->Flush();
|
||||||
|
TrimTexturePool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D11Device::WaitForGPUIdle()
|
||||||
|
{
|
||||||
|
m_context->Flush();
|
||||||
|
TrimTexturePool();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D11Device::CreateBuffers()
|
bool D3D11Device::CreateBuffers()
|
||||||
@ -610,63 +643,29 @@ void D3D11Device::InvalidateRenderTarget(GPUTexture* t)
|
|||||||
T->CommitClear(m_context.Get());
|
T->CommitClear(m_context.Get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D11Device::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle)
|
GPUDevice::PresentResult D3D11Device::BeginPresent(GPUSwapChain* swap_chain, u32 clear_color)
|
||||||
{
|
{
|
||||||
m_allow_present_throttle = allow_present_throttle;
|
D3D11SwapChain* const SC = static_cast<D3D11SwapChain*>(swap_chain);
|
||||||
|
|
||||||
// Using mailbox-style no-allow-tearing causes tearing in exclusive fullscreen.
|
|
||||||
if (mode == GPUVSyncMode::Mailbox && m_is_exclusive_fullscreen)
|
|
||||||
{
|
|
||||||
WARNING_LOG("Using FIFO instead of Mailbox vsync due to exclusive fullscreen.");
|
|
||||||
mode = GPUVSyncMode::FIFO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_vsync_mode == mode)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const u32 old_buffer_count = GetSwapChainBufferCount();
|
|
||||||
m_vsync_mode = mode;
|
|
||||||
if (!m_swap_chain)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (GetSwapChainBufferCount() != old_buffer_count)
|
|
||||||
{
|
|
||||||
DestroySwapChain();
|
|
||||||
if (!CreateSwapChain())
|
|
||||||
Panic("Failed to recreate swap chain after vsync change.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GPUDevice::PresentResult D3D11Device::BeginPresent(u32 clear_color)
|
|
||||||
{
|
|
||||||
if (!m_swap_chain)
|
|
||||||
{
|
|
||||||
// Note: Really slow on Intel...
|
|
||||||
m_context->Flush();
|
|
||||||
TrimTexturePool();
|
|
||||||
return PresentResult::SkipPresent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we lost exclusive fullscreen. If so, notify the host, so it can switch to windowed mode.
|
// Check if we lost exclusive fullscreen. If so, notify the host, so it can switch to windowed mode.
|
||||||
// This might get called repeatedly if it takes a while to switch back, that's the host's problem.
|
// This might get called repeatedly if it takes a while to switch back, that's the host's problem.
|
||||||
BOOL is_fullscreen;
|
BOOL is_fullscreen;
|
||||||
if (m_is_exclusive_fullscreen &&
|
if (SC->IsExclusiveFullscreen() &&
|
||||||
(FAILED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) || !is_fullscreen))
|
(FAILED(SC->GetSwapChain()->GetFullscreenState(&is_fullscreen, nullptr)) || !is_fullscreen))
|
||||||
{
|
{
|
||||||
Host::SetFullscreen(false);
|
|
||||||
TrimTexturePool();
|
TrimTexturePool();
|
||||||
return PresentResult::SkipPresent;
|
return PresentResult::ExclusiveFullscreenLost;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When using vsync, the time here seems to include the time for the buffer to become available.
|
// When using vsync, the time here seems to include the time for the buffer to become available.
|
||||||
// This blows our our GPU usage number considerably, so read the timestamp before the final blit
|
// This blows our our GPU usage number considerably, so read the timestamp before the final blit
|
||||||
// in this configuration. It does reduce accuracy a little, but better than seeing 100% all of
|
// in this configuration. It does reduce accuracy a little, but better than seeing 100% all of
|
||||||
// the time, when it's more like a couple of percent.
|
// the time, when it's more like a couple of percent.
|
||||||
if (m_vsync_mode == GPUVSyncMode::FIFO && m_gpu_timing_enabled)
|
if (SC == m_main_swap_chain.get() && SC->GetVSyncMode() == GPUVSyncMode::FIFO && m_gpu_timing_enabled)
|
||||||
PopTimestampQuery();
|
PopTimestampQuery();
|
||||||
|
|
||||||
m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), GSVector4::rgba32(clear_color).F32);
|
m_context->ClearRenderTargetView(SC->GetRTV(), GSVector4::rgba32(clear_color).F32);
|
||||||
m_context->OMSetRenderTargets(1, m_swap_chain_rtv.GetAddressOf(), nullptr);
|
m_context->OMSetRenderTargets(1, SC->GetRTVArray(), nullptr);
|
||||||
s_stats.num_render_passes++;
|
s_stats.num_render_passes++;
|
||||||
m_num_current_render_targets = 0;
|
m_num_current_render_targets = 0;
|
||||||
m_current_render_pass_flags = GPUPipeline::NoRenderPassFlags;
|
m_current_render_pass_flags = GPUPipeline::NoRenderPassFlags;
|
||||||
@ -675,17 +674,19 @@ GPUDevice::PresentResult D3D11Device::BeginPresent(u32 clear_color)
|
|||||||
return PresentResult::OK;
|
return PresentResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D11Device::EndPresent(bool explicit_present, u64 present_time)
|
void D3D11Device::EndPresent(GPUSwapChain* swap_chain, bool explicit_present, u64 present_time)
|
||||||
{
|
{
|
||||||
|
D3D11SwapChain* const SC = static_cast<D3D11SwapChain*>(swap_chain);
|
||||||
DebugAssert(!explicit_present && present_time == 0);
|
DebugAssert(!explicit_present && present_time == 0);
|
||||||
DebugAssert(m_num_current_render_targets == 0 && !m_current_depth_target);
|
DebugAssert(m_num_current_render_targets == 0 && !m_current_depth_target);
|
||||||
|
|
||||||
if (m_vsync_mode != GPUVSyncMode::FIFO && m_gpu_timing_enabled)
|
if (SC == m_main_swap_chain.get() && SC->GetVSyncMode() != GPUVSyncMode::FIFO && m_gpu_timing_enabled)
|
||||||
PopTimestampQuery();
|
PopTimestampQuery();
|
||||||
|
|
||||||
const UINT sync_interval = static_cast<UINT>(m_vsync_mode == GPUVSyncMode::FIFO);
|
const UINT sync_interval = static_cast<UINT>(SC->GetVSyncMode() == GPUVSyncMode::FIFO);
|
||||||
const UINT flags = (m_vsync_mode == GPUVSyncMode::Disabled && m_using_allow_tearing) ? DXGI_PRESENT_ALLOW_TEARING : 0;
|
const UINT flags =
|
||||||
m_swap_chain->Present(sync_interval, flags);
|
(SC->GetVSyncMode() == GPUVSyncMode::Disabled && SC->IsUsingAllowTearing()) ? DXGI_PRESENT_ALLOW_TEARING : 0;
|
||||||
|
SC->GetSwapChain()->Present(sync_interval, flags);
|
||||||
|
|
||||||
if (m_gpu_timing_enabled)
|
if (m_gpu_timing_enabled)
|
||||||
KickTimestampQuery();
|
KickTimestampQuery();
|
||||||
@ -693,7 +694,7 @@ void D3D11Device::EndPresent(bool explicit_present, u64 present_time)
|
|||||||
TrimTexturePool();
|
TrimTexturePool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D11Device::SubmitPresent()
|
void D3D11Device::SubmitPresent(GPUSwapChain* swap_chain)
|
||||||
{
|
{
|
||||||
Panic("Not supported by this API.");
|
Panic("Not supported by this API.");
|
||||||
}
|
}
|
||||||
@ -1124,4 +1125,4 @@ void D3D11Device::DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex)
|
|||||||
void D3D11Device::DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type)
|
void D3D11Device::DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type)
|
||||||
{
|
{
|
||||||
Panic("Barriers are not supported");
|
Panic("Barriers are not supported");
|
||||||
}
|
}
|
||||||
|
@ -32,21 +32,23 @@ public:
|
|||||||
~D3D11Device();
|
~D3D11Device();
|
||||||
|
|
||||||
ALWAYS_INLINE static D3D11Device& GetInstance() { return *static_cast<D3D11Device*>(g_gpu_device.get()); }
|
ALWAYS_INLINE static D3D11Device& GetInstance() { return *static_cast<D3D11Device*>(g_gpu_device.get()); }
|
||||||
ALWAYS_INLINE static ID3D11Device* GetD3DDevice() { return GetInstance().m_device.Get(); }
|
ALWAYS_INLINE static ID3D11Device1* GetD3DDevice() { return GetInstance().m_device.Get(); }
|
||||||
ALWAYS_INLINE static ID3D11DeviceContext1* GetD3DContext() { return GetInstance().m_context.Get(); }
|
ALWAYS_INLINE static ID3D11DeviceContext1* GetD3DContext() { return GetInstance().m_context.Get(); }
|
||||||
|
ALWAYS_INLINE static IDXGIFactory5* GetDXGIFactory() { return GetInstance().m_dxgi_factory.Get(); }
|
||||||
ALWAYS_INLINE static D3D_FEATURE_LEVEL GetMaxFeatureLevel() { return GetInstance().m_max_feature_level; }
|
ALWAYS_INLINE static D3D_FEATURE_LEVEL GetMaxFeatureLevel() { return GetInstance().m_max_feature_level; }
|
||||||
|
|
||||||
bool HasSurface() const override;
|
|
||||||
|
|
||||||
bool UpdateWindow() override;
|
|
||||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
|
||||||
bool SupportsExclusiveFullscreen() const override;
|
bool SupportsExclusiveFullscreen() const override;
|
||||||
void DestroySurface() override;
|
|
||||||
|
|
||||||
std::string GetDriverInfo() const override;
|
std::string GetDriverInfo() const override;
|
||||||
|
|
||||||
void ExecuteAndWaitForGPUIdle() override;
|
void FlushCommands() override;
|
||||||
|
void WaitForGPUIdle() override;
|
||||||
|
|
||||||
|
std::unique_ptr<GPUSwapChain> CreateSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||||
|
bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control,
|
||||||
|
Error* error) override;
|
||||||
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
||||||
GPUTexture::Type type, GPUTexture::Format format,
|
GPUTexture::Type type, GPUTexture::Format format,
|
||||||
const void* data = nullptr, u32 data_stride = 0) override;
|
const void* data = nullptr, u32 data_stride = 0) override;
|
||||||
@ -97,21 +99,21 @@ public:
|
|||||||
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
||||||
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
||||||
|
|
||||||
void SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle) override;
|
|
||||||
|
|
||||||
bool SetGPUTimingEnabled(bool enabled) override;
|
bool SetGPUTimingEnabled(bool enabled) override;
|
||||||
float GetAndResetAccumulatedGPUTime() override;
|
float GetAndResetAccumulatedGPUTime() override;
|
||||||
|
|
||||||
PresentResult BeginPresent(u32 clear_color) override;
|
PresentResult BeginPresent(GPUSwapChain* swap_chain, u32 clear_color) override;
|
||||||
void EndPresent(bool explicit_present, u64 present_time) override;
|
void EndPresent(GPUSwapChain* swap_chain, bool explicit_present, u64 present_time) override;
|
||||||
void SubmitPresent() override;
|
void SubmitPresent(GPUSwapChain* swap_chain) override;
|
||||||
|
|
||||||
void UnbindPipeline(D3D11Pipeline* pl);
|
void UnbindPipeline(D3D11Pipeline* pl);
|
||||||
void UnbindTexture(D3D11Texture* tex);
|
void UnbindTexture(D3D11Texture* tex);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool CreateDevice(std::string_view adapter, std::optional<bool> exclusive_fullscreen_control,
|
bool CreateDeviceAndMainSwapChain(std::string_view adapter, FeatureMask disabled_features, const WindowInfo& wi,
|
||||||
FeatureMask disabled_features, Error* error) override;
|
GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control, Error* error) override;
|
||||||
void DestroyDevice() override;
|
void DestroyDevice() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -136,11 +138,6 @@ private:
|
|||||||
|
|
||||||
void SetFeatures(FeatureMask disabled_features);
|
void SetFeatures(FeatureMask disabled_features);
|
||||||
|
|
||||||
u32 GetSwapChainBufferCount() const;
|
|
||||||
bool CreateSwapChain();
|
|
||||||
bool CreateSwapChainRTV();
|
|
||||||
void DestroySwapChain();
|
|
||||||
|
|
||||||
bool CreateBuffers();
|
bool CreateBuffers();
|
||||||
void DestroyBuffers();
|
void DestroyBuffers();
|
||||||
|
|
||||||
@ -161,8 +158,6 @@ private:
|
|||||||
ComPtr<ID3DUserDefinedAnnotation> m_annotation;
|
ComPtr<ID3DUserDefinedAnnotation> m_annotation;
|
||||||
|
|
||||||
ComPtr<IDXGIFactory5> m_dxgi_factory;
|
ComPtr<IDXGIFactory5> m_dxgi_factory;
|
||||||
ComPtr<IDXGISwapChain1> m_swap_chain;
|
|
||||||
ComPtr<ID3D11RenderTargetView> m_swap_chain_rtv;
|
|
||||||
|
|
||||||
RasterizationStateMap m_rasterization_states;
|
RasterizationStateMap m_rasterization_states;
|
||||||
DepthStateMap m_depth_states;
|
DepthStateMap m_depth_states;
|
||||||
@ -170,10 +165,6 @@ private:
|
|||||||
InputLayoutMap m_input_layouts;
|
InputLayoutMap m_input_layouts;
|
||||||
|
|
||||||
D3D_FEATURE_LEVEL m_max_feature_level = D3D_FEATURE_LEVEL_10_0;
|
D3D_FEATURE_LEVEL m_max_feature_level = D3D_FEATURE_LEVEL_10_0;
|
||||||
bool m_allow_tearing_supported = false;
|
|
||||||
bool m_using_flip_model_swap_chain = true;
|
|
||||||
bool m_using_allow_tearing = false;
|
|
||||||
bool m_is_exclusive_fullscreen = false;
|
|
||||||
|
|
||||||
D3D11StreamBuffer m_vertex_buffer;
|
D3D11StreamBuffer m_vertex_buffer;
|
||||||
D3D11StreamBuffer m_index_buffer;
|
D3D11StreamBuffer m_index_buffer;
|
||||||
@ -207,4 +198,45 @@ private:
|
|||||||
float m_accumulated_gpu_time = 0.0f;
|
float m_accumulated_gpu_time = 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class D3D11SwapChain : public GPUSwapChain
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template<typename T>
|
||||||
|
using ComPtr = Microsoft::WRL::ComPtr<T>;
|
||||||
|
|
||||||
|
friend D3D11Device;
|
||||||
|
|
||||||
|
D3D11SwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
|
const GPUDevice::ExclusiveFullscreenMode* fullscreen_mode);
|
||||||
|
~D3D11SwapChain() override;
|
||||||
|
|
||||||
|
ALWAYS_INLINE IDXGISwapChain1* GetSwapChain() const { return m_swap_chain.Get(); }
|
||||||
|
ALWAYS_INLINE ID3D11RenderTargetView* GetRTV() const { return m_swap_chain_rtv.Get(); }
|
||||||
|
ALWAYS_INLINE ID3D11RenderTargetView* const* GetRTVArray() const { return m_swap_chain_rtv.GetAddressOf(); }
|
||||||
|
ALWAYS_INLINE bool IsUsingAllowTearing() const { return m_using_allow_tearing; }
|
||||||
|
ALWAYS_INLINE bool IsExclusiveFullscreen() const { return m_fullscreen_mode.has_value(); }
|
||||||
|
|
||||||
|
bool ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error) override;
|
||||||
|
bool SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static u32 GetNewBufferCount(GPUVSyncMode vsync_mode);
|
||||||
|
|
||||||
|
bool InitializeExclusiveFullscreenMode(const GPUDevice::ExclusiveFullscreenMode* mode);
|
||||||
|
|
||||||
|
bool CreateSwapChain(Error* error);
|
||||||
|
bool CreateRTV(Error* error);
|
||||||
|
|
||||||
|
void DestroySwapChain();
|
||||||
|
|
||||||
|
ComPtr<IDXGISwapChain1> m_swap_chain;
|
||||||
|
ComPtr<ID3D11RenderTargetView> m_swap_chain_rtv;
|
||||||
|
|
||||||
|
ComPtr<IDXGIOutput> m_fullscreen_output;
|
||||||
|
std::optional<DXGI_MODE_DESC> m_fullscreen_mode;
|
||||||
|
|
||||||
|
bool m_using_flip_model_swap_chain = true;
|
||||||
|
bool m_using_allow_tearing = false;
|
||||||
|
};
|
||||||
|
|
||||||
void SetD3DDebugObjectName(ID3D11DeviceChild* obj, std::string_view name);
|
void SetD3DDebugObjectName(ID3D11DeviceChild* obj, std::string_view name);
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
#include "common/error.h"
|
#include "common/error.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
|
|
||||||
LOG_CHANNEL(D3D11Device);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
D3D11StreamBuffer::D3D11StreamBuffer()
|
D3D11StreamBuffer::D3D11StreamBuffer()
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
LOG_CHANNEL(D3D11Device);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
std::unique_ptr<GPUTexture> D3D11Device::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
std::unique_ptr<GPUTexture> D3D11Device::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
||||||
GPUTexture::Type type, GPUTexture::Format format,
|
GPUTexture::Type type, GPUTexture::Format format,
|
||||||
|
@ -8,8 +8,6 @@
|
|||||||
#include "d3d12_texture.h"
|
#include "d3d12_texture.h"
|
||||||
#include "d3d_common.h"
|
#include "d3d_common.h"
|
||||||
|
|
||||||
#include "core/host.h"
|
|
||||||
|
|
||||||
#include "common/align.h"
|
#include "common/align.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/bitutils.h"
|
#include "common/bitutils.h"
|
||||||
@ -27,7 +25,7 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
LOG_CHANNEL(D3D12Device);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
// Tweakables
|
// Tweakables
|
||||||
enum : u32
|
enum : u32
|
||||||
@ -69,6 +67,8 @@ static u32 s_debug_scope_depth = 0;
|
|||||||
|
|
||||||
D3D12Device::D3D12Device()
|
D3D12Device::D3D12Device()
|
||||||
{
|
{
|
||||||
|
m_render_api = RenderAPI::D3D12;
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
s_debug_scope_depth = 0;
|
s_debug_scope_depth = 0;
|
||||||
#endif
|
#endif
|
||||||
@ -117,8 +117,11 @@ D3D12Device::ComPtr<ID3D12RootSignature> D3D12Device::CreateRootSignature(const
|
|||||||
return rs;
|
return rs;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D12Device::CreateDevice(std::string_view adapter, std::optional<bool> exclusive_fullscreen_control,
|
bool D3D12Device::CreateDeviceAndMainSwapChain(std::string_view adapter, FeatureMask disabled_features,
|
||||||
FeatureMask disabled_features, Error* error)
|
const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||||
|
bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control, Error* error)
|
||||||
{
|
{
|
||||||
std::unique_lock lock(s_instance_mutex);
|
std::unique_lock lock(s_instance_mutex);
|
||||||
|
|
||||||
@ -238,8 +241,13 @@ bool D3D12Device::CreateDevice(std::string_view adapter, std::optional<bool> exc
|
|||||||
if (!CreateCommandLists(error) || !CreateDescriptorHeaps(error))
|
if (!CreateCommandLists(error) || !CreateDescriptorHeaps(error))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!m_window_info.IsSurfaceless() && !CreateSwapChain(error))
|
if (!wi.IsSurfaceless())
|
||||||
return false;
|
{
|
||||||
|
m_main_swap_chain = CreateSwapChain(wi, vsync_mode, allow_present_throttle, exclusive_fullscreen_mode,
|
||||||
|
exclusive_fullscreen_control, error);
|
||||||
|
if (!m_main_swap_chain)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!CreateRootSignatures(error) || !CreateBuffers(error))
|
if (!CreateRootSignatures(error) || !CreateBuffers(error))
|
||||||
return false;
|
return false;
|
||||||
@ -256,7 +264,9 @@ void D3D12Device::DestroyDevice()
|
|||||||
if (InRenderPass())
|
if (InRenderPass())
|
||||||
EndRenderPass();
|
EndRenderPass();
|
||||||
|
|
||||||
WaitForGPUIdle();
|
WaitForAllFences();
|
||||||
|
|
||||||
|
m_main_swap_chain.reset();
|
||||||
|
|
||||||
DestroyDeferredObjects(m_current_fence_value);
|
DestroyDeferredObjects(m_current_fence_value);
|
||||||
DestroySamplers();
|
DestroySamplers();
|
||||||
@ -264,7 +274,6 @@ void D3D12Device::DestroyDevice()
|
|||||||
DestroyBuffers();
|
DestroyBuffers();
|
||||||
DestroyDescriptorHeaps();
|
DestroyDescriptorHeaps();
|
||||||
DestroyRootSignatures();
|
DestroyRootSignatures();
|
||||||
DestroySwapChain();
|
|
||||||
DestroyCommandLists();
|
DestroyCommandLists();
|
||||||
|
|
||||||
m_pipeline_library.Reset();
|
m_pipeline_library.Reset();
|
||||||
@ -656,7 +665,7 @@ void D3D12Device::WaitForFence(u64 fence)
|
|||||||
DestroyDeferredObjects(m_completed_fence_value);
|
DestroyDeferredObjects(m_completed_fence_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D12Device::WaitForGPUIdle()
|
void D3D12Device::WaitForAllFences()
|
||||||
{
|
{
|
||||||
u32 index = (m_current_command_list + 1) % NUM_COMMAND_LISTS;
|
u32 index = (m_current_command_list + 1) % NUM_COMMAND_LISTS;
|
||||||
for (u32 i = 0; i < (NUM_COMMAND_LISTS - 1); i++)
|
for (u32 i = 0; i < (NUM_COMMAND_LISTS - 1); i++)
|
||||||
@ -666,7 +675,16 @@ void D3D12Device::WaitForGPUIdle()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D12Device::ExecuteAndWaitForGPUIdle()
|
void D3D12Device::FlushCommands()
|
||||||
|
{
|
||||||
|
if (InRenderPass())
|
||||||
|
EndRenderPass();
|
||||||
|
|
||||||
|
SubmitCommandList(false);
|
||||||
|
TrimTexturePool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12Device::WaitForGPUIdle()
|
||||||
{
|
{
|
||||||
if (InRenderPass())
|
if (InRenderPass())
|
||||||
EndRenderPass();
|
EndRenderPass();
|
||||||
@ -790,54 +808,54 @@ void D3D12Device::DestroyDeferredObjects(u64 fence_value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D12Device::HasSurface() const
|
D3D12SwapChain::D3D12SwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
|
const GPUDevice::ExclusiveFullscreenMode* fullscreen_mode)
|
||||||
|
: GPUSwapChain(wi, vsync_mode, allow_present_throttle)
|
||||||
{
|
{
|
||||||
return static_cast<bool>(m_swap_chain);
|
if (fullscreen_mode)
|
||||||
|
InitializeExclusiveFullscreenMode(fullscreen_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 D3D12Device::GetSwapChainBufferCount() const
|
D3D12SwapChain::~D3D12SwapChain()
|
||||||
{
|
{
|
||||||
// With vsync off, we only need two buffers. Same for blocking vsync.
|
DestroyRTVs();
|
||||||
// With triple buffering, we need three.
|
DestroySwapChain();
|
||||||
return (m_vsync_mode == GPUVSyncMode::Mailbox) ? 3 : 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D12Device::CreateSwapChain(Error* error)
|
bool D3D12SwapChain::InitializeExclusiveFullscreenMode(const GPUDevice::ExclusiveFullscreenMode* mode)
|
||||||
{
|
{
|
||||||
if (m_window_info.type != WindowInfo::Type::Win32)
|
|
||||||
{
|
|
||||||
Error::SetStringView(error, "D3D12 expects a Win32 window.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const D3DCommon::DXGIFormatMapping& fm = D3DCommon::GetFormatMapping(s_swap_chain_format);
|
const D3DCommon::DXGIFormatMapping& fm = D3DCommon::GetFormatMapping(s_swap_chain_format);
|
||||||
|
|
||||||
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
|
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
|
||||||
RECT client_rc{};
|
RECT client_rc{};
|
||||||
GetClientRect(window_hwnd, &client_rc);
|
GetClientRect(window_hwnd, &client_rc);
|
||||||
|
|
||||||
DXGI_MODE_DESC fullscreen_mode = {};
|
m_fullscreen_mode =
|
||||||
ComPtr<IDXGIOutput> fullscreen_output;
|
D3DCommon::GetRequestedExclusiveFullscreenModeDesc(D3D12Device::GetInstance().GetDXGIFactory(), client_rc, mode,
|
||||||
if (Host::IsFullscreen())
|
fm.resource_format, m_fullscreen_output.GetAddressOf());
|
||||||
{
|
return m_fullscreen_mode.has_value();
|
||||||
u32 fullscreen_width, fullscreen_height;
|
}
|
||||||
float fullscreen_refresh_rate;
|
|
||||||
m_is_exclusive_fullscreen =
|
|
||||||
GetRequestedExclusiveFullscreenMode(&fullscreen_width, &fullscreen_height, &fullscreen_refresh_rate) &&
|
|
||||||
D3DCommon::GetRequestedExclusiveFullscreenModeDesc(m_dxgi_factory.Get(), client_rc, fullscreen_width,
|
|
||||||
fullscreen_height, fullscreen_refresh_rate, fm.resource_format,
|
|
||||||
&fullscreen_mode, fullscreen_output.GetAddressOf());
|
|
||||||
|
|
||||||
// Using mailbox-style no-allow-tearing causes tearing in exclusive fullscreen.
|
u32 D3D12SwapChain::GetNewBufferCount(GPUVSyncMode vsync_mode)
|
||||||
if (m_vsync_mode == GPUVSyncMode::Mailbox && m_is_exclusive_fullscreen)
|
{
|
||||||
{
|
// With vsync off, we only need two buffers. Same for blocking vsync.
|
||||||
WARNING_LOG("Using FIFO instead of Mailbox vsync due to exclusive fullscreen.");
|
// With triple buffering, we need three.
|
||||||
m_vsync_mode = GPUVSyncMode::FIFO;
|
return (vsync_mode == GPUVSyncMode::Mailbox) ? 3 : 2;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
bool D3D12SwapChain::CreateSwapChain(D3D12Device& dev, Error* error)
|
||||||
|
{
|
||||||
|
const D3DCommon::DXGIFormatMapping& fm = D3DCommon::GetFormatMapping(s_swap_chain_format);
|
||||||
|
|
||||||
|
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
|
||||||
|
RECT client_rc{};
|
||||||
|
GetClientRect(window_hwnd, &client_rc);
|
||||||
|
|
||||||
|
// Using mailbox-style no-allow-tearing causes tearing in exclusive fullscreen.
|
||||||
|
if (IsExclusiveFullscreen() && m_vsync_mode == GPUVSyncMode::Mailbox)
|
||||||
{
|
{
|
||||||
m_is_exclusive_fullscreen = false;
|
WARNING_LOG("Using FIFO instead of Mailbox vsync due to exclusive fullscreen.");
|
||||||
|
m_vsync_mode = GPUVSyncMode::FIFO;
|
||||||
}
|
}
|
||||||
|
|
||||||
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
|
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
|
||||||
@ -845,45 +863,44 @@ bool D3D12Device::CreateSwapChain(Error* error)
|
|||||||
swap_chain_desc.Height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
swap_chain_desc.Height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
||||||
swap_chain_desc.Format = fm.resource_format;
|
swap_chain_desc.Format = fm.resource_format;
|
||||||
swap_chain_desc.SampleDesc.Count = 1;
|
swap_chain_desc.SampleDesc.Count = 1;
|
||||||
swap_chain_desc.BufferCount = GetSwapChainBufferCount();
|
swap_chain_desc.BufferCount = GetNewBufferCount(m_vsync_mode);
|
||||||
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||||
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||||
|
|
||||||
m_using_allow_tearing = (m_allow_tearing_supported && !m_is_exclusive_fullscreen);
|
|
||||||
if (m_using_allow_tearing)
|
|
||||||
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
|
||||||
|
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
if (m_is_exclusive_fullscreen)
|
if (IsExclusiveFullscreen())
|
||||||
{
|
{
|
||||||
DXGI_SWAP_CHAIN_DESC1 fs_sd_desc = swap_chain_desc;
|
DXGI_SWAP_CHAIN_DESC1 fs_sd_desc = swap_chain_desc;
|
||||||
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
|
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
|
||||||
|
|
||||||
fs_sd_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
fs_sd_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
||||||
fs_sd_desc.Width = fullscreen_mode.Width;
|
fs_sd_desc.Width = m_fullscreen_mode->Width;
|
||||||
fs_sd_desc.Height = fullscreen_mode.Height;
|
fs_sd_desc.Height = m_fullscreen_mode->Height;
|
||||||
fs_desc.RefreshRate = fullscreen_mode.RefreshRate;
|
fs_desc.RefreshRate = m_fullscreen_mode->RefreshRate;
|
||||||
fs_desc.ScanlineOrdering = fullscreen_mode.ScanlineOrdering;
|
fs_desc.ScanlineOrdering = m_fullscreen_mode->ScanlineOrdering;
|
||||||
fs_desc.Scaling = fullscreen_mode.Scaling;
|
fs_desc.Scaling = m_fullscreen_mode->Scaling;
|
||||||
fs_desc.Windowed = FALSE;
|
fs_desc.Windowed = FALSE;
|
||||||
|
|
||||||
VERBOSE_LOG("Creating a {}x{} exclusive fullscreen swap chain", fs_sd_desc.Width, fs_sd_desc.Height);
|
VERBOSE_LOG("Creating a {}x{} exclusive fullscreen swap chain", fs_sd_desc.Width, fs_sd_desc.Height);
|
||||||
hr = m_dxgi_factory->CreateSwapChainForHwnd(m_command_queue.Get(), window_hwnd, &fs_sd_desc, &fs_desc,
|
hr = dev.GetDXGIFactory()->CreateSwapChainForHwnd(dev.GetCommandQueue(), window_hwnd, &fs_sd_desc, &fs_desc,
|
||||||
fullscreen_output.Get(), m_swap_chain.ReleaseAndGetAddressOf());
|
m_fullscreen_output.Get(), m_swap_chain.ReleaseAndGetAddressOf());
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
WARNING_LOG("Failed to create fullscreen swap chain, trying windowed.");
|
WARNING_LOG("Failed to create fullscreen swap chain, trying windowed.");
|
||||||
m_is_exclusive_fullscreen = false;
|
m_fullscreen_output.Reset();
|
||||||
m_using_allow_tearing = m_allow_tearing_supported;
|
m_fullscreen_mode.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_is_exclusive_fullscreen)
|
if (!IsExclusiveFullscreen())
|
||||||
{
|
{
|
||||||
VERBOSE_LOG("Creating a {}x{} windowed swap chain", swap_chain_desc.Width, swap_chain_desc.Height);
|
VERBOSE_LOG("Creating a {}x{} windowed swap chain", swap_chain_desc.Width, swap_chain_desc.Height);
|
||||||
hr = m_dxgi_factory->CreateSwapChainForHwnd(m_command_queue.Get(), window_hwnd, &swap_chain_desc, nullptr, nullptr,
|
m_using_allow_tearing = D3DCommon::SupportsAllowTearing(dev.GetDXGIFactory());
|
||||||
m_swap_chain.ReleaseAndGetAddressOf());
|
if (m_using_allow_tearing)
|
||||||
|
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
||||||
|
hr = dev.GetDXGIFactory()->CreateSwapChainForHwnd(dev.GetCommandQueue(), window_hwnd, &swap_chain_desc, nullptr,
|
||||||
|
nullptr, m_swap_chain.ReleaseAndGetAddressOf());
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
Error::SetHResult(error, "CreateSwapChainForHwnd() failed: ", hr);
|
Error::SetHResult(error, "CreateSwapChainForHwnd() failed: ", hr);
|
||||||
@ -891,22 +908,14 @@ bool D3D12Device::CreateSwapChain(Error* error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = m_dxgi_factory->MakeWindowAssociation(window_hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
|
hr = dev.GetDXGIFactory()->MakeWindowAssociation(window_hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
WARNING_LOG("MakeWindowAssociation() to disable ALT+ENTER failed");
|
WARNING_LOG("MakeWindowAssociation() to disable ALT+ENTER failed");
|
||||||
|
|
||||||
if (!CreateSwapChainRTV(error))
|
|
||||||
{
|
|
||||||
DestroySwapChain();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render a frame as soon as possible to clear out whatever was previously being displayed.
|
|
||||||
RenderBlankFrame();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D12Device::CreateSwapChainRTV(Error* error)
|
bool D3D12SwapChain::CreateRTV(D3D12Device& dev, Error* error)
|
||||||
{
|
{
|
||||||
DXGI_SWAP_CHAIN_DESC swap_chain_desc;
|
DXGI_SWAP_CHAIN_DESC swap_chain_desc;
|
||||||
HRESULT hr = m_swap_chain->GetDesc(&swap_chain_desc);
|
HRESULT hr = m_swap_chain->GetDesc(&swap_chain_desc);
|
||||||
@ -925,148 +934,162 @@ bool D3D12Device::CreateSwapChainRTV(Error* error)
|
|||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
Error::SetHResult(error, "GetBuffer for RTV failed: ", hr);
|
Error::SetHResult(error, "GetBuffer for RTV failed: ", hr);
|
||||||
DestroySwapChainRTVs();
|
DestroyRTVs();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
D3D12::SetObjectName(backbuffer.Get(), TinyString::from_format("Swap Chain Buffer #{}", i));
|
D3D12::SetObjectName(backbuffer.Get(), TinyString::from_format("Swap Chain Buffer #{}", i));
|
||||||
|
|
||||||
D3D12DescriptorHandle rtv;
|
D3D12DescriptorHandle rtv;
|
||||||
if (!m_rtv_heap_manager.Allocate(&rtv))
|
if (!dev.GetRTVHeapManager().Allocate(&rtv))
|
||||||
{
|
{
|
||||||
Error::SetStringView(error, "Failed to allocate RTV handle.");
|
Error::SetStringView(error, "Failed to allocate RTV handle.");
|
||||||
DestroySwapChainRTVs();
|
DestroyRTVs();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_device->CreateRenderTargetView(backbuffer.Get(), &rtv_desc, rtv);
|
dev.GetDevice()->CreateRenderTargetView(backbuffer.Get(), &rtv_desc, rtv);
|
||||||
m_swap_chain_buffers.emplace_back(std::move(backbuffer), rtv);
|
m_swap_chain_buffers.emplace_back(std::move(backbuffer), rtv);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_window_info.surface_width = swap_chain_desc.BufferDesc.Width;
|
m_window_info.surface_width = static_cast<u16>(swap_chain_desc.BufferDesc.Width);
|
||||||
m_window_info.surface_height = swap_chain_desc.BufferDesc.Height;
|
m_window_info.surface_height = static_cast<u16>(swap_chain_desc.BufferDesc.Height);
|
||||||
m_window_info.surface_format = s_swap_chain_format;
|
m_window_info.surface_format = s_swap_chain_format;
|
||||||
VERBOSE_LOG("Swap chain buffer size: {}x{}", m_window_info.surface_width, m_window_info.surface_height);
|
VERBOSE_LOG("Swap chain buffer size: {}x{}", m_window_info.surface_width, m_window_info.surface_height);
|
||||||
|
|
||||||
if (m_window_info.type == WindowInfo::Type::Win32)
|
BOOL fullscreen = FALSE;
|
||||||
|
DXGI_SWAP_CHAIN_DESC desc;
|
||||||
|
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen &&
|
||||||
|
SUCCEEDED(m_swap_chain->GetDesc(&desc)))
|
||||||
{
|
{
|
||||||
BOOL fullscreen = FALSE;
|
m_window_info.surface_refresh_rate = static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
|
||||||
DXGI_SWAP_CHAIN_DESC desc;
|
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||||
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen &&
|
|
||||||
SUCCEEDED(m_swap_chain->GetDesc(&desc)))
|
|
||||||
{
|
|
||||||
m_window_info.surface_refresh_rate = static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
|
|
||||||
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_current_swap_chain_buffer = 0;
|
m_current_swap_chain_buffer = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D12Device::DestroySwapChainRTVs()
|
void D3D12SwapChain::DestroyRTVs()
|
||||||
{
|
{
|
||||||
|
if (m_swap_chain_buffers.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
D3D12Device& dev = D3D12Device::GetInstance();
|
||||||
|
|
||||||
// Runtime gets cranky if we don't submit the current buffer...
|
// Runtime gets cranky if we don't submit the current buffer...
|
||||||
if (InRenderPass())
|
if (dev.InRenderPass())
|
||||||
EndRenderPass();
|
dev.EndRenderPass();
|
||||||
SubmitCommandList(true);
|
dev.SubmitCommandList(true);
|
||||||
|
|
||||||
for (auto it = m_swap_chain_buffers.rbegin(); it != m_swap_chain_buffers.rend(); ++it)
|
for (auto it = m_swap_chain_buffers.rbegin(); it != m_swap_chain_buffers.rend(); ++it)
|
||||||
{
|
{
|
||||||
m_rtv_heap_manager.Free(it->second.index);
|
dev.GetRTVHeapManager().Free(it->second.index);
|
||||||
it->first.Reset();
|
it->first.Reset();
|
||||||
}
|
}
|
||||||
m_swap_chain_buffers.clear();
|
m_swap_chain_buffers.clear();
|
||||||
m_current_swap_chain_buffer = 0;
|
m_current_swap_chain_buffer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D12Device::DestroySwapChain()
|
void D3D12SwapChain::DestroySwapChain()
|
||||||
{
|
{
|
||||||
if (!m_swap_chain)
|
if (!m_swap_chain)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DestroySwapChainRTVs();
|
|
||||||
|
|
||||||
// switch out of fullscreen before destroying
|
// switch out of fullscreen before destroying
|
||||||
BOOL is_fullscreen;
|
BOOL is_fullscreen;
|
||||||
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen)
|
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen)
|
||||||
m_swap_chain->SetFullscreenState(FALSE, nullptr);
|
m_swap_chain->SetFullscreenState(FALSE, nullptr);
|
||||||
|
|
||||||
m_swap_chain.Reset();
|
m_swap_chain.Reset();
|
||||||
m_is_exclusive_fullscreen = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D12Device::RenderBlankFrame()
|
bool D3D12SwapChain::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error)
|
||||||
{
|
{
|
||||||
if (InRenderPass())
|
m_allow_present_throttle = allow_present_throttle;
|
||||||
EndRenderPass();
|
|
||||||
|
|
||||||
auto& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
|
// Using mailbox-style no-allow-tearing causes tearing in exclusive fullscreen.
|
||||||
ID3D12GraphicsCommandList4* cmdlist = GetCommandList();
|
if (mode == GPUVSyncMode::Mailbox && IsExclusiveFullscreen())
|
||||||
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
|
{
|
||||||
D3D12Texture::TransitionSubresourceToState(cmdlist, swap_chain_buf.first.Get(), 0, D3D12_RESOURCE_STATE_COMMON,
|
WARNING_LOG("Using FIFO instead of Mailbox vsync due to exclusive fullscreen.");
|
||||||
D3D12_RESOURCE_STATE_RENDER_TARGET);
|
mode = GPUVSyncMode::FIFO;
|
||||||
cmdlist->ClearRenderTargetView(swap_chain_buf.second, GSVector4::cxpr(0.0f, 0.0f, 0.0f, 1.0f).F32, 0, nullptr);
|
}
|
||||||
D3D12Texture::TransitionSubresourceToState(cmdlist, swap_chain_buf.first.Get(), 0, D3D12_RESOURCE_STATE_RENDER_TARGET,
|
|
||||||
D3D12_RESOURCE_STATE_PRESENT);
|
|
||||||
SubmitCommandList(false);
|
|
||||||
m_swap_chain->Present(0, m_using_allow_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool D3D12Device::UpdateWindow()
|
if (m_vsync_mode == mode)
|
||||||
{
|
|
||||||
WaitForGPUIdle();
|
|
||||||
DestroySwapChain();
|
|
||||||
|
|
||||||
if (!AcquireWindow(false))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (m_window_info.IsSurfaceless())
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
Error error;
|
const u32 old_buffer_count = GetNewBufferCount(m_vsync_mode);
|
||||||
if (!CreateSwapChain(&error))
|
const u32 new_buffer_count = GetNewBufferCount(mode);
|
||||||
{
|
m_vsync_mode = mode;
|
||||||
ERROR_LOG("Failed to create swap chain on updated window: {}", error.GetDescription());
|
if (old_buffer_count == new_buffer_count)
|
||||||
return false;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
RenderBlankFrame();
|
// Buffer count change => needs recreation.
|
||||||
return true;
|
DestroyRTVs();
|
||||||
|
DestroySwapChain();
|
||||||
|
|
||||||
|
D3D12Device& dev = D3D12Device::GetInstance();
|
||||||
|
return CreateSwapChain(dev, error) && CreateRTV(dev, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D12Device::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
bool D3D12SwapChain::ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error)
|
||||||
{
|
{
|
||||||
if (!m_swap_chain)
|
m_window_info.surface_scale = new_scale;
|
||||||
return;
|
if (m_window_info.surface_width == new_width && m_window_info.surface_height == new_height)
|
||||||
|
return true;
|
||||||
|
|
||||||
m_window_info.surface_scale = new_window_scale;
|
DestroyRTVs();
|
||||||
|
|
||||||
if (m_window_info.surface_width == static_cast<u32>(new_window_width) &&
|
|
||||||
m_window_info.surface_height == static_cast<u32>(new_window_height))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DestroySwapChainRTVs();
|
|
||||||
|
|
||||||
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN,
|
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN,
|
||||||
m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
|
m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
ERROR_LOG("ResizeBuffers() failed: 0x{:08X}", static_cast<unsigned>(hr));
|
ERROR_LOG("ResizeBuffers() failed: 0x{:08X}", static_cast<unsigned>(hr));
|
||||||
|
|
||||||
Error error;
|
return CreateRTV(D3D12Device::GetInstance(), error);
|
||||||
if (!CreateSwapChainRTV(&error))
|
|
||||||
{
|
|
||||||
ERROR_LOG("Failed to recreate swap chain RTV after resize", error.GetDescription());
|
|
||||||
Panic("Failed to recreate swap chain RTV after resize");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D12Device::DestroySurface()
|
std::unique_ptr<GPUSwapChain> D3D12Device::CreateSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||||
|
bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control,
|
||||||
|
Error* error)
|
||||||
{
|
{
|
||||||
DestroySwapChainRTVs();
|
std::unique_ptr<D3D12SwapChain> ret;
|
||||||
DestroySwapChain();
|
if (wi.type != WindowInfo::Type::Win32)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "Cannot create a swap chain on non-win32 window.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = std::make_unique<D3D12SwapChain>(wi, vsync_mode, allow_present_throttle, exclusive_fullscreen_mode);
|
||||||
|
if (ret->CreateSwapChain(*this, error) && ret->CreateRTV(*this, error))
|
||||||
|
{
|
||||||
|
// Render a frame as soon as possible to clear out whatever was previously being displayed.
|
||||||
|
RenderBlankFrame(ret.get());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12Device::RenderBlankFrame(D3D12SwapChain* swap_chain)
|
||||||
|
{
|
||||||
|
if (InRenderPass())
|
||||||
|
EndRenderPass();
|
||||||
|
|
||||||
|
const D3D12SwapChain::BufferPair& swap_chain_buf = swap_chain->GetCurrentBuffer();
|
||||||
|
ID3D12GraphicsCommandList4* cmdlist = GetCommandList();
|
||||||
|
D3D12Texture::TransitionSubresourceToState(cmdlist, swap_chain_buf.first.Get(), 0, D3D12_RESOURCE_STATE_COMMON,
|
||||||
|
D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||||
|
cmdlist->ClearRenderTargetView(swap_chain_buf.second, GSVector4::cxpr(0.0f, 0.0f, 0.0f, 1.0f).F32, 0, nullptr);
|
||||||
|
D3D12Texture::TransitionSubresourceToState(cmdlist, swap_chain_buf.first.Get(), 0, D3D12_RESOURCE_STATE_RENDER_TARGET,
|
||||||
|
D3D12_RESOURCE_STATE_PRESENT);
|
||||||
|
SubmitCommandList(false);
|
||||||
|
swap_chain->GetSwapChain()->Present(0, swap_chain->IsUsingAllowTearing() ? DXGI_PRESENT_ALLOW_TEARING : 0);
|
||||||
|
swap_chain->AdvanceBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D12Device::SupportsTextureFormat(GPUTexture::Format format) const
|
bool D3D12Device::SupportsTextureFormat(GPUTexture::Format format) const
|
||||||
@ -1105,79 +1128,77 @@ std::string D3D12Device::GetDriverInfo() const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D12Device::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle)
|
GPUDevice::PresentResult D3D12Device::BeginPresent(GPUSwapChain* swap_chain, u32 clear_color)
|
||||||
{
|
|
||||||
m_allow_present_throttle = allow_present_throttle;
|
|
||||||
|
|
||||||
// Using mailbox-style no-allow-tearing causes tearing in exclusive fullscreen.
|
|
||||||
if (mode == GPUVSyncMode::Mailbox && m_is_exclusive_fullscreen)
|
|
||||||
{
|
|
||||||
WARNING_LOG("Using FIFO instead of Mailbox vsync due to exclusive fullscreen.");
|
|
||||||
mode = GPUVSyncMode::FIFO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_vsync_mode == mode)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const u32 old_buffer_count = GetSwapChainBufferCount();
|
|
||||||
m_vsync_mode = mode;
|
|
||||||
if (!m_swap_chain)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (GetSwapChainBufferCount() != old_buffer_count)
|
|
||||||
{
|
|
||||||
DestroySwapChain();
|
|
||||||
|
|
||||||
Error error;
|
|
||||||
if (!CreateSwapChain(&error))
|
|
||||||
{
|
|
||||||
ERROR_LOG("Failed to recreate swap chain after vsync change: {}", error.GetDescription());
|
|
||||||
Panic("Failed to recreate swap chain after vsync change.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GPUDevice::PresentResult D3D12Device::BeginPresent(u32 clear_color)
|
|
||||||
{
|
{
|
||||||
|
D3D12SwapChain* const SC = static_cast<D3D12SwapChain*>(swap_chain);
|
||||||
if (InRenderPass())
|
if (InRenderPass())
|
||||||
EndRenderPass();
|
EndRenderPass();
|
||||||
|
|
||||||
if (m_device_was_lost) [[unlikely]]
|
if (m_device_was_lost) [[unlikely]]
|
||||||
return PresentResult::DeviceLost;
|
return PresentResult::DeviceLost;
|
||||||
|
|
||||||
// If we're running surfaceless, kick the command buffer so we don't run out of descriptors.
|
|
||||||
if (!m_swap_chain)
|
|
||||||
{
|
|
||||||
SubmitCommandList(false);
|
|
||||||
TrimTexturePool();
|
|
||||||
return PresentResult::SkipPresent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Check if the device was lost.
|
// TODO: Check if the device was lost.
|
||||||
|
|
||||||
// Check if we lost exclusive fullscreen. If so, notify the host, so it can switch to windowed mode.
|
// Check if we lost exclusive fullscreen. If so, notify the host, so it can switch to windowed mode.
|
||||||
// This might get called repeatedly if it takes a while to switch back, that's the host's problem.
|
// This might get called repeatedly if it takes a while to switch back, that's the host's problem.
|
||||||
BOOL is_fullscreen;
|
BOOL is_fullscreen;
|
||||||
if (m_is_exclusive_fullscreen &&
|
if (SC->IsExclusiveFullscreen() &&
|
||||||
(FAILED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) || !is_fullscreen))
|
(FAILED(SC->GetSwapChain()->GetFullscreenState(&is_fullscreen, nullptr)) || !is_fullscreen))
|
||||||
{
|
{
|
||||||
Host::RunOnCPUThread([]() { Host::SetFullscreen(false); });
|
FlushCommands();
|
||||||
TrimTexturePool();
|
TrimTexturePool();
|
||||||
return PresentResult::SkipPresent;
|
return PresentResult::ExclusiveFullscreenLost;
|
||||||
}
|
}
|
||||||
|
|
||||||
BeginSwapChainRenderPass(clear_color);
|
m_current_swap_chain = SC;
|
||||||
|
|
||||||
|
const D3D12SwapChain::BufferPair& swap_chain_buf = SC->GetCurrentBuffer();
|
||||||
|
ID3D12GraphicsCommandList4* const cmdlist = GetCommandList();
|
||||||
|
|
||||||
|
D3D12Texture::TransitionSubresourceToState(cmdlist, swap_chain_buf.first.Get(), 0, D3D12_RESOURCE_STATE_COMMON,
|
||||||
|
D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||||
|
|
||||||
|
// All textures should be in shader read only optimal already, but just in case..
|
||||||
|
const u32 num_textures = GetActiveTexturesForLayout(m_current_pipeline_layout);
|
||||||
|
for (u32 i = 0; i < num_textures; i++)
|
||||||
|
{
|
||||||
|
if (m_current_textures[i])
|
||||||
|
m_current_textures[i]->TransitionToState(cmdlist, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D12_RENDER_PASS_RENDER_TARGET_DESC rt_desc = {swap_chain_buf.second,
|
||||||
|
{D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR, {}},
|
||||||
|
{D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE, {}}};
|
||||||
|
GSVector4::store<false>(rt_desc.BeginningAccess.Clear.ClearValue.Color, GSVector4::rgba32(clear_color));
|
||||||
|
cmdlist->BeginRenderPass(1, &rt_desc, nullptr, D3D12_RENDER_PASS_FLAG_NONE);
|
||||||
|
|
||||||
|
std::memset(m_current_render_targets.data(), 0, sizeof(m_current_render_targets));
|
||||||
|
m_num_current_render_targets = 0;
|
||||||
|
m_dirty_flags =
|
||||||
|
(m_dirty_flags & ~DIRTY_FLAG_RT_UAVS) | ((IsUsingROVRootSignature()) ? DIRTY_FLAG_PIPELINE_LAYOUT : 0);
|
||||||
|
m_current_render_pass_flags = GPUPipeline::NoRenderPassFlags;
|
||||||
|
m_current_depth_target = nullptr;
|
||||||
|
m_in_render_pass = true;
|
||||||
|
s_stats.num_render_passes++;
|
||||||
|
|
||||||
|
// Clear pipeline, it's likely incompatible.
|
||||||
|
m_current_pipeline = nullptr;
|
||||||
|
|
||||||
return PresentResult::OK;
|
return PresentResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D12Device::EndPresent(bool explicit_present, u64 present_time)
|
void D3D12Device::EndPresent(GPUSwapChain* swap_chain, bool explicit_present, u64 present_time)
|
||||||
{
|
{
|
||||||
|
D3D12SwapChain* const SC = static_cast<D3D12SwapChain*>(swap_chain);
|
||||||
DebugAssert(present_time == 0);
|
DebugAssert(present_time == 0);
|
||||||
DebugAssert(InRenderPass() && m_num_current_render_targets == 0 && !m_current_depth_target);
|
DebugAssert(InRenderPass() && m_num_current_render_targets == 0 && !m_current_depth_target);
|
||||||
EndRenderPass();
|
EndRenderPass();
|
||||||
|
|
||||||
const auto& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
|
DebugAssert(SC == m_current_swap_chain);
|
||||||
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
|
m_current_swap_chain = nullptr;
|
||||||
|
|
||||||
|
const D3D12SwapChain::BufferPair& swap_chain_buf = SC->GetCurrentBuffer();
|
||||||
|
SC->AdvanceBuffer();
|
||||||
|
|
||||||
ID3D12GraphicsCommandList* cmdlist = GetCommandList();
|
ID3D12GraphicsCommandList* cmdlist = GetCommandList();
|
||||||
D3D12Texture::TransitionSubresourceToState(cmdlist, swap_chain_buf.first.Get(), 0, D3D12_RESOURCE_STATE_RENDER_TARGET,
|
D3D12Texture::TransitionSubresourceToState(cmdlist, swap_chain_buf.first.Get(), 0, D3D12_RESOURCE_STATE_RENDER_TARGET,
|
||||||
@ -1187,18 +1208,19 @@ void D3D12Device::EndPresent(bool explicit_present, u64 present_time)
|
|||||||
TrimTexturePool();
|
TrimTexturePool();
|
||||||
|
|
||||||
if (!explicit_present)
|
if (!explicit_present)
|
||||||
SubmitPresent();
|
SubmitPresent(swap_chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D12Device::SubmitPresent()
|
void D3D12Device::SubmitPresent(GPUSwapChain* swap_chain)
|
||||||
{
|
{
|
||||||
DebugAssert(m_swap_chain);
|
D3D12SwapChain* const SC = static_cast<D3D12SwapChain*>(swap_chain);
|
||||||
if (m_device_was_lost) [[unlikely]]
|
if (m_device_was_lost) [[unlikely]]
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const UINT sync_interval = static_cast<UINT>(m_vsync_mode == GPUVSyncMode::FIFO);
|
const UINT sync_interval = static_cast<UINT>(SC->GetVSyncMode() == GPUVSyncMode::FIFO);
|
||||||
const UINT flags = (m_vsync_mode == GPUVSyncMode::Disabled && m_using_allow_tearing) ? DXGI_PRESENT_ALLOW_TEARING : 0;
|
const UINT flags =
|
||||||
m_swap_chain->Present(sync_interval, flags);
|
(SC->GetVSyncMode() == GPUVSyncMode::Disabled && SC->IsUsingAllowTearing()) ? DXGI_PRESENT_ALLOW_TEARING : 0;
|
||||||
|
SC->GetSwapChain()->Present(sync_interval, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
@ -1251,7 +1273,6 @@ void D3D12Device::InsertDebugMessage(const char* msg)
|
|||||||
|
|
||||||
void D3D12Device::SetFeatures(D3D_FEATURE_LEVEL feature_level, FeatureMask disabled_features)
|
void D3D12Device::SetFeatures(D3D_FEATURE_LEVEL feature_level, FeatureMask disabled_features)
|
||||||
{
|
{
|
||||||
m_render_api = RenderAPI::D3D12;
|
|
||||||
m_render_api_version = D3DCommon::GetRenderAPIVersionForFeatureLevel(feature_level);
|
m_render_api_version = D3DCommon::GetRenderAPIVersionForFeatureLevel(feature_level);
|
||||||
m_max_texture_size = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
m_max_texture_size = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
||||||
m_max_multisamples = 1;
|
m_max_multisamples = 1;
|
||||||
@ -1286,11 +1307,6 @@ void D3D12Device::SetFeatures(D3D_FEATURE_LEVEL feature_level, FeatureMask disab
|
|||||||
m_features.pipeline_cache = true;
|
m_features.pipeline_cache = true;
|
||||||
m_features.prefer_unused_textures = true;
|
m_features.prefer_unused_textures = true;
|
||||||
|
|
||||||
BOOL allow_tearing_supported = false;
|
|
||||||
HRESULT hr = m_dxgi_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported,
|
|
||||||
sizeof(allow_tearing_supported));
|
|
||||||
m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
|
|
||||||
|
|
||||||
m_features.raster_order_views = false;
|
m_features.raster_order_views = false;
|
||||||
if (!(disabled_features & FEATURE_MASK_RASTER_ORDER_VIEWS))
|
if (!(disabled_features & FEATURE_MASK_RASTER_ORDER_VIEWS))
|
||||||
{
|
{
|
||||||
@ -1841,7 +1857,7 @@ void D3D12Device::BeginRenderPass()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Re-rendering to swap chain.
|
// Re-rendering to swap chain.
|
||||||
const auto& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
|
const auto& swap_chain_buf = m_current_swap_chain->GetCurrentBuffer();
|
||||||
rt_desc[0] = {swap_chain_buf.second,
|
rt_desc[0] = {swap_chain_buf.second,
|
||||||
{D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE, {}},
|
{D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE, {}},
|
||||||
{D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE, {}}};
|
{D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE, {}}};
|
||||||
@ -1869,43 +1885,6 @@ void D3D12Device::BeginRenderPass()
|
|||||||
SetInitialPipelineState();
|
SetInitialPipelineState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D12Device::BeginSwapChainRenderPass(u32 clear_color)
|
|
||||||
{
|
|
||||||
DebugAssert(!InRenderPass());
|
|
||||||
|
|
||||||
ID3D12GraphicsCommandList4* const cmdlist = GetCommandList();
|
|
||||||
const auto& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
|
|
||||||
|
|
||||||
D3D12Texture::TransitionSubresourceToState(cmdlist, swap_chain_buf.first.Get(), 0, D3D12_RESOURCE_STATE_COMMON,
|
|
||||||
D3D12_RESOURCE_STATE_RENDER_TARGET);
|
|
||||||
|
|
||||||
// All textures should be in shader read only optimal already, but just in case..
|
|
||||||
const u32 num_textures = GetActiveTexturesForLayout(m_current_pipeline_layout);
|
|
||||||
for (u32 i = 0; i < num_textures; i++)
|
|
||||||
{
|
|
||||||
if (m_current_textures[i])
|
|
||||||
m_current_textures[i]->TransitionToState(cmdlist, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
D3D12_RENDER_PASS_RENDER_TARGET_DESC rt_desc = {swap_chain_buf.second,
|
|
||||||
{D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR, {}},
|
|
||||||
{D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE, {}}};
|
|
||||||
GSVector4::store<false>(rt_desc.BeginningAccess.Clear.ClearValue.Color, GSVector4::rgba32(clear_color));
|
|
||||||
cmdlist->BeginRenderPass(1, &rt_desc, nullptr, D3D12_RENDER_PASS_FLAG_NONE);
|
|
||||||
|
|
||||||
std::memset(m_current_render_targets.data(), 0, sizeof(m_current_render_targets));
|
|
||||||
m_num_current_render_targets = 0;
|
|
||||||
m_dirty_flags =
|
|
||||||
(m_dirty_flags & ~DIRTY_FLAG_RT_UAVS) | ((IsUsingROVRootSignature()) ? DIRTY_FLAG_PIPELINE_LAYOUT : 0);
|
|
||||||
m_current_render_pass_flags = GPUPipeline::NoRenderPassFlags;
|
|
||||||
m_current_depth_target = nullptr;
|
|
||||||
m_in_render_pass = true;
|
|
||||||
s_stats.num_render_passes++;
|
|
||||||
|
|
||||||
// Clear pipeline, it's likely incompatible.
|
|
||||||
m_current_pipeline = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool D3D12Device::InRenderPass()
|
bool D3D12Device::InRenderPass()
|
||||||
{
|
{
|
||||||
return m_in_render_pass;
|
return m_in_render_pass;
|
||||||
|
@ -37,6 +37,8 @@ namespace D3D12MA {
|
|||||||
class Allocator;
|
class Allocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class D3D12SwapChain;
|
||||||
|
|
||||||
class D3D12Device final : public GPUDevice
|
class D3D12Device final : public GPUDevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -58,17 +60,16 @@ public:
|
|||||||
D3D12Device();
|
D3D12Device();
|
||||||
~D3D12Device() override;
|
~D3D12Device() override;
|
||||||
|
|
||||||
bool HasSurface() const override;
|
|
||||||
|
|
||||||
bool UpdateWindow() override;
|
|
||||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
|
||||||
|
|
||||||
void DestroySurface() override;
|
|
||||||
|
|
||||||
std::string GetDriverInfo() const override;
|
std::string GetDriverInfo() const override;
|
||||||
|
|
||||||
void ExecuteAndWaitForGPUIdle() override;
|
void FlushCommands() override;
|
||||||
|
void WaitForGPUIdle() override;
|
||||||
|
|
||||||
|
std::unique_ptr<GPUSwapChain> CreateSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||||
|
bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control,
|
||||||
|
Error* error) override;
|
||||||
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
||||||
GPUTexture::Type type, GPUTexture::Format format,
|
GPUTexture::Type type, GPUTexture::Format format,
|
||||||
const void* data = nullptr, u32 data_stride = 0) override;
|
const void* data = nullptr, u32 data_stride = 0) override;
|
||||||
@ -119,23 +120,22 @@ public:
|
|||||||
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
||||||
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
||||||
|
|
||||||
void SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle) override;
|
|
||||||
|
|
||||||
bool SetGPUTimingEnabled(bool enabled) override;
|
bool SetGPUTimingEnabled(bool enabled) override;
|
||||||
float GetAndResetAccumulatedGPUTime() override;
|
float GetAndResetAccumulatedGPUTime() override;
|
||||||
|
|
||||||
PresentResult BeginPresent(u32 clear_color) override;
|
PresentResult BeginPresent(GPUSwapChain* swap_chain, u32 clear_color) override;
|
||||||
void EndPresent(bool explicit_present, u64 present_time) override;
|
void EndPresent(GPUSwapChain* swap_chain, bool explicit_present, u64 present_time) override;
|
||||||
void SubmitPresent() override;
|
void SubmitPresent(GPUSwapChain* swap_chain) override;
|
||||||
|
|
||||||
// Global state accessors
|
// Global state accessors
|
||||||
ALWAYS_INLINE static D3D12Device& GetInstance() { return *static_cast<D3D12Device*>(g_gpu_device.get()); }
|
ALWAYS_INLINE static D3D12Device& GetInstance() { return *static_cast<D3D12Device*>(g_gpu_device.get()); }
|
||||||
ALWAYS_INLINE IDXGIAdapter1* GetAdapter() const { return m_adapter.Get(); }
|
ALWAYS_INLINE IDXGIAdapter1* GetAdapter() const { return m_adapter.Get(); }
|
||||||
ALWAYS_INLINE ID3D12Device1* GetDevice() const { return m_device.Get(); }
|
ALWAYS_INLINE ID3D12Device1* GetDevice() const { return m_device.Get(); }
|
||||||
ALWAYS_INLINE ID3D12CommandQueue* GetCommandQueue() const { return m_command_queue.Get(); }
|
ALWAYS_INLINE ID3D12CommandQueue* GetCommandQueue() const { return m_command_queue.Get(); }
|
||||||
|
ALWAYS_INLINE IDXGIFactory5* GetDXGIFactory() { return m_dxgi_factory.Get(); }
|
||||||
ALWAYS_INLINE D3D12MA::Allocator* GetAllocator() const { return m_allocator.Get(); }
|
ALWAYS_INLINE D3D12MA::Allocator* GetAllocator() const { return m_allocator.Get(); }
|
||||||
|
|
||||||
void WaitForGPUIdle();
|
void WaitForAllFences();
|
||||||
|
|
||||||
// Descriptor manager access.
|
// Descriptor manager access.
|
||||||
D3D12DescriptorHeapManager& GetDescriptorHeapManager() { return m_descriptor_heap_manager; }
|
D3D12DescriptorHeapManager& GetDescriptorHeapManager() { return m_descriptor_heap_manager; }
|
||||||
@ -173,6 +173,12 @@ public:
|
|||||||
// Also invokes callbacks for completion.
|
// Also invokes callbacks for completion.
|
||||||
void WaitForFence(u64 fence_counter);
|
void WaitForFence(u64 fence_counter);
|
||||||
|
|
||||||
|
// Ends a render pass if we're currently in one.
|
||||||
|
// When Bind() is next called, the pass will be restarted.
|
||||||
|
void BeginRenderPass();
|
||||||
|
void EndRenderPass();
|
||||||
|
bool InRenderPass();
|
||||||
|
|
||||||
/// Ends any render pass, executes the command buffer, and invalidates cached state.
|
/// Ends any render pass, executes the command buffer, and invalidates cached state.
|
||||||
void SubmitCommandList(bool wait_for_completion);
|
void SubmitCommandList(bool wait_for_completion);
|
||||||
void SubmitCommandList(bool wait_for_completion, const std::string_view reason);
|
void SubmitCommandList(bool wait_for_completion, const std::string_view reason);
|
||||||
@ -183,8 +189,10 @@ public:
|
|||||||
void UnbindTextureBuffer(D3D12TextureBuffer* buf);
|
void UnbindTextureBuffer(D3D12TextureBuffer* buf);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool CreateDevice(std::string_view adapter, std::optional<bool> exclusive_fullscreen_control,
|
bool CreateDeviceAndMainSwapChain(std::string_view adapter, FeatureMask disabled_features, const WindowInfo& wi,
|
||||||
FeatureMask disabled_features, Error* error) override;
|
GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control, Error* error) override;
|
||||||
void DestroyDevice() override;
|
void DestroyDevice() override;
|
||||||
|
|
||||||
bool ReadPipelineCache(DynamicHeapArray<u8> data, Error* error) override;
|
bool ReadPipelineCache(DynamicHeapArray<u8> data, Error* error) override;
|
||||||
@ -232,12 +240,6 @@ private:
|
|||||||
void GetPipelineCacheHeader(PIPELINE_CACHE_HEADER* hdr);
|
void GetPipelineCacheHeader(PIPELINE_CACHE_HEADER* hdr);
|
||||||
void SetFeatures(D3D_FEATURE_LEVEL feature_level, FeatureMask disabled_features);
|
void SetFeatures(D3D_FEATURE_LEVEL feature_level, FeatureMask disabled_features);
|
||||||
|
|
||||||
u32 GetSwapChainBufferCount() const;
|
|
||||||
bool CreateSwapChain(Error* error);
|
|
||||||
bool CreateSwapChainRTV(Error* error);
|
|
||||||
void DestroySwapChainRTVs();
|
|
||||||
void DestroySwapChain();
|
|
||||||
|
|
||||||
bool CreateCommandLists(Error* error);
|
bool CreateCommandLists(Error* error);
|
||||||
void DestroyCommandLists();
|
void DestroyCommandLists();
|
||||||
bool CreateRootSignatures(Error* error);
|
bool CreateRootSignatures(Error* error);
|
||||||
@ -252,7 +254,7 @@ private:
|
|||||||
void DestroySamplers();
|
void DestroySamplers();
|
||||||
void DestroyDeferredObjects(u64 fence_value);
|
void DestroyDeferredObjects(u64 fence_value);
|
||||||
|
|
||||||
void RenderBlankFrame();
|
void RenderBlankFrame(D3D12SwapChain* swap_chain);
|
||||||
void MoveToNextCommandList();
|
void MoveToNextCommandList();
|
||||||
|
|
||||||
bool CreateSRVDescriptor(ID3D12Resource* resource, u32 layers, u32 levels, u32 samples, DXGI_FORMAT format,
|
bool CreateSRVDescriptor(ID3D12Resource* resource, u32 layers, u32 levels, u32 samples, DXGI_FORMAT format,
|
||||||
@ -280,13 +282,6 @@ private:
|
|||||||
bool UpdateParametersForLayout(u32 dirty);
|
bool UpdateParametersForLayout(u32 dirty);
|
||||||
bool UpdateRootParameters(u32 dirty);
|
bool UpdateRootParameters(u32 dirty);
|
||||||
|
|
||||||
// Ends a render pass if we're currently in one.
|
|
||||||
// When Bind() is next called, the pass will be restarted.
|
|
||||||
void BeginRenderPass();
|
|
||||||
void BeginSwapChainRenderPass(u32 clear_color);
|
|
||||||
void EndRenderPass();
|
|
||||||
bool InRenderPass();
|
|
||||||
|
|
||||||
ComPtr<IDXGIAdapter1> m_adapter;
|
ComPtr<IDXGIAdapter1> m_adapter;
|
||||||
ComPtr<ID3D12Device1> m_device;
|
ComPtr<ID3D12Device1> m_device;
|
||||||
ComPtr<ID3D12CommandQueue> m_command_queue;
|
ComPtr<ID3D12CommandQueue> m_command_queue;
|
||||||
@ -299,15 +294,9 @@ private:
|
|||||||
|
|
||||||
std::array<CommandList, NUM_COMMAND_LISTS> m_command_lists;
|
std::array<CommandList, NUM_COMMAND_LISTS> m_command_lists;
|
||||||
u32 m_current_command_list = NUM_COMMAND_LISTS - 1;
|
u32 m_current_command_list = NUM_COMMAND_LISTS - 1;
|
||||||
|
bool m_device_was_lost = false;
|
||||||
|
|
||||||
ComPtr<IDXGIFactory5> m_dxgi_factory;
|
ComPtr<IDXGIFactory5> m_dxgi_factory;
|
||||||
ComPtr<IDXGISwapChain1> m_swap_chain;
|
|
||||||
std::vector<std::pair<ComPtr<ID3D12Resource>, D3D12DescriptorHandle>> m_swap_chain_buffers;
|
|
||||||
u32 m_current_swap_chain_buffer = 0;
|
|
||||||
bool m_allow_tearing_supported = false;
|
|
||||||
bool m_using_allow_tearing = false;
|
|
||||||
bool m_is_exclusive_fullscreen = false;
|
|
||||||
bool m_device_was_lost = false;
|
|
||||||
|
|
||||||
D3D12DescriptorHeapManager m_descriptor_heap_manager;
|
D3D12DescriptorHeapManager m_descriptor_heap_manager;
|
||||||
D3D12DescriptorHeapManager m_rtv_heap_manager;
|
D3D12DescriptorHeapManager m_rtv_heap_manager;
|
||||||
@ -358,4 +347,52 @@ private:
|
|||||||
D3D12TextureBuffer* m_current_texture_buffer = nullptr;
|
D3D12TextureBuffer* m_current_texture_buffer = nullptr;
|
||||||
GSVector4i m_current_viewport = GSVector4i::cxpr(0, 0, 1, 1);
|
GSVector4i m_current_viewport = GSVector4i::cxpr(0, 0, 1, 1);
|
||||||
GSVector4i m_current_scissor = {};
|
GSVector4i m_current_scissor = {};
|
||||||
|
|
||||||
|
D3D12SwapChain* m_current_swap_chain = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class D3D12SwapChain : public GPUSwapChain
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template<typename T>
|
||||||
|
using ComPtr = Microsoft::WRL::ComPtr<T>;
|
||||||
|
|
||||||
|
friend D3D12Device;
|
||||||
|
|
||||||
|
using BufferPair = std::pair<ComPtr<ID3D12Resource>, D3D12DescriptorHandle>;
|
||||||
|
|
||||||
|
D3D12SwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
|
const GPUDevice::ExclusiveFullscreenMode* fullscreen_mode);
|
||||||
|
~D3D12SwapChain() override;
|
||||||
|
|
||||||
|
ALWAYS_INLINE IDXGISwapChain1* GetSwapChain() const { return m_swap_chain.Get(); }
|
||||||
|
ALWAYS_INLINE const BufferPair& GetCurrentBuffer() const { return m_swap_chain_buffers[m_current_swap_chain_buffer]; }
|
||||||
|
ALWAYS_INLINE bool IsUsingAllowTearing() const { return m_using_allow_tearing; }
|
||||||
|
ALWAYS_INLINE bool IsExclusiveFullscreen() const { return m_fullscreen_mode.has_value(); }
|
||||||
|
|
||||||
|
void AdvanceBuffer()
|
||||||
|
{
|
||||||
|
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
|
||||||
|
}
|
||||||
|
bool ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error) override;
|
||||||
|
bool SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static u32 GetNewBufferCount(GPUVSyncMode vsync_mode);
|
||||||
|
|
||||||
|
bool InitializeExclusiveFullscreenMode(const GPUDevice::ExclusiveFullscreenMode* mode);
|
||||||
|
|
||||||
|
bool CreateSwapChain(D3D12Device& dev, Error* error);
|
||||||
|
bool CreateRTV(D3D12Device& dev, Error* error);
|
||||||
|
|
||||||
|
void DestroySwapChain();
|
||||||
|
void DestroyRTVs();
|
||||||
|
|
||||||
|
ComPtr<IDXGISwapChain1> m_swap_chain;
|
||||||
|
std::vector<BufferPair> m_swap_chain_buffers;
|
||||||
|
u32 m_current_swap_chain_buffer = 0;
|
||||||
|
bool m_using_allow_tearing = false;
|
||||||
|
|
||||||
|
ComPtr<IDXGIOutput> m_fullscreen_output;
|
||||||
|
std::optional<DXGI_MODE_DESC> m_fullscreen_mode;
|
||||||
};
|
};
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
#include <d3dcompiler.h>
|
#include <d3dcompiler.h>
|
||||||
|
|
||||||
LOG_CHANNEL(D3D12Device);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
D3D12Shader::D3D12Shader(GPUShaderStage stage, Bytecode bytecode) : GPUShader(stage), m_bytecode(std::move(bytecode))
|
D3D12Shader::D3D12Shader(GPUShaderStage stage, Bytecode bytecode) : GPUShader(stage), m_bytecode(std::move(bytecode))
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
LOG_CHANNEL(D3D12StreamBuffer);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
D3D12StreamBuffer::D3D12StreamBuffer() = default;
|
D3D12StreamBuffer::D3D12StreamBuffer() = default;
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
#include "D3D12MemAlloc.h"
|
#include "D3D12MemAlloc.h"
|
||||||
|
|
||||||
LOG_CHANNEL(D3D12Device);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
D3D12Texture::D3D12Texture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format,
|
D3D12Texture::D3D12Texture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format,
|
||||||
DXGI_FORMAT dxgi_format, ComPtr<ID3D12Resource> resource,
|
DXGI_FORMAT dxgi_format, ComPtr<ID3D12Resource> resource,
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#include <dxcapi.h>
|
#include <dxcapi.h>
|
||||||
#include <dxgi1_5.h>
|
#include <dxgi1_5.h>
|
||||||
|
|
||||||
LOG_CHANNEL(D3DCommon);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
namespace D3DCommon {
|
namespace D3DCommon {
|
||||||
namespace {
|
namespace {
|
||||||
@ -123,6 +123,14 @@ Microsoft::WRL::ComPtr<IDXGIFactory5> D3DCommon::CreateFactory(bool debug, Error
|
|||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool D3DCommon::SupportsAllowTearing(IDXGIFactory5* factory)
|
||||||
|
{
|
||||||
|
BOOL allow_tearing_supported = false;
|
||||||
|
HRESULT hr = factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported,
|
||||||
|
sizeof(allow_tearing_supported));
|
||||||
|
return (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
static std::string FixupDuplicateAdapterNames(const GPUDevice::AdapterInfoList& adapter_names, std::string adapter_name)
|
static std::string FixupDuplicateAdapterNames(const GPUDevice::AdapterInfoList& adapter_names, std::string adapter_name)
|
||||||
{
|
{
|
||||||
if (std::any_of(adapter_names.begin(), adapter_names.end(),
|
if (std::any_of(adapter_names.begin(), adapter_names.end(),
|
||||||
@ -183,9 +191,11 @@ GPUDevice::AdapterInfoList D3DCommon::GetAdapterInfoList()
|
|||||||
{
|
{
|
||||||
for (const DXGI_MODE_DESC& mode : dmodes)
|
for (const DXGI_MODE_DESC& mode : dmodes)
|
||||||
{
|
{
|
||||||
ai.fullscreen_modes.push_back(GPUDevice::GetFullscreenModeString(
|
ai.fullscreen_modes.push_back(
|
||||||
mode.Width, mode.Height,
|
GPUDevice::ExclusiveFullscreenMode{.width = mode.Width,
|
||||||
static_cast<float>(mode.RefreshRate.Numerator) / static_cast<float>(mode.RefreshRate.Denominator)));
|
.height = mode.Height,
|
||||||
|
.refresh_rate = static_cast<float>(mode.RefreshRate.Numerator) /
|
||||||
|
static_cast<float>(mode.RefreshRate.Denominator)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -211,10 +221,13 @@ GPUDevice::AdapterInfoList D3DCommon::GetAdapterInfoList()
|
|||||||
return adapters;
|
return adapters;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3DCommon::GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width,
|
std::optional<DXGI_MODE_DESC>
|
||||||
u32 height, float refresh_rate, DXGI_FORMAT format,
|
D3DCommon::GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect,
|
||||||
DXGI_MODE_DESC* fullscreen_mode, IDXGIOutput** output)
|
const GPUDevice::ExclusiveFullscreenMode* requested_fullscreen_mode,
|
||||||
|
DXGI_FORMAT format, IDXGIOutput** output)
|
||||||
{
|
{
|
||||||
|
std::optional<DXGI_MODE_DESC> ret;
|
||||||
|
|
||||||
// We need to find which monitor the window is located on.
|
// We need to find which monitor the window is located on.
|
||||||
const GSVector4i client_rc_vec(window_rect.left, window_rect.top, window_rect.right, window_rect.bottom);
|
const GSVector4i client_rc_vec(window_rect.left, window_rect.top, window_rect.right, window_rect.bottom);
|
||||||
|
|
||||||
@ -260,7 +273,7 @@ bool D3DCommon::GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory,
|
|||||||
if (!first_output)
|
if (!first_output)
|
||||||
{
|
{
|
||||||
ERROR_LOG("No DXGI output found. Can't use exclusive fullscreen.");
|
ERROR_LOG("No DXGI output found. Can't use exclusive fullscreen.");
|
||||||
return false;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
WARNING_LOG("No DXGI output found for window, using first.");
|
WARNING_LOG("No DXGI output found for window, using first.");
|
||||||
@ -268,22 +281,25 @@ bool D3DCommon::GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory,
|
|||||||
}
|
}
|
||||||
|
|
||||||
DXGI_MODE_DESC request_mode = {};
|
DXGI_MODE_DESC request_mode = {};
|
||||||
request_mode.Width = width;
|
request_mode.Width = requested_fullscreen_mode->width;
|
||||||
request_mode.Height = height;
|
request_mode.Height = requested_fullscreen_mode->height;
|
||||||
request_mode.Format = format;
|
request_mode.Format = format;
|
||||||
request_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(refresh_rate * 1000.0f));
|
request_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(requested_fullscreen_mode->refresh_rate * 1000.0f));
|
||||||
request_mode.RefreshRate.Denominator = 1000u;
|
request_mode.RefreshRate.Denominator = 1000u;
|
||||||
|
|
||||||
if (FAILED(hr = intersecting_output->FindClosestMatchingMode(&request_mode, fullscreen_mode, nullptr)) ||
|
ret = DXGI_MODE_DESC();
|
||||||
|
|
||||||
|
if (FAILED(hr = intersecting_output->FindClosestMatchingMode(&request_mode, &ret.value(), nullptr)) ||
|
||||||
request_mode.Format != format)
|
request_mode.Format != format)
|
||||||
{
|
{
|
||||||
ERROR_LOG("Failed to find closest matching mode, hr={:08X}", static_cast<unsigned>(hr));
|
ERROR_LOG("Failed to find closest matching mode, hr={:08X}", static_cast<unsigned>(hr));
|
||||||
return false;
|
ret.reset();
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
*output = intersecting_output.Get();
|
*output = intersecting_output.Get();
|
||||||
intersecting_output->AddRef();
|
intersecting_output->AddRef();
|
||||||
return true;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
Microsoft::WRL::ComPtr<IDXGIAdapter1> D3DCommon::GetAdapterByName(IDXGIFactory5* factory, std::string_view name)
|
Microsoft::WRL::ComPtr<IDXGIAdapter1> D3DCommon::GetAdapterByName(IDXGIFactory5* factory, std::string_view name)
|
||||||
|
@ -35,14 +35,16 @@ D3D_FEATURE_LEVEL GetDeviceMaxFeatureLevel(IDXGIAdapter1* adapter);
|
|||||||
|
|
||||||
// create a dxgi factory
|
// create a dxgi factory
|
||||||
Microsoft::WRL::ComPtr<IDXGIFactory5> CreateFactory(bool debug, Error* error);
|
Microsoft::WRL::ComPtr<IDXGIFactory5> CreateFactory(bool debug, Error* error);
|
||||||
|
bool SupportsAllowTearing(IDXGIFactory5* factory);
|
||||||
|
|
||||||
// returns a list of all adapter names
|
// returns a list of all adapter names
|
||||||
GPUDevice::AdapterInfoList GetAdapterInfoList();
|
GPUDevice::AdapterInfoList GetAdapterInfoList();
|
||||||
|
|
||||||
// returns the fullscreen mode to use for the specified dimensions
|
// returns the fullscreen mode to use for the specified dimensions
|
||||||
bool GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width, u32 height,
|
std::optional<DXGI_MODE_DESC>
|
||||||
float refresh_rate, DXGI_FORMAT format, DXGI_MODE_DESC* fullscreen_mode,
|
GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect,
|
||||||
IDXGIOutput** output);
|
const GPUDevice::ExclusiveFullscreenMode* requested_fullscreen_mode,
|
||||||
|
DXGI_FORMAT format, IDXGIOutput** output);
|
||||||
|
|
||||||
// get an adapter based on name
|
// get an adapter based on name
|
||||||
Microsoft::WRL::ComPtr<IDXGIAdapter1> GetAdapterByName(IDXGIFactory5* factory, std::string_view name);
|
Microsoft::WRL::ComPtr<IDXGIAdapter1> GetAdapterByName(IDXGIFactory5* factory, std::string_view name);
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
#include "gpu_device.h"
|
#include "gpu_device.h"
|
||||||
#include "compress_helpers.h"
|
#include "compress_helpers.h"
|
||||||
#include "core/host.h" // TODO: Remove, needed for getting fullscreen mode.
|
|
||||||
#include "core/settings.h" // TODO: Remove, needed for dump directory.
|
|
||||||
#include "gpu_framebuffer_manager.h"
|
#include "gpu_framebuffer_manager.h"
|
||||||
#include "shadergen.h"
|
#include "shadergen.h"
|
||||||
|
|
||||||
@ -44,6 +42,7 @@ LOG_CHANNEL(GPUDevice);
|
|||||||
|
|
||||||
std::unique_ptr<GPUDevice> g_gpu_device;
|
std::unique_ptr<GPUDevice> g_gpu_device;
|
||||||
|
|
||||||
|
static std::string s_shader_dump_path;
|
||||||
static std::string s_pipeline_cache_path;
|
static std::string s_pipeline_cache_path;
|
||||||
static size_t s_pipeline_cache_size;
|
static size_t s_pipeline_cache_size;
|
||||||
static std::array<u8, SHA1Digest::DIGEST_SIZE> s_pipeline_cache_hash;
|
static std::array<u8, SHA1Digest::DIGEST_SIZE> s_pipeline_cache_hash;
|
||||||
@ -226,6 +225,49 @@ size_t GPUFramebufferManagerBase::KeyHash::operator()(const Key& key) const
|
|||||||
return XXH32(&key, sizeof(key), 0x1337);
|
return XXH32(&key, sizeof(key), 0x1337);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GPUSwapChain::GPUSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode, bool allow_present_throttle)
|
||||||
|
: m_window_info(wi), m_vsync_mode(vsync_mode), m_allow_present_throttle(allow_present_throttle)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GPUSwapChain::~GPUSwapChain() = default;
|
||||||
|
|
||||||
|
bool GPUSwapChain::ShouldSkipPresentingFrame()
|
||||||
|
{
|
||||||
|
// Only needed with FIFO. But since we're so fast, we allow it always.
|
||||||
|
if (!m_allow_present_throttle)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const float throttle_rate = (m_window_info.surface_refresh_rate > 0.0f) ? m_window_info.surface_refresh_rate : 60.0f;
|
||||||
|
const float throttle_period = 1.0f / throttle_rate;
|
||||||
|
|
||||||
|
const u64 now = Common::Timer::GetCurrentValue();
|
||||||
|
const double diff = Common::Timer::ConvertValueToSeconds(now - m_last_frame_displayed_time);
|
||||||
|
if (diff < throttle_period)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
m_last_frame_displayed_time = now;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPUSwapChain::ThrottlePresentation()
|
||||||
|
{
|
||||||
|
const float throttle_rate = (m_window_info.surface_refresh_rate > 0.0f) ? m_window_info.surface_refresh_rate : 60.0f;
|
||||||
|
|
||||||
|
const u64 sleep_period = Common::Timer::ConvertNanosecondsToValue(1e+9f / static_cast<double>(throttle_rate));
|
||||||
|
const u64 current_ts = Common::Timer::GetCurrentValue();
|
||||||
|
|
||||||
|
// Allow it to fall behind/run ahead up to 2*period. Sleep isn't that precise, plus we need to
|
||||||
|
// allow time for the actual rendering.
|
||||||
|
const u64 max_variance = sleep_period * 2;
|
||||||
|
if (static_cast<u64>(std::abs(static_cast<s64>(current_ts - m_last_frame_displayed_time))) > max_variance)
|
||||||
|
m_last_frame_displayed_time = current_ts + sleep_period;
|
||||||
|
else
|
||||||
|
m_last_frame_displayed_time += sleep_period;
|
||||||
|
|
||||||
|
Common::Timer::SleepUntil(m_last_frame_displayed_time, false);
|
||||||
|
}
|
||||||
|
|
||||||
GPUDevice::GPUDevice()
|
GPUDevice::GPUDevice()
|
||||||
{
|
{
|
||||||
ResetStatistics();
|
ResetStatistics();
|
||||||
@ -346,21 +388,18 @@ GPUDevice::AdapterInfoList GPUDevice::GetAdapterListForAPI(RenderAPI api)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPUDevice::Create(std::string_view adapter, std::string_view shader_cache_path, u32 shader_cache_version,
|
bool GPUDevice::Create(std::string_view adapter, FeatureMask disabled_features, std::string_view shader_dump_path,
|
||||||
bool debug_device, GPUVSyncMode vsync, bool allow_present_throttle,
|
std::string_view shader_cache_path, u32 shader_cache_version, bool debug_device,
|
||||||
std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, Error* error)
|
const WindowInfo& wi, GPUVSyncMode vsync, bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control, Error* error)
|
||||||
{
|
{
|
||||||
m_vsync_mode = vsync;
|
|
||||||
m_allow_present_throttle = allow_present_throttle;
|
|
||||||
m_debug_device = debug_device;
|
m_debug_device = debug_device;
|
||||||
|
s_shader_dump_path = shader_dump_path;
|
||||||
|
|
||||||
if (!AcquireWindow(true))
|
INFO_LOG("Main render window is {}x{}.", wi.surface_width, wi.surface_height);
|
||||||
{
|
if (!CreateDeviceAndMainSwapChain(adapter, disabled_features, wi, vsync, allow_present_throttle,
|
||||||
Error::SetStringView(error, "Failed to acquire window from host.");
|
exclusive_fullscreen_mode, exclusive_fullscreen_control, error))
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CreateDevice(adapter, exclusive_fullscreen_control, disabled_features, error))
|
|
||||||
{
|
{
|
||||||
if (error && !error->IsValid())
|
if (error && !error->IsValid())
|
||||||
error->SetStringView("Failed to create device.");
|
error->SetStringView("Failed to create device.");
|
||||||
@ -383,14 +422,30 @@ bool GPUDevice::Create(std::string_view adapter, std::string_view shader_cache_p
|
|||||||
|
|
||||||
void GPUDevice::Destroy()
|
void GPUDevice::Destroy()
|
||||||
{
|
{
|
||||||
|
s_shader_dump_path = {};
|
||||||
|
|
||||||
PurgeTexturePool();
|
PurgeTexturePool();
|
||||||
if (HasSurface())
|
|
||||||
DestroySurface();
|
|
||||||
DestroyResources();
|
DestroyResources();
|
||||||
CloseShaderCache();
|
CloseShaderCache();
|
||||||
DestroyDevice();
|
DestroyDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GPUDevice::RecreateMainSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control, Error* error)
|
||||||
|
{
|
||||||
|
|
||||||
|
m_main_swap_chain.reset();
|
||||||
|
m_main_swap_chain = CreateSwapChain(wi, vsync_mode, allow_present_throttle, exclusive_fullscreen_mode,
|
||||||
|
exclusive_fullscreen_control, error);
|
||||||
|
return static_cast<bool>(m_main_swap_chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPUDevice::DestroyMainSwapChain()
|
||||||
|
{
|
||||||
|
m_main_swap_chain.reset();
|
||||||
|
}
|
||||||
|
|
||||||
bool GPUDevice::SupportsExclusiveFullscreen() const
|
bool GPUDevice::SupportsExclusiveFullscreen() const
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -533,17 +588,6 @@ bool GPUDevice::GetPipelineCacheData(DynamicHeapArray<u8>* data, Error* error)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPUDevice::AcquireWindow(bool recreate_window)
|
|
||||||
{
|
|
||||||
std::optional<WindowInfo> wi = Host::AcquireRenderWindow(recreate_window);
|
|
||||||
if (!wi.has_value())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
INFO_LOG("Render window is {}x{}.", wi->surface_width, wi->surface_height);
|
|
||||||
m_window_info = wi.value();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GPUDevice::CreateResources(Error* error)
|
bool GPUDevice::CreateResources(Error* error)
|
||||||
{
|
{
|
||||||
if (!(m_nearest_sampler = CreateSampler(GPUSampler::GetNearestConfig())) ||
|
if (!(m_nearest_sampler = CreateSampler(GPUSampler::GetNearestConfig())) ||
|
||||||
@ -587,7 +631,7 @@ bool GPUDevice::CreateResources(Error* error)
|
|||||||
plconfig.depth = GPUPipeline::DepthState::GetNoTestsState();
|
plconfig.depth = GPUPipeline::DepthState::GetNoTestsState();
|
||||||
plconfig.blend = GPUPipeline::BlendState::GetAlphaBlendingState();
|
plconfig.blend = GPUPipeline::BlendState::GetAlphaBlendingState();
|
||||||
plconfig.blend.write_mask = 0x7;
|
plconfig.blend.write_mask = 0x7;
|
||||||
plconfig.SetTargetFormats(HasSurface() ? m_window_info.surface_format : GPUTexture::Format::RGBA8);
|
plconfig.SetTargetFormats(m_main_swap_chain ? m_main_swap_chain->GetFormat() : GPUTexture::Format::RGBA8);
|
||||||
plconfig.samples = 1;
|
plconfig.samples = 1;
|
||||||
plconfig.per_sample_shading = false;
|
plconfig.per_sample_shading = false;
|
||||||
plconfig.render_pass_flags = GPUPipeline::NoRenderPassFlags;
|
plconfig.render_pass_flags = GPUPipeline::NoRenderPassFlags;
|
||||||
@ -619,23 +663,23 @@ void GPUDevice::DestroyResources()
|
|||||||
m_shader_cache.Close();
|
m_shader_cache.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUDevice::RenderImGui()
|
void GPUDevice::RenderImGui(GPUSwapChain* swap_chain)
|
||||||
{
|
{
|
||||||
GL_SCOPE("RenderImGui");
|
GL_SCOPE("RenderImGui");
|
||||||
|
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
|
|
||||||
const ImDrawData* draw_data = ImGui::GetDrawData();
|
const ImDrawData* draw_data = ImGui::GetDrawData();
|
||||||
if (draw_data->CmdListsCount == 0)
|
if (draw_data->CmdListsCount == 0 || !swap_chain)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SetPipeline(m_imgui_pipeline.get());
|
SetPipeline(m_imgui_pipeline.get());
|
||||||
SetViewportAndScissor(0, 0, m_window_info.surface_width, m_window_info.surface_height);
|
SetViewportAndScissor(0, 0, swap_chain->GetWidth(), swap_chain->GetHeight());
|
||||||
|
|
||||||
const float L = 0.0f;
|
const float L = 0.0f;
|
||||||
const float R = static_cast<float>(m_window_info.surface_width);
|
const float R = static_cast<float>(swap_chain->GetWidth());
|
||||||
const float T = 0.0f;
|
const float T = 0.0f;
|
||||||
const float B = static_cast<float>(m_window_info.surface_height);
|
const float B = static_cast<float>(swap_chain->GetHeight());
|
||||||
const float ortho_projection[4][4] = {
|
const float ortho_projection[4][4] = {
|
||||||
{2.0f / (R - L), 0.0f, 0.0f, 0.0f},
|
{2.0f / (R - L), 0.0f, 0.0f, 0.0f},
|
||||||
{0.0f, 2.0f / (T - B), 0.0f, 0.0f},
|
{0.0f, 2.0f / (T - B), 0.0f, 0.0f},
|
||||||
@ -666,8 +710,7 @@ void GPUDevice::RenderImGui()
|
|||||||
if (flip)
|
if (flip)
|
||||||
{
|
{
|
||||||
const s32 height = static_cast<s32>(pcmd->ClipRect.w - pcmd->ClipRect.y);
|
const s32 height = static_cast<s32>(pcmd->ClipRect.w - pcmd->ClipRect.y);
|
||||||
const s32 flipped_y =
|
const s32 flipped_y = static_cast<s32>(swap_chain->GetHeight()) - static_cast<s32>(pcmd->ClipRect.y) - height;
|
||||||
static_cast<s32>(m_window_info.surface_height) - static_cast<s32>(pcmd->ClipRect.y) - height;
|
|
||||||
SetScissor(static_cast<s32>(pcmd->ClipRect.x), flipped_y, static_cast<s32>(pcmd->ClipRect.z - pcmd->ClipRect.x),
|
SetScissor(static_cast<s32>(pcmd->ClipRect.x), flipped_y, static_cast<s32>(pcmd->ClipRect.z - pcmd->ClipRect.x),
|
||||||
height);
|
height);
|
||||||
}
|
}
|
||||||
@ -789,69 +832,59 @@ std::unique_ptr<GPUShader> GPUDevice::CreateShader(GPUShaderStage stage, GPUShad
|
|||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPUDevice::GetRequestedExclusiveFullscreenMode(u32* width, u32* height, float* refresh_rate)
|
std::optional<GPUDevice::ExclusiveFullscreenMode> GPUDevice::ExclusiveFullscreenMode::Parse(std::string_view str)
|
||||||
{
|
{
|
||||||
const std::string mode = Host::GetBaseStringSettingValue("GPU", "FullscreenMode", "");
|
std::optional<ExclusiveFullscreenMode> ret;
|
||||||
if (!mode.empty())
|
std::string_view::size_type sep1 = str.find('x');
|
||||||
|
if (sep1 != std::string_view::npos)
|
||||||
{
|
{
|
||||||
const std::string_view mode_view = mode;
|
std::optional<u32> owidth = StringUtil::FromChars<u32>(str.substr(0, sep1));
|
||||||
std::string_view::size_type sep1 = mode.find('x');
|
sep1++;
|
||||||
if (sep1 != std::string_view::npos)
|
|
||||||
{
|
while (sep1 < str.length() && std::isspace(str[sep1]))
|
||||||
std::optional<u32> owidth = StringUtil::FromChars<u32>(mode_view.substr(0, sep1));
|
|
||||||
sep1++;
|
sep1++;
|
||||||
|
|
||||||
while (sep1 < mode.length() && std::isspace(mode[sep1]))
|
if (owidth.has_value() && sep1 < str.length())
|
||||||
sep1++;
|
{
|
||||||
|
std::string_view::size_type sep2 = str.find('@', sep1);
|
||||||
if (owidth.has_value() && sep1 < mode.length())
|
if (sep2 != std::string_view::npos)
|
||||||
{
|
{
|
||||||
std::string_view::size_type sep2 = mode.find('@', sep1);
|
std::optional<u32> oheight = StringUtil::FromChars<u32>(str.substr(sep1, sep2 - sep1));
|
||||||
if (sep2 != std::string_view::npos)
|
sep2++;
|
||||||
{
|
|
||||||
std::optional<u32> oheight = StringUtil::FromChars<u32>(mode_view.substr(sep1, sep2 - sep1));
|
while (sep2 < str.length() && std::isspace(str[sep2]))
|
||||||
sep2++;
|
sep2++;
|
||||||
|
|
||||||
while (sep2 < mode.length() && std::isspace(mode[sep2]))
|
if (oheight.has_value() && sep2 < str.length())
|
||||||
sep2++;
|
{
|
||||||
|
std::optional<float> orefresh_rate = StringUtil::FromChars<float>(str.substr(sep2));
|
||||||
if (oheight.has_value() && sep2 < mode.length())
|
if (orefresh_rate.has_value())
|
||||||
{
|
{
|
||||||
std::optional<float> orefresh_rate = StringUtil::FromChars<float>(mode_view.substr(sep2));
|
ret = ExclusiveFullscreenMode{
|
||||||
if (orefresh_rate.has_value())
|
.width = owidth.value(), .height = oheight.value(), .refresh_rate = orefresh_rate.value()};
|
||||||
{
|
|
||||||
*width = owidth.value();
|
|
||||||
*height = oheight.value();
|
|
||||||
*refresh_rate = orefresh_rate.value();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*width = 0;
|
return ret;
|
||||||
*height = 0;
|
|
||||||
*refresh_rate = 0;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GPUDevice::GetFullscreenModeString(u32 width, u32 height, float refresh_rate)
|
TinyString GPUDevice::ExclusiveFullscreenMode::ToString() const
|
||||||
{
|
{
|
||||||
return fmt::format("{} x {} @ {} hz", width, height, refresh_rate);
|
return TinyString::from_format("{} x {} @ {} hz", width, height, refresh_rate);
|
||||||
}
|
|
||||||
|
|
||||||
std::string GPUDevice::GetShaderDumpPath(std::string_view name)
|
|
||||||
{
|
|
||||||
return Path::Combine(EmuFolders::Dumps, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUDevice::DumpBadShader(std::string_view code, std::string_view errors)
|
void GPUDevice::DumpBadShader(std::string_view code, std::string_view errors)
|
||||||
{
|
{
|
||||||
static u32 next_bad_shader_id = 0;
|
static u32 next_bad_shader_id = 0;
|
||||||
|
|
||||||
const std::string filename = GetShaderDumpPath(fmt::format("bad_shader_{}.txt", ++next_bad_shader_id));
|
if (s_shader_dump_path.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::string filename =
|
||||||
|
Path::Combine(s_shader_dump_path, TinyString::from_format("bad_shader_{}.txt", ++next_bad_shader_id));
|
||||||
auto fp = FileSystem::OpenManagedCFile(filename.c_str(), "wb");
|
auto fp = FileSystem::OpenManagedCFile(filename.c_str(), "wb");
|
||||||
if (fp)
|
if (fp)
|
||||||
{
|
{
|
||||||
@ -1124,42 +1157,6 @@ bool GPUDevice::ResizeTexture(std::unique_ptr<GPUTexture>* tex, u32 new_width, u
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPUDevice::ShouldSkipPresentingFrame()
|
|
||||||
{
|
|
||||||
// Only needed with FIFO. But since we're so fast, we allow it always.
|
|
||||||
if (!m_allow_present_throttle)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const float throttle_rate = (m_window_info.surface_refresh_rate > 0.0f) ? m_window_info.surface_refresh_rate : 60.0f;
|
|
||||||
const float throttle_period = 1.0f / throttle_rate;
|
|
||||||
|
|
||||||
const u64 now = Common::Timer::GetCurrentValue();
|
|
||||||
const double diff = Common::Timer::ConvertValueToSeconds(now - m_last_frame_displayed_time);
|
|
||||||
if (diff < throttle_period)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
m_last_frame_displayed_time = now;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUDevice::ThrottlePresentation()
|
|
||||||
{
|
|
||||||
const float throttle_rate = (m_window_info.surface_refresh_rate > 0.0f) ? m_window_info.surface_refresh_rate : 60.0f;
|
|
||||||
|
|
||||||
const u64 sleep_period = Common::Timer::ConvertNanosecondsToValue(1e+9f / static_cast<double>(throttle_rate));
|
|
||||||
const u64 current_ts = Common::Timer::GetCurrentValue();
|
|
||||||
|
|
||||||
// Allow it to fall behind/run ahead up to 2*period. Sleep isn't that precise, plus we need to
|
|
||||||
// allow time for the actual rendering.
|
|
||||||
const u64 max_variance = sleep_period * 2;
|
|
||||||
if (static_cast<u64>(std::abs(static_cast<s64>(current_ts - m_last_frame_displayed_time))) > max_variance)
|
|
||||||
m_last_frame_displayed_time = current_ts + sleep_period;
|
|
||||||
else
|
|
||||||
m_last_frame_displayed_time += sleep_period;
|
|
||||||
|
|
||||||
Common::Timer::SleepUntil(m_last_frame_displayed_time, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GPUDevice::SetGPUTimingEnabled(bool enabled)
|
bool GPUDevice::SetGPUTimingEnabled(bool enabled)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -453,6 +453,38 @@ protected:
|
|||||||
u32 m_current_position = 0;
|
u32 m_current_position = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GPUSwapChain
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GPUSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode, bool allow_present_throttle);
|
||||||
|
virtual ~GPUSwapChain();
|
||||||
|
|
||||||
|
ALWAYS_INLINE const WindowInfo& GetWindowInfo() const { return m_window_info; }
|
||||||
|
ALWAYS_INLINE u32 GetWidth() const { return m_window_info.surface_width; }
|
||||||
|
ALWAYS_INLINE u32 GetHeight() const { return m_window_info.surface_height; }
|
||||||
|
ALWAYS_INLINE float GetScale() const { return m_window_info.surface_scale; }
|
||||||
|
ALWAYS_INLINE GPUTexture::Format GetFormat() const { return m_window_info.surface_format; }
|
||||||
|
|
||||||
|
ALWAYS_INLINE GPUVSyncMode GetVSyncMode() const { return m_vsync_mode; }
|
||||||
|
ALWAYS_INLINE bool IsVSyncModeBlocking() const { return (m_vsync_mode == GPUVSyncMode::FIFO); }
|
||||||
|
ALWAYS_INLINE bool IsPresentThrottleAllowed() const { return m_allow_present_throttle; }
|
||||||
|
|
||||||
|
virtual bool ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error) = 0;
|
||||||
|
virtual bool SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error) = 0;
|
||||||
|
|
||||||
|
bool ShouldSkipPresentingFrame();
|
||||||
|
void ThrottlePresentation();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// TODO: Merge WindowInfo into this struct...
|
||||||
|
WindowInfo m_window_info;
|
||||||
|
|
||||||
|
GPUVSyncMode m_vsync_mode = GPUVSyncMode::Disabled;
|
||||||
|
bool m_allow_present_throttle = false;
|
||||||
|
|
||||||
|
u64 m_last_frame_displayed_time = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class GPUDevice
|
class GPUDevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -485,6 +517,7 @@ public:
|
|||||||
{
|
{
|
||||||
OK,
|
OK,
|
||||||
SkipPresent,
|
SkipPresent,
|
||||||
|
ExclusiveFullscreenLost,
|
||||||
DeviceLost,
|
DeviceLost,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -521,10 +554,22 @@ public:
|
|||||||
u32 num_uploads;
|
u32 num_uploads;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Parameters for exclusive fullscreen.
|
||||||
|
struct ExclusiveFullscreenMode
|
||||||
|
{
|
||||||
|
u32 width;
|
||||||
|
u32 height;
|
||||||
|
float refresh_rate;
|
||||||
|
|
||||||
|
TinyString ToString() const;
|
||||||
|
|
||||||
|
static std::optional<ExclusiveFullscreenMode> Parse(std::string_view str);
|
||||||
|
};
|
||||||
|
|
||||||
struct AdapterInfo
|
struct AdapterInfo
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<std::string> fullscreen_modes;
|
std::vector<ExclusiveFullscreenMode> fullscreen_modes;
|
||||||
u32 max_texture_size;
|
u32 max_texture_size;
|
||||||
u32 max_multisamples;
|
u32 max_multisamples;
|
||||||
bool supports_sample_shading;
|
bool supports_sample_shading;
|
||||||
@ -565,15 +610,6 @@ public:
|
|||||||
/// Returns a list of adapters for the given API.
|
/// Returns a list of adapters for the given API.
|
||||||
static AdapterInfoList GetAdapterListForAPI(RenderAPI api);
|
static AdapterInfoList GetAdapterListForAPI(RenderAPI api);
|
||||||
|
|
||||||
/// Parses a fullscreen mode into its components (width * height @ refresh hz)
|
|
||||||
static bool GetRequestedExclusiveFullscreenMode(u32* width, u32* height, float* refresh_rate);
|
|
||||||
|
|
||||||
/// Converts a fullscreen mode to a string.
|
|
||||||
static std::string GetFullscreenModeString(u32 width, u32 height, float refresh_rate);
|
|
||||||
|
|
||||||
/// Returns the directory bad shaders are saved to.
|
|
||||||
static std::string GetShaderDumpPath(std::string_view name);
|
|
||||||
|
|
||||||
/// Dumps out a shader that failed compilation.
|
/// Dumps out a shader that failed compilation.
|
||||||
static void DumpBadShader(std::string_view code, std::string_view errors);
|
static void DumpBadShader(std::string_view code, std::string_view errors);
|
||||||
|
|
||||||
@ -600,35 +636,44 @@ public:
|
|||||||
ALWAYS_INLINE u32 GetMaxTextureSize() const { return m_max_texture_size; }
|
ALWAYS_INLINE u32 GetMaxTextureSize() const { return m_max_texture_size; }
|
||||||
ALWAYS_INLINE u32 GetMaxMultisamples() const { return m_max_multisamples; }
|
ALWAYS_INLINE u32 GetMaxMultisamples() const { return m_max_multisamples; }
|
||||||
|
|
||||||
ALWAYS_INLINE const WindowInfo& GetWindowInfo() const { return m_window_info; }
|
ALWAYS_INLINE GPUSwapChain* GetMainSwapChain() const { return m_main_swap_chain.get(); }
|
||||||
ALWAYS_INLINE s32 GetWindowWidth() const { return static_cast<s32>(m_window_info.surface_width); }
|
ALWAYS_INLINE bool HasMainSwapChain() const { return static_cast<bool>(m_main_swap_chain); }
|
||||||
ALWAYS_INLINE s32 GetWindowHeight() const { return static_cast<s32>(m_window_info.surface_height); }
|
// ALWAYS_INLINE u32 GetMainSwapChainWidth() const { return m_main_swap_chain->GetWidth(); }
|
||||||
ALWAYS_INLINE float GetWindowScale() const { return m_window_info.surface_scale; }
|
// ALWAYS_INLINE u32 GetMainSwapChainHeight() const { return m_main_swap_chain->GetHeight(); }
|
||||||
ALWAYS_INLINE GPUTexture::Format GetWindowFormat() const { return m_window_info.surface_format; }
|
// ALWAYS_INLINE float GetWindowScale() const { return m_window_info.surface_scale; }
|
||||||
|
// ALWAYS_INLINE GPUTexture::Format GetWindowFormat() const { return m_window_info.surface_format; }
|
||||||
|
|
||||||
ALWAYS_INLINE GPUSampler* GetLinearSampler() const { return m_linear_sampler.get(); }
|
ALWAYS_INLINE GPUSampler* GetLinearSampler() const { return m_linear_sampler.get(); }
|
||||||
ALWAYS_INLINE GPUSampler* GetNearestSampler() const { return m_nearest_sampler.get(); }
|
ALWAYS_INLINE GPUSampler* GetNearestSampler() const { return m_nearest_sampler.get(); }
|
||||||
|
|
||||||
ALWAYS_INLINE bool IsGPUTimingEnabled() const { return m_gpu_timing_enabled; }
|
ALWAYS_INLINE bool IsGPUTimingEnabled() const { return m_gpu_timing_enabled; }
|
||||||
|
|
||||||
bool Create(std::string_view adapter, std::string_view shader_cache_path, u32 shader_cache_version, bool debug_device,
|
bool Create(std::string_view adapter, FeatureMask disabled_features, std::string_view shader_dump_path,
|
||||||
GPUVSyncMode vsync, bool allow_present_throttle, std::optional<bool> exclusive_fullscreen_control,
|
std::string_view shader_cache_path, u32 shader_cache_version, bool debug_device, const WindowInfo& wi,
|
||||||
FeatureMask disabled_features, Error* error);
|
GPUVSyncMode vsync, bool allow_present_throttle, const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control, Error* error);
|
||||||
void Destroy();
|
void Destroy();
|
||||||
|
|
||||||
virtual bool HasSurface() const = 0;
|
virtual std::unique_ptr<GPUSwapChain> CreateSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||||
virtual void DestroySurface() = 0;
|
bool allow_present_throttle,
|
||||||
virtual bool UpdateWindow() = 0;
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control,
|
||||||
|
Error* error) = 0;
|
||||||
|
|
||||||
|
bool RecreateMainSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control, Error* error);
|
||||||
|
void DestroyMainSwapChain();
|
||||||
|
|
||||||
virtual bool SupportsExclusiveFullscreen() const;
|
virtual bool SupportsExclusiveFullscreen() const;
|
||||||
|
|
||||||
/// Call when the window size changes externally to recreate any resources.
|
|
||||||
virtual void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) = 0;
|
|
||||||
|
|
||||||
virtual std::string GetDriverInfo() const = 0;
|
virtual std::string GetDriverInfo() const = 0;
|
||||||
|
|
||||||
|
// Flushes current command buffer, but does not wait for completion.
|
||||||
|
virtual void FlushCommands() = 0;
|
||||||
|
|
||||||
// Executes current command buffer, waits for its completion, and destroys all pending resources.
|
// Executes current command buffer, waits for its completion, and destroys all pending resources.
|
||||||
virtual void ExecuteAndWaitForGPUIdle() = 0;
|
virtual void WaitForGPUIdle() = 0;
|
||||||
|
|
||||||
virtual std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
virtual std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
||||||
GPUTexture::Type type, GPUTexture::Format format,
|
GPUTexture::Type type, GPUTexture::Format format,
|
||||||
@ -710,17 +755,12 @@ public:
|
|||||||
virtual void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) = 0;
|
virtual void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) = 0;
|
||||||
|
|
||||||
/// Returns false if the window was completely occluded.
|
/// Returns false if the window was completely occluded.
|
||||||
virtual PresentResult BeginPresent(u32 clear_color = DEFAULT_CLEAR_COLOR) = 0;
|
virtual PresentResult BeginPresent(GPUSwapChain* swap_chain, u32 clear_color = DEFAULT_CLEAR_COLOR) = 0;
|
||||||
virtual void EndPresent(bool explicit_submit, u64 submit_time = 0) = 0;
|
virtual void EndPresent(GPUSwapChain* swap_chain, bool explicit_submit, u64 submit_time = 0) = 0;
|
||||||
virtual void SubmitPresent() = 0;
|
virtual void SubmitPresent(GPUSwapChain* swap_chain) = 0;
|
||||||
|
|
||||||
/// Renders ImGui screen elements. Call before EndPresent().
|
/// Renders ImGui screen elements. Call before EndPresent().
|
||||||
void RenderImGui();
|
void RenderImGui(GPUSwapChain* swap_chain);
|
||||||
|
|
||||||
ALWAYS_INLINE GPUVSyncMode GetVSyncMode() const { return m_vsync_mode; }
|
|
||||||
ALWAYS_INLINE bool IsVSyncModeBlocking() const { return (m_vsync_mode == GPUVSyncMode::FIFO); }
|
|
||||||
ALWAYS_INLINE bool IsPresentThrottleAllowed() const { return m_allow_present_throttle; }
|
|
||||||
virtual void SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle) = 0;
|
|
||||||
|
|
||||||
ALWAYS_INLINE bool IsDebugDevice() const { return m_debug_device; }
|
ALWAYS_INLINE bool IsDebugDevice() const { return m_debug_device; }
|
||||||
ALWAYS_INLINE size_t GetVRAMUsage() const { return s_total_vram_usage; }
|
ALWAYS_INLINE size_t GetVRAMUsage() const { return s_total_vram_usage; }
|
||||||
@ -730,8 +770,6 @@ public:
|
|||||||
static GSVector4i FlipToLowerLeft(GSVector4i rc, s32 target_height);
|
static GSVector4i FlipToLowerLeft(GSVector4i rc, s32 target_height);
|
||||||
bool ResizeTexture(std::unique_ptr<GPUTexture>* tex, u32 new_width, u32 new_height, GPUTexture::Type type,
|
bool ResizeTexture(std::unique_ptr<GPUTexture>* tex, u32 new_width, u32 new_height, GPUTexture::Type type,
|
||||||
GPUTexture::Format format, bool preserve = true);
|
GPUTexture::Format format, bool preserve = true);
|
||||||
bool ShouldSkipPresentingFrame();
|
|
||||||
void ThrottlePresentation();
|
|
||||||
|
|
||||||
virtual bool SupportsTextureFormat(GPUTexture::Format format) const = 0;
|
virtual bool SupportsTextureFormat(GPUTexture::Format format) const = 0;
|
||||||
|
|
||||||
@ -745,8 +783,10 @@ public:
|
|||||||
static void ResetStatistics();
|
static void ResetStatistics();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool CreateDevice(std::string_view adapter, std::optional<bool> exclusive_fullscreen_control,
|
virtual bool CreateDeviceAndMainSwapChain(std::string_view adapter, FeatureMask disabled_features,
|
||||||
FeatureMask disabled_features, Error* error) = 0;
|
const WindowInfo& wi, GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control, Error* error) = 0;
|
||||||
virtual void DestroyDevice() = 0;
|
virtual void DestroyDevice() = 0;
|
||||||
|
|
||||||
std::string GetShaderCacheBaseName(std::string_view type) const;
|
std::string GetShaderCacheBaseName(std::string_view type) const;
|
||||||
@ -762,8 +802,6 @@ protected:
|
|||||||
std::string_view source, const char* entry_point,
|
std::string_view source, const char* entry_point,
|
||||||
DynamicHeapArray<u8>* out_binary, Error* error) = 0;
|
DynamicHeapArray<u8>* out_binary, Error* error) = 0;
|
||||||
|
|
||||||
bool AcquireWindow(bool recreate_window);
|
|
||||||
|
|
||||||
void TrimTexturePool();
|
void TrimTexturePool();
|
||||||
|
|
||||||
bool CompileGLSLShaderToVulkanSpv(GPUShaderStage stage, GPUShaderLanguage source_language, std::string_view source,
|
bool CompileGLSLShaderToVulkanSpv(GPUShaderStage stage, GPUShaderLanguage source_language, std::string_view source,
|
||||||
@ -784,8 +822,7 @@ protected:
|
|||||||
u32 m_max_texture_size = 0;
|
u32 m_max_texture_size = 0;
|
||||||
u32 m_max_multisamples = 0;
|
u32 m_max_multisamples = 0;
|
||||||
|
|
||||||
WindowInfo m_window_info;
|
std::unique_ptr<GPUSwapChain> m_main_swap_chain;
|
||||||
u64 m_last_frame_displayed_time = 0;
|
|
||||||
|
|
||||||
GPUShaderCache m_shader_cache;
|
GPUShaderCache m_shader_cache;
|
||||||
|
|
||||||
@ -852,8 +889,6 @@ private:
|
|||||||
protected:
|
protected:
|
||||||
static Statistics s_stats;
|
static Statistics s_stats;
|
||||||
|
|
||||||
GPUVSyncMode m_vsync_mode = GPUVSyncMode::Disabled;
|
|
||||||
bool m_allow_present_throttle = false;
|
|
||||||
bool m_gpu_timing_enabled = false;
|
bool m_gpu_timing_enabled = false;
|
||||||
bool m_debug_device = false;
|
bool m_debug_device = false;
|
||||||
};
|
};
|
||||||
@ -865,21 +900,6 @@ ALWAYS_INLINE void GPUDevice::PooledTextureDeleter::operator()(GPUTexture* const
|
|||||||
g_gpu_device->RecycleTexture(std::unique_ptr<GPUTexture>(tex));
|
g_gpu_device->RecycleTexture(std::unique_ptr<GPUTexture>(tex));
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Host {
|
|
||||||
/// Called when the core is creating a render device.
|
|
||||||
/// This could also be fullscreen transition.
|
|
||||||
std::optional<WindowInfo> AcquireRenderWindow(bool recreate_window);
|
|
||||||
|
|
||||||
/// Called when the core is finished with a render window.
|
|
||||||
void ReleaseRenderWindow();
|
|
||||||
|
|
||||||
/// Returns true if the hosting application is currently fullscreen.
|
|
||||||
bool IsFullscreen();
|
|
||||||
|
|
||||||
/// Alters fullscreen state of hosting application.
|
|
||||||
void SetFullscreen(bool enabled);
|
|
||||||
} // namespace Host
|
|
||||||
|
|
||||||
// Macros for debug messages.
|
// Macros for debug messages.
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
struct GLAutoPop
|
struct GLAutoPop
|
||||||
|
@ -231,7 +231,8 @@ bool ImGuiManager::Initialize(float global_scale, Error* error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
s_global_prescale = global_scale;
|
s_global_prescale = global_scale;
|
||||||
s_global_scale = std::max(g_gpu_device->GetWindowScale() * global_scale, 1.0f);
|
s_global_scale = std::max(
|
||||||
|
(g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetScale() : 1.0f) * global_scale, 1.0f);
|
||||||
s_scale_changed = false;
|
s_scale_changed = false;
|
||||||
|
|
||||||
ImGui::CreateContext();
|
ImGui::CreateContext();
|
||||||
@ -250,8 +251,10 @@ bool ImGuiManager::Initialize(float global_scale, Error* error)
|
|||||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
s_window_width = static_cast<float>(g_gpu_device->GetWindowWidth());
|
s_window_width =
|
||||||
s_window_height = static_cast<float>(g_gpu_device->GetWindowHeight());
|
g_gpu_device->HasMainSwapChain() ? static_cast<float>(g_gpu_device->GetMainSwapChain()->GetWidth()) : 0.0f;
|
||||||
|
s_window_height =
|
||||||
|
g_gpu_device->HasMainSwapChain() ? static_cast<float>(g_gpu_device->GetMainSwapChain()->GetHeight()) : 0.0f;
|
||||||
io.DisplayFramebufferScale = ImVec2(1, 1); // We already scale things ourselves, this would double-apply scaling
|
io.DisplayFramebufferScale = ImVec2(1, 1); // We already scale things ourselves, this would double-apply scaling
|
||||||
io.DisplaySize = ImVec2(s_window_width, s_window_height);
|
io.DisplaySize = ImVec2(s_window_width, s_window_height);
|
||||||
|
|
||||||
@ -316,7 +319,8 @@ void ImGuiManager::RequestScaleUpdate()
|
|||||||
|
|
||||||
void ImGuiManager::UpdateScale()
|
void ImGuiManager::UpdateScale()
|
||||||
{
|
{
|
||||||
const float window_scale = g_gpu_device ? g_gpu_device->GetWindowScale() : 1.0f;
|
const float window_scale =
|
||||||
|
(g_gpu_device && g_gpu_device->HasMainSwapChain()) ? g_gpu_device->GetMainSwapChain()->GetScale() : 1.0f;
|
||||||
const float scale = std::max(window_scale * s_global_prescale, 1.0f);
|
const float scale = std::max(window_scale * s_global_prescale, 1.0f);
|
||||||
|
|
||||||
if ((!HasFullscreenFonts() || !ImGuiFullscreen::UpdateLayoutScale()) && scale == s_global_scale)
|
if ((!HasFullscreenFonts() || !ImGuiFullscreen::UpdateLayoutScale()) && scale == s_global_scale)
|
||||||
|
@ -607,11 +607,12 @@ static std::array<const char*, static_cast<u32>(InputSourceType::Count)> s_input
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
"DInput",
|
"DInput",
|
||||||
"XInput",
|
"XInput",
|
||||||
#endif
|
|
||||||
#ifndef __ANDROID__
|
|
||||||
"SDL",
|
|
||||||
"RawInput",
|
"RawInput",
|
||||||
#else
|
#endif
|
||||||
|
#ifdef ENABLE_SDL
|
||||||
|
"SDL",
|
||||||
|
#endif
|
||||||
|
#ifdef __ANDROID__
|
||||||
"Android",
|
"Android",
|
||||||
#endif
|
#endif
|
||||||
}};
|
}};
|
||||||
@ -640,14 +641,17 @@ bool InputManager::GetInputSourceDefaultEnabled(InputSourceType type)
|
|||||||
|
|
||||||
case InputSourceType::XInput:
|
case InputSourceType::XInput:
|
||||||
return false;
|
return false;
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef __ANDROID__
|
|
||||||
case InputSourceType::SDL:
|
|
||||||
return true;
|
|
||||||
case InputSourceType::RawInput:
|
case InputSourceType::RawInput:
|
||||||
return false;
|
return false;
|
||||||
#else
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_SDL
|
||||||
|
case InputSourceType::SDL:
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
case InputSourceType::Android:
|
case InputSourceType::Android:
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
@ -1229,7 +1233,7 @@ void InputManager::UpdatePointerCount()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __ANDROID__
|
#ifdef _WIN32
|
||||||
InputSource* ris = GetInputSourceInterface(InputSourceType::RawInput);
|
InputSource* ris = GetInputSourceInterface(InputSourceType::RawInput);
|
||||||
DebugAssert(ris);
|
DebugAssert(ris);
|
||||||
|
|
||||||
@ -2048,9 +2052,10 @@ void InputManager::ReloadSources(SettingsInterface& si, std::unique_lock<std::mu
|
|||||||
UpdateInputSourceState(si, settings_lock, InputSourceType::XInput, &InputSource::CreateXInputSource);
|
UpdateInputSourceState(si, settings_lock, InputSourceType::XInput, &InputSource::CreateXInputSource);
|
||||||
UpdateInputSourceState(si, settings_lock, InputSourceType::RawInput, &InputSource::CreateWin32RawInputSource);
|
UpdateInputSourceState(si, settings_lock, InputSourceType::RawInput, &InputSource::CreateWin32RawInputSource);
|
||||||
#endif
|
#endif
|
||||||
#ifndef __ANDROID__
|
#ifdef ENABLE_SDL
|
||||||
UpdateInputSourceState(si, settings_lock, InputSourceType::SDL, &InputSource::CreateSDLSource);
|
UpdateInputSourceState(si, settings_lock, InputSourceType::SDL, &InputSource::CreateSDLSource);
|
||||||
#else
|
#endif
|
||||||
|
#ifdef __ANDROID__
|
||||||
UpdateInputSourceState(si, settings_lock, InputSourceType::Android, &InputSource::CreateAndroidSource);
|
UpdateInputSourceState(si, settings_lock, InputSourceType::Android, &InputSource::CreateAndroidSource);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -27,11 +27,12 @@ enum class InputSourceType : u32
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
DInput,
|
DInput,
|
||||||
XInput,
|
XInput,
|
||||||
#endif
|
|
||||||
#ifndef __ANDROID__
|
|
||||||
SDL,
|
|
||||||
RawInput,
|
RawInput,
|
||||||
#else
|
#endif
|
||||||
|
#ifdef ENABLE_SDL
|
||||||
|
SDL,
|
||||||
|
#endif
|
||||||
|
#ifdef __ANDROID__
|
||||||
Android,
|
Android,
|
||||||
#endif
|
#endif
|
||||||
Count,
|
Count,
|
||||||
|
@ -184,6 +184,23 @@ private:
|
|||||||
MetalStreamBuffer m_buffer;
|
MetalStreamBuffer m_buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MetalSwapChain : public GPUSwapChain
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MetalSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode, bool allow_present_throttle, CAMetalLayer* layer);
|
||||||
|
~MetalSwapChain() override;
|
||||||
|
|
||||||
|
void Destroy(bool wait_for_gpu);
|
||||||
|
|
||||||
|
CAMetalLayer* GetLayer() const { return m_layer; }
|
||||||
|
|
||||||
|
bool ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error) override;
|
||||||
|
bool SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CAMetalLayer* m_layer = nil;
|
||||||
|
};
|
||||||
|
|
||||||
class MetalDevice final : public GPUDevice
|
class MetalDevice final : public GPUDevice
|
||||||
{
|
{
|
||||||
friend MetalTexture;
|
friend MetalTexture;
|
||||||
@ -198,16 +215,16 @@ public:
|
|||||||
MetalDevice();
|
MetalDevice();
|
||||||
~MetalDevice();
|
~MetalDevice();
|
||||||
|
|
||||||
bool HasSurface() const override;
|
|
||||||
|
|
||||||
bool UpdateWindow() override;
|
|
||||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
|
||||||
void DestroySurface() override;
|
|
||||||
|
|
||||||
std::string GetDriverInfo() const override;
|
std::string GetDriverInfo() const override;
|
||||||
|
|
||||||
void ExecuteAndWaitForGPUIdle() override;
|
void FlushCommands() override;
|
||||||
|
void WaitForGPUIdle() override;
|
||||||
|
|
||||||
|
std::unique_ptr<GPUSwapChain> CreateSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||||
|
bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control,
|
||||||
|
Error* error) override;
|
||||||
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
||||||
GPUTexture::Type type, GPUTexture::Format format,
|
GPUTexture::Type type, GPUTexture::Format format,
|
||||||
const void* data = nullptr, u32 data_stride = 0) override;
|
const void* data = nullptr, u32 data_stride = 0) override;
|
||||||
@ -261,11 +278,9 @@ public:
|
|||||||
bool SetGPUTimingEnabled(bool enabled) override;
|
bool SetGPUTimingEnabled(bool enabled) override;
|
||||||
float GetAndResetAccumulatedGPUTime() override;
|
float GetAndResetAccumulatedGPUTime() override;
|
||||||
|
|
||||||
void SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle) override;
|
PresentResult BeginPresent(GPUSwapChain* swap_chain, u32 clear_color) override;
|
||||||
|
void EndPresent(GPUSwapChain* swap_chain, bool explicit_submit, u64 present_time) override;
|
||||||
PresentResult BeginPresent(u32 clear_color) override;
|
void SubmitPresent(GPUSwapChain* swap_chain) override;
|
||||||
void EndPresent(bool explicit_submit, u64 present_time) override;
|
|
||||||
void SubmitPresent() override;
|
|
||||||
|
|
||||||
void WaitForFenceCounter(u64 counter);
|
void WaitForFenceCounter(u64 counter);
|
||||||
|
|
||||||
@ -285,8 +300,10 @@ public:
|
|||||||
static void DeferRelease(u64 fence_counter, id obj);
|
static void DeferRelease(u64 fence_counter, id obj);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool CreateDevice(std::string_view adapter, std::optional<bool> exclusive_fullscreen_control,
|
bool CreateDeviceAndMainSwapChain(std::string_view adapter, FeatureMask disabled_features, const WindowInfo& wi,
|
||||||
FeatureMask disabled_features, Error* error) override;
|
GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control, Error* error) override;
|
||||||
void DestroyDevice() override;
|
void DestroyDevice() override;
|
||||||
bool OpenPipelineCache(const std::string& path, Error* error) override;
|
bool OpenPipelineCache(const std::string& path, Error* error) override;
|
||||||
bool CreatePipelineCache(const std::string& path, Error* error) override;
|
bool CreatePipelineCache(const std::string& path, Error* error) override;
|
||||||
@ -315,8 +332,6 @@ private:
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(ClearPipelineConfig) == 8);
|
static_assert(sizeof(ClearPipelineConfig) == 8);
|
||||||
|
|
||||||
ALWAYS_INLINE NSView* GetWindowView() const { return (__bridge NSView*)m_window_info.window_handle; }
|
|
||||||
|
|
||||||
void SetFeatures(FeatureMask disabled_features);
|
void SetFeatures(FeatureMask disabled_features);
|
||||||
bool LoadShaders();
|
bool LoadShaders();
|
||||||
|
|
||||||
@ -340,15 +355,12 @@ private:
|
|||||||
void EndInlineUploading();
|
void EndInlineUploading();
|
||||||
void EndAnyEncoding();
|
void EndAnyEncoding();
|
||||||
|
|
||||||
GSVector4i ClampToFramebufferSize(const GSVector4i rc) const;
|
|
||||||
void PreDrawCheck();
|
void PreDrawCheck();
|
||||||
void SetInitialEncoderState();
|
void SetInitialEncoderState();
|
||||||
void SetViewportInRenderEncoder();
|
void SetViewportInRenderEncoder();
|
||||||
void SetScissorInRenderEncoder();
|
void SetScissorInRenderEncoder();
|
||||||
|
|
||||||
bool CreateLayer();
|
void RenderBlankFrame(MetalSwapChain* swap_chain);
|
||||||
void DestroyLayer();
|
|
||||||
void RenderBlankFrame();
|
|
||||||
|
|
||||||
bool CreateBuffers();
|
bool CreateBuffers();
|
||||||
void DestroyBuffers();
|
void DestroyBuffers();
|
||||||
@ -358,10 +370,6 @@ private:
|
|||||||
id<MTLDevice> m_device;
|
id<MTLDevice> m_device;
|
||||||
id<MTLCommandQueue> m_queue;
|
id<MTLCommandQueue> m_queue;
|
||||||
|
|
||||||
CAMetalLayer* m_layer = nil;
|
|
||||||
id<MTLDrawable> m_layer_drawable = nil;
|
|
||||||
MTLRenderPassDescriptor* m_layer_pass_desc = nil;
|
|
||||||
|
|
||||||
std::mutex m_fence_mutex;
|
std::mutex m_fence_mutex;
|
||||||
u64 m_current_fence_counter = 0;
|
u64 m_current_fence_counter = 0;
|
||||||
std::atomic<u64> m_completed_fence_counter{0};
|
std::atomic<u64> m_completed_fence_counter{0};
|
||||||
@ -402,10 +410,11 @@ private:
|
|||||||
id<MTLBuffer> m_current_ssbo = nil;
|
id<MTLBuffer> m_current_ssbo = nil;
|
||||||
GSVector4i m_current_viewport = {};
|
GSVector4i m_current_viewport = {};
|
||||||
GSVector4i m_current_scissor = {};
|
GSVector4i m_current_scissor = {};
|
||||||
|
GSVector4i m_current_framebuffer_size = {};
|
||||||
bool m_vsync_enabled = false;
|
|
||||||
bool m_pipeline_cache_modified = false;
|
|
||||||
|
|
||||||
double m_accumulated_gpu_time = 0;
|
double m_accumulated_gpu_time = 0;
|
||||||
double m_last_gpu_time_end = 0;
|
double m_last_gpu_time_end = 0;
|
||||||
|
|
||||||
|
id<MTLDrawable> m_layer_drawable = nil;
|
||||||
|
bool m_pipeline_cache_modified = false;
|
||||||
};
|
};
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
#include <mach/mach_time.h>
|
#include <mach/mach_time.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
LOG_CHANNEL(MetalDevice);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
// TODO: Disable hazard tracking and issue barriers explicitly.
|
// TODO: Disable hazard tracking and issue barriers explicitly.
|
||||||
|
|
||||||
@ -128,34 +128,160 @@ static void RunOnMainThread(F&& f)
|
|||||||
|
|
||||||
MetalDevice::MetalDevice() : m_current_viewport(0, 0, 1, 1), m_current_scissor(0, 0, 1, 1)
|
MetalDevice::MetalDevice() : m_current_viewport(0, 0, 1, 1), m_current_scissor(0, 0, 1, 1)
|
||||||
{
|
{
|
||||||
|
m_render_api = RenderAPI::Metal;
|
||||||
}
|
}
|
||||||
|
|
||||||
MetalDevice::~MetalDevice()
|
MetalDevice::~MetalDevice()
|
||||||
{
|
{
|
||||||
Assert(m_pipeline_archive == nil && m_layer == nil && m_device == nil);
|
Assert(m_pipeline_archive == nil && m_layer_drawable == nil && m_device == nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MetalDevice::HasSurface() const
|
MetalSwapChain::MetalSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
|
CAMetalLayer* layer)
|
||||||
|
: GPUSwapChain(wi, vsync_mode, allow_present_throttle), m_layer(layer)
|
||||||
{
|
{
|
||||||
return (m_layer != nil);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetalDevice::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle)
|
MetalSwapChain::~MetalSwapChain()
|
||||||
|
{
|
||||||
|
Destroy(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetalSwapChain::Destroy(bool wait_for_gpu)
|
||||||
|
{
|
||||||
|
if (!m_layer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (wait_for_gpu)
|
||||||
|
MetalDevice::GetInstance().WaitForGPUIdle();
|
||||||
|
|
||||||
|
RunOnMainThread([this]() {
|
||||||
|
NSView* view = (__bridge NSView*)m_window_info.window_handle;
|
||||||
|
[view setLayer:nil];
|
||||||
|
[view setWantsLayer:FALSE];
|
||||||
|
[m_layer release];
|
||||||
|
m_layer = nullptr;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MetalSwapChain::ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error)
|
||||||
|
{
|
||||||
|
@autoreleasepool
|
||||||
|
{
|
||||||
|
m_window_info.surface_scale = new_scale;
|
||||||
|
if (new_width == m_window_info.surface_width && new_height == m_window_info.surface_height)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_window_info.surface_width = new_width;
|
||||||
|
m_window_info.surface_height = new_height;
|
||||||
|
|
||||||
|
[m_layer setDrawableSize:CGSizeMake(new_width, new_height)];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MetalSwapChain::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error)
|
||||||
{
|
{
|
||||||
// Metal does not support mailbox mode.
|
// Metal does not support mailbox mode.
|
||||||
mode = (mode == GPUVSyncMode::Mailbox) ? GPUVSyncMode::FIFO : mode;
|
mode = (mode == GPUVSyncMode::Mailbox) ? GPUVSyncMode::FIFO : mode;
|
||||||
m_allow_present_throttle = allow_present_throttle;
|
m_allow_present_throttle = allow_present_throttle;
|
||||||
|
|
||||||
if (m_vsync_mode == mode)
|
if (m_vsync_mode == mode)
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
m_vsync_mode = mode;
|
m_vsync_mode = mode;
|
||||||
if (m_layer != nil)
|
if (m_layer != nil)
|
||||||
[m_layer setDisplaySyncEnabled:m_vsync_mode == GPUVSyncMode::FIFO];
|
[m_layer setDisplaySyncEnabled:m_vsync_mode == GPUVSyncMode::FIFO];
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MetalDevice::CreateDevice(std::string_view adapter, std::optional<bool> exclusive_fullscreen_control,
|
std::unique_ptr<GPUSwapChain> MetalDevice::CreateSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||||
FeatureMask disabled_features, Error* error)
|
bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control,
|
||||||
|
Error* error)
|
||||||
|
{
|
||||||
|
@autoreleasepool
|
||||||
|
{
|
||||||
|
CAMetalLayer* layer;
|
||||||
|
WindowInfo wi_copy(wi);
|
||||||
|
RunOnMainThread([this, &layer, &wi_copy, error]() {
|
||||||
|
@autoreleasepool
|
||||||
|
{
|
||||||
|
INFO_LOG("Creating a {}x{} Metal layer.", wi_copy.surface_width, wi_copy.surface_height);
|
||||||
|
layer = [CAMetalLayer layer]; // TODO: Does this need retain??
|
||||||
|
if (layer == nil)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "Failed to create metal layer.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[layer setDevice:m_device];
|
||||||
|
[layer setDrawableSize:CGSizeMake(static_cast<float>(wi_copy.surface_width),
|
||||||
|
static_cast<float>(wi_copy.surface_height))];
|
||||||
|
|
||||||
|
// Default should be BGRA8.
|
||||||
|
const MTLPixelFormat layer_fmt = [layer pixelFormat];
|
||||||
|
wi_copy.surface_format = GetTextureFormatForMTLFormat(layer_fmt);
|
||||||
|
if (wi_copy.surface_format == GPUTexture::Format::Unknown)
|
||||||
|
{
|
||||||
|
ERROR_LOG("Invalid pixel format {} in layer, using BGRA8.", static_cast<u32>(layer_fmt));
|
||||||
|
[layer setPixelFormat:MTLPixelFormatBGRA8Unorm];
|
||||||
|
wi_copy.surface_format = GPUTexture::Format::BGRA8;
|
||||||
|
}
|
||||||
|
|
||||||
|
VERBOSE_LOG("Metal layer pixel format is {}.", GPUTexture::GetFormatName(wi_copy.surface_format));
|
||||||
|
|
||||||
|
NSView* view = (__bridge NSView*)wi_copy.window_handle;
|
||||||
|
[view setWantsLayer:TRUE];
|
||||||
|
[view setLayer:layer];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!layer)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// Metal does not support mailbox mode.
|
||||||
|
vsync_mode = (vsync_mode == GPUVSyncMode::Mailbox) ? GPUVSyncMode::FIFO : vsync_mode;
|
||||||
|
[layer setDisplaySyncEnabled:vsync_mode == GPUVSyncMode::FIFO];
|
||||||
|
|
||||||
|
// Clear it out ASAP.
|
||||||
|
std::unique_ptr<MetalSwapChain> swap_chain =
|
||||||
|
std::make_unique<MetalSwapChain>(wi_copy, vsync_mode, allow_present_throttle, layer);
|
||||||
|
RenderBlankFrame(swap_chain.get());
|
||||||
|
return swap_chain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetalDevice::RenderBlankFrame(MetalSwapChain* swap_chain)
|
||||||
|
{
|
||||||
|
@autoreleasepool
|
||||||
|
{
|
||||||
|
// has to be encoding, we don't "begin" a render pass here, so the inline encoder won't get flushed otherwise.
|
||||||
|
EndAnyEncoding();
|
||||||
|
|
||||||
|
id<MTLDrawable> drawable = [[swap_chain->GetLayer() nextDrawable] retain];
|
||||||
|
MTLRenderPassDescriptor* desc = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||||
|
desc.colorAttachments[0].loadAction = MTLLoadActionClear;
|
||||||
|
desc.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||||
|
desc.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0);
|
||||||
|
desc.colorAttachments[0].texture = [drawable texture];
|
||||||
|
id<MTLRenderCommandEncoder> encoder = [m_render_cmdbuf renderCommandEncoderWithDescriptor:desc];
|
||||||
|
[encoder endEncoding];
|
||||||
|
[m_render_cmdbuf presentDrawable:drawable];
|
||||||
|
DeferRelease(drawable);
|
||||||
|
SubmitCommandBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MetalDevice::CreateDeviceAndMainSwapChain(std::string_view adapter, FeatureMask disabled_features,
|
||||||
|
const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||||
|
bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control, Error* error)
|
||||||
{
|
{
|
||||||
@autoreleasepool
|
@autoreleasepool
|
||||||
{
|
{
|
||||||
@ -199,15 +325,20 @@ bool MetalDevice::CreateDevice(std::string_view adapter, std::optional<bool> exc
|
|||||||
INFO_LOG("Metal Device: {}", [[m_device name] UTF8String]);
|
INFO_LOG("Metal Device: {}", [[m_device name] UTF8String]);
|
||||||
|
|
||||||
SetFeatures(disabled_features);
|
SetFeatures(disabled_features);
|
||||||
|
|
||||||
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateLayer())
|
|
||||||
{
|
|
||||||
Error::SetStringView(error, "Failed to create layer.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateCommandBuffer();
|
CreateCommandBuffer();
|
||||||
RenderBlankFrame();
|
|
||||||
|
if (!wi.IsSurfaceless())
|
||||||
|
{
|
||||||
|
m_main_swap_chain = CreateSwapChain(wi, vsync_mode, allow_present_throttle, exclusive_fullscreen_mode,
|
||||||
|
exclusive_fullscreen_control, error);
|
||||||
|
if (!m_main_swap_chain)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "Failed to create layer.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderBlankFrame(static_cast<MetalSwapChain*>(m_main_swap_chain.get()));
|
||||||
|
}
|
||||||
|
|
||||||
if (!LoadShaders())
|
if (!LoadShaders())
|
||||||
{
|
{
|
||||||
@ -228,7 +359,6 @@ bool MetalDevice::CreateDevice(std::string_view adapter, std::optional<bool> exc
|
|||||||
void MetalDevice::SetFeatures(FeatureMask disabled_features)
|
void MetalDevice::SetFeatures(FeatureMask disabled_features)
|
||||||
{
|
{
|
||||||
// Set version to Metal 2.3, that's all we're using. Use SPIRV-Cross version encoding.
|
// Set version to Metal 2.3, that's all we're using. Use SPIRV-Cross version encoding.
|
||||||
m_render_api = RenderAPI::Metal;
|
|
||||||
m_render_api_version = 20300;
|
m_render_api_version = 20300;
|
||||||
m_max_texture_size = GetMetalMaxTextureSize(m_device);
|
m_max_texture_size = GetMetalMaxTextureSize(m_device);
|
||||||
m_max_multisamples = GetMetalMaxMultisamples(m_device);
|
m_max_multisamples = GetMetalMaxMultisamples(m_device);
|
||||||
@ -416,6 +546,12 @@ void MetalDevice::DestroyDevice()
|
|||||||
m_render_cmdbuf = nil;
|
m_render_cmdbuf = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_main_swap_chain)
|
||||||
|
{
|
||||||
|
static_cast<MetalSwapChain*>(m_main_swap_chain.get())->Destroy(false);
|
||||||
|
m_main_swap_chain.reset();
|
||||||
|
}
|
||||||
|
|
||||||
DestroyBuffers();
|
DestroyBuffers();
|
||||||
|
|
||||||
for (auto& it : m_cleanup_objects)
|
for (auto& it : m_cleanup_objects)
|
||||||
@ -457,135 +593,6 @@ void MetalDevice::DestroyDevice()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MetalDevice::CreateLayer()
|
|
||||||
{
|
|
||||||
@autoreleasepool
|
|
||||||
{
|
|
||||||
RunOnMainThread([this]() {
|
|
||||||
@autoreleasepool
|
|
||||||
{
|
|
||||||
INFO_LOG("Creating a {}x{} Metal layer.", m_window_info.surface_width, m_window_info.surface_height);
|
|
||||||
const auto size =
|
|
||||||
CGSizeMake(static_cast<float>(m_window_info.surface_width), static_cast<float>(m_window_info.surface_height));
|
|
||||||
m_layer = [CAMetalLayer layer];
|
|
||||||
[m_layer setDevice:m_device];
|
|
||||||
[m_layer setDrawableSize:size];
|
|
||||||
|
|
||||||
// Default should be BGRA8.
|
|
||||||
const MTLPixelFormat layer_fmt = [m_layer pixelFormat];
|
|
||||||
m_window_info.surface_format = GetTextureFormatForMTLFormat(layer_fmt);
|
|
||||||
if (m_window_info.surface_format == GPUTexture::Format::Unknown)
|
|
||||||
{
|
|
||||||
ERROR_LOG("Invalid pixel format {} in layer, using BGRA8.", static_cast<u32>(layer_fmt));
|
|
||||||
[m_layer setPixelFormat:MTLPixelFormatBGRA8Unorm];
|
|
||||||
m_window_info.surface_format = GPUTexture::Format::BGRA8;
|
|
||||||
}
|
|
||||||
|
|
||||||
VERBOSE_LOG("Metal layer pixel format is {}.", GPUTexture::GetFormatName(m_window_info.surface_format));
|
|
||||||
|
|
||||||
NSView* view = GetWindowView();
|
|
||||||
[view setWantsLayer:TRUE];
|
|
||||||
[view setLayer:m_layer];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Metal does not support mailbox mode.
|
|
||||||
m_vsync_mode = (m_vsync_mode == GPUVSyncMode::Mailbox) ? GPUVSyncMode::FIFO : m_vsync_mode;
|
|
||||||
[m_layer setDisplaySyncEnabled:m_vsync_mode == GPUVSyncMode::FIFO];
|
|
||||||
|
|
||||||
DebugAssert(m_layer_pass_desc == nil);
|
|
||||||
m_layer_pass_desc = [[MTLRenderPassDescriptor renderPassDescriptor] retain];
|
|
||||||
m_layer_pass_desc.renderTargetWidth = m_window_info.surface_width;
|
|
||||||
m_layer_pass_desc.renderTargetHeight = m_window_info.surface_height;
|
|
||||||
m_layer_pass_desc.colorAttachments[0].loadAction = MTLLoadActionClear;
|
|
||||||
m_layer_pass_desc.colorAttachments[0].storeAction = MTLStoreActionStore;
|
|
||||||
m_layer_pass_desc.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MetalDevice::DestroyLayer()
|
|
||||||
{
|
|
||||||
if (m_layer == nil)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Should wait for previous command buffers to finish, which might be rendering to drawables.
|
|
||||||
WaitForPreviousCommandBuffers();
|
|
||||||
|
|
||||||
[m_layer_pass_desc release];
|
|
||||||
m_layer_pass_desc = nil;
|
|
||||||
m_window_info.surface_format = GPUTexture::Format::Unknown;
|
|
||||||
|
|
||||||
RunOnMainThread([this]() {
|
|
||||||
NSView* view = GetWindowView();
|
|
||||||
[view setLayer:nil];
|
|
||||||
[view setWantsLayer:FALSE];
|
|
||||||
[m_layer release];
|
|
||||||
m_layer = nullptr;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void MetalDevice::RenderBlankFrame()
|
|
||||||
{
|
|
||||||
DebugAssert(!InRenderPass());
|
|
||||||
if (m_layer == nil)
|
|
||||||
return;
|
|
||||||
|
|
||||||
@autoreleasepool
|
|
||||||
{
|
|
||||||
id<MTLDrawable> drawable = [[m_layer nextDrawable] retain];
|
|
||||||
m_layer_pass_desc.colorAttachments[0].texture = [drawable texture];
|
|
||||||
id<MTLRenderCommandEncoder> encoder = [m_render_cmdbuf renderCommandEncoderWithDescriptor:m_layer_pass_desc];
|
|
||||||
[encoder endEncoding];
|
|
||||||
[m_render_cmdbuf presentDrawable:drawable];
|
|
||||||
DeferRelease(drawable);
|
|
||||||
SubmitCommandBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MetalDevice::UpdateWindow()
|
|
||||||
{
|
|
||||||
if (InRenderPass())
|
|
||||||
EndRenderPass();
|
|
||||||
DestroyLayer();
|
|
||||||
|
|
||||||
if (!AcquireWindow(false))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateLayer())
|
|
||||||
{
|
|
||||||
ERROR_LOG("Failed to create layer on updated window");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MetalDevice::DestroySurface()
|
|
||||||
{
|
|
||||||
DestroyLayer();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MetalDevice::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
|
||||||
{
|
|
||||||
@autoreleasepool
|
|
||||||
{
|
|
||||||
m_window_info.surface_scale = new_window_scale;
|
|
||||||
if (static_cast<u32>(new_window_width) == m_window_info.surface_width &&
|
|
||||||
static_cast<u32>(new_window_height) == m_window_info.surface_height)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_window_info.surface_width = new_window_width;
|
|
||||||
m_window_info.surface_height = new_window_height;
|
|
||||||
|
|
||||||
[m_layer setDrawableSize:CGSizeMake(new_window_width, new_window_height)];
|
|
||||||
m_layer_pass_desc.renderTargetWidth = m_window_info.surface_width;
|
|
||||||
m_layer_pass_desc.renderTargetHeight = m_window_info.surface_height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string MetalDevice::GetDriverInfo() const
|
std::string MetalDevice::GetDriverInfo() const
|
||||||
{
|
{
|
||||||
@autoreleasepool
|
@autoreleasepool
|
||||||
@ -2082,6 +2089,8 @@ void MetalDevice::BeginRenderPass()
|
|||||||
// Rendering to view, but we got interrupted...
|
// Rendering to view, but we got interrupted...
|
||||||
desc.colorAttachments[0].texture = [m_layer_drawable texture];
|
desc.colorAttachments[0].texture = [m_layer_drawable texture];
|
||||||
desc.colorAttachments[0].loadAction = MTLLoadActionLoad;
|
desc.colorAttachments[0].loadAction = MTLLoadActionLoad;
|
||||||
|
desc.renderTargetWidth = m_current_framebuffer_size.width();
|
||||||
|
desc.renderTargetHeight = m_current_framebuffer_size.height();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -2157,6 +2166,10 @@ void MetalDevice::BeginRenderPass()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MetalTexture* rt_or_ds =
|
||||||
|
(m_num_current_render_targets > 0) ? m_current_render_targets[0] : m_current_depth_target;
|
||||||
|
m_current_framebuffer_size = GSVector4i(0, 0, rt_or_ds->GetWidth(), rt_or_ds->GetHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
m_render_encoder = [[m_render_cmdbuf renderCommandEncoderWithDescriptor:desc] retain];
|
m_render_encoder = [[m_render_cmdbuf renderCommandEncoderWithDescriptor:desc] retain];
|
||||||
@ -2218,7 +2231,7 @@ void MetalDevice::SetInitialEncoderState()
|
|||||||
|
|
||||||
void MetalDevice::SetViewportInRenderEncoder()
|
void MetalDevice::SetViewportInRenderEncoder()
|
||||||
{
|
{
|
||||||
const GSVector4i rc = ClampToFramebufferSize(m_current_viewport);
|
const GSVector4i rc = m_current_viewport.rintersect(m_current_framebuffer_size);
|
||||||
[m_render_encoder
|
[m_render_encoder
|
||||||
setViewport:(MTLViewport){static_cast<double>(rc.left), static_cast<double>(rc.top),
|
setViewport:(MTLViewport){static_cast<double>(rc.left), static_cast<double>(rc.top),
|
||||||
static_cast<double>(rc.width()), static_cast<double>(rc.height()), 0.0, 1.0}];
|
static_cast<double>(rc.width()), static_cast<double>(rc.height()), 0.0, 1.0}];
|
||||||
@ -2226,21 +2239,12 @@ void MetalDevice::SetViewportInRenderEncoder()
|
|||||||
|
|
||||||
void MetalDevice::SetScissorInRenderEncoder()
|
void MetalDevice::SetScissorInRenderEncoder()
|
||||||
{
|
{
|
||||||
const GSVector4i rc = ClampToFramebufferSize(m_current_scissor);
|
const GSVector4i rc = m_current_scissor.rintersect(m_current_framebuffer_size);
|
||||||
[m_render_encoder
|
[m_render_encoder
|
||||||
setScissorRect:(MTLScissorRect){static_cast<NSUInteger>(rc.left), static_cast<NSUInteger>(rc.top),
|
setScissorRect:(MTLScissorRect){static_cast<NSUInteger>(rc.left), static_cast<NSUInteger>(rc.top),
|
||||||
static_cast<NSUInteger>(rc.width()), static_cast<NSUInteger>(rc.height())}];
|
static_cast<NSUInteger>(rc.width()), static_cast<NSUInteger>(rc.height())}];
|
||||||
}
|
}
|
||||||
|
|
||||||
GSVector4i MetalDevice::ClampToFramebufferSize(const GSVector4i rc) const
|
|
||||||
{
|
|
||||||
const MetalTexture* rt_or_ds =
|
|
||||||
(m_num_current_render_targets > 0) ? m_current_render_targets[0] : m_current_depth_target;
|
|
||||||
const s32 clamp_width = rt_or_ds ? rt_or_ds->GetWidth() : m_window_info.surface_width;
|
|
||||||
const s32 clamp_height = rt_or_ds ? rt_or_ds->GetHeight() : m_window_info.surface_height;
|
|
||||||
return rc.rintersect(GSVector4i(0, 0, clamp_width, clamp_height));
|
|
||||||
}
|
|
||||||
|
|
||||||
void MetalDevice::PreDrawCheck()
|
void MetalDevice::PreDrawCheck()
|
||||||
{
|
{
|
||||||
if (!InRenderPass())
|
if (!InRenderPass())
|
||||||
@ -2413,35 +2417,35 @@ id<MTLBlitCommandEncoder> MetalDevice::GetBlitEncoder(bool is_inline)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GPUDevice::PresentResult MetalDevice::BeginPresent(u32 clear_color)
|
GPUDevice::PresentResult MetalDevice::BeginPresent(GPUSwapChain* swap_chain, u32 clear_color)
|
||||||
{
|
{
|
||||||
@autoreleasepool
|
@autoreleasepool
|
||||||
{
|
{
|
||||||
if (m_layer == nil)
|
|
||||||
{
|
|
||||||
TrimTexturePool();
|
|
||||||
return PresentResult::SkipPresent;
|
|
||||||
}
|
|
||||||
|
|
||||||
EndAnyEncoding();
|
EndAnyEncoding();
|
||||||
|
|
||||||
m_layer_drawable = [[m_layer nextDrawable] retain];
|
m_layer_drawable = [[static_cast<MetalSwapChain*>(swap_chain)->GetLayer() nextDrawable] retain];
|
||||||
if (m_layer_drawable == nil)
|
if (m_layer_drawable == nil)
|
||||||
{
|
{
|
||||||
|
WARNING_LOG("Failed to get drawable from layer.");
|
||||||
|
SubmitCommandBuffer();
|
||||||
TrimTexturePool();
|
TrimTexturePool();
|
||||||
return PresentResult::SkipPresent;
|
return PresentResult::SkipPresent;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetViewportAndScissor(0, 0, m_window_info.surface_width, m_window_info.surface_height);
|
m_current_framebuffer_size = GSVector4i(0, 0, swap_chain->GetWidth(), swap_chain->GetHeight());
|
||||||
|
SetViewportAndScissor(m_current_framebuffer_size);
|
||||||
|
|
||||||
// Set up rendering to layer.
|
// Set up rendering to layer.
|
||||||
const GSVector4 clear_color_v = GSVector4::rgba32(clear_color);
|
const GSVector4 clear_color_v = GSVector4::rgba32(clear_color);
|
||||||
id<MTLTexture> layer_texture = [m_layer_drawable texture];
|
id<MTLTexture> layer_texture = [m_layer_drawable texture];
|
||||||
m_layer_pass_desc.colorAttachments[0].texture = layer_texture;
|
MTLRenderPassDescriptor* desc = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||||
m_layer_pass_desc.colorAttachments[0].loadAction = MTLLoadActionClear;
|
desc.colorAttachments[0].texture = layer_texture;
|
||||||
m_layer_pass_desc.colorAttachments[0].clearColor =
|
desc.colorAttachments[0].loadAction = MTLLoadActionClear;
|
||||||
|
desc.colorAttachments[0].clearColor =
|
||||||
MTLClearColorMake(clear_color_v.r, clear_color_v.g, clear_color_v.g, clear_color_v.a);
|
MTLClearColorMake(clear_color_v.r, clear_color_v.g, clear_color_v.g, clear_color_v.a);
|
||||||
m_render_encoder = [[m_render_cmdbuf renderCommandEncoderWithDescriptor:m_layer_pass_desc] retain];
|
desc.renderTargetWidth = swap_chain->GetWidth();
|
||||||
|
desc.renderTargetHeight = swap_chain->GetHeight();
|
||||||
|
m_render_encoder = [[m_render_cmdbuf renderCommandEncoderWithDescriptor:desc] retain];
|
||||||
s_stats.num_render_passes++;
|
s_stats.num_render_passes++;
|
||||||
std::memset(m_current_render_targets.data(), 0, sizeof(m_current_render_targets));
|
std::memset(m_current_render_targets.data(), 0, sizeof(m_current_render_targets));
|
||||||
m_num_current_render_targets = 0;
|
m_num_current_render_targets = 0;
|
||||||
@ -2454,7 +2458,7 @@ GPUDevice::PresentResult MetalDevice::BeginPresent(u32 clear_color)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetalDevice::EndPresent(bool explicit_present, u64 present_time)
|
void MetalDevice::EndPresent(GPUSwapChain* swap_chain, bool explicit_present, u64 present_time)
|
||||||
{
|
{
|
||||||
DebugAssert(!explicit_present);
|
DebugAssert(!explicit_present);
|
||||||
DebugAssert(m_num_current_render_targets == 0 && !m_current_depth_target);
|
DebugAssert(m_num_current_render_targets == 0 && !m_current_depth_target);
|
||||||
@ -2480,7 +2484,7 @@ void MetalDevice::EndPresent(bool explicit_present, u64 present_time)
|
|||||||
TrimTexturePool();
|
TrimTexturePool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetalDevice::SubmitPresent()
|
void MetalDevice::SubmitPresent(GPUSwapChain* swap_chainwel)
|
||||||
{
|
{
|
||||||
Panic("Not supported by this API.");
|
Panic("Not supported by this API.");
|
||||||
}
|
}
|
||||||
@ -2585,12 +2589,18 @@ void MetalDevice::WaitForPreviousCommandBuffers()
|
|||||||
WaitForFenceCounter(m_current_fence_counter - 1);
|
WaitForFenceCounter(m_current_fence_counter - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetalDevice::ExecuteAndWaitForGPUIdle()
|
void MetalDevice::WaitForGPUIdle()
|
||||||
{
|
{
|
||||||
SubmitCommandBuffer(true);
|
SubmitCommandBuffer(true);
|
||||||
CleanupObjects();
|
CleanupObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MetalDevice::FlushCommands()
|
||||||
|
{
|
||||||
|
SubmitCommandBuffer();
|
||||||
|
TrimTexturePool();
|
||||||
|
}
|
||||||
|
|
||||||
void MetalDevice::CleanupObjects()
|
void MetalDevice::CleanupObjects()
|
||||||
{
|
{
|
||||||
const u64 counter = m_completed_fence_counter.load(std::memory_order_acquire);
|
const u64 counter = m_completed_fence_counter.load(std::memory_order_acquire);
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||||
|
|
||||||
|
class Error;
|
||||||
struct WindowInfo;
|
struct WindowInfo;
|
||||||
|
|
||||||
namespace CocoaTools {
|
namespace CocoaTools {
|
||||||
/// Creates metal layer on specified window surface.
|
/// Creates metal layer on specified window surface.
|
||||||
bool CreateMetalLayer(WindowInfo* wi);
|
void* CreateMetalLayer(const WindowInfo& wi, Error* error);
|
||||||
|
|
||||||
/// Destroys metal layer on specified window surface.
|
/// Destroys metal layer on specified window surface.
|
||||||
void DestroyMetalLayer(WindowInfo* wi);
|
void DestroyMetalLayer(const WindowInfo& wi, void* layer);
|
||||||
} // namespace CocoaTools
|
} // namespace CocoaTools
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
|
|
||||||
LOG_CHANNEL(MetalDevice);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
MetalStreamBuffer::MetalStreamBuffer() = default;
|
MetalStreamBuffer::MetalStreamBuffer() = default;
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LOG_CHANNEL(OpenGLContext);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
static bool ShouldPreferESContext()
|
static bool ShouldPreferESContext()
|
||||||
{
|
{
|
||||||
@ -105,13 +105,11 @@ static void DisableBrokenExtensions(const char* gl_vendor, const char* gl_render
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenGLContext::OpenGLContext(const WindowInfo& wi) : m_wi(wi)
|
OpenGLContext::OpenGLContext() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenGLContext::~OpenGLContext() = default;
|
OpenGLContext::~OpenGLContext() = default;
|
||||||
|
|
||||||
std::unique_ptr<OpenGLContext> OpenGLContext::Create(const WindowInfo& wi, Error* error)
|
std::unique_ptr<OpenGLContext> OpenGLContext::Create(WindowInfo& wi, SurfaceHandle* surface, Error* error)
|
||||||
{
|
{
|
||||||
static constexpr std::array<Version, 14> vlist = {{{Profile::Core, 4, 6},
|
static constexpr std::array<Version, 14> vlist = {{{Profile::Core, 4, 6},
|
||||||
{Profile::Core, 4, 5},
|
{Profile::Core, 4, 5},
|
||||||
@ -149,22 +147,22 @@ std::unique_ptr<OpenGLContext> OpenGLContext::Create(const WindowInfo& wi, Error
|
|||||||
|
|
||||||
std::unique_ptr<OpenGLContext> context;
|
std::unique_ptr<OpenGLContext> context;
|
||||||
#if defined(_WIN32) && !defined(_M_ARM64)
|
#if defined(_WIN32) && !defined(_M_ARM64)
|
||||||
context = OpenGLContextWGL::Create(wi, versions_to_try, error);
|
context = OpenGLContextWGL::Create(wi, surface, versions_to_try, error);
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
context = OpenGLContextAGL::Create(wi, versions_to_try, error);
|
context = OpenGLContextAGL::Create(wi, surface, versions_to_try, error);
|
||||||
#elif defined(__ANDROID__)
|
#elif defined(__ANDROID__)
|
||||||
context = OpenGLContextEGLAndroid::Create(wi, versions_to_try, error);
|
context = OpenGLContextEGLAndroid::Create(wi, surface, versions_to_try, error);
|
||||||
#else
|
#else
|
||||||
#if defined(ENABLE_X11)
|
#if defined(ENABLE_X11)
|
||||||
if (wi.type == WindowInfo::Type::X11)
|
if (wi.type == WindowInfo::Type::X11)
|
||||||
context = OpenGLContextEGLX11::Create(wi, versions_to_try, error);
|
context = OpenGLContextEGLX11::Create(wi, surface, versions_to_try, error);
|
||||||
#endif
|
#endif
|
||||||
#if defined(ENABLE_WAYLAND)
|
#if defined(ENABLE_WAYLAND)
|
||||||
if (wi.type == WindowInfo::Type::Wayland)
|
if (wi.type == WindowInfo::Type::Wayland)
|
||||||
context = OpenGLContextEGLWayland::Create(wi, versions_to_try, error);
|
context = OpenGLContextEGLWayland::Create(wi, surface, versions_to_try, error);
|
||||||
#endif
|
#endif
|
||||||
if (wi.type == WindowInfo::Type::Surfaceless)
|
if (wi.type == WindowInfo::Type::Surfaceless)
|
||||||
context = OpenGLContextEGL::Create(wi, versions_to_try, error);
|
context = OpenGLContextEGL::Create(wi, surface, versions_to_try, error);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!context)
|
if (!context)
|
||||||
|
@ -15,9 +15,12 @@ class Error;
|
|||||||
class OpenGLContext
|
class OpenGLContext
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OpenGLContext(const WindowInfo& wi);
|
OpenGLContext();
|
||||||
virtual ~OpenGLContext();
|
virtual ~OpenGLContext();
|
||||||
|
|
||||||
|
using SurfaceHandle = void*;
|
||||||
|
static constexpr SurfaceHandle MAIN_SURFACE = nullptr;
|
||||||
|
|
||||||
enum class Profile
|
enum class Profile
|
||||||
{
|
{
|
||||||
NoProfile,
|
NoProfile,
|
||||||
@ -32,26 +35,22 @@ public:
|
|||||||
int minor_version;
|
int minor_version;
|
||||||
};
|
};
|
||||||
|
|
||||||
ALWAYS_INLINE const WindowInfo& GetWindowInfo() const { return m_wi; }
|
|
||||||
ALWAYS_INLINE bool IsGLES() const { return (m_version.profile == Profile::ES); }
|
ALWAYS_INLINE bool IsGLES() const { return (m_version.profile == Profile::ES); }
|
||||||
ALWAYS_INLINE u32 GetSurfaceWidth() const { return m_wi.surface_width; }
|
|
||||||
ALWAYS_INLINE u32 GetSurfaceHeight() const { return m_wi.surface_height; }
|
|
||||||
ALWAYS_INLINE GPUTexture::Format GetSurfaceFormat() const { return m_wi.surface_format; }
|
|
||||||
|
|
||||||
virtual void* GetProcAddress(const char* name) = 0;
|
virtual void* GetProcAddress(const char* name) = 0;
|
||||||
virtual bool ChangeSurface(const WindowInfo& new_wi) = 0;
|
virtual SurfaceHandle CreateSurface(WindowInfo& wi, Error* error = nullptr) = 0;
|
||||||
virtual void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) = 0;
|
virtual void DestroySurface(SurfaceHandle handle) = 0;
|
||||||
|
virtual void ResizeSurface(WindowInfo& wi, SurfaceHandle handle) = 0;
|
||||||
virtual bool SwapBuffers() = 0;
|
virtual bool SwapBuffers() = 0;
|
||||||
virtual bool IsCurrent() const = 0;
|
virtual bool IsCurrent() const = 0;
|
||||||
virtual bool MakeCurrent() = 0;
|
virtual bool MakeCurrent(SurfaceHandle surface, Error* error = nullptr) = 0;
|
||||||
virtual bool DoneCurrent() = 0;
|
virtual bool DoneCurrent() = 0;
|
||||||
virtual bool SupportsNegativeSwapInterval() const = 0;
|
virtual bool SupportsNegativeSwapInterval() const = 0;
|
||||||
virtual bool SetSwapInterval(s32 interval) = 0;
|
virtual bool SetSwapInterval(s32 interval, Error* error = nullptr) = 0;
|
||||||
virtual std::unique_ptr<OpenGLContext> CreateSharedContext(const WindowInfo& wi, Error* error) = 0;
|
virtual std::unique_ptr<OpenGLContext> CreateSharedContext(WindowInfo& wi, SurfaceHandle* surface, Error* error) = 0;
|
||||||
|
|
||||||
static std::unique_ptr<OpenGLContext> Create(const WindowInfo& wi, Error* error);
|
static std::unique_ptr<OpenGLContext> Create(WindowInfo& wi, SurfaceHandle* surface, Error* error);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
WindowInfo m_wi;
|
|
||||||
Version m_version = {};
|
Version m_version = {};
|
||||||
};
|
};
|
||||||
|
@ -18,32 +18,30 @@ struct NSView;
|
|||||||
class OpenGLContextAGL final : public OpenGLContext
|
class OpenGLContextAGL final : public OpenGLContext
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OpenGLContextAGL(const WindowInfo& wi);
|
OpenGLContextAGL();
|
||||||
~OpenGLContextAGL() override;
|
~OpenGLContextAGL() override;
|
||||||
|
|
||||||
static std::unique_ptr<OpenGLContext> Create(const WindowInfo& wi, std::span<const Version> versions_to_try,
|
static std::unique_ptr<OpenGLContext> Create(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
Error* error);
|
std::span<const Version> versions_to_try, Error* error);
|
||||||
|
|
||||||
void* GetProcAddress(const char* name) override;
|
void* GetProcAddress(const char* name) override;
|
||||||
bool ChangeSurface(const WindowInfo& new_wi) override;
|
SurfaceHandle CreateSurface(WindowInfo& wi, Error* error = nullptr) override;
|
||||||
void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
|
void DestroySurface(SurfaceHandle handle) override;
|
||||||
|
void ResizeSurface(WindowInfo& wi, SurfaceHandle handle) override;
|
||||||
bool SwapBuffers() override;
|
bool SwapBuffers() override;
|
||||||
bool IsCurrent() const override;
|
bool IsCurrent() const override;
|
||||||
bool MakeCurrent() override;
|
bool MakeCurrent(SurfaceHandle surface, Error* error = nullptr) override;
|
||||||
bool DoneCurrent() override;
|
bool DoneCurrent() override;
|
||||||
bool SupportsNegativeSwapInterval() const override;
|
bool SupportsNegativeSwapInterval() const override;
|
||||||
bool SetSwapInterval(s32 interval) override;
|
bool SetSwapInterval(s32 interval, Error* error = nullptr) override;
|
||||||
std::unique_ptr<OpenGLContext> CreateSharedContext(const WindowInfo& wi, Error* error) override;
|
std::unique_ptr<OpenGLContext> CreateSharedContext(WindowInfo& wi, SurfaceHandle* surface, Error* error) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ALWAYS_INLINE NSView* GetView() const { return static_cast<NSView*>((__bridge NSView*)m_wi.window_handle); }
|
bool Initialize(WindowInfo& wi, SurfaceHandle* surface, std::span<const Version> versions_to_try, Error* error);
|
||||||
|
bool CreateContext(NSOpenGLContext* share_context, int profile, Error* error);
|
||||||
|
|
||||||
bool Initialize(std::span<const Version> versions_to_try, Error* error);
|
static void BindContextToView(WindowInfo& wi, NSOpenGLContext* context);
|
||||||
bool CreateContext(NSOpenGLContext* share_context, int profile, bool make_current, Error* error);
|
static void UpdateSurfaceSize(WindowInfo& wi, NSOpenGLContext* context);
|
||||||
void BindContextToView();
|
|
||||||
|
|
||||||
// returns true if dimensions have changed
|
|
||||||
bool UpdateDimensions();
|
|
||||||
|
|
||||||
NSOpenGLContext* m_context = nullptr;
|
NSOpenGLContext* m_context = nullptr;
|
||||||
NSOpenGLPixelFormat* m_pixel_format = nullptr;
|
NSOpenGLPixelFormat* m_pixel_format = nullptr;
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
|
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|
||||||
LOG_CHANNEL(OpenGLContext);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
OpenGLContextAGL::OpenGLContextAGL(const WindowInfo& wi) : OpenGLContext(wi)
|
OpenGLContextAGL::OpenGLContextAGL() : OpenGLContext()
|
||||||
{
|
{
|
||||||
m_opengl_module_handle = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", RTLD_NOW);
|
m_opengl_module_handle = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", RTLD_NOW);
|
||||||
if (!m_opengl_module_handle)
|
if (!m_opengl_module_handle)
|
||||||
@ -33,24 +33,28 @@ OpenGLContextAGL::~OpenGLContextAGL()
|
|||||||
dlclose(m_opengl_module_handle);
|
dlclose(m_opengl_module_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<OpenGLContext> OpenGLContextAGL::Create(const WindowInfo& wi, std::span<const Version> versions_to_try, Error* error)
|
std::unique_ptr<OpenGLContext> OpenGLContextAGL::Create(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
|
std::span<const Version> versions_to_try, Error* error)
|
||||||
{
|
{
|
||||||
std::unique_ptr<OpenGLContextAGL> context = std::make_unique<OpenGLContextAGL>(wi);
|
std::unique_ptr<OpenGLContextAGL> context = std::make_unique<OpenGLContextAGL>();
|
||||||
if (!context->Initialize(versions_to_try, error))
|
if (!context->Initialize(wi, surface, versions_to_try, error))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextAGL::Initialize(const std::span<const Version> versions_to_try, Error* error)
|
bool OpenGLContextAGL::Initialize(WindowInfo& wi, SurfaceHandle* surface, std::span<const Version> versions_to_try,
|
||||||
|
Error* error)
|
||||||
{
|
{
|
||||||
for (const Version& cv : versions_to_try)
|
for (const Version& cv : versions_to_try)
|
||||||
{
|
{
|
||||||
if (cv.profile == Profile::NoProfile && CreateContext(nullptr, NSOpenGLProfileVersionLegacy, true, error))
|
if (cv.profile == Profile::NoProfile && CreateContext(nullptr, NSOpenGLProfileVersionLegacy, error))
|
||||||
{
|
{
|
||||||
// we already have the dummy context, so just use that
|
// we already have the dummy context, so just use that
|
||||||
|
BindContextToView(wi, m_context);
|
||||||
|
*surface = wi.window_handle;
|
||||||
m_version = cv;
|
m_version = cv;
|
||||||
return true;
|
return MakeCurrent(*surface, error);
|
||||||
}
|
}
|
||||||
else if (cv.profile == Profile::Core)
|
else if (cv.profile == Profile::Core)
|
||||||
{
|
{
|
||||||
@ -59,10 +63,12 @@ bool OpenGLContextAGL::Initialize(const std::span<const Version> versions_to_try
|
|||||||
|
|
||||||
const NSOpenGLPixelFormatAttribute profile =
|
const NSOpenGLPixelFormatAttribute profile =
|
||||||
(cv.major_version > 3 || cv.minor_version > 2) ? NSOpenGLProfileVersion4_1Core : NSOpenGLProfileVersion3_2Core;
|
(cv.major_version > 3 || cv.minor_version > 2) ? NSOpenGLProfileVersion4_1Core : NSOpenGLProfileVersion3_2Core;
|
||||||
if (CreateContext(nullptr, static_cast<int>(profile), true, error))
|
if (CreateContext(nullptr, static_cast<int>(profile), error))
|
||||||
{
|
{
|
||||||
|
BindContextToView(wi, m_context);
|
||||||
|
*surface = wi.window_handle;
|
||||||
m_version = cv;
|
m_version = cv;
|
||||||
return true;
|
return MakeCurrent(*surface, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,41 +86,31 @@ void* OpenGLContextAGL::GetProcAddress(const char* name)
|
|||||||
return dlsym(RTLD_NEXT, name);
|
return dlsym(RTLD_NEXT, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextAGL::ChangeSurface(const WindowInfo& new_wi)
|
OpenGLContext::SurfaceHandle OpenGLContextAGL::CreateSurface(WindowInfo& wi, Error* error)
|
||||||
{
|
{
|
||||||
m_wi = new_wi;
|
if (m_context.view != nil)
|
||||||
BindContextToView();
|
{
|
||||||
return true;
|
Error::SetStringView(error, "Multiple windows are not supported on this backend.");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
BindContextToView(wi, m_context);
|
||||||
|
return wi.window_handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLContextAGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/)
|
void OpenGLContextAGL::DestroySurface(SurfaceHandle handle)
|
||||||
{
|
{
|
||||||
UpdateDimensions();
|
if (!handle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DebugAssert(m_context.view == handle);
|
||||||
|
[m_context setView:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextAGL::UpdateDimensions()
|
void OpenGLContextAGL::ResizeSurface(WindowInfo& wi, SurfaceHandle handle)
|
||||||
{
|
{
|
||||||
const NSSize window_size = [GetView() frame].size;
|
DebugAssert(m_context.view == handle);
|
||||||
const CGFloat window_scale = [[GetView() window] backingScaleFactor];
|
UpdateSurfaceSize(wi, m_context);
|
||||||
const u32 new_width = static_cast<u32>(static_cast<CGFloat>(window_size.width) * window_scale);
|
|
||||||
const u32 new_height = static_cast<u32>(static_cast<CGFloat>(window_size.height) * window_scale);
|
|
||||||
|
|
||||||
if (m_wi.surface_width == new_width && m_wi.surface_height == new_height)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
m_wi.surface_width = new_width;
|
|
||||||
m_wi.surface_height = new_height;
|
|
||||||
|
|
||||||
dispatch_block_t block = ^{
|
|
||||||
[m_context update];
|
|
||||||
};
|
|
||||||
|
|
||||||
if ([NSThread isMainThread])
|
|
||||||
block();
|
|
||||||
else
|
|
||||||
dispatch_sync(dispatch_get_main_queue(), block);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextAGL::SwapBuffers()
|
bool OpenGLContextAGL::SwapBuffers()
|
||||||
@ -128,8 +124,9 @@ bool OpenGLContextAGL::IsCurrent() const
|
|||||||
return (m_context != nil && [NSOpenGLContext currentContext] == m_context);
|
return (m_context != nil && [NSOpenGLContext currentContext] == m_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextAGL::MakeCurrent()
|
bool OpenGLContextAGL::MakeCurrent(SurfaceHandle surface, Error* error)
|
||||||
{
|
{
|
||||||
|
DebugAssert(surface == m_context.view);
|
||||||
[m_context makeCurrentContext];
|
[m_context makeCurrentContext];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -145,35 +142,21 @@ bool OpenGLContextAGL::SupportsNegativeSwapInterval() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextAGL::SetSwapInterval(s32 interval)
|
bool OpenGLContextAGL::SetSwapInterval(s32 interval, Error* error)
|
||||||
{
|
{
|
||||||
GLint gl_interval = static_cast<GLint>(interval);
|
GLint gl_interval = static_cast<GLint>(interval);
|
||||||
[m_context setValues:&gl_interval forParameter:NSOpenGLCPSwapInterval];
|
[m_context setValues:&gl_interval forParameter:NSOpenGLCPSwapInterval];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<OpenGLContext> OpenGLContextAGL::CreateSharedContext(const WindowInfo& wi, Error* error)
|
std::unique_ptr<OpenGLContext> OpenGLContextAGL::CreateSharedContext(WindowInfo& wi, SurfaceHandle* handle,
|
||||||
|
Error* error)
|
||||||
{
|
{
|
||||||
std::unique_ptr<OpenGLContextAGL> context = std::make_unique<OpenGLContextAGL>(wi);
|
Error::SetStringView(error, "Not supported on this backend.");
|
||||||
|
return {};
|
||||||
context->m_context = [[NSOpenGLContext alloc] initWithFormat:m_pixel_format shareContext:m_context];
|
|
||||||
if (context->m_context == nil)
|
|
||||||
{
|
|
||||||
Error::SetStringView(error, "NSOpenGLContext initWithFormat failed");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
context->m_version = m_version;
|
|
||||||
context->m_pixel_format = m_pixel_format;
|
|
||||||
[context->m_pixel_format retain];
|
|
||||||
|
|
||||||
if (wi.type == WindowInfo::Type::MacOS)
|
|
||||||
context->BindContextToView();
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextAGL::CreateContext(NSOpenGLContext* share_context, int profile, bool make_current, Error* error)
|
bool OpenGLContextAGL::CreateContext(NSOpenGLContext* share_context, int profile, Error* error)
|
||||||
{
|
{
|
||||||
if (m_context)
|
if (m_context)
|
||||||
{
|
{
|
||||||
@ -201,26 +184,18 @@ bool OpenGLContextAGL::CreateContext(NSOpenGLContext* share_context, int profile
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_wi.type == WindowInfo::Type::MacOS)
|
|
||||||
BindContextToView();
|
|
||||||
|
|
||||||
if (make_current)
|
|
||||||
[m_context makeCurrentContext];
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLContextAGL::BindContextToView()
|
void OpenGLContextAGL::BindContextToView(WindowInfo& wi, NSOpenGLContext* context)
|
||||||
{
|
{
|
||||||
NSView* const view = GetView();
|
NSView* const view = static_cast<NSView*>((__bridge NSView*)wi.window_handle);
|
||||||
NSWindow* const window = [view window];
|
NSWindow* const window = [view window];
|
||||||
[view setWantsBestResolutionOpenGLSurface:YES];
|
[view setWantsBestResolutionOpenGLSurface:YES];
|
||||||
|
|
||||||
UpdateDimensions();
|
|
||||||
|
|
||||||
dispatch_block_t block = ^{
|
dispatch_block_t block = ^{
|
||||||
[window makeFirstResponder:view];
|
[window makeFirstResponder:view];
|
||||||
[m_context setView:view];
|
[context setView:view];
|
||||||
[window makeKeyAndOrderFront:nil];
|
[window makeKeyAndOrderFront:nil];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -228,4 +203,32 @@ void OpenGLContextAGL::BindContextToView()
|
|||||||
block();
|
block();
|
||||||
else
|
else
|
||||||
dispatch_sync(dispatch_get_main_queue(), block);
|
dispatch_sync(dispatch_get_main_queue(), block);
|
||||||
|
|
||||||
|
UpdateSurfaceSize(wi, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLContextAGL::UpdateSurfaceSize(WindowInfo& wi, NSOpenGLContext* context)
|
||||||
|
{
|
||||||
|
NSView* const view = static_cast<NSView*>((__bridge NSView*)wi.window_handle);
|
||||||
|
const NSSize window_size = [view frame].size;
|
||||||
|
const CGFloat window_scale = [[view window] backingScaleFactor];
|
||||||
|
const u32 new_width = static_cast<u32>(static_cast<CGFloat>(window_size.width) * window_scale);
|
||||||
|
const u32 new_height = static_cast<u32>(static_cast<CGFloat>(window_size.height) * window_scale);
|
||||||
|
|
||||||
|
if (wi.surface_width == new_width && wi.surface_height == new_height)
|
||||||
|
return;
|
||||||
|
|
||||||
|
wi.surface_width = static_cast<u16>(new_width);
|
||||||
|
wi.surface_height = static_cast<u16>(new_height);
|
||||||
|
|
||||||
|
dispatch_block_t block = ^{
|
||||||
|
[context update];
|
||||||
|
};
|
||||||
|
|
||||||
|
if ([NSThread isMainThread])
|
||||||
|
block();
|
||||||
|
else
|
||||||
|
dispatch_sync(dispatch_get_main_queue(), block);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
LOG_CHANNEL(OpenGLContext);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
static DynamicLibrary s_egl_library;
|
static DynamicLibrary s_egl_library;
|
||||||
static std::atomic_uint32_t s_egl_refcount = 0;
|
static std::atomic_uint32_t s_egl_refcount = 0;
|
||||||
@ -67,34 +67,42 @@ static bool LoadGLADEGL(EGLDisplay display, Error* error)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenGLContextEGL::OpenGLContextEGL(const WindowInfo& wi) : OpenGLContext(wi)
|
OpenGLContextEGL::OpenGLContextEGL() : OpenGLContext()
|
||||||
{
|
{
|
||||||
LoadEGL();
|
LoadEGL();
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenGLContextEGL::~OpenGLContextEGL()
|
OpenGLContextEGL::~OpenGLContextEGL()
|
||||||
{
|
{
|
||||||
DestroySurface();
|
if (m_context != EGL_NO_CONTEXT && eglGetCurrentContext() == m_context)
|
||||||
DestroyContext();
|
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||||
|
|
||||||
|
if (m_pbuffer_surface != EGL_NO_SURFACE)
|
||||||
|
eglDestroySurface(m_display, m_pbuffer_surface);
|
||||||
|
|
||||||
|
if (m_context != EGL_NO_CONTEXT)
|
||||||
|
eglDestroyContext(m_display, m_context);
|
||||||
|
|
||||||
UnloadEGL();
|
UnloadEGL();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<OpenGLContext> OpenGLContextEGL::Create(const WindowInfo& wi, std::span<const Version> versions_to_try,
|
std::unique_ptr<OpenGLContext> OpenGLContextEGL::Create(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
Error* error)
|
std::span<const Version> versions_to_try, Error* error)
|
||||||
{
|
{
|
||||||
std::unique_ptr<OpenGLContextEGL> context = std::make_unique<OpenGLContextEGL>(wi);
|
std::unique_ptr<OpenGLContextEGL> context = std::make_unique<OpenGLContextEGL>();
|
||||||
if (!context->Initialize(versions_to_try, error))
|
if (!context->Initialize(wi, surface, versions_to_try, error))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextEGL::Initialize(std::span<const Version> versions_to_try, Error* error)
|
bool OpenGLContextEGL::Initialize(WindowInfo& wi, SurfaceHandle* surface, std::span<const Version> versions_to_try,
|
||||||
|
Error* error)
|
||||||
{
|
{
|
||||||
if (!LoadGLADEGL(EGL_NO_DISPLAY, error))
|
if (!LoadGLADEGL(EGL_NO_DISPLAY, error))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_display = GetPlatformDisplay(error);
|
m_display = GetPlatformDisplay(wi, error);
|
||||||
if (m_display == EGL_NO_DISPLAY)
|
if (m_display == EGL_NO_DISPLAY)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -117,7 +125,7 @@ bool OpenGLContextEGL::Initialize(std::span<const Version> versions_to_try, Erro
|
|||||||
|
|
||||||
for (const Version& cv : versions_to_try)
|
for (const Version& cv : versions_to_try)
|
||||||
{
|
{
|
||||||
if (CreateContextAndSurface(cv, nullptr, true))
|
if (CreateContextAndSurface(wi, surface, cv, nullptr, true, error))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,24 +133,30 @@ bool OpenGLContextEGL::Initialize(std::span<const Version> versions_to_try, Erro
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
EGLDisplay OpenGLContextEGL::GetPlatformDisplay(Error* error)
|
EGLDisplay OpenGLContextEGL::GetPlatformDisplay(const WindowInfo& wi, Error* error)
|
||||||
{
|
{
|
||||||
EGLDisplay dpy = TryGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, "EGL_MESA_platform_surfaceless");
|
EGLDisplay dpy =
|
||||||
|
TryGetPlatformDisplay(wi.display_connection, EGL_PLATFORM_SURFACELESS_MESA, "EGL_MESA_platform_surfaceless");
|
||||||
if (dpy == EGL_NO_DISPLAY)
|
if (dpy == EGL_NO_DISPLAY)
|
||||||
dpy = GetFallbackDisplay(error);
|
dpy = GetFallbackDisplay(wi.display_connection, error);
|
||||||
|
|
||||||
return dpy;
|
return dpy;
|
||||||
}
|
}
|
||||||
|
|
||||||
EGLSurface OpenGLContextEGL::CreatePlatformSurface(EGLConfig config, void* win, Error* error)
|
EGLSurface OpenGLContextEGL::CreatePlatformSurface(EGLConfig config, const WindowInfo& wi, Error* error)
|
||||||
{
|
{
|
||||||
EGLSurface surface = TryCreatePlatformSurface(config, win, error);
|
EGLSurface surface = TryCreatePlatformSurface(config, wi.window_handle, error);
|
||||||
if (!surface)
|
if (surface == EGL_NO_SURFACE)
|
||||||
surface = CreateFallbackSurface(config, win, error);
|
surface = CreateFallbackSurface(config, wi.window_handle, error);
|
||||||
return surface;
|
return surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
EGLDisplay OpenGLContextEGL::TryGetPlatformDisplay(EGLenum platform, const char* platform_ext)
|
bool OpenGLContextEGL::SupportsSurfaceless() const
|
||||||
|
{
|
||||||
|
return GLAD_EGL_KHR_surfaceless_context;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLDisplay OpenGLContextEGL::TryGetPlatformDisplay(void* display, EGLenum platform, const char* platform_ext)
|
||||||
{
|
{
|
||||||
const char* extensions_str = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
const char* extensions_str = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
||||||
if (!extensions_str)
|
if (!extensions_str)
|
||||||
@ -160,7 +174,7 @@ EGLDisplay OpenGLContextEGL::TryGetPlatformDisplay(EGLenum platform, const char*
|
|||||||
(PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
|
(PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
|
||||||
if (get_platform_display_ext)
|
if (get_platform_display_ext)
|
||||||
{
|
{
|
||||||
dpy = get_platform_display_ext(platform, m_wi.display_connection, nullptr);
|
dpy = get_platform_display_ext(platform, display, nullptr);
|
||||||
m_use_ext_platform_base = (dpy != EGL_NO_DISPLAY);
|
m_use_ext_platform_base = (dpy != EGL_NO_DISPLAY);
|
||||||
if (!m_use_ext_platform_base)
|
if (!m_use_ext_platform_base)
|
||||||
{
|
{
|
||||||
@ -181,7 +195,7 @@ EGLDisplay OpenGLContextEGL::TryGetPlatformDisplay(EGLenum platform, const char*
|
|||||||
return dpy;
|
return dpy;
|
||||||
}
|
}
|
||||||
|
|
||||||
EGLSurface OpenGLContextEGL::TryCreatePlatformSurface(EGLConfig config, void* win, Error* error)
|
EGLSurface OpenGLContextEGL::TryCreatePlatformSurface(EGLConfig config, void* window, Error* error)
|
||||||
{
|
{
|
||||||
EGLSurface surface = EGL_NO_SURFACE;
|
EGLSurface surface = EGL_NO_SURFACE;
|
||||||
if (m_use_ext_platform_base)
|
if (m_use_ext_platform_base)
|
||||||
@ -190,7 +204,7 @@ EGLSurface OpenGLContextEGL::TryCreatePlatformSurface(EGLConfig config, void* wi
|
|||||||
(PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
|
(PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
|
||||||
if (create_platform_window_surface_ext)
|
if (create_platform_window_surface_ext)
|
||||||
{
|
{
|
||||||
surface = create_platform_window_surface_ext(m_display, config, win, nullptr);
|
surface = create_platform_window_surface_ext(m_display, config, window, nullptr);
|
||||||
if (surface == EGL_NO_SURFACE)
|
if (surface == EGL_NO_SURFACE)
|
||||||
{
|
{
|
||||||
const EGLint err = eglGetError();
|
const EGLint err = eglGetError();
|
||||||
@ -206,11 +220,11 @@ EGLSurface OpenGLContextEGL::TryCreatePlatformSurface(EGLConfig config, void* wi
|
|||||||
return surface;
|
return surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
EGLDisplay OpenGLContextEGL::GetFallbackDisplay(Error* error)
|
EGLDisplay OpenGLContextEGL::GetFallbackDisplay(void* display, Error* error)
|
||||||
{
|
{
|
||||||
WARNING_LOG("Using fallback eglGetDisplay() path.");
|
WARNING_LOG("Using fallback eglGetDisplay() path.");
|
||||||
|
|
||||||
EGLDisplay dpy = eglGetDisplay(m_wi.display_connection);
|
EGLDisplay dpy = eglGetDisplay((EGLNativeDisplayType)display);
|
||||||
if (dpy == EGL_NO_DISPLAY)
|
if (dpy == EGL_NO_DISPLAY)
|
||||||
{
|
{
|
||||||
const EGLint err = eglGetError();
|
const EGLint err = eglGetError();
|
||||||
@ -234,61 +248,56 @@ EGLSurface OpenGLContextEGL::CreateFallbackSurface(EGLConfig config, void* win,
|
|||||||
return surface;
|
return surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenGLContextEGL::DestroyPlatformSurface(EGLSurface surface)
|
||||||
|
{
|
||||||
|
eglDestroySurface(m_display, surface);
|
||||||
|
}
|
||||||
|
|
||||||
void* OpenGLContextEGL::GetProcAddress(const char* name)
|
void* OpenGLContextEGL::GetProcAddress(const char* name)
|
||||||
{
|
{
|
||||||
return reinterpret_cast<void*>(eglGetProcAddress(name));
|
return reinterpret_cast<void*>(eglGetProcAddress(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextEGL::ChangeSurface(const WindowInfo& new_wi)
|
OpenGLContext::SurfaceHandle OpenGLContextEGL::CreateSurface(WindowInfo& wi, Error* error /* = nullptr */)
|
||||||
{
|
{
|
||||||
const bool was_current = (eglGetCurrentContext() == m_context);
|
if (wi.IsSurfaceless()) [[unlikely]]
|
||||||
if (was_current)
|
|
||||||
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
||||||
|
|
||||||
if (m_surface != EGL_NO_SURFACE)
|
|
||||||
{
|
{
|
||||||
eglDestroySurface(m_display, m_surface);
|
Error::SetStringView(error, "Trying to create a surfaceless surface.");
|
||||||
m_surface = EGL_NO_SURFACE;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_wi = new_wi;
|
EGLSurface surface = CreatePlatformSurface(m_config, wi, error);
|
||||||
if (!CreateSurface())
|
if (surface == EGL_NO_SURFACE)
|
||||||
return false;
|
return nullptr;
|
||||||
|
|
||||||
if (was_current && !eglMakeCurrent(m_display, m_surface, m_surface, m_context))
|
UpdateWindowInfoSize(wi, surface);
|
||||||
{
|
return (SurfaceHandle)surface;
|
||||||
ERROR_LOG("Failed to make context current again after surface change");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLContextEGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/)
|
void OpenGLContextEGL::DestroySurface(SurfaceHandle handle)
|
||||||
{
|
{
|
||||||
if (new_surface_width == 0 && new_surface_height == 0)
|
// pbuffer surface?
|
||||||
{
|
if (!handle)
|
||||||
EGLint surface_width, surface_height;
|
return;
|
||||||
if (eglQuerySurface(m_display, m_surface, EGL_WIDTH, &surface_width) &&
|
|
||||||
eglQuerySurface(m_display, m_surface, EGL_HEIGHT, &surface_height)) [[likely]]
|
|
||||||
{
|
|
||||||
m_wi.surface_width = static_cast<u32>(surface_width);
|
|
||||||
m_wi.surface_height = static_cast<u32>(surface_height);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ERROR_LOG("eglQuerySurface() failed: 0x{:X}", eglGetError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_wi.surface_width = new_surface_width;
|
EGLSurface surface = (EGLSurface)handle;
|
||||||
m_wi.surface_height = new_surface_height;
|
if (eglGetCurrentSurface(EGL_DRAW) == surface)
|
||||||
|
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||||
|
|
||||||
|
DestroyPlatformSurface(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLContextEGL::ResizeSurface(WindowInfo& wi, SurfaceHandle handle)
|
||||||
|
{
|
||||||
|
if (!handle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UpdateWindowInfoSize(wi, (EGLSurface)handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextEGL::SwapBuffers()
|
bool OpenGLContextEGL::SwapBuffers()
|
||||||
{
|
{
|
||||||
return eglSwapBuffers(m_display, m_surface);
|
return eglSwapBuffers(m_display, m_current_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextEGL::IsCurrent() const
|
bool OpenGLContextEGL::IsCurrent() const
|
||||||
@ -296,20 +305,29 @@ bool OpenGLContextEGL::IsCurrent() const
|
|||||||
return m_context && eglGetCurrentContext() == m_context;
|
return m_context && eglGetCurrentContext() == m_context;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextEGL::MakeCurrent()
|
bool OpenGLContextEGL::MakeCurrent(SurfaceHandle surface, Error* error /* = nullptr */)
|
||||||
{
|
{
|
||||||
if (!eglMakeCurrent(m_display, m_surface, m_surface, m_context)) [[unlikely]]
|
EGLSurface esurface = surface ? (EGLSurface)surface : GetSurfacelessSurface();
|
||||||
|
if (esurface == m_current_surface)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!eglMakeCurrent(m_display, esurface, esurface, m_context)) [[unlikely]]
|
||||||
{
|
{
|
||||||
ERROR_LOG("eglMakeCurrent() failed: 0x{:X}", eglGetError());
|
Error::SetStringFmt(error, "eglMakeCurrent() failed: 0x{:X}", eglGetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_current_surface = esurface;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextEGL::DoneCurrent()
|
bool OpenGLContextEGL::DoneCurrent()
|
||||||
{
|
{
|
||||||
return eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
if (!eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_current_surface = EGL_NO_SURFACE;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextEGL::SupportsNegativeSwapInterval() const
|
bool OpenGLContextEGL::SupportsNegativeSwapInterval() const
|
||||||
@ -317,17 +335,24 @@ bool OpenGLContextEGL::SupportsNegativeSwapInterval() const
|
|||||||
return m_supports_negative_swap_interval;
|
return m_supports_negative_swap_interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextEGL::SetSwapInterval(s32 interval)
|
bool OpenGLContextEGL::SetSwapInterval(s32 interval, Error* error /* = nullptr */)
|
||||||
{
|
{
|
||||||
return eglSwapInterval(m_display, interval);
|
if (!eglSwapInterval(m_display, interval))
|
||||||
|
{
|
||||||
|
Error::SetStringFmt(error, "eglMakeCurrent() failed: 0x{:X}", eglGetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<OpenGLContext> OpenGLContextEGL::CreateSharedContext(const WindowInfo& wi, Error* error)
|
std::unique_ptr<OpenGLContext> OpenGLContextEGL::CreateSharedContext(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
|
Error* error)
|
||||||
{
|
{
|
||||||
std::unique_ptr<OpenGLContextEGL> context = std::make_unique<OpenGLContextEGL>(wi);
|
std::unique_ptr<OpenGLContextEGL> context = std::make_unique<OpenGLContextEGL>();
|
||||||
context->m_display = m_display;
|
context->m_display = m_display;
|
||||||
|
|
||||||
if (!context->CreateContextAndSurface(m_version, m_context, false))
|
if (!context->CreateContextAndSurface(wi, surface, m_version, m_context, false, error))
|
||||||
{
|
{
|
||||||
Error::SetStringView(error, "Failed to create context/surface");
|
Error::SetStringView(error, "Failed to create context/surface");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -336,63 +361,33 @@ std::unique_ptr<OpenGLContext> OpenGLContextEGL::CreateSharedContext(const Windo
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextEGL::CreateSurface()
|
EGLSurface OpenGLContextEGL::GetSurfacelessSurface()
|
||||||
{
|
{
|
||||||
if (m_wi.type == WindowInfo::Type::Surfaceless)
|
return SupportsSurfaceless() ? EGL_NO_SURFACE : GetPBufferSurface(nullptr);
|
||||||
{
|
|
||||||
if (GLAD_EGL_KHR_surfaceless_context)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return CreatePBufferSurface();
|
|
||||||
}
|
|
||||||
|
|
||||||
Error error;
|
|
||||||
m_surface = CreatePlatformSurface(m_config, m_wi.window_handle, &error);
|
|
||||||
if (m_surface == EGL_NO_SURFACE)
|
|
||||||
{
|
|
||||||
ERROR_LOG("Failed to create platform surface: {}", error.GetDescription());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some implementations may require the size to be queried at runtime.
|
|
||||||
EGLint surface_width, surface_height;
|
|
||||||
if (eglQuerySurface(m_display, m_surface, EGL_WIDTH, &surface_width) &&
|
|
||||||
eglQuerySurface(m_display, m_surface, EGL_HEIGHT, &surface_height))
|
|
||||||
{
|
|
||||||
m_wi.surface_width = static_cast<u32>(surface_width);
|
|
||||||
m_wi.surface_height = static_cast<u32>(surface_height);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ERROR_LOG("eglQuerySurface() failed: 0x{:X}", eglGetError());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_wi.surface_format = GetSurfaceTextureFormat();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextEGL::CreatePBufferSurface()
|
EGLSurface OpenGLContextEGL::GetPBufferSurface(Error* error)
|
||||||
{
|
{
|
||||||
const u32 width = std::max<u32>(m_wi.surface_width, 1);
|
if (m_pbuffer_surface)
|
||||||
const u32 height = std::max<u32>(m_wi.surface_height, 1);
|
return m_pbuffer_surface;
|
||||||
|
|
||||||
// TODO: Format
|
|
||||||
EGLint attrib_list[] = {
|
EGLint attrib_list[] = {
|
||||||
EGL_WIDTH, static_cast<EGLint>(width), EGL_HEIGHT, static_cast<EGLint>(height), EGL_NONE,
|
EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
m_surface = eglCreatePbufferSurface(m_display, m_config, attrib_list);
|
m_pbuffer_surface = eglCreatePbufferSurface(m_display, m_config, attrib_list);
|
||||||
if (!m_surface) [[unlikely]]
|
if (!m_pbuffer_surface) [[unlikely]]
|
||||||
{
|
{
|
||||||
ERROR_LOG("eglCreatePbufferSurface() failed: {}", eglGetError());
|
if (error)
|
||||||
return false;
|
error->SetStringFmt("eglCreatePbufferSurface() failed: {}", eglGetError());
|
||||||
|
else
|
||||||
|
ERROR_LOG("eglCreatePbufferSurface() failed: {}", eglGetError());
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_wi.surface_format = GetSurfaceTextureFormat();
|
DEV_LOG("Created pbuffer surface");
|
||||||
|
return m_pbuffer_surface;
|
||||||
DEV_LOG("Created {}x{} pbuffer surface", width, height);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextEGL::CheckConfigSurfaceFormat(EGLConfig config, GPUTexture::Format format)
|
bool OpenGLContextEGL::CheckConfigSurfaceFormat(EGLConfig config, GPUTexture::Format format)
|
||||||
@ -425,8 +420,21 @@ bool OpenGLContextEGL::CheckConfigSurfaceFormat(EGLConfig config, GPUTexture::Fo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GPUTexture::Format OpenGLContextEGL::GetSurfaceTextureFormat() const
|
void OpenGLContextEGL::UpdateWindowInfoSize(WindowInfo& wi, EGLSurface surface) const
|
||||||
{
|
{
|
||||||
|
// Some implementations may require the size to be queried at runtime.
|
||||||
|
EGLint surface_width, surface_height;
|
||||||
|
if (eglQuerySurface(m_display, surface, EGL_WIDTH, &surface_width) &&
|
||||||
|
eglQuerySurface(m_display, surface, EGL_HEIGHT, &surface_height))
|
||||||
|
{
|
||||||
|
wi.surface_width = static_cast<u16>(surface_width);
|
||||||
|
wi.surface_height = static_cast<u16>(surface_height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR_LOG("eglQuerySurface() failed: 0x{:X}", eglGetError());
|
||||||
|
}
|
||||||
|
|
||||||
int red_size = 0, green_size = 0, blue_size = 0, alpha_size = 0;
|
int red_size = 0, green_size = 0, blue_size = 0, alpha_size = 0;
|
||||||
eglGetConfigAttrib(m_display, m_config, EGL_RED_SIZE, &red_size);
|
eglGetConfigAttrib(m_display, m_config, EGL_RED_SIZE, &red_size);
|
||||||
eglGetConfigAttrib(m_display, m_config, EGL_GREEN_SIZE, &green_size);
|
eglGetConfigAttrib(m_display, m_config, EGL_GREEN_SIZE, &green_size);
|
||||||
@ -435,48 +443,25 @@ GPUTexture::Format OpenGLContextEGL::GetSurfaceTextureFormat() const
|
|||||||
|
|
||||||
if (red_size == 5 && green_size == 6 && blue_size == 5)
|
if (red_size == 5 && green_size == 6 && blue_size == 5)
|
||||||
{
|
{
|
||||||
return GPUTexture::Format::RGB565;
|
wi.surface_format = GPUTexture::Format::RGB565;
|
||||||
}
|
}
|
||||||
else if (red_size == 5 && green_size == 5 && blue_size == 5 && alpha_size == 1)
|
else if (red_size == 5 && green_size == 5 && blue_size == 5 && alpha_size == 1)
|
||||||
{
|
{
|
||||||
return GPUTexture::Format::RGBA5551;
|
wi.surface_format = GPUTexture::Format::RGBA5551;
|
||||||
}
|
}
|
||||||
else if (red_size == 8 && green_size == 8 && blue_size == 8 && alpha_size == 8)
|
else if (red_size == 8 && green_size == 8 && blue_size == 8 && alpha_size == 8)
|
||||||
{
|
{
|
||||||
return GPUTexture::Format::RGBA8;
|
wi.surface_format = GPUTexture::Format::RGBA8;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ERROR_LOG("Unknown surface format: R={}, G={}, B={}, A={}", red_size, green_size, blue_size, alpha_size);
|
ERROR_LOG("Unknown surface format: R={}, G={}, B={}, A={}", red_size, green_size, blue_size, alpha_size);
|
||||||
return GPUTexture::Format::RGBA8;
|
wi.surface_format = GPUTexture::Format::RGBA8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLContextEGL::DestroyContext()
|
bool OpenGLContextEGL::CreateContext(bool surfaceless, GPUTexture::Format surface_format, const Version& version,
|
||||||
{
|
EGLContext share_context, Error* error)
|
||||||
if (eglGetCurrentContext() == m_context)
|
|
||||||
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
||||||
|
|
||||||
if (m_context != EGL_NO_CONTEXT)
|
|
||||||
{
|
|
||||||
eglDestroyContext(m_display, m_context);
|
|
||||||
m_context = EGL_NO_CONTEXT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenGLContextEGL::DestroySurface()
|
|
||||||
{
|
|
||||||
if (eglGetCurrentSurface(EGL_DRAW) == m_surface)
|
|
||||||
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
||||||
|
|
||||||
if (m_surface != EGL_NO_SURFACE)
|
|
||||||
{
|
|
||||||
eglDestroySurface(m_display, m_surface);
|
|
||||||
m_surface = EGL_NO_SURFACE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OpenGLContextEGL::CreateContext(const Version& version, EGLContext share_context)
|
|
||||||
{
|
{
|
||||||
DEV_LOG("Trying version {}.{} ({})", version.major_version, version.minor_version,
|
DEV_LOG("Trying version {}.{} ({})", version.major_version, version.minor_version,
|
||||||
version.profile == OpenGLContext::Profile::ES ?
|
version.profile == OpenGLContext::Profile::ES ?
|
||||||
@ -489,18 +474,14 @@ bool OpenGLContextEGL::CreateContext(const Version& version, EGLContext share_co
|
|||||||
((version.major_version == 2) ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES_BIT)) :
|
((version.major_version == 2) ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES_BIT)) :
|
||||||
EGL_OPENGL_BIT,
|
EGL_OPENGL_BIT,
|
||||||
EGL_SURFACE_TYPE,
|
EGL_SURFACE_TYPE,
|
||||||
(m_wi.type != WindowInfo::Type::Surfaceless) ? EGL_WINDOW_BIT : 0,
|
surfaceless ? 0 : EGL_WINDOW_BIT,
|
||||||
};
|
};
|
||||||
int nsurface_attribs = 4;
|
int nsurface_attribs = 4;
|
||||||
|
|
||||||
const GPUTexture::Format format = m_wi.surface_format;
|
if (surface_format == GPUTexture::Format::Unknown)
|
||||||
if (format == GPUTexture::Format::Unknown)
|
surface_format = GPUTexture::Format::RGBA8;
|
||||||
{
|
|
||||||
WARNING_LOG("Surface format not specified, assuming RGBA8.");
|
|
||||||
m_wi.surface_format = GPUTexture::Format::RGBA8;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (m_wi.surface_format)
|
switch (surface_format)
|
||||||
{
|
{
|
||||||
case GPUTexture::Format::RGBA8:
|
case GPUTexture::Format::RGBA8:
|
||||||
surface_attribs[nsurface_attribs++] = EGL_RED_SIZE;
|
surface_attribs[nsurface_attribs++] = EGL_RED_SIZE;
|
||||||
@ -522,11 +503,8 @@ bool OpenGLContextEGL::CreateContext(const Version& version, EGLContext share_co
|
|||||||
surface_attribs[nsurface_attribs++] = 5;
|
surface_attribs[nsurface_attribs++] = 5;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GPUTexture::Format::Unknown:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
UnreachableCode();
|
Error::SetStringFmt(error, "Unsupported texture format {}", GPUTexture::GetFormatName(surface_format));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,14 +514,14 @@ bool OpenGLContextEGL::CreateContext(const Version& version, EGLContext share_co
|
|||||||
EGLint num_configs;
|
EGLint num_configs;
|
||||||
if (!eglChooseConfig(m_display, surface_attribs, nullptr, 0, &num_configs) || num_configs == 0)
|
if (!eglChooseConfig(m_display, surface_attribs, nullptr, 0, &num_configs) || num_configs == 0)
|
||||||
{
|
{
|
||||||
ERROR_LOG("eglChooseConfig() failed: 0x{:x}", static_cast<unsigned>(eglGetError()));
|
Error::SetStringFmt(error, "eglChooseConfig() failed: 0x{:x}", static_cast<unsigned>(eglGetError()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<EGLConfig> configs(static_cast<u32>(num_configs));
|
std::vector<EGLConfig> configs(static_cast<u32>(num_configs));
|
||||||
if (!eglChooseConfig(m_display, surface_attribs, configs.data(), num_configs, &num_configs))
|
if (!eglChooseConfig(m_display, surface_attribs, configs.data(), num_configs, &num_configs))
|
||||||
{
|
{
|
||||||
ERROR_LOG("eglChooseConfig() failed: 0x{:x}", static_cast<unsigned>(eglGetError()));
|
Error::SetStringFmt(error, "eglChooseConfig() failed: 0x{:x}", static_cast<unsigned>(eglGetError()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
configs.resize(static_cast<u32>(num_configs));
|
configs.resize(static_cast<u32>(num_configs));
|
||||||
@ -551,7 +529,7 @@ bool OpenGLContextEGL::CreateContext(const Version& version, EGLContext share_co
|
|||||||
std::optional<EGLConfig> config;
|
std::optional<EGLConfig> config;
|
||||||
for (EGLConfig check_config : configs)
|
for (EGLConfig check_config : configs)
|
||||||
{
|
{
|
||||||
if (CheckConfigSurfaceFormat(check_config, m_wi.surface_format))
|
if (CheckConfigSurfaceFormat(check_config, surface_format))
|
||||||
{
|
{
|
||||||
config = check_config;
|
config = check_config;
|
||||||
break;
|
break;
|
||||||
@ -578,14 +556,15 @@ bool OpenGLContextEGL::CreateContext(const Version& version, EGLContext share_co
|
|||||||
|
|
||||||
if (!eglBindAPI((version.profile == Profile::ES) ? EGL_OPENGL_ES_API : EGL_OPENGL_API))
|
if (!eglBindAPI((version.profile == Profile::ES) ? EGL_OPENGL_ES_API : EGL_OPENGL_API))
|
||||||
{
|
{
|
||||||
ERROR_LOG("eglBindAPI({}) failed", (version.profile == Profile::ES) ? "EGL_OPENGL_ES_API" : "EGL_OPENGL_API");
|
Error::SetStringFmt(error, "eglBindAPI({}) failed",
|
||||||
|
(version.profile == Profile::ES) ? "EGL_OPENGL_ES_API" : "EGL_OPENGL_API");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_context = eglCreateContext(m_display, config.value(), share_context, attribs);
|
m_context = eglCreateContext(m_display, config.value(), share_context, attribs);
|
||||||
if (!m_context)
|
if (!m_context)
|
||||||
{
|
{
|
||||||
ERROR_LOG("eglCreateContext() failed: 0x{:x}", static_cast<unsigned>(eglGetError()));
|
Error::SetStringFmt(error, "eglCreateContext() failed: 0x{:x}", static_cast<unsigned>(eglGetError()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,30 +590,62 @@ bool OpenGLContextEGL::CreateContext(const Version& version, EGLContext share_co
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextEGL::CreateContextAndSurface(const Version& version, EGLContext share_context, bool make_current)
|
bool OpenGLContextEGL::CreateContextAndSurface(WindowInfo& wi, SurfaceHandle* surface, const Version& version,
|
||||||
|
EGLContext share_context, bool make_current, Error* error)
|
||||||
{
|
{
|
||||||
if (!CreateContext(version, share_context))
|
if (!CreateContext(wi.IsSurfaceless(), wi.surface_format, version, share_context, error))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!CreateSurface())
|
// create actual surface, need to handle surfaceless here
|
||||||
|
EGLSurface esurface;
|
||||||
|
if (wi.IsSurfaceless())
|
||||||
{
|
{
|
||||||
ERROR_LOG("Failed to create surface for context");
|
if (!SupportsSurfaceless())
|
||||||
eglDestroyContext(m_display, m_context);
|
{
|
||||||
m_context = EGL_NO_CONTEXT;
|
esurface = GetPBufferSurface(error);
|
||||||
return false;
|
if (esurface == EGL_NO_SURFACE)
|
||||||
|
{
|
||||||
|
ERROR_LOG("Failed to create pbuffer surface for context");
|
||||||
|
eglDestroyContext(m_display, m_context);
|
||||||
|
m_context = EGL_NO_SURFACE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
esurface = EGL_NO_SURFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*surface = nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
esurface = CreatePlatformSurface(m_config, wi, error);
|
||||||
|
if (esurface == EGL_NO_SURFACE)
|
||||||
|
{
|
||||||
|
ERROR_LOG("Failed to create surface for context");
|
||||||
|
eglDestroyContext(m_display, m_context);
|
||||||
|
m_context = EGL_NO_SURFACE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateWindowInfoSize(wi, esurface);
|
||||||
|
*surface = esurface;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (make_current && !eglMakeCurrent(m_display, m_surface, m_surface, m_context))
|
if (make_current)
|
||||||
{
|
{
|
||||||
ERROR_LOG("eglMakeCurrent() failed: 0x{:X}", eglGetError());
|
if (!eglMakeCurrent(m_display, esurface, esurface, m_context))
|
||||||
if (m_surface != EGL_NO_SURFACE)
|
|
||||||
{
|
{
|
||||||
eglDestroySurface(m_display, m_surface);
|
Error::SetStringFmt(error, "eglMakeCurrent() failed: 0x{:X}", eglGetError());
|
||||||
m_surface = EGL_NO_SURFACE;
|
if (esurface != EGL_NO_SURFACE && esurface != m_pbuffer_surface)
|
||||||
|
DestroyPlatformSurface(esurface);
|
||||||
|
eglDestroyContext(m_display, m_context);
|
||||||
|
m_context = EGL_NO_CONTEXT;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
eglDestroyContext(m_display, m_context);
|
|
||||||
m_context = EGL_NO_CONTEXT;
|
m_current_surface = esurface;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -10,48 +10,54 @@
|
|||||||
class OpenGLContextEGL : public OpenGLContext
|
class OpenGLContextEGL : public OpenGLContext
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OpenGLContextEGL(const WindowInfo& wi);
|
OpenGLContextEGL();
|
||||||
~OpenGLContextEGL() override;
|
~OpenGLContextEGL() override;
|
||||||
|
|
||||||
static std::unique_ptr<OpenGLContext> Create(const WindowInfo& wi, std::span<const Version> versions_to_try,
|
static std::unique_ptr<OpenGLContext> Create(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
Error* error);
|
std::span<const Version> versions_to_try, Error* error);
|
||||||
|
|
||||||
void* GetProcAddress(const char* name) override;
|
void* GetProcAddress(const char* name) override;
|
||||||
virtual bool ChangeSurface(const WindowInfo& new_wi) override;
|
SurfaceHandle CreateSurface(WindowInfo& wi, Error* error = nullptr) override;
|
||||||
virtual void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
|
void DestroySurface(SurfaceHandle handle) override;
|
||||||
|
void ResizeSurface(WindowInfo& wi, SurfaceHandle handle) override;
|
||||||
bool SwapBuffers() override;
|
bool SwapBuffers() override;
|
||||||
bool IsCurrent() const override;
|
bool IsCurrent() const override;
|
||||||
bool MakeCurrent() override;
|
bool MakeCurrent(SurfaceHandle surface, Error* error = nullptr) override;
|
||||||
bool DoneCurrent() override;
|
bool DoneCurrent() override;
|
||||||
bool SupportsNegativeSwapInterval() const override;
|
bool SupportsNegativeSwapInterval() const override;
|
||||||
bool SetSwapInterval(s32 interval) override;
|
bool SetSwapInterval(s32 interval, Error* error = nullptr) override;
|
||||||
virtual std::unique_ptr<OpenGLContext> CreateSharedContext(const WindowInfo& wi, Error* error) override;
|
std::unique_ptr<OpenGLContext> CreateSharedContext(WindowInfo& wi, SurfaceHandle* surface, Error* error) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual EGLDisplay GetPlatformDisplay(Error* error);
|
virtual EGLDisplay GetPlatformDisplay(const WindowInfo& wi, Error* error);
|
||||||
virtual EGLSurface CreatePlatformSurface(EGLConfig config, void* win, Error* error);
|
virtual EGLSurface CreatePlatformSurface(EGLConfig config, const WindowInfo& wi, Error* error);
|
||||||
|
virtual void DestroyPlatformSurface(EGLSurface surface);
|
||||||
|
|
||||||
EGLDisplay TryGetPlatformDisplay(EGLenum platform, const char* platform_ext);
|
bool SupportsSurfaceless() const;
|
||||||
|
|
||||||
|
EGLDisplay TryGetPlatformDisplay(void* display, EGLenum platform, const char* platform_ext);
|
||||||
EGLSurface TryCreatePlatformSurface(EGLConfig config, void* window, Error* error);
|
EGLSurface TryCreatePlatformSurface(EGLConfig config, void* window, Error* error);
|
||||||
EGLDisplay GetFallbackDisplay(Error* error);
|
EGLDisplay GetFallbackDisplay(void* display, Error* error);
|
||||||
EGLSurface CreateFallbackSurface(EGLConfig config, void* window, Error* error);
|
EGLSurface CreateFallbackSurface(EGLConfig config, void* window, Error* error);
|
||||||
|
|
||||||
bool Initialize(std::span<const Version> versions_to_try, Error* error);
|
bool Initialize(WindowInfo& wi, SurfaceHandle* surface, std::span<const Version> versions_to_try, Error* error);
|
||||||
bool CreateContext(const Version& version, EGLContext share_context);
|
bool CreateContext(bool surfaceless, GPUTexture::Format surface_format, const Version& version,
|
||||||
bool CreateContextAndSurface(const Version& version, EGLContext share_context, bool make_current);
|
EGLContext share_context, Error* error);
|
||||||
bool CreateSurface();
|
bool CreateContextAndSurface(WindowInfo& wi, SurfaceHandle* surface, const Version& version, EGLContext share_context,
|
||||||
bool CreatePBufferSurface();
|
bool make_current, Error* error);
|
||||||
|
EGLSurface GetPBufferSurface(Error* error);
|
||||||
|
EGLSurface GetSurfacelessSurface();
|
||||||
bool CheckConfigSurfaceFormat(EGLConfig config, GPUTexture::Format format);
|
bool CheckConfigSurfaceFormat(EGLConfig config, GPUTexture::Format format);
|
||||||
GPUTexture::Format GetSurfaceTextureFormat() const;
|
void UpdateWindowInfoSize(WindowInfo& wi, EGLSurface surface) const;
|
||||||
void DestroyContext();
|
|
||||||
void DestroySurface();
|
|
||||||
|
|
||||||
EGLDisplay m_display = EGL_NO_DISPLAY;
|
EGLDisplay m_display = EGL_NO_DISPLAY;
|
||||||
EGLSurface m_surface = EGL_NO_SURFACE;
|
|
||||||
EGLContext m_context = EGL_NO_CONTEXT;
|
EGLContext m_context = EGL_NO_CONTEXT;
|
||||||
|
EGLSurface m_current_surface = EGL_NO_SURFACE;
|
||||||
|
|
||||||
EGLConfig m_config = {};
|
EGLConfig m_config = {};
|
||||||
|
|
||||||
|
EGLSurface m_pbuffer_surface = EGL_NO_SURFACE;
|
||||||
|
|
||||||
bool m_use_ext_platform_base = false;
|
bool m_use_ext_platform_base = false;
|
||||||
bool m_supports_negative_swap_interval = false;
|
bool m_supports_negative_swap_interval = false;
|
||||||
};
|
};
|
||||||
|
@ -3,91 +3,22 @@
|
|||||||
|
|
||||||
#include "opengl_context_egl_wayland.h"
|
#include "opengl_context_egl_wayland.h"
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
#include "common/error.h"
|
#include "common/error.h"
|
||||||
|
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|
||||||
static const char* WAYLAND_EGL_MODNAME = "libwayland-egl.so.1";
|
static const char* WAYLAND_EGL_MODNAME = "libwayland-egl.so.1";
|
||||||
|
|
||||||
OpenGLContextEGLWayland::OpenGLContextEGLWayland(const WindowInfo& wi) : OpenGLContextEGL(wi)
|
OpenGLContextEGLWayland::OpenGLContextEGLWayland() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenGLContextEGLWayland::~OpenGLContextEGLWayland()
|
OpenGLContextEGLWayland::~OpenGLContextEGLWayland()
|
||||||
{
|
{
|
||||||
if (m_wl_window)
|
AssertMsg(m_wl_window_map.empty(), "WL window map should be empty on destructor.");
|
||||||
m_wl_egl_window_destroy(m_wl_window);
|
|
||||||
if (m_wl_module)
|
if (m_wl_module)
|
||||||
dlclose(m_wl_module);
|
dlclose(m_wl_module);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<OpenGLContext> OpenGLContextEGLWayland::Create(const WindowInfo& wi,
|
|
||||||
std::span<const Version> versions_to_try, Error* error)
|
|
||||||
{
|
|
||||||
std::unique_ptr<OpenGLContextEGLWayland> context = std::make_unique<OpenGLContextEGLWayland>(wi);
|
|
||||||
if (!context->LoadModule(error) || !context->Initialize(versions_to_try, error))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<OpenGLContext> OpenGLContextEGLWayland::CreateSharedContext(const WindowInfo& wi, Error* error)
|
|
||||||
{
|
|
||||||
std::unique_ptr<OpenGLContextEGLWayland> context = std::make_unique<OpenGLContextEGLWayland>(wi);
|
|
||||||
context->m_display = m_display;
|
|
||||||
|
|
||||||
if (!context->LoadModule(error) || !context->CreateContextAndSurface(m_version, m_context, false))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenGLContextEGLWayland::ResizeSurface(u32 new_surface_width, u32 new_surface_height)
|
|
||||||
{
|
|
||||||
if (m_wl_window)
|
|
||||||
m_wl_egl_window_resize(m_wl_window, new_surface_width, new_surface_height, 0, 0);
|
|
||||||
|
|
||||||
OpenGLContextEGL::ResizeSurface(new_surface_width, new_surface_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
EGLDisplay OpenGLContextEGLWayland::GetPlatformDisplay(Error* error)
|
|
||||||
{
|
|
||||||
EGLDisplay dpy = TryGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, "EGL_EXT_platform_wayland");
|
|
||||||
if (dpy == EGL_NO_DISPLAY)
|
|
||||||
dpy = GetFallbackDisplay(error);
|
|
||||||
|
|
||||||
return dpy;
|
|
||||||
}
|
|
||||||
|
|
||||||
EGLSurface OpenGLContextEGLWayland::CreatePlatformSurface(EGLConfig config, void* win, Error* error)
|
|
||||||
{
|
|
||||||
if (m_wl_window)
|
|
||||||
{
|
|
||||||
m_wl_egl_window_destroy(m_wl_window);
|
|
||||||
m_wl_window = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_wl_window = m_wl_egl_window_create(static_cast<wl_surface*>(win), m_wi.surface_width, m_wi.surface_height);
|
|
||||||
if (!m_wl_window)
|
|
||||||
{
|
|
||||||
Error::SetStringView(error, "wl_egl_window_create() failed");
|
|
||||||
return EGL_NO_SURFACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
EGLSurface surface = TryCreatePlatformSurface(config, m_wl_window, error);
|
|
||||||
if (surface == EGL_NO_SURFACE)
|
|
||||||
{
|
|
||||||
surface = CreateFallbackSurface(config, m_wl_window, error);
|
|
||||||
if (surface == EGL_NO_SURFACE)
|
|
||||||
{
|
|
||||||
m_wl_egl_window_destroy(m_wl_window);
|
|
||||||
m_wl_window = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return surface;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OpenGLContextEGLWayland::LoadModule(Error* error)
|
bool OpenGLContextEGLWayland::LoadModule(Error* error)
|
||||||
{
|
{
|
||||||
m_wl_module = dlopen(WAYLAND_EGL_MODNAME, RTLD_NOW | RTLD_GLOBAL);
|
m_wl_module = dlopen(WAYLAND_EGL_MODNAME, RTLD_NOW | RTLD_GLOBAL);
|
||||||
@ -112,3 +43,78 @@ bool OpenGLContextEGLWayland::LoadModule(Error* error)
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EGLDisplay OpenGLContextEGLWayland::GetPlatformDisplay(const WindowInfo& wi, Error* error)
|
||||||
|
{
|
||||||
|
EGLDisplay dpy = TryGetPlatformDisplay(wi.display_connection, EGL_PLATFORM_WAYLAND_KHR, "EGL_EXT_platform_wayland");
|
||||||
|
if (dpy == EGL_NO_DISPLAY)
|
||||||
|
dpy = GetFallbackDisplay(wi.display_connection, error);
|
||||||
|
|
||||||
|
return dpy;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLSurface OpenGLContextEGLWayland::CreatePlatformSurface(EGLConfig config, const WindowInfo& wi, Error* error)
|
||||||
|
{
|
||||||
|
struct wl_egl_window* wl_window =
|
||||||
|
m_wl_egl_window_create(static_cast<wl_surface*>(wi.window_handle), wi.surface_width, wi.surface_height);
|
||||||
|
if (!wl_window)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "wl_egl_window_create() failed");
|
||||||
|
return EGL_NO_SURFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLSurface surface = TryCreatePlatformSurface(config, wl_window, error);
|
||||||
|
if (surface == EGL_NO_SURFACE)
|
||||||
|
{
|
||||||
|
surface = CreateFallbackSurface(config, wl_window, error);
|
||||||
|
if (surface == EGL_NO_SURFACE)
|
||||||
|
{
|
||||||
|
m_wl_egl_window_destroy(wl_window);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_wl_window_map.emplace(surface, wl_window);
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLContextEGLWayland::ResizeSurface(WindowInfo& wi, SurfaceHandle handle)
|
||||||
|
{
|
||||||
|
const auto it = m_wl_window_map.find((EGLSurface)handle);
|
||||||
|
AssertMsg(it != m_wl_window_map.end(), "Missing WL window");
|
||||||
|
m_wl_egl_window_resize(it->second, wi.surface_width, wi.surface_height, 0, 0);
|
||||||
|
|
||||||
|
OpenGLContextEGL::ResizeSurface(wi, handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLContextEGLWayland::DestroyPlatformSurface(EGLSurface surface)
|
||||||
|
{
|
||||||
|
const auto it = m_wl_window_map.find((EGLSurface)surface);
|
||||||
|
AssertMsg(it != m_wl_window_map.end(), "Missing WL window");
|
||||||
|
m_wl_egl_window_destroy(it->second);
|
||||||
|
m_wl_window_map.erase(it);
|
||||||
|
|
||||||
|
OpenGLContextEGL::DestroyPlatformSurface(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<OpenGLContext> OpenGLContextEGLWayland::Create(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
|
std::span<const Version> versions_to_try, Error* error)
|
||||||
|
{
|
||||||
|
std::unique_ptr<OpenGLContextEGLWayland> context = std::make_unique<OpenGLContextEGLWayland>();
|
||||||
|
if (!context->LoadModule(error) || !context->Initialize(wi, surface, versions_to_try, error))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<OpenGLContext> OpenGLContextEGLWayland::CreateSharedContext(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
|
Error* error)
|
||||||
|
{
|
||||||
|
std::unique_ptr<OpenGLContextEGLWayland> context = std::make_unique<OpenGLContextEGLWayland>();
|
||||||
|
context->m_display = m_display;
|
||||||
|
|
||||||
|
if (!context->LoadModule(error) || !context->CreateContextAndSurface(wi, surface, m_version, m_context, false, error))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
@ -10,26 +10,32 @@
|
|||||||
class OpenGLContextEGLWayland final : public OpenGLContextEGL
|
class OpenGLContextEGLWayland final : public OpenGLContextEGL
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OpenGLContextEGLWayland(const WindowInfo& wi);
|
OpenGLContextEGLWayland();
|
||||||
~OpenGLContextEGLWayland() override;
|
~OpenGLContextEGLWayland() override;
|
||||||
|
|
||||||
static std::unique_ptr<OpenGLContext> Create(const WindowInfo& wi, std::span<const Version> versions_to_try,
|
static std::unique_ptr<OpenGLContext> Create(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
Error* error);
|
std::span<const Version> versions_to_try, Error* error);
|
||||||
|
|
||||||
std::unique_ptr<OpenGLContext> CreateSharedContext(const WindowInfo& wi, Error* error) override;
|
std::unique_ptr<OpenGLContext> CreateSharedContext(WindowInfo& wi, SurfaceHandle* surface, Error* error) override;
|
||||||
void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
|
|
||||||
|
void ResizeSurface(WindowInfo& wi, SurfaceHandle handle) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
EGLDisplay GetPlatformDisplay(Error* error) override;
|
EGLDisplay GetPlatformDisplay(const WindowInfo& wi, Error* error) override;
|
||||||
EGLSurface CreatePlatformSurface(EGLConfig config, void* win, Error* error) override;
|
EGLSurface CreatePlatformSurface(EGLConfig config, const WindowInfo& wi, Error* error) override;
|
||||||
|
void DestroyPlatformSurface(EGLSurface surface) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool LoadModule(Error* error);
|
// Truely awful, I hate this so much, and all this work for a bloody windowing system where
|
||||||
|
// we can't even position windows to begin with, which makes multi-window kinda pointless...
|
||||||
|
using WLWindowMap = std::unordered_map<EGLSurface, struct wl_egl_window*>;
|
||||||
|
|
||||||
wl_egl_window* m_wl_window = nullptr;
|
bool LoadModule(Error* error);
|
||||||
|
|
||||||
void* m_wl_module = nullptr;
|
void* m_wl_module = nullptr;
|
||||||
wl_egl_window* (*m_wl_egl_window_create)(struct wl_surface* surface, int width, int height);
|
wl_egl_window* (*m_wl_egl_window_create)(struct wl_surface* surface, int width, int height);
|
||||||
void (*m_wl_egl_window_destroy)(struct wl_egl_window* egl_window);
|
void (*m_wl_egl_window_destroy)(struct wl_egl_window* egl_window);
|
||||||
void (*m_wl_egl_window_resize)(struct wl_egl_window* egl_window, int width, int height, int dx, int dy);
|
void (*m_wl_egl_window_resize)(struct wl_egl_window* egl_window, int width, int height, int dx, int dy);
|
||||||
|
|
||||||
|
WLWindowMap m_wl_window_map;
|
||||||
};
|
};
|
||||||
|
@ -5,49 +5,49 @@
|
|||||||
|
|
||||||
#include "common/error.h"
|
#include "common/error.h"
|
||||||
|
|
||||||
OpenGLContextEGLX11::OpenGLContextEGLX11(const WindowInfo& wi) : OpenGLContextEGL(wi)
|
OpenGLContextEGLX11::OpenGLContextEGLX11() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenGLContextEGLX11::~OpenGLContextEGLX11() = default;
|
OpenGLContextEGLX11::~OpenGLContextEGLX11() = default;
|
||||||
|
|
||||||
std::unique_ptr<OpenGLContext> OpenGLContextEGLX11::Create(const WindowInfo& wi,
|
std::unique_ptr<OpenGLContext> OpenGLContextEGLX11::Create(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
std::span<const Version> versions_to_try, Error* error)
|
std::span<const Version> versions_to_try, Error* error)
|
||||||
{
|
{
|
||||||
std::unique_ptr<OpenGLContextEGLX11> context = std::make_unique<OpenGLContextEGLX11>(wi);
|
std::unique_ptr<OpenGLContextEGLX11> context = std::make_unique<OpenGLContextEGLX11>();
|
||||||
if (!context->Initialize(versions_to_try, error))
|
if (!context->Initialize(wi, surface, versions_to_try, error))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<OpenGLContext> OpenGLContextEGLX11::CreateSharedContext(const WindowInfo& wi, Error* error)
|
std::unique_ptr<OpenGLContext> OpenGLContextEGLX11::CreateSharedContext(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
|
Error* error)
|
||||||
{
|
{
|
||||||
std::unique_ptr<OpenGLContextEGLX11> context = std::make_unique<OpenGLContextEGLX11>(wi);
|
std::unique_ptr<OpenGLContextEGLX11> context = std::make_unique<OpenGLContextEGLX11>();
|
||||||
context->m_display = m_display;
|
context->m_display = m_display;
|
||||||
|
|
||||||
if (!context->CreateContextAndSurface(m_version, m_context, false))
|
if (!context->CreateContextAndSurface(wi, surface, m_version, m_context, false, error))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
EGLDisplay OpenGLContextEGLX11::GetPlatformDisplay(Error* error)
|
EGLDisplay OpenGLContextEGLX11::GetPlatformDisplay(const WindowInfo& wi, Error* error)
|
||||||
{
|
{
|
||||||
EGLDisplay dpy = TryGetPlatformDisplay(EGL_PLATFORM_X11_KHR, "EGL_EXT_platform_x11");
|
EGLDisplay dpy = TryGetPlatformDisplay(wi.display_connection, EGL_PLATFORM_X11_KHR, "EGL_EXT_platform_x11");
|
||||||
if (dpy == EGL_NO_DISPLAY)
|
if (dpy == EGL_NO_DISPLAY)
|
||||||
dpy = GetFallbackDisplay(error);
|
dpy = GetFallbackDisplay(wi.display_connection, error);
|
||||||
|
|
||||||
return dpy;
|
return dpy;
|
||||||
}
|
}
|
||||||
|
|
||||||
EGLSurface OpenGLContextEGLX11::CreatePlatformSurface(EGLConfig config, void* win, Error* error)
|
EGLSurface OpenGLContextEGLX11::CreatePlatformSurface(EGLConfig config, const WindowInfo& wi, Error* error)
|
||||||
{
|
{
|
||||||
// This is hideous.. the EXT version requires a pointer to the window, whereas the base
|
// This is hideous.. the EXT version requires a pointer to the window, whereas the base
|
||||||
// version requires the window itself, casted to void*...
|
// version requires the window itself, casted to void*...
|
||||||
|
void* win = wi.window_handle;
|
||||||
EGLSurface surface = TryCreatePlatformSurface(config, &win, error);
|
EGLSurface surface = TryCreatePlatformSurface(config, &win, error);
|
||||||
if (surface == EGL_NO_SURFACE)
|
if (surface == EGL_NO_SURFACE)
|
||||||
surface = CreateFallbackSurface(config, win, error);
|
surface = CreateFallbackSurface(config, wi.window_handle, error);
|
||||||
|
|
||||||
return surface;
|
return surface;
|
||||||
}
|
}
|
||||||
|
@ -8,15 +8,15 @@
|
|||||||
class OpenGLContextEGLX11 final : public OpenGLContextEGL
|
class OpenGLContextEGLX11 final : public OpenGLContextEGL
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OpenGLContextEGLX11(const WindowInfo& wi);
|
OpenGLContextEGLX11();
|
||||||
~OpenGLContextEGLX11() override;
|
~OpenGLContextEGLX11() override;
|
||||||
|
|
||||||
static std::unique_ptr<OpenGLContext> Create(const WindowInfo& wi, std::span<const Version> versions_to_try,
|
static std::unique_ptr<OpenGLContext> Create(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
Error* error);
|
std::span<const Version> versions_to_try, Error* error);
|
||||||
|
|
||||||
std::unique_ptr<OpenGLContext> CreateSharedContext(const WindowInfo& wi, Error* error) override;
|
std::unique_ptr<OpenGLContext> CreateSharedContext(WindowInfo& wi, SurfaceHandle* surface, Error* error) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
EGLDisplay GetPlatformDisplay(Error* error) override;
|
EGLDisplay GetPlatformDisplay(const WindowInfo& wi, Error* error) override;
|
||||||
EGLSurface CreatePlatformSurface(EGLConfig config, void* win, Error* error) override;
|
EGLSurface CreatePlatformSurface(EGLConfig config, const WindowInfo& wi, Error* error) override;
|
||||||
};
|
};
|
||||||
|
@ -5,77 +5,134 @@
|
|||||||
#include "opengl_loader.h"
|
#include "opengl_loader.h"
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/dynamic_library.h"
|
||||||
#include "common/error.h"
|
#include "common/error.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/scoped_guard.h"
|
#include "common/scoped_guard.h"
|
||||||
|
|
||||||
LOG_CHANNEL(GL::OpenGLContext);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
#pragma clang diagnostic ignored "-Wmicrosoft-cast"
|
#pragma clang diagnostic ignored "-Wmicrosoft-cast"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void* GetProcAddressCallback(const char* name)
|
namespace dyn_libs {
|
||||||
|
|
||||||
|
#define OPENGL_FUNCTIONS(X) \
|
||||||
|
X(wglCreateContext) \
|
||||||
|
X(wglDeleteContext) \
|
||||||
|
X(wglGetCurrentContext) \
|
||||||
|
X(wglGetCurrentDC) \
|
||||||
|
X(wglGetProcAddress) \
|
||||||
|
X(wglMakeCurrent) \
|
||||||
|
X(wglShareLists)
|
||||||
|
|
||||||
|
static bool LoadOpenGLLibrary(Error* error);
|
||||||
|
static void CloseOpenGLLibrary();
|
||||||
|
static void* GetProcAddressCallback(const char* name);
|
||||||
|
|
||||||
|
static DynamicLibrary s_opengl_library;
|
||||||
|
|
||||||
|
#define DECLARE_OPENGL_FUNCTION(F) static decltype(&::F) F;
|
||||||
|
OPENGL_FUNCTIONS(DECLARE_OPENGL_FUNCTION)
|
||||||
|
#undef DECLARE_OPENGL_FUNCTION
|
||||||
|
} // namespace dyn_libs
|
||||||
|
|
||||||
|
bool dyn_libs::LoadOpenGLLibrary(Error* error)
|
||||||
{
|
{
|
||||||
void* addr = wglGetProcAddress(name);
|
if (s_opengl_library.IsOpen())
|
||||||
|
return true;
|
||||||
|
else if (!s_opengl_library.Open("opengl32.dll", error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool result = true;
|
||||||
|
#define RESOLVE_OPENGL_FUNCTION(F) result = result && s_opengl_library.GetSymbol(#F, &F);
|
||||||
|
OPENGL_FUNCTIONS(RESOLVE_OPENGL_FUNCTION);
|
||||||
|
#undef RESOLVE_OPENGL_FUNCTION
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
CloseOpenGLLibrary();
|
||||||
|
Error::SetStringView(error, "One or more required functions from opengl32.dll is missing.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::atexit(&CloseOpenGLLibrary);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dyn_libs::CloseOpenGLLibrary()
|
||||||
|
{
|
||||||
|
#define CLOSE_OPENGL_FUNCTION(F) F = nullptr;
|
||||||
|
OPENGL_FUNCTIONS(CLOSE_OPENGL_FUNCTION);
|
||||||
|
#undef CLOSE_OPENGL_FUNCTION
|
||||||
|
|
||||||
|
s_opengl_library.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef OPENGL_FUNCTIONS
|
||||||
|
|
||||||
|
void* dyn_libs::GetProcAddressCallback(const char* name)
|
||||||
|
{
|
||||||
|
void* addr = dyn_libs::wglGetProcAddress(name);
|
||||||
if (addr)
|
if (addr)
|
||||||
return addr;
|
return addr;
|
||||||
|
|
||||||
// try opengl32.dll
|
// try opengl32.dll
|
||||||
return ::GetProcAddress(GetModuleHandleA("opengl32.dll"), name);
|
return s_opengl_library.GetSymbolAddress(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ReloadWGL(HDC dc)
|
static bool ReloadWGL(HDC dc, Error* error)
|
||||||
{
|
{
|
||||||
if (!gladLoadWGL(dc, [](const char* name) { return (GLADapiproc)wglGetProcAddress(name); }))
|
if (!gladLoadWGL(dc, [](const char* name) { return (GLADapiproc)dyn_libs::wglGetProcAddress(name); }))
|
||||||
{
|
{
|
||||||
ERROR_LOG("Loading GLAD WGL functions failed");
|
Error::SetStringView(error, "Loading GLAD WGL functions failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenGLContextWGL::OpenGLContextWGL(const WindowInfo& wi) : OpenGLContext(wi)
|
OpenGLContextWGL::OpenGLContextWGL() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenGLContextWGL::~OpenGLContextWGL()
|
OpenGLContextWGL::~OpenGLContextWGL()
|
||||||
{
|
{
|
||||||
if (wglGetCurrentContext() == m_rc)
|
|
||||||
wglMakeCurrent(m_dc, nullptr);
|
|
||||||
|
|
||||||
if (m_rc)
|
if (m_rc)
|
||||||
wglDeleteContext(m_rc);
|
{
|
||||||
|
if (dyn_libs::wglGetCurrentContext() == m_rc)
|
||||||
|
dyn_libs::wglMakeCurrent(nullptr, nullptr);
|
||||||
|
|
||||||
ReleaseDC();
|
dyn_libs::wglDeleteContext(m_rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pbuffer)
|
||||||
|
{
|
||||||
|
wglReleasePbufferDCARB(m_pbuffer, m_pbuffer_dc);
|
||||||
|
wglDestroyPbufferARB(m_pbuffer);
|
||||||
|
DeleteDC(m_dummy_dc);
|
||||||
|
DestroyWindow(m_dummy_window);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<OpenGLContext> OpenGLContextWGL::Create(const WindowInfo& wi, std::span<const Version> versions_to_try,
|
std::unique_ptr<OpenGLContext> OpenGLContextWGL::Create(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
Error* error)
|
std::span<const Version> versions_to_try, Error* error)
|
||||||
{
|
{
|
||||||
std::unique_ptr<OpenGLContextWGL> context = std::make_unique<OpenGLContextWGL>(wi);
|
std::unique_ptr<OpenGLContextWGL> context = std::make_unique<OpenGLContextWGL>();
|
||||||
if (!context->Initialize(versions_to_try, error))
|
if (!dyn_libs::LoadOpenGLLibrary(error) || !context->Initialize(wi, surface, versions_to_try, error))
|
||||||
return nullptr;
|
context.reset();
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextWGL::Initialize(std::span<const Version> versions_to_try, Error* error)
|
bool OpenGLContextWGL::Initialize(WindowInfo& wi, SurfaceHandle* surface, std::span<const Version> versions_to_try,
|
||||||
|
Error* error)
|
||||||
{
|
{
|
||||||
if (m_wi.type == WindowInfo::Type::Win32)
|
const HDC hdc = wi.IsSurfaceless() ? GetPBufferDC(error) : CreateDCAndSetPixelFormat(wi, error);
|
||||||
{
|
if (!hdc)
|
||||||
if (!InitializeDC(error))
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!CreatePBuffer(error))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everything including core/ES requires a dummy profile to load the WGL extensions.
|
// Everything including core/ES requires a dummy profile to load the WGL extensions.
|
||||||
if (!CreateAnyContext(nullptr, true, error))
|
if (!CreateAnyContext(hdc, nullptr, true, error))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (const Version& cv : versions_to_try)
|
for (const Version& cv : versions_to_try)
|
||||||
@ -84,11 +141,13 @@ bool OpenGLContextWGL::Initialize(std::span<const Version> versions_to_try, Erro
|
|||||||
{
|
{
|
||||||
// we already have the dummy context, so just use that
|
// we already have the dummy context, so just use that
|
||||||
m_version = cv;
|
m_version = cv;
|
||||||
|
*surface = hdc;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (CreateVersionContext(cv, nullptr, true, error))
|
else if (CreateVersionContext(cv, hdc, nullptr, true, error))
|
||||||
{
|
{
|
||||||
m_version = cv;
|
m_version = cv;
|
||||||
|
*surface = hdc;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,65 +158,76 @@ bool OpenGLContextWGL::Initialize(std::span<const Version> versions_to_try, Erro
|
|||||||
|
|
||||||
void* OpenGLContextWGL::GetProcAddress(const char* name)
|
void* OpenGLContextWGL::GetProcAddress(const char* name)
|
||||||
{
|
{
|
||||||
return GetProcAddressCallback(name);
|
return dyn_libs::GetProcAddressCallback(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextWGL::ChangeSurface(const WindowInfo& new_wi)
|
OpenGLContext::SurfaceHandle OpenGLContextWGL::CreateSurface(WindowInfo& wi, Error* error /*= nullptr*/)
|
||||||
{
|
{
|
||||||
const bool was_current = (wglGetCurrentContext() == m_rc);
|
if (wi.IsSurfaceless()) [[unlikely]]
|
||||||
Error error;
|
|
||||||
|
|
||||||
ReleaseDC();
|
|
||||||
|
|
||||||
m_wi = new_wi;
|
|
||||||
if (!InitializeDC(&error))
|
|
||||||
{
|
{
|
||||||
ERROR_LOG("Failed to change surface: {}", error.GetDescription());
|
Error::SetStringView(error, "Trying to create a surfaceless surface.");
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (was_current && !wglMakeCurrent(m_dc, m_rc))
|
return CreateDCAndSetPixelFormat(wi, error);
|
||||||
{
|
|
||||||
error.SetWin32(GetLastError());
|
|
||||||
ERROR_LOG("Failed to make context current again after surface change: {}", error.GetDescription());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLContextWGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/)
|
void OpenGLContextWGL::DestroySurface(SurfaceHandle handle)
|
||||||
|
{
|
||||||
|
// pbuffer/surfaceless?
|
||||||
|
if (!handle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// current buffer? switch to pbuffer first
|
||||||
|
if (dyn_libs::wglGetCurrentDC() == static_cast<HDC>(handle))
|
||||||
|
MakeCurrent(nullptr);
|
||||||
|
|
||||||
|
DeleteDC(static_cast<HDC>(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLContextWGL::ResizeSurface(WindowInfo& wi, SurfaceHandle handle)
|
||||||
{
|
{
|
||||||
RECT client_rc = {};
|
RECT client_rc = {};
|
||||||
GetClientRect(GetHWND(), &client_rc);
|
GetClientRect(static_cast<HWND>(wi.window_handle), &client_rc);
|
||||||
m_wi.surface_width = static_cast<u32>(client_rc.right - client_rc.left);
|
wi.surface_width = static_cast<u16>(client_rc.right - client_rc.left);
|
||||||
m_wi.surface_height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
wi.surface_height = static_cast<u16>(client_rc.bottom - client_rc.top);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextWGL::SwapBuffers()
|
bool OpenGLContextWGL::SwapBuffers()
|
||||||
{
|
{
|
||||||
return ::SwapBuffers(m_dc);
|
return ::SwapBuffers(m_current_dc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextWGL::IsCurrent() const
|
bool OpenGLContextWGL::IsCurrent() const
|
||||||
{
|
{
|
||||||
return (m_rc && wglGetCurrentContext() == m_rc);
|
return (m_rc && dyn_libs::wglGetCurrentContext() == m_rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextWGL::MakeCurrent()
|
bool OpenGLContextWGL::MakeCurrent(SurfaceHandle surface, Error* error /* = nullptr */)
|
||||||
{
|
{
|
||||||
if (!wglMakeCurrent(m_dc, m_rc))
|
const HDC new_dc = surface ? static_cast<HDC>(surface) : GetPBufferDC(error);
|
||||||
|
if (!new_dc)
|
||||||
|
return false;
|
||||||
|
else if (m_current_dc == new_dc)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!dyn_libs::wglMakeCurrent(new_dc, m_rc))
|
||||||
{
|
{
|
||||||
ERROR_LOG("wglMakeCurrent() failed: {}", GetLastError());
|
ERROR_LOG("wglMakeCurrent() failed: {}", GetLastError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_current_dc = new_dc;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextWGL::DoneCurrent()
|
bool OpenGLContextWGL::DoneCurrent()
|
||||||
{
|
{
|
||||||
return wglMakeCurrent(m_dc, nullptr);
|
if (!dyn_libs::wglMakeCurrent(m_current_dc, nullptr))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_current_dc = nullptr;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextWGL::SupportsNegativeSwapInterval() const
|
bool OpenGLContextWGL::SupportsNegativeSwapInterval() const
|
||||||
@ -165,45 +235,55 @@ bool OpenGLContextWGL::SupportsNegativeSwapInterval() const
|
|||||||
return GLAD_WGL_EXT_swap_control && GLAD_WGL_EXT_swap_control_tear;
|
return GLAD_WGL_EXT_swap_control && GLAD_WGL_EXT_swap_control_tear;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextWGL::SetSwapInterval(s32 interval)
|
bool OpenGLContextWGL::SetSwapInterval(s32 interval, Error* error)
|
||||||
{
|
{
|
||||||
if (!GLAD_WGL_EXT_swap_control)
|
if (!GLAD_WGL_EXT_swap_control)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "WGL_EXT_swap_control is not supported.");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return wglSwapIntervalEXT(interval);
|
if (!wglSwapIntervalEXT(interval))
|
||||||
|
{
|
||||||
|
Error::SetWin32(error, "wglSwapIntervalEXT() failed: ", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<OpenGLContext> OpenGLContextWGL::CreateSharedContext(const WindowInfo& wi, Error* error)
|
std::unique_ptr<OpenGLContext> OpenGLContextWGL::CreateSharedContext(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
|
Error* error)
|
||||||
{
|
{
|
||||||
std::unique_ptr<OpenGLContextWGL> context = std::make_unique<OpenGLContextWGL>(wi);
|
std::unique_ptr<OpenGLContextWGL> context = std::make_unique<OpenGLContextWGL>();
|
||||||
if (wi.type == WindowInfo::Type::Win32)
|
const HDC hdc = wi.IsSurfaceless() ? context->GetPBufferDC(error) : context->CreateDCAndSetPixelFormat(wi, error);
|
||||||
{
|
if (!hdc)
|
||||||
if (!context->InitializeDC(error))
|
return nullptr;
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!context->CreatePBuffer(error))
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_version.profile == Profile::NoProfile)
|
if (m_version.profile == Profile::NoProfile)
|
||||||
{
|
{
|
||||||
if (!context->CreateAnyContext(m_rc, false, error))
|
if (!context->CreateAnyContext(hdc, m_rc, false, error))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!context->CreateVersionContext(m_version, m_rc, false, error))
|
if (!context->CreateVersionContext(m_version, hdc, m_rc, false, error))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
context->m_version = m_version;
|
context->m_version = m_version;
|
||||||
|
*surface = wi.IsSurfaceless() ? hdc : nullptr;
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
HDC OpenGLContextWGL::GetDCAndSetPixelFormat(HWND hwnd, Error* error)
|
HDC OpenGLContextWGL::CreateDCAndSetPixelFormat(WindowInfo& wi, Error* error)
|
||||||
{
|
{
|
||||||
|
if (wi.type != WindowInfo::Type::Win32)
|
||||||
|
{
|
||||||
|
Error::SetStringFmt(error, "Unknown window info type {}", static_cast<unsigned>(wi.type));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
PIXELFORMATDESCRIPTOR pfd = {};
|
PIXELFORMATDESCRIPTOR pfd = {};
|
||||||
pfd.nSize = sizeof(pfd);
|
pfd.nSize = sizeof(pfd);
|
||||||
pfd.nVersion = 1;
|
pfd.nVersion = 1;
|
||||||
@ -215,6 +295,7 @@ HDC OpenGLContextWGL::GetDCAndSetPixelFormat(HWND hwnd, Error* error)
|
|||||||
pfd.cBlueBits = 8;
|
pfd.cBlueBits = 8;
|
||||||
pfd.cColorBits = 24;
|
pfd.cColorBits = 24;
|
||||||
|
|
||||||
|
const HWND hwnd = static_cast<HWND>(wi.window_handle);
|
||||||
HDC hDC = ::GetDC(hwnd);
|
HDC hDC = ::GetDC(hwnd);
|
||||||
if (!hDC)
|
if (!hDC)
|
||||||
{
|
{
|
||||||
@ -228,7 +309,7 @@ HDC OpenGLContextWGL::GetDCAndSetPixelFormat(HWND hwnd, Error* error)
|
|||||||
if (pf == 0)
|
if (pf == 0)
|
||||||
{
|
{
|
||||||
Error::SetWin32(error, "ChoosePixelFormat() failed: ", GetLastError());
|
Error::SetWin32(error, "ChoosePixelFormat() failed: ", GetLastError());
|
||||||
::ReleaseDC(hwnd, hDC);
|
DeleteDC(hDC);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,63 +319,22 @@ HDC OpenGLContextWGL::GetDCAndSetPixelFormat(HWND hwnd, Error* error)
|
|||||||
if (!SetPixelFormat(hDC, m_pixel_format.value(), &pfd))
|
if (!SetPixelFormat(hDC, m_pixel_format.value(), &pfd))
|
||||||
{
|
{
|
||||||
Error::SetWin32(error, "SetPixelFormat() failed: ", GetLastError());
|
Error::SetWin32(error, "SetPixelFormat() failed: ", GetLastError());
|
||||||
::ReleaseDC(hwnd, hDC);
|
DeleteDC(hDC);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
m_wi.surface_format = GPUTexture::Format::RGBA8;
|
wi.surface_format = GPUTexture::Format::RGBA8;
|
||||||
return hDC;
|
return hDC;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextWGL::InitializeDC(Error* error)
|
HDC OpenGLContextWGL::GetPBufferDC(Error* error)
|
||||||
{
|
|
||||||
if (m_wi.type == WindowInfo::Type::Win32)
|
|
||||||
{
|
|
||||||
m_dc = GetDCAndSetPixelFormat(GetHWND(), error);
|
|
||||||
if (!m_dc)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (m_wi.type == WindowInfo::Type::Surfaceless)
|
|
||||||
{
|
|
||||||
return CreatePBuffer(error);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Error::SetStringFmt(error, "Unknown window info type {}", static_cast<unsigned>(m_wi.type));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenGLContextWGL::ReleaseDC()
|
|
||||||
{
|
|
||||||
if (m_pbuffer)
|
|
||||||
{
|
|
||||||
wglReleasePbufferDCARB(m_pbuffer, m_dc);
|
|
||||||
m_dc = {};
|
|
||||||
|
|
||||||
wglDestroyPbufferARB(m_pbuffer);
|
|
||||||
m_pbuffer = {};
|
|
||||||
|
|
||||||
::ReleaseDC(m_dummy_window, m_dummy_dc);
|
|
||||||
m_dummy_dc = {};
|
|
||||||
|
|
||||||
DestroyWindow(m_dummy_window);
|
|
||||||
m_dummy_window = {};
|
|
||||||
}
|
|
||||||
else if (m_dc)
|
|
||||||
{
|
|
||||||
::ReleaseDC(GetHWND(), m_dc);
|
|
||||||
m_dc = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OpenGLContextWGL::CreatePBuffer(Error* error)
|
|
||||||
{
|
{
|
||||||
static bool window_class_registered = false;
|
static bool window_class_registered = false;
|
||||||
static const wchar_t* window_class_name = L"ContextWGLPBuffer";
|
static const wchar_t* window_class_name = L"ContextWGLPBuffer";
|
||||||
|
|
||||||
|
if (m_pbuffer_dc)
|
||||||
|
return m_pbuffer_dc;
|
||||||
|
|
||||||
if (!window_class_registered)
|
if (!window_class_registered)
|
||||||
{
|
{
|
||||||
WNDCLASSEXW wc = {};
|
WNDCLASSEXW wc = {};
|
||||||
@ -314,24 +354,28 @@ bool OpenGLContextWGL::CreatePBuffer(Error* error)
|
|||||||
if (!RegisterClassExW(&wc))
|
if (!RegisterClassExW(&wc))
|
||||||
{
|
{
|
||||||
Error::SetStringView(error, "(ContextWGL::CreatePBuffer) RegisterClassExW() failed");
|
Error::SetStringView(error, "(ContextWGL::CreatePBuffer) RegisterClassExW() failed");
|
||||||
return false;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
window_class_registered = true;
|
window_class_registered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Assert(m_dummy_window == NULL);
|
||||||
|
Assert(m_pbuffer == NULL);
|
||||||
|
|
||||||
HWND hwnd = CreateWindowExW(0, window_class_name, window_class_name, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
|
HWND hwnd = CreateWindowExW(0, window_class_name, window_class_name, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
|
||||||
if (!hwnd)
|
if (!hwnd)
|
||||||
{
|
{
|
||||||
Error::SetStringView(error, "(ContextWGL::CreatePBuffer) CreateWindowEx() failed");
|
Error::SetStringView(error, "(ContextWGL::CreatePBuffer) CreateWindowEx() failed");
|
||||||
return false;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedGuard hwnd_guard([hwnd]() { DestroyWindow(hwnd); });
|
ScopedGuard hwnd_guard([hwnd]() { DestroyWindow(hwnd); });
|
||||||
|
|
||||||
HDC hdc = GetDCAndSetPixelFormat(hwnd, error);
|
WindowInfo wi = {.type = WindowInfo::Type::Win32, .window_handle = hwnd};
|
||||||
|
HDC hdc = CreateDCAndSetPixelFormat(wi, error);
|
||||||
if (!hdc)
|
if (!hdc)
|
||||||
return false;
|
return NULL;
|
||||||
|
|
||||||
ScopedGuard hdc_guard([hdc, hwnd]() { ::ReleaseDC(hwnd, hdc); });
|
ScopedGuard hdc_guard([hdc, hwnd]() { ::ReleaseDC(hwnd, hdc); });
|
||||||
|
|
||||||
@ -341,25 +385,28 @@ bool OpenGLContextWGL::CreatePBuffer(Error* error)
|
|||||||
ScopedGuard temp_rc_guard([&temp_rc, hdc]() {
|
ScopedGuard temp_rc_guard([&temp_rc, hdc]() {
|
||||||
if (temp_rc)
|
if (temp_rc)
|
||||||
{
|
{
|
||||||
wglMakeCurrent(hdc, nullptr);
|
dyn_libs::wglMakeCurrent(hdc, nullptr);
|
||||||
wglDeleteContext(temp_rc);
|
dyn_libs::wglDeleteContext(temp_rc);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!GLAD_WGL_ARB_pbuffer)
|
if (!GLAD_WGL_ARB_pbuffer)
|
||||||
{
|
{
|
||||||
// we're probably running completely surfaceless... need a temporary context.
|
// we're probably running completely surfaceless... need a temporary context.
|
||||||
temp_rc = wglCreateContext(hdc);
|
temp_rc = dyn_libs::wglCreateContext(hdc);
|
||||||
if (!temp_rc || !wglMakeCurrent(hdc, temp_rc))
|
if (!temp_rc || !dyn_libs::wglMakeCurrent(hdc, temp_rc))
|
||||||
{
|
{
|
||||||
Error::SetStringView(error, "Failed to create temporary context to load WGL for pbuffer.");
|
Error::SetStringView(error, "Failed to create temporary context to load WGL for pbuffer.");
|
||||||
return false;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ReloadWGL(hdc) || !GLAD_WGL_ARB_pbuffer)
|
if (!ReloadWGL(hdc, error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!GLAD_WGL_ARB_pbuffer)
|
||||||
{
|
{
|
||||||
Error::SetStringView(error, "Missing WGL_ARB_pbuffer");
|
Error::SetStringView(error, "Missing WGL_ARB_pbuffer");
|
||||||
return false;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,32 +415,33 @@ bool OpenGLContextWGL::CreatePBuffer(Error* error)
|
|||||||
if (!pbuffer)
|
if (!pbuffer)
|
||||||
{
|
{
|
||||||
Error::SetStringView(error, "(ContextWGL::CreatePBuffer) wglCreatePbufferARB() failed");
|
Error::SetStringView(error, "(ContextWGL::CreatePBuffer) wglCreatePbufferARB() failed");
|
||||||
return false;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedGuard pbuffer_guard([pbuffer]() { wglDestroyPbufferARB(pbuffer); });
|
ScopedGuard pbuffer_guard([pbuffer]() { wglDestroyPbufferARB(pbuffer); });
|
||||||
|
|
||||||
m_dc = wglGetPbufferDCARB(pbuffer);
|
HDC dc = wglGetPbufferDCARB(pbuffer);
|
||||||
if (!m_dc)
|
if (!dc)
|
||||||
{
|
{
|
||||||
Error::SetStringView(error, "(ContextWGL::CreatePbuffer) wglGetPbufferDCARB() failed");
|
Error::SetStringView(error, "(ContextWGL::CreatePbuffer) wglGetPbufferDCARB() failed");
|
||||||
return false;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_dummy_window = hwnd;
|
m_dummy_window = hwnd;
|
||||||
m_dummy_dc = hdc;
|
m_dummy_dc = hdc;
|
||||||
m_pbuffer = pbuffer;
|
m_pbuffer = pbuffer;
|
||||||
|
m_pbuffer_dc = dc;
|
||||||
|
|
||||||
temp_rc_guard.Run();
|
temp_rc_guard.Run();
|
||||||
pbuffer_guard.Cancel();
|
pbuffer_guard.Cancel();
|
||||||
hdc_guard.Cancel();
|
hdc_guard.Cancel();
|
||||||
hwnd_guard.Cancel();
|
hwnd_guard.Cancel();
|
||||||
return true;
|
return dc;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextWGL::CreateAnyContext(HGLRC share_context, bool make_current, Error* error)
|
bool OpenGLContextWGL::CreateAnyContext(HDC hdc, HGLRC share_context, bool make_current, Error* error)
|
||||||
{
|
{
|
||||||
m_rc = wglCreateContext(m_dc);
|
m_rc = dyn_libs::wglCreateContext(hdc);
|
||||||
if (!m_rc)
|
if (!m_rc)
|
||||||
{
|
{
|
||||||
Error::SetWin32(error, "wglCreateContext() failed: ", GetLastError());
|
Error::SetWin32(error, "wglCreateContext() failed: ", GetLastError());
|
||||||
@ -402,21 +450,23 @@ bool OpenGLContextWGL::CreateAnyContext(HGLRC share_context, bool make_current,
|
|||||||
|
|
||||||
if (make_current)
|
if (make_current)
|
||||||
{
|
{
|
||||||
if (!wglMakeCurrent(m_dc, m_rc))
|
if (!dyn_libs::wglMakeCurrent(hdc, m_rc))
|
||||||
{
|
{
|
||||||
Error::SetWin32(error, "wglMakeCurrent() failed: ", GetLastError());
|
Error::SetWin32(error, "wglMakeCurrent() failed: ", GetLastError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_current_dc = hdc;
|
||||||
|
|
||||||
// re-init glad-wgl
|
// re-init glad-wgl
|
||||||
if (!gladLoadWGL(m_dc, [](const char* name) { return (GLADapiproc)wglGetProcAddress(name); }))
|
if (!ReloadWGL(m_current_dc, error))
|
||||||
{
|
{
|
||||||
Error::SetStringView(error, "Loading GLAD WGL functions failed");
|
Error::SetStringView(error, "Loading GLAD WGL functions failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (share_context && !wglShareLists(share_context, m_rc))
|
if (share_context && !dyn_libs::wglShareLists(share_context, m_rc))
|
||||||
{
|
{
|
||||||
Error::SetWin32(error, "wglShareLists() failed: ", GetLastError());
|
Error::SetWin32(error, "wglShareLists() failed: ", GetLastError());
|
||||||
return false;
|
return false;
|
||||||
@ -425,7 +475,7 @@ bool OpenGLContextWGL::CreateAnyContext(HGLRC share_context, bool make_current,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLContextWGL::CreateVersionContext(const Version& version, HGLRC share_context, bool make_current,
|
bool OpenGLContextWGL::CreateVersionContext(const Version& version, HDC hdc, HGLRC share_context, bool make_current,
|
||||||
Error* error)
|
Error* error)
|
||||||
{
|
{
|
||||||
// we need create context attribs
|
// we need create context attribs
|
||||||
@ -454,7 +504,7 @@ bool OpenGLContextWGL::CreateVersionContext(const Version& version, HGLRC share_
|
|||||||
0,
|
0,
|
||||||
0};
|
0};
|
||||||
|
|
||||||
new_rc = wglCreateContextAttribsARB(m_dc, share_context, attribs);
|
new_rc = wglCreateContextAttribsARB(hdc, share_context, attribs);
|
||||||
}
|
}
|
||||||
else if (version.profile == Profile::ES)
|
else if (version.profile == Profile::ES)
|
||||||
{
|
{
|
||||||
@ -475,7 +525,7 @@ bool OpenGLContextWGL::CreateVersionContext(const Version& version, HGLRC share_
|
|||||||
0,
|
0,
|
||||||
0};
|
0};
|
||||||
|
|
||||||
new_rc = wglCreateContextAttribsARB(m_dc, share_context, attribs);
|
new_rc = wglCreateContextAttribsARB(hdc, share_context, attribs);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -489,18 +539,20 @@ bool OpenGLContextWGL::CreateVersionContext(const Version& version, HGLRC share_
|
|||||||
// destroy and swap contexts
|
// destroy and swap contexts
|
||||||
if (m_rc)
|
if (m_rc)
|
||||||
{
|
{
|
||||||
if (!wglMakeCurrent(m_dc, make_current ? new_rc : nullptr))
|
if (!dyn_libs::wglMakeCurrent(hdc, make_current ? new_rc : nullptr))
|
||||||
{
|
{
|
||||||
Error::SetWin32(error, "wglMakeCurrent() failed: ", GetLastError());
|
Error::SetWin32(error, "wglMakeCurrent() failed: ", GetLastError());
|
||||||
wglDeleteContext(new_rc);
|
dyn_libs::wglDeleteContext(new_rc);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_current_dc = hdc;
|
||||||
|
|
||||||
// re-init glad-wgl
|
// re-init glad-wgl
|
||||||
if (make_current && !ReloadWGL(m_dc))
|
if (make_current && !ReloadWGL(hdc, error))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
wglDeleteContext(m_rc);
|
dyn_libs::wglDeleteContext(m_rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_rc = new_rc;
|
m_rc = new_rc;
|
||||||
|
@ -15,36 +15,34 @@
|
|||||||
class OpenGLContextWGL final : public OpenGLContext
|
class OpenGLContextWGL final : public OpenGLContext
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OpenGLContextWGL(const WindowInfo& wi);
|
OpenGLContextWGL();
|
||||||
~OpenGLContextWGL() override;
|
~OpenGLContextWGL() override;
|
||||||
|
|
||||||
static std::unique_ptr<OpenGLContext> Create(const WindowInfo& wi, std::span<const Version> versions_to_try,
|
static std::unique_ptr<OpenGLContext> Create(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
Error* error);
|
std::span<const Version> versions_to_try, Error* error);
|
||||||
|
|
||||||
void* GetProcAddress(const char* name) override;
|
void* GetProcAddress(const char* name) override;
|
||||||
bool ChangeSurface(const WindowInfo& new_wi) override;
|
SurfaceHandle CreateSurface(WindowInfo& wi, Error* error = nullptr) override;
|
||||||
void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
|
void DestroySurface(SurfaceHandle handle) override;
|
||||||
|
void ResizeSurface(WindowInfo& wi, SurfaceHandle handle) override;
|
||||||
bool SwapBuffers() override;
|
bool SwapBuffers() override;
|
||||||
bool IsCurrent() const override;
|
bool IsCurrent() const override;
|
||||||
bool MakeCurrent() override;
|
bool MakeCurrent(SurfaceHandle surface, Error* error = nullptr) override;
|
||||||
bool DoneCurrent() override;
|
bool DoneCurrent() override;
|
||||||
bool SupportsNegativeSwapInterval() const override;
|
bool SupportsNegativeSwapInterval() const override;
|
||||||
bool SetSwapInterval(s32 interval) override;
|
bool SetSwapInterval(s32 interval, Error* error = nullptr) override;
|
||||||
std::unique_ptr<OpenGLContext> CreateSharedContext(const WindowInfo& wi, Error* error) override;
|
std::unique_ptr<OpenGLContext> CreateSharedContext(WindowInfo& wi, SurfaceHandle* surface, Error* error) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ALWAYS_INLINE HWND GetHWND() const { return static_cast<HWND>(m_wi.window_handle); }
|
bool Initialize(WindowInfo& wi, SurfaceHandle* surface, std::span<const Version> versions_to_try, Error* error);
|
||||||
|
|
||||||
HDC GetDCAndSetPixelFormat(HWND hwnd, Error* error);
|
bool CreateAnyContext(HDC hdc, HGLRC share_context, bool make_current, Error* error);
|
||||||
|
bool CreateVersionContext(const Version& version, HDC hdc, HGLRC share_context, bool make_current, Error* error);
|
||||||
|
|
||||||
bool Initialize(std::span<const Version> versions_to_try, Error* error);
|
HDC CreateDCAndSetPixelFormat(WindowInfo& wi, Error* error);
|
||||||
bool InitializeDC(Error* error);
|
HDC GetPBufferDC(Error* error);
|
||||||
void ReleaseDC();
|
|
||||||
bool CreatePBuffer(Error* error);
|
|
||||||
bool CreateAnyContext(HGLRC share_context, bool make_current, Error* error);
|
|
||||||
bool CreateVersionContext(const Version& version, HGLRC share_context, bool make_current, Error* error);
|
|
||||||
|
|
||||||
HDC m_dc = {};
|
HDC m_current_dc = {};
|
||||||
HGLRC m_rc = {};
|
HGLRC m_rc = {};
|
||||||
|
|
||||||
// Can't change pixel format once it's set for a RC.
|
// Can't change pixel format once it's set for a RC.
|
||||||
@ -54,4 +52,5 @@ private:
|
|||||||
HWND m_dummy_window = {};
|
HWND m_dummy_window = {};
|
||||||
HDC m_dummy_dc = {};
|
HDC m_dummy_dc = {};
|
||||||
HPBUFFERARB m_pbuffer = {};
|
HPBUFFERARB m_pbuffer = {};
|
||||||
|
HDC m_pbuffer_dc = {};
|
||||||
};
|
};
|
||||||
|
@ -19,13 +19,16 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
LOG_CHANNEL(OpenGLDevice);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
static constexpr const std::array<GLenum, GPUDevice::MAX_RENDER_TARGETS> s_draw_buffers = {
|
static constexpr const std::array<GLenum, GPUDevice::MAX_RENDER_TARGETS> s_draw_buffers = {
|
||||||
{GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3}};
|
{GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3}};
|
||||||
|
|
||||||
OpenGLDevice::OpenGLDevice()
|
OpenGLDevice::OpenGLDevice()
|
||||||
{
|
{
|
||||||
|
// Could change to GLES later.
|
||||||
|
m_render_api = RenderAPI::OpenGL;
|
||||||
|
|
||||||
// Something which won't be matched..
|
// Something which won't be matched..
|
||||||
std::memset(&m_last_rasterization_state, 0xFF, sizeof(m_last_rasterization_state));
|
std::memset(&m_last_rasterization_state, 0xFF, sizeof(m_last_rasterization_state));
|
||||||
std::memset(&m_last_depth_state, 0xFF, sizeof(m_last_depth_state));
|
std::memset(&m_last_depth_state, 0xFF, sizeof(m_last_depth_state));
|
||||||
@ -238,19 +241,6 @@ void OpenGLDevice::InsertDebugMessage(const char* msg)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLDevice::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle)
|
|
||||||
{
|
|
||||||
// OpenGL does not support Mailbox.
|
|
||||||
mode = (mode == GPUVSyncMode::Mailbox) ? GPUVSyncMode::FIFO : mode;
|
|
||||||
m_allow_present_throttle = allow_present_throttle;
|
|
||||||
|
|
||||||
if (m_vsync_mode == mode)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_vsync_mode = mode;
|
|
||||||
SetSwapInterval();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GLAD_API_PTR GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
|
static void GLAD_API_PTR GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
|
||||||
const GLchar* message, const void* userParam)
|
const GLchar* message, const void* userParam)
|
||||||
{
|
{
|
||||||
@ -271,15 +261,15 @@ static void GLAD_API_PTR GLDebugCallback(GLenum source, GLenum type, GLuint id,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLDevice::HasSurface() const
|
bool OpenGLDevice::CreateDeviceAndMainSwapChain(std::string_view adapter, FeatureMask disabled_features,
|
||||||
|
const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||||
|
bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control, Error* error)
|
||||||
{
|
{
|
||||||
return m_window_info.type != WindowInfo::Type::Surfaceless;
|
WindowInfo wi_copy(wi);
|
||||||
}
|
OpenGLContext::SurfaceHandle wi_surface;
|
||||||
|
m_gl_context = OpenGLContext::Create(wi_copy, &wi_surface, error);
|
||||||
bool OpenGLDevice::CreateDevice(std::string_view adapter, std::optional<bool> exclusive_fullscreen_control,
|
|
||||||
FeatureMask disabled_features, Error* error)
|
|
||||||
{
|
|
||||||
m_gl_context = OpenGLContext::Create(m_window_info, error);
|
|
||||||
if (!m_gl_context)
|
if (!m_gl_context)
|
||||||
{
|
{
|
||||||
ERROR_LOG("Failed to create any GL context");
|
ERROR_LOG("Failed to create any GL context");
|
||||||
@ -287,9 +277,11 @@ bool OpenGLDevice::CreateDevice(std::string_view adapter, std::optional<bool> ex
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
// Is this needed?
|
// Is this needed?
|
||||||
m_window_info = m_gl_context->GetWindowInfo();
|
m_window_info = m_gl_context->GetWindowInfo();
|
||||||
m_vsync_mode = (m_vsync_mode == GPUVSyncMode::Mailbox) ? GPUVSyncMode::FIFO : m_vsync_mode;
|
m_vsync_mode = ;
|
||||||
|
#endif
|
||||||
|
|
||||||
const bool opengl_is_available =
|
const bool opengl_is_available =
|
||||||
((!m_gl_context->IsGLES() && (GLAD_GL_VERSION_3_0 || GLAD_GL_ARB_uniform_buffer_object)) ||
|
((!m_gl_context->IsGLES() && (GLAD_GL_VERSION_3_0 || GLAD_GL_ARB_uniform_buffer_object)) ||
|
||||||
@ -303,10 +295,6 @@ bool OpenGLDevice::CreateDevice(std::string_view adapter, std::optional<bool> ex
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetSwapInterval();
|
|
||||||
if (HasSurface())
|
|
||||||
RenderBlankFrame();
|
|
||||||
|
|
||||||
if (m_debug_device && GLAD_GL_KHR_debug)
|
if (m_debug_device && GLAD_GL_KHR_debug)
|
||||||
{
|
{
|
||||||
if (m_gl_context->IsGLES())
|
if (m_gl_context->IsGLES())
|
||||||
@ -326,6 +314,21 @@ bool OpenGLDevice::CreateDevice(std::string_view adapter, std::optional<bool> ex
|
|||||||
glObjectLabel = nullptr;
|
glObjectLabel = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create main swap chain
|
||||||
|
if (!wi_copy.IsSurfaceless())
|
||||||
|
{
|
||||||
|
// OpenGL does not support mailbox.
|
||||||
|
m_main_swap_chain = std::make_unique<OpenGLSwapChain>(
|
||||||
|
wi_copy, (vsync_mode == GPUVSyncMode::Mailbox) ? GPUVSyncMode::FIFO : vsync_mode, allow_present_throttle,
|
||||||
|
wi_surface);
|
||||||
|
|
||||||
|
Error swap_interval_error;
|
||||||
|
if (!OpenGLSwapChain::SetSwapInterval(m_gl_context.get(), m_main_swap_chain->GetVSyncMode(), &swap_interval_error))
|
||||||
|
WARNING_LOG("Failed to set swap interval on main swap chain: {}", swap_interval_error.GetDescription());
|
||||||
|
|
||||||
|
RenderBlankFrame();
|
||||||
|
}
|
||||||
|
|
||||||
if (!CheckFeatures(disabled_features))
|
if (!CheckFeatures(disabled_features))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -532,50 +535,104 @@ void OpenGLDevice::DestroyDevice()
|
|||||||
DestroyBuffers();
|
DestroyBuffers();
|
||||||
|
|
||||||
m_gl_context->DoneCurrent();
|
m_gl_context->DoneCurrent();
|
||||||
|
m_main_swap_chain.reset();
|
||||||
m_gl_context.reset();
|
m_gl_context.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLDevice::UpdateWindow()
|
OpenGLSwapChain::OpenGLSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
|
OpenGLContext::SurfaceHandle surface_handle)
|
||||||
|
: GPUSwapChain(wi, vsync_mode, allow_present_throttle), m_surface_handle(surface_handle)
|
||||||
{
|
{
|
||||||
Assert(m_gl_context);
|
}
|
||||||
|
|
||||||
DestroySurface();
|
OpenGLSwapChain::~OpenGLSwapChain()
|
||||||
|
{
|
||||||
|
OpenGLDevice::GetContext()->DestroySurface(m_surface_handle);
|
||||||
|
}
|
||||||
|
|
||||||
if (!AcquireWindow(false))
|
bool OpenGLSwapChain::ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error)
|
||||||
return false;
|
{
|
||||||
|
m_window_info.surface_scale = new_scale;
|
||||||
|
if (m_window_info.surface_width == new_width && m_window_info.surface_height == new_height)
|
||||||
|
return true;
|
||||||
|
|
||||||
if (!m_gl_context->ChangeSurface(m_window_info))
|
m_window_info.surface_width = new_width;
|
||||||
{
|
m_window_info.surface_height = new_height;
|
||||||
ERROR_LOG("Failed to change surface");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_window_info = m_gl_context->GetWindowInfo();
|
|
||||||
|
|
||||||
if (m_window_info.type != WindowInfo::Type::Surfaceless)
|
|
||||||
{
|
|
||||||
// reset vsync rate, since it (usually) gets lost
|
|
||||||
SetSwapInterval();
|
|
||||||
RenderBlankFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
OpenGLDevice::GetContext()->ResizeSurface(m_window_info, m_surface_handle);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLDevice::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
bool OpenGLSwapChain::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error)
|
||||||
{
|
{
|
||||||
if (m_window_info.IsSurfaceless())
|
// OpenGL does not support Mailbox.
|
||||||
return;
|
mode = (mode == GPUVSyncMode::Mailbox) ? GPUVSyncMode::FIFO : mode;
|
||||||
|
m_allow_present_throttle = allow_present_throttle;
|
||||||
|
|
||||||
m_window_info.surface_scale = new_window_scale;
|
if (m_vsync_mode == mode)
|
||||||
if (m_window_info.surface_width == static_cast<u32>(new_window_width) &&
|
return true;
|
||||||
m_window_info.surface_height == static_cast<u32>(new_window_height))
|
|
||||||
|
const bool is_main_swap_chain = (g_gpu_device->GetMainSwapChain() == this);
|
||||||
|
|
||||||
|
OpenGLContext* ctx = OpenGLDevice::GetContext();
|
||||||
|
if (!is_main_swap_chain && !ctx->MakeCurrent(m_surface_handle))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const bool result = SetSwapInterval(ctx, mode, error);
|
||||||
|
|
||||||
|
if (!is_main_swap_chain)
|
||||||
|
ctx->MakeCurrent(static_cast<OpenGLSwapChain*>(g_gpu_device->GetMainSwapChain())->m_surface_handle);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_vsync_mode = mode;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLSwapChain::SetSwapInterval(OpenGLContext* ctx, GPUVSyncMode mode, Error* error)
|
||||||
|
{
|
||||||
|
// Window framebuffer has to be bound to call SetSwapInterval.
|
||||||
|
const s32 interval = static_cast<s32>(mode == GPUVSyncMode::FIFO);
|
||||||
|
GLint current_fbo = 0;
|
||||||
|
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
const bool result = ctx->SetSwapInterval(interval);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<GPUSwapChain> OpenGLDevice::CreateSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||||
|
bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control,
|
||||||
|
Error* error)
|
||||||
|
{
|
||||||
|
if (wi.IsSurfaceless())
|
||||||
{
|
{
|
||||||
return;
|
Error::SetStringView(error, "Trying to create a surfaceless swap chain.");
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
m_gl_context->ResizeSurface(static_cast<u32>(new_window_width), static_cast<u32>(new_window_height));
|
WindowInfo wi_copy(wi);
|
||||||
m_window_info = m_gl_context->GetWindowInfo();
|
const OpenGLContext::SurfaceHandle surface_handle = m_gl_context->CreateSurface(wi_copy, error);
|
||||||
|
if (!surface_handle || !m_gl_context->MakeCurrent(surface_handle, error))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
Error swap_interval_error;
|
||||||
|
if (!OpenGLSwapChain::SetSwapInterval(m_gl_context.get(), vsync_mode, &swap_interval_error))
|
||||||
|
WARNING_LOG("Failed to set swap interval on new swap chain: {}", swap_interval_error.GetDescription());
|
||||||
|
|
||||||
|
RenderBlankFrame();
|
||||||
|
|
||||||
|
// only bother switching back if we actually have a main swap chain, avoids a couple of
|
||||||
|
// SetCurrent() calls when we're switching to and from fullscreen.
|
||||||
|
if (m_main_swap_chain)
|
||||||
|
m_gl_context->MakeCurrent(static_cast<OpenGLSwapChain*>(m_main_swap_chain.get())->GetSurfaceHandle());
|
||||||
|
|
||||||
|
return std::make_unique<OpenGLSwapChain>(wi_copy, vsync_mode, allow_present_throttle, surface_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string OpenGLDevice::GetDriverInfo() const
|
std::string OpenGLDevice::GetDriverInfo() const
|
||||||
@ -588,29 +645,18 @@ std::string OpenGLDevice::GetDriverInfo() const
|
|||||||
gl_shading_language_version);
|
gl_shading_language_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLDevice::ExecuteAndWaitForGPUIdle()
|
void OpenGLDevice::FlushCommands()
|
||||||
|
{
|
||||||
|
glFlush();
|
||||||
|
TrimTexturePool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDevice::WaitForGPUIdle()
|
||||||
{
|
{
|
||||||
// Could be glFinish(), but I'm afraid for mobile drivers...
|
// Could be glFinish(), but I'm afraid for mobile drivers...
|
||||||
glFlush();
|
glFlush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLDevice::SetSwapInterval()
|
|
||||||
{
|
|
||||||
if (m_window_info.type == WindowInfo::Type::Surfaceless)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Window framebuffer has to be bound to call SetSwapInterval.
|
|
||||||
const s32 interval = static_cast<s32>(m_vsync_mode == GPUVSyncMode::FIFO);
|
|
||||||
GLint current_fbo = 0;
|
|
||||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
|
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
||||||
|
|
||||||
if (!m_gl_context->SetSwapInterval(interval))
|
|
||||||
WARNING_LOG("Failed to set swap interval to {}", interval);
|
|
||||||
|
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenGLDevice::RenderBlankFrame()
|
void OpenGLDevice::RenderBlankFrame()
|
||||||
{
|
{
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
@ -675,16 +721,6 @@ void OpenGLDevice::DestroyFramebuffer(GLuint fbo)
|
|||||||
glDeleteFramebuffers(1, &fbo);
|
glDeleteFramebuffers(1, &fbo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLDevice::DestroySurface()
|
|
||||||
{
|
|
||||||
if (!m_gl_context)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_window_info.SetSurfaceless();
|
|
||||||
if (!m_gl_context->ChangeSurface(m_window_info))
|
|
||||||
ERROR_LOG("Failed to switch to surfaceless");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OpenGLDevice::CreateBuffers()
|
bool OpenGLDevice::CreateBuffers()
|
||||||
{
|
{
|
||||||
if (!(m_vertex_buffer = OpenGLStreamBuffer::Create(GL_ARRAY_BUFFER, VERTEX_BUFFER_SIZE)) ||
|
if (!(m_vertex_buffer = OpenGLStreamBuffer::Create(GL_ARRAY_BUFFER, VERTEX_BUFFER_SIZE)) ||
|
||||||
@ -742,14 +778,9 @@ void OpenGLDevice::DestroyBuffers()
|
|||||||
m_vertex_buffer.reset();
|
m_vertex_buffer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
GPUDevice::PresentResult OpenGLDevice::BeginPresent(u32 clear_color)
|
GPUDevice::PresentResult OpenGLDevice::BeginPresent(GPUSwapChain* swap_chain, u32 clear_color)
|
||||||
{
|
{
|
||||||
if (m_window_info.type == WindowInfo::Type::Surfaceless)
|
m_gl_context->MakeCurrent(static_cast<OpenGLSwapChain*>(swap_chain)->GetSurfaceHandle());
|
||||||
{
|
|
||||||
glFlush();
|
|
||||||
TrimTexturePool();
|
|
||||||
return PresentResult::SkipPresent;
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
glDisable(GL_SCISSOR_TEST);
|
glDisable(GL_SCISSOR_TEST);
|
||||||
@ -764,7 +795,8 @@ GPUDevice::PresentResult OpenGLDevice::BeginPresent(u32 clear_color)
|
|||||||
std::memset(m_current_render_targets.data(), 0, sizeof(m_current_render_targets));
|
std::memset(m_current_render_targets.data(), 0, sizeof(m_current_render_targets));
|
||||||
m_current_depth_target = nullptr;
|
m_current_depth_target = nullptr;
|
||||||
|
|
||||||
const GSVector4i window_rc = GSVector4i(0, 0, m_window_info.surface_width, m_window_info.surface_height);
|
const GSVector4i window_rc =
|
||||||
|
GSVector4i(0, 0, static_cast<s32>(swap_chain->GetWidth()), static_cast<s32>(swap_chain->GetHeight()));
|
||||||
m_last_viewport = window_rc;
|
m_last_viewport = window_rc;
|
||||||
m_last_scissor = window_rc;
|
m_last_scissor = window_rc;
|
||||||
UpdateViewport();
|
UpdateViewport();
|
||||||
@ -772,23 +804,23 @@ GPUDevice::PresentResult OpenGLDevice::BeginPresent(u32 clear_color)
|
|||||||
return PresentResult::OK;
|
return PresentResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLDevice::EndPresent(bool explicit_present, u64 present_time)
|
void OpenGLDevice::EndPresent(GPUSwapChain* swap_chain, bool explicit_present, u64 present_time)
|
||||||
{
|
{
|
||||||
DebugAssert(!explicit_present && present_time == 0);
|
DebugAssert(!explicit_present && present_time == 0);
|
||||||
DebugAssert(m_current_fbo == 0);
|
DebugAssert(m_current_fbo == 0);
|
||||||
|
|
||||||
if (m_gpu_timing_enabled)
|
if (swap_chain == m_main_swap_chain.get() && m_gpu_timing_enabled)
|
||||||
PopTimestampQuery();
|
PopTimestampQuery();
|
||||||
|
|
||||||
m_gl_context->SwapBuffers();
|
m_gl_context->SwapBuffers();
|
||||||
|
|
||||||
if (m_gpu_timing_enabled)
|
if (swap_chain == m_main_swap_chain.get() && m_gpu_timing_enabled)
|
||||||
KickTimestampQuery();
|
KickTimestampQuery();
|
||||||
|
|
||||||
TrimTexturePool();
|
TrimTexturePool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLDevice::SubmitPresent()
|
void OpenGLDevice::SubmitPresent(GPUSwapChain* swap_chain)
|
||||||
{
|
{
|
||||||
Panic("Not supported by this API.");
|
Panic("Not supported by this API.");
|
||||||
}
|
}
|
||||||
|
@ -36,20 +36,21 @@ public:
|
|||||||
return GetInstance().m_texture_stream_buffer.get();
|
return GetInstance().m_texture_stream_buffer.get();
|
||||||
}
|
}
|
||||||
ALWAYS_INLINE static bool IsGLES() { return GetInstance().m_gl_context->IsGLES(); }
|
ALWAYS_INLINE static bool IsGLES() { return GetInstance().m_gl_context->IsGLES(); }
|
||||||
|
ALWAYS_INLINE static OpenGLContext* GetContext() { return GetInstance().m_gl_context.get(); }
|
||||||
static void BindUpdateTextureUnit();
|
static void BindUpdateTextureUnit();
|
||||||
static bool ShouldUsePBOsForDownloads();
|
static bool ShouldUsePBOsForDownloads();
|
||||||
static void SetErrorObject(Error* errptr, std::string_view prefix, GLenum glerr);
|
static void SetErrorObject(Error* errptr, std::string_view prefix, GLenum glerr);
|
||||||
|
|
||||||
bool HasSurface() const override;
|
|
||||||
void DestroySurface() override;
|
|
||||||
|
|
||||||
bool UpdateWindow() override;
|
|
||||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
|
||||||
|
|
||||||
std::string GetDriverInfo() const override;
|
std::string GetDriverInfo() const override;
|
||||||
|
|
||||||
void ExecuteAndWaitForGPUIdle() override;
|
void FlushCommands() override;
|
||||||
|
void WaitForGPUIdle() override;
|
||||||
|
|
||||||
|
std::unique_ptr<GPUSwapChain> CreateSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||||
|
bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control,
|
||||||
|
Error* error) override;
|
||||||
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
||||||
GPUTexture::Type type, GPUTexture::Format format,
|
GPUTexture::Type type, GPUTexture::Format format,
|
||||||
const void* data = nullptr, u32 data_stride = 0) override;
|
const void* data = nullptr, u32 data_stride = 0) override;
|
||||||
@ -100,11 +101,9 @@ public:
|
|||||||
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
||||||
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
||||||
|
|
||||||
void SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle) override;
|
PresentResult BeginPresent(GPUSwapChain* swap_chain, u32 clear_color) override;
|
||||||
|
void EndPresent(GPUSwapChain* swap_chain, bool explicit_present, u64 present_time) override;
|
||||||
PresentResult BeginPresent(u32 clear_color) override;
|
void SubmitPresent(GPUSwapChain* swap_chain) override;
|
||||||
void EndPresent(bool explicit_present, u64 present_time) override;
|
|
||||||
void SubmitPresent() override;
|
|
||||||
|
|
||||||
bool SetGPUTimingEnabled(bool enabled) override;
|
bool SetGPUTimingEnabled(bool enabled) override;
|
||||||
float GetAndResetAccumulatedGPUTime() override;
|
float GetAndResetAccumulatedGPUTime() override;
|
||||||
@ -131,9 +130,13 @@ public:
|
|||||||
void UnbindSampler(GLuint id);
|
void UnbindSampler(GLuint id);
|
||||||
void UnbindPipeline(const OpenGLPipeline* pl);
|
void UnbindPipeline(const OpenGLPipeline* pl);
|
||||||
|
|
||||||
|
void RenderBlankFrame();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool CreateDevice(std::string_view adapter, std::optional<bool> exclusive_fullscreen_control,
|
bool CreateDeviceAndMainSwapChain(std::string_view adapter, FeatureMask disabled_features, const WindowInfo& wi,
|
||||||
FeatureMask disabled_features, Error* error) override;
|
GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control, Error* error) override;
|
||||||
void DestroyDevice() override;
|
void DestroyDevice() override;
|
||||||
|
|
||||||
bool OpenPipelineCache(const std::string& path, Error* error) override;
|
bool OpenPipelineCache(const std::string& path, Error* error) override;
|
||||||
@ -154,9 +157,6 @@ private:
|
|||||||
bool CreateBuffers();
|
bool CreateBuffers();
|
||||||
void DestroyBuffers();
|
void DestroyBuffers();
|
||||||
|
|
||||||
void SetSwapInterval();
|
|
||||||
void RenderBlankFrame();
|
|
||||||
|
|
||||||
s32 IsRenderTargetBound(const GPUTexture* tex) const;
|
s32 IsRenderTargetBound(const GPUTexture* tex) const;
|
||||||
static GLuint CreateFramebuffer(GPUTexture* const* rts, u32 num_rts, GPUTexture* ds, u32 flags);
|
static GLuint CreateFramebuffer(GPUTexture* const* rts, u32 num_rts, GPUTexture* ds, u32 flags);
|
||||||
static void DestroyFramebuffer(GLuint fbo);
|
static void DestroyFramebuffer(GLuint fbo);
|
||||||
@ -230,3 +230,21 @@ private:
|
|||||||
bool m_disable_pbo = false;
|
bool m_disable_pbo = false;
|
||||||
bool m_disable_async_download = false;
|
bool m_disable_async_download = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class OpenGLSwapChain : public GPUSwapChain
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenGLSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
|
OpenGLContext::SurfaceHandle surface_handle);
|
||||||
|
~OpenGLSwapChain() override;
|
||||||
|
|
||||||
|
ALWAYS_INLINE OpenGLContext::SurfaceHandle GetSurfaceHandle() const { return m_surface_handle; }
|
||||||
|
|
||||||
|
bool ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error) override;
|
||||||
|
bool SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error) override;
|
||||||
|
|
||||||
|
static bool SetSwapInterval(OpenGLContext* ctx, GPUVSyncMode mode, Error* error);
|
||||||
|
|
||||||
|
private:
|
||||||
|
OpenGLContext::SurfaceHandle m_surface_handle;
|
||||||
|
};
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|
||||||
LOG_CHANNEL(OpenGLDevice);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
struct PipelineDiskCacheFooter
|
struct PipelineDiskCacheFooter
|
||||||
{
|
{
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
LOG_CHANNEL(OpenGLDevice);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
// Looking across a range of GPUs, the optimal copy alignment for Vulkan drivers seems
|
// Looking across a range of GPUs, the optimal copy alignment for Vulkan drivers seems
|
||||||
// to be between 1 (AMD/NV) and 64 (Intel). So, we'll go with 64 here.
|
// to be between 1 (AMD/NV) and 64 (Intel). So, we'll go with 64 here.
|
||||||
@ -260,8 +260,7 @@ bool OpenGLTexture::Update(u32 x, u32 y, u32 width, u32 height, const void* data
|
|||||||
const GLenum target = GetGLTarget();
|
const GLenum target = GetGLTarget();
|
||||||
const auto [gl_internal_format, gl_format, gl_type] = GetPixelFormatMapping(m_format, OpenGLDevice::IsGLES());
|
const auto [gl_internal_format, gl_format, gl_type] = GetPixelFormatMapping(m_format, OpenGLDevice::IsGLES());
|
||||||
const u32 pixel_size = GetPixelSize();
|
const u32 pixel_size = GetPixelSize();
|
||||||
const u32 preferred_pitch =
|
const u32 preferred_pitch = Common::AlignUpPow2(static_cast<u32>(width) * pixel_size, TEXTURE_UPLOAD_PITCH_ALIGNMENT);
|
||||||
Common::AlignUpPow2(static_cast<u32>(width) * pixel_size, TEXTURE_UPLOAD_PITCH_ALIGNMENT);
|
|
||||||
const u32 map_size = preferred_pitch * static_cast<u32>(height);
|
const u32 map_size = preferred_pitch * static_cast<u32>(height);
|
||||||
OpenGLStreamBuffer* sb = OpenGLDevice::GetTextureStreamBuffer();
|
OpenGLStreamBuffer* sb = OpenGLDevice::GetTextureStreamBuffer();
|
||||||
|
|
||||||
@ -551,7 +550,8 @@ void OpenGLDevice::CommitRTClearInFB(OpenGLTexture* tex, u32 idx)
|
|||||||
case GPUTexture::State::Invalidated:
|
case GPUTexture::State::Invalidated:
|
||||||
{
|
{
|
||||||
const GLenum attachment = GL_COLOR_ATTACHMENT0 + idx;
|
const GLenum attachment = GL_COLOR_ATTACHMENT0 + idx;
|
||||||
glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, &attachment);
|
if (glInvalidateFramebuffer)
|
||||||
|
glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, &attachment);
|
||||||
tex->SetState(GPUTexture::State::Dirty);
|
tex->SetState(GPUTexture::State::Dirty);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -589,7 +589,8 @@ void OpenGLDevice::CommitDSClearInFB(OpenGLTexture* tex)
|
|||||||
case GPUTexture::State::Invalidated:
|
case GPUTexture::State::Invalidated:
|
||||||
{
|
{
|
||||||
const GLenum attachment = GL_DEPTH_ATTACHMENT;
|
const GLenum attachment = GL_DEPTH_ATTACHMENT;
|
||||||
glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, &attachment);
|
if (glInvalidateFramebuffer)
|
||||||
|
glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, &attachment);
|
||||||
tex->SetState(GPUTexture::State::Dirty);
|
tex->SetState(GPUTexture::State::Dirty);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "platform_misc.h"
|
#include "platform_misc.h"
|
||||||
#include "window_info.h"
|
#include "window_info.h"
|
||||||
|
|
||||||
|
#include "common/error.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/small_string.h"
|
#include "common/small_string.h"
|
||||||
|
|
||||||
@ -87,49 +88,45 @@ bool PlatformMisc::PlaySoundAsync(const char* path)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CocoaTools::CreateMetalLayer(WindowInfo* wi)
|
void* CocoaTools::CreateMetalLayer(const WindowInfo& wi, Error* error)
|
||||||
{
|
{
|
||||||
// Punt off to main thread if we're not calling from it already.
|
// Punt off to main thread if we're not calling from it already.
|
||||||
if (![NSThread isMainThread])
|
if (![NSThread isMainThread])
|
||||||
{
|
{
|
||||||
bool ret;
|
void* ret;
|
||||||
dispatch_sync(dispatch_get_main_queue(), [&ret, wi]() { ret = CreateMetalLayer(wi); });
|
dispatch_sync(dispatch_get_main_queue(), [&ret, &wi, error]() { ret = CreateMetalLayer(wi, error); });
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
CAMetalLayer* layer = [CAMetalLayer layer];
|
CAMetalLayer* layer = [CAMetalLayer layer];
|
||||||
if (layer == nil)
|
if (layer == nil)
|
||||||
{
|
{
|
||||||
ERROR_LOG("Failed to create CAMetalLayer");
|
Error::SetStringView(error, "Failed to create CAMetalLayer");
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSView* view = (__bridge NSView*)wi->window_handle;
|
NSView* view = (__bridge NSView*)wi.window_handle;
|
||||||
[view setWantsLayer:TRUE];
|
[view setWantsLayer:TRUE];
|
||||||
[view setLayer:layer];
|
[view setLayer:layer];
|
||||||
[layer setContentsScale:[[[view window] screen] backingScaleFactor]];
|
[layer setContentsScale:[[[view window] screen] backingScaleFactor]];
|
||||||
|
|
||||||
wi->surface_handle = (__bridge void*)layer;
|
return (__bridge void*)layer;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CocoaTools::DestroyMetalLayer(WindowInfo* wi)
|
void CocoaTools::DestroyMetalLayer(const WindowInfo& wi, void* layer)
|
||||||
{
|
{
|
||||||
if (!wi->surface_handle)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Punt off to main thread if we're not calling from it already.
|
// Punt off to main thread if we're not calling from it already.
|
||||||
if (![NSThread isMainThread])
|
if (![NSThread isMainThread])
|
||||||
{
|
{
|
||||||
dispatch_sync(dispatch_get_main_queue(), [wi]() { DestroyMetalLayer(wi); });
|
dispatch_sync(dispatch_get_main_queue(), [&wi, layer]() { DestroyMetalLayer(wi, layer); });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSView* view = (__bridge NSView*)wi->window_handle;
|
NSView* view = (__bridge NSView*)wi.window_handle;
|
||||||
CAMetalLayer* layer = (__bridge CAMetalLayer*)wi->surface_handle;
|
CAMetalLayer* clayer = (__bridge CAMetalLayer*)layer;
|
||||||
[view setLayer:nil];
|
[view setLayer:nil];
|
||||||
[view setWantsLayer:NO];
|
[view setWantsLayer:NO];
|
||||||
[layer release];
|
[clayer release];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<float> CocoaTools::GetViewRefreshRate(const WindowInfo& wi)
|
std::optional<float> CocoaTools::GetViewRefreshRate(const WindowInfo& wi)
|
||||||
@ -137,7 +134,7 @@ std::optional<float> CocoaTools::GetViewRefreshRate(const WindowInfo& wi)
|
|||||||
if (![NSThread isMainThread])
|
if (![NSThread isMainThread])
|
||||||
{
|
{
|
||||||
std::optional<float> ret;
|
std::optional<float> ret;
|
||||||
dispatch_sync(dispatch_get_main_queue(), [&ret, wi]{ ret = GetViewRefreshRate(wi); });
|
dispatch_sync(dispatch_get_main_queue(), [&ret, wi] { ret = GetViewRefreshRate(wi); });
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,10 +437,10 @@ void PostProcessing::Chain::LoadStages()
|
|||||||
DEV_LOG("Loaded {} post-processing stages.", stage_count);
|
DEV_LOG("Loaded {} post-processing stages.", stage_count);
|
||||||
|
|
||||||
// precompile shaders
|
// precompile shaders
|
||||||
if (!IsInternalChain() && g_gpu_device && g_gpu_device->GetWindowFormat() != GPUTexture::Format::Unknown)
|
if (!IsInternalChain() && g_gpu_device && g_gpu_device->HasMainSwapChain())
|
||||||
{
|
{
|
||||||
CheckTargets(g_gpu_device->GetWindowFormat(), g_gpu_device->GetWindowWidth(), g_gpu_device->GetWindowHeight(),
|
CheckTargets(g_gpu_device->GetMainSwapChain()->GetFormat(), g_gpu_device->GetMainSwapChain()->GetWidth(),
|
||||||
&progress);
|
g_gpu_device->GetMainSwapChain()->GetHeight(), &progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
// must be down here, because we need to compile first, triggered by CheckTargets()
|
// must be down here, because we need to compile first, triggered by CheckTargets()
|
||||||
|
@ -333,9 +333,9 @@ bool PostProcessing::ReShadeFXShader::LoadFromString(std::string name, std::stri
|
|||||||
// TODO: This could use spv, it's probably fastest.
|
// TODO: This could use spv, it's probably fastest.
|
||||||
const auto& [cg, cg_language] = CreateRFXCodegen();
|
const auto& [cg, cg_language] = CreateRFXCodegen();
|
||||||
|
|
||||||
if (!CreateModule(only_config ? DEFAULT_BUFFER_WIDTH : g_gpu_device->GetWindowWidth(),
|
if (!CreateModule(only_config ? DEFAULT_BUFFER_WIDTH : g_gpu_device->GetMainSwapChain()->GetWidth(),
|
||||||
only_config ? DEFAULT_BUFFER_HEIGHT : g_gpu_device->GetWindowHeight(), cg.get(), std::move(code),
|
only_config ? DEFAULT_BUFFER_HEIGHT : g_gpu_device->GetMainSwapChain()->GetHeight(), cg.get(),
|
||||||
error))
|
std::move(code), error))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1762,7 +1762,8 @@ GPUDevice::PresentResult PostProcessing::ReShadeFXShader::Apply(GPUTexture* inpu
|
|||||||
if (pass.render_targets.size() == 1 && pass.render_targets[0] == OUTPUT_COLOR_TEXTURE && !final_target)
|
if (pass.render_targets.size() == 1 && pass.render_targets[0] == OUTPUT_COLOR_TEXTURE && !final_target)
|
||||||
{
|
{
|
||||||
// Special case: drawing to final buffer.
|
// Special case: drawing to final buffer.
|
||||||
if (const GPUDevice::PresentResult pres = g_gpu_device->BeginPresent(); pres != GPUDevice::PresentResult::OK)
|
const GPUDevice::PresentResult pres = g_gpu_device->BeginPresent(g_gpu_device->GetMainSwapChain());
|
||||||
|
if (pres != GPUDevice::PresentResult::OK)
|
||||||
{
|
{
|
||||||
GL_POP();
|
GL_POP();
|
||||||
return pres;
|
return pres;
|
||||||
|
@ -177,7 +177,8 @@ GPUDevice::PresentResult PostProcessing::GLSLShader::Apply(GPUTexture* input_col
|
|||||||
// Assumes final stage has been cleared already.
|
// Assumes final stage has been cleared already.
|
||||||
if (!final_target)
|
if (!final_target)
|
||||||
{
|
{
|
||||||
if (const GPUDevice::PresentResult pres = g_gpu_device->BeginPresent(); pres != GPUDevice::PresentResult::OK)
|
const GPUDevice::PresentResult pres = g_gpu_device->BeginPresent(g_gpu_device->GetMainSwapChain());
|
||||||
|
if (pres != GPUDevice::PresentResult::OK)
|
||||||
return pres;
|
return pres;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);CPUINFO_SHARED=1;ENABLE_VULKAN=1</PreprocessorDefinitions>
|
<PreprocessorDefinitions>%(PreprocessorDefinitions);CPUINFO_SHARED=1;ENABLE_VULKAN=1;ENABLE_SDL=1</PreprocessorDefinitions>
|
||||||
<PreprocessorDefinitions Condition="'$(Platform)'!='ARM64'">%(PreprocessorDefinitions);ENABLE_OPENGL=1</PreprocessorDefinitions>
|
<PreprocessorDefinitions Condition="'$(Platform)'!='ARM64'">%(PreprocessorDefinitions);ENABLE_OPENGL=1</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\kissfft\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\libchdr\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\d3d12ma\include;$(SolutionDir)dep\vulkan\include;$(SolutionDir)dep\ffmpeg\include</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\kissfft\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\libchdr\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\d3d12ma\include;$(SolutionDir)dep\vulkan\include;$(SolutionDir)dep\ffmpeg\include</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories Condition="'$(Platform)'!='ARM64'">%(AdditionalIncludeDirectories);$(SolutionDir)dep\glad\include</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories Condition="'$(Platform)'!='ARM64'">%(AdditionalIncludeDirectories);$(SolutionDir)dep\glad\include</AdditionalIncludeDirectories>
|
||||||
@ -14,7 +14,6 @@
|
|||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
<Link>
|
<Link>
|
||||||
<AdditionalDependencies>%(AdditionalDependencies);d3d11.lib;d3d12.lib;d3dcompiler.lib;dxgi.lib;Dwmapi.lib;winhttp.lib</AdditionalDependencies>
|
<AdditionalDependencies>%(AdditionalDependencies);d3d11.lib;d3d12.lib;d3dcompiler.lib;dxgi.lib;Dwmapi.lib;winhttp.lib</AdditionalDependencies>
|
||||||
<AdditionalDependencies Condition="'$(Platform)'!='ARM64'">%(AdditionalDependencies);opengl32.lib</AdditionalDependencies>
|
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
LOG_CHANNEL(VulkanDevice);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
// TODO: VK_KHR_display.
|
// TODO: VK_KHR_display.
|
||||||
|
|
||||||
@ -110,6 +110,8 @@ static std::mutex s_instance_mutex;
|
|||||||
|
|
||||||
VulkanDevice::VulkanDevice()
|
VulkanDevice::VulkanDevice()
|
||||||
{
|
{
|
||||||
|
m_render_api = RenderAPI::Vulkan;
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
s_debug_scope_depth = 0;
|
s_debug_scope_depth = 0;
|
||||||
#endif
|
#endif
|
||||||
@ -637,14 +639,6 @@ bool VulkanDevice::CreateDevice(VkSurfaceKHR surface, bool enable_validation_lay
|
|||||||
enabled_features.fragmentStoresAndAtomics = available_features.fragmentStoresAndAtomics;
|
enabled_features.fragmentStoresAndAtomics = available_features.fragmentStoresAndAtomics;
|
||||||
device_info.pEnabledFeatures = &enabled_features;
|
device_info.pEnabledFeatures = &enabled_features;
|
||||||
|
|
||||||
// Enable debug layer on debug builds
|
|
||||||
if (enable_validation_layer)
|
|
||||||
{
|
|
||||||
static const char* layer_names[] = {"VK_LAYER_LUNARG_standard_validation"};
|
|
||||||
device_info.enabledLayerCount = 1;
|
|
||||||
device_info.ppEnabledLayerNames = layer_names;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT rasterization_order_access_feature = {
|
VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT rasterization_order_access_feature = {
|
||||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT, nullptr, VK_TRUE, VK_FALSE,
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT, nullptr, VK_TRUE, VK_FALSE,
|
||||||
VK_FALSE};
|
VK_FALSE};
|
||||||
@ -1266,11 +1260,6 @@ void VulkanDevice::WaitForFenceCounter(u64 fence_counter)
|
|||||||
WaitForCommandBufferCompletion(index);
|
WaitForCommandBufferCompletion(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDevice::WaitForGPUIdle()
|
|
||||||
{
|
|
||||||
vkDeviceWaitIdle(m_device);
|
|
||||||
}
|
|
||||||
|
|
||||||
float VulkanDevice::GetAndResetAccumulatedGPUTime()
|
float VulkanDevice::GetAndResetAccumulatedGPUTime()
|
||||||
{
|
{
|
||||||
const float time = m_accumulated_gpu_time;
|
const float time = m_accumulated_gpu_time;
|
||||||
@ -1445,9 +1434,15 @@ void VulkanDevice::QueuePresent(VulkanSwapChain* present_swap_chain)
|
|||||||
{
|
{
|
||||||
// VK_ERROR_OUT_OF_DATE_KHR is not fatal, just means we need to recreate our swap chain.
|
// VK_ERROR_OUT_OF_DATE_KHR is not fatal, just means we need to recreate our swap chain.
|
||||||
if (res == VK_ERROR_OUT_OF_DATE_KHR)
|
if (res == VK_ERROR_OUT_OF_DATE_KHR)
|
||||||
ResizeWindow(0, 0, m_window_info.surface_scale);
|
{
|
||||||
|
Error error;
|
||||||
|
if (!present_swap_chain->ResizeBuffers(0, 0, present_swap_chain->GetScale(), &error)) [[unlikely]]
|
||||||
|
WARNING_LOG("Failed to reszie swap chain: {}", error.GetDescription());
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkQueuePresentKHR failed: ");
|
LOG_VULKAN_ERROR(res, "vkQueuePresentKHR failed: ");
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1889,13 +1884,11 @@ bool VulkanDevice::IsSuitableDefaultRenderer()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanDevice::HasSurface() const
|
bool VulkanDevice::CreateDeviceAndMainSwapChain(std::string_view adapter, FeatureMask disabled_features,
|
||||||
{
|
const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||||
return static_cast<bool>(m_swap_chain);
|
bool allow_present_throttle,
|
||||||
}
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control, Error* error)
|
||||||
bool VulkanDevice::CreateDevice(std::string_view adapter, std::optional<bool> exclusive_fullscreen_control,
|
|
||||||
FeatureMask disabled_features, Error* error)
|
|
||||||
{
|
{
|
||||||
std::unique_lock lock(s_instance_mutex);
|
std::unique_lock lock(s_instance_mutex);
|
||||||
bool enable_debug_utils = m_debug_device;
|
bool enable_debug_utils = m_debug_device;
|
||||||
@ -1908,7 +1901,7 @@ bool VulkanDevice::CreateDevice(std::string_view adapter, std::optional<bool> ex
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_instance = CreateVulkanInstance(m_window_info, &m_optional_extensions, enable_debug_utils, enable_validation_layer);
|
m_instance = CreateVulkanInstance(wi, &m_optional_extensions, enable_debug_utils, enable_validation_layer);
|
||||||
if (m_instance == VK_NULL_HANDLE)
|
if (m_instance == VK_NULL_HANDLE)
|
||||||
{
|
{
|
||||||
if (enable_debug_utils || enable_validation_layer)
|
if (enable_debug_utils || enable_validation_layer)
|
||||||
@ -1916,8 +1909,7 @@ bool VulkanDevice::CreateDevice(std::string_view adapter, std::optional<bool> ex
|
|||||||
// Try again without the validation layer.
|
// Try again without the validation layer.
|
||||||
enable_debug_utils = false;
|
enable_debug_utils = false;
|
||||||
enable_validation_layer = false;
|
enable_validation_layer = false;
|
||||||
m_instance =
|
m_instance = CreateVulkanInstance(wi, &m_optional_extensions, enable_debug_utils, enable_validation_layer);
|
||||||
CreateVulkanInstance(m_window_info, &m_optional_extensions, enable_debug_utils, enable_validation_layer);
|
|
||||||
if (m_instance == VK_NULL_HANDLE)
|
if (m_instance == VK_NULL_HANDLE)
|
||||||
{
|
{
|
||||||
Error::SetStringView(error, "Failed to create Vulkan instance. Does your GPU and/or driver support Vulkan?");
|
Error::SetStringView(error, "Failed to create Vulkan instance. Does your GPU and/or driver support Vulkan?");
|
||||||
@ -1983,21 +1975,21 @@ bool VulkanDevice::CreateDevice(std::string_view adapter, std::optional<bool> ex
|
|||||||
if (enable_debug_utils)
|
if (enable_debug_utils)
|
||||||
EnableDebugUtils();
|
EnableDebugUtils();
|
||||||
|
|
||||||
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
std::unique_ptr<VulkanSwapChain> swap_chain;
|
||||||
ScopedGuard surface_cleanup = [this, &surface]() {
|
if (!wi.IsSurfaceless())
|
||||||
if (surface != VK_NULL_HANDLE)
|
|
||||||
vkDestroySurfaceKHR(m_instance, surface, nullptr);
|
|
||||||
};
|
|
||||||
if (m_window_info.type != WindowInfo::Type::Surfaceless)
|
|
||||||
{
|
{
|
||||||
surface = VulkanSwapChain::CreateVulkanSurface(m_instance, m_physical_device, &m_window_info);
|
swap_chain =
|
||||||
if (surface == VK_NULL_HANDLE)
|
std::make_unique<VulkanSwapChain>(wi, vsync_mode, allow_present_throttle, exclusive_fullscreen_control);
|
||||||
|
if (!swap_chain->CreateSurface(m_instance, m_physical_device, error))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to create the device.
|
// Attempt to create the device.
|
||||||
if (!CreateDevice(surface, enable_validation_layer, disabled_features, error))
|
if (!CreateDevice(swap_chain ? swap_chain->GetSurface() : VK_NULL_HANDLE, enable_validation_layer, disabled_features,
|
||||||
|
error))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// And critical resources.
|
// And critical resources.
|
||||||
if (!CreateAllocator() || !CreatePersistentDescriptorPool() || !CreateCommandBuffers() || !CreatePipelineLayouts())
|
if (!CreateAllocator() || !CreatePersistentDescriptorPool() || !CreateCommandBuffers() || !CreatePipelineLayouts())
|
||||||
@ -2005,26 +1997,16 @@ bool VulkanDevice::CreateDevice(std::string_view adapter, std::optional<bool> ex
|
|||||||
|
|
||||||
m_exclusive_fullscreen_control = exclusive_fullscreen_control;
|
m_exclusive_fullscreen_control = exclusive_fullscreen_control;
|
||||||
|
|
||||||
if (surface != VK_NULL_HANDLE)
|
if (swap_chain)
|
||||||
{
|
{
|
||||||
VkPresentModeKHR present_mode;
|
// Render a frame as soon as possible to clear out whatever was previously being displayed.
|
||||||
if (!VulkanSwapChain::SelectPresentMode(surface, &m_vsync_mode, &present_mode) ||
|
if (!swap_chain->CreateSwapChain(*this, error) || !swap_chain->CreateSwapChainImages(*this, error))
|
||||||
!(m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, present_mode, m_exclusive_fullscreen_control)))
|
|
||||||
{
|
|
||||||
Error::SetStringView(error, "Failed to create swap chain");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: This is assigned afterwards, because some platforms can modify the window info (e.g. Metal).
|
RenderBlankFrame(swap_chain.get());
|
||||||
m_window_info = m_swap_chain->GetWindowInfo();
|
m_main_swap_chain = std::move(swap_chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
surface_cleanup.Cancel();
|
|
||||||
|
|
||||||
// Render a frame as soon as possible to clear out whatever was previously being displayed.
|
|
||||||
if (m_window_info.type != WindowInfo::Type::Surfaceless)
|
|
||||||
RenderBlankFrame();
|
|
||||||
|
|
||||||
if (!CreateNullTexture())
|
if (!CreateNullTexture())
|
||||||
{
|
{
|
||||||
Error::SetStringView(error, "Failed to create dummy texture");
|
Error::SetStringView(error, "Failed to create dummy texture");
|
||||||
@ -2049,9 +2031,14 @@ void VulkanDevice::DestroyDevice()
|
|||||||
|
|
||||||
// Don't both submitting the current command buffer, just toss it.
|
// Don't both submitting the current command buffer, just toss it.
|
||||||
if (m_device != VK_NULL_HANDLE)
|
if (m_device != VK_NULL_HANDLE)
|
||||||
WaitForGPUIdle();
|
vkDeviceWaitIdle(m_device);
|
||||||
|
|
||||||
m_swap_chain.reset();
|
if (m_main_swap_chain)
|
||||||
|
{
|
||||||
|
// Explicit swap chain destroy, we don't want to execute the current cmdbuffer.
|
||||||
|
static_cast<VulkanSwapChain*>(m_main_swap_chain.get())->Destroy(*this, false);
|
||||||
|
m_main_swap_chain.reset();
|
||||||
|
}
|
||||||
|
|
||||||
if (m_null_texture)
|
if (m_null_texture)
|
||||||
{
|
{
|
||||||
@ -2208,73 +2195,27 @@ bool VulkanDevice::GetPipelineCacheData(DynamicHeapArray<u8>* data, Error* error
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanDevice::UpdateWindow()
|
std::unique_ptr<GPUSwapChain> VulkanDevice::CreateSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||||
|
bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control,
|
||||||
|
Error* error)
|
||||||
{
|
{
|
||||||
DestroySurface();
|
std::unique_ptr<VulkanSwapChain> swap_chain =
|
||||||
|
std::make_unique<VulkanSwapChain>(wi, vsync_mode, allow_present_throttle, exclusive_fullscreen_control);
|
||||||
if (!AcquireWindow(false))
|
if (swap_chain->CreateSurface(m_instance, m_physical_device, error) && swap_chain->CreateSwapChain(*this, error) &&
|
||||||
return false;
|
swap_chain->CreateSwapChainImages(*this, error))
|
||||||
|
|
||||||
if (m_window_info.IsSurfaceless())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// make sure previous frames are presented
|
|
||||||
if (InRenderPass())
|
|
||||||
EndRenderPass();
|
|
||||||
SubmitCommandBuffer(false);
|
|
||||||
WaitForGPUIdle();
|
|
||||||
|
|
||||||
VkSurfaceKHR surface = VulkanSwapChain::CreateVulkanSurface(m_instance, m_physical_device, &m_window_info);
|
|
||||||
if (surface == VK_NULL_HANDLE)
|
|
||||||
{
|
{
|
||||||
ERROR_LOG("Failed to create new surface for swap chain");
|
if (InRenderPass())
|
||||||
return false;
|
EndRenderPass();
|
||||||
|
RenderBlankFrame(swap_chain.get());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
swap_chain.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
VkPresentModeKHR present_mode;
|
return swap_chain;
|
||||||
if (!VulkanSwapChain::SelectPresentMode(surface, &m_vsync_mode, &present_mode) ||
|
|
||||||
!(m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, present_mode, m_exclusive_fullscreen_control)))
|
|
||||||
{
|
|
||||||
ERROR_LOG("Failed to create swap chain");
|
|
||||||
VulkanSwapChain::DestroyVulkanSurface(m_instance, &m_window_info, surface);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_window_info = m_swap_chain->GetWindowInfo();
|
|
||||||
RenderBlankFrame();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanDevice::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
|
||||||
{
|
|
||||||
if (!m_swap_chain)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_swap_chain->GetWidth() == static_cast<u32>(new_window_width) &&
|
|
||||||
m_swap_chain->GetHeight() == static_cast<u32>(new_window_height))
|
|
||||||
{
|
|
||||||
// skip unnecessary resizes
|
|
||||||
m_window_info.surface_scale = new_window_scale;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure previous frames are presented
|
|
||||||
WaitForGPUIdle();
|
|
||||||
|
|
||||||
if (!m_swap_chain->ResizeSwapChain(new_window_width, new_window_height, new_window_scale))
|
|
||||||
{
|
|
||||||
// AcquireNextImage() will fail, and we'll recreate the surface.
|
|
||||||
ERROR_LOG("Failed to resize swap chain. Next present will fail.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_window_info = m_swap_chain->GetWindowInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanDevice::DestroySurface()
|
|
||||||
{
|
|
||||||
WaitForGPUIdle();
|
|
||||||
m_swap_chain.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanDevice::SupportsTextureFormat(GPUTexture::Format format) const
|
bool VulkanDevice::SupportsTextureFormat(GPUTexture::Format format) const
|
||||||
@ -2308,7 +2249,16 @@ std::string VulkanDevice::GetDriverInfo() const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDevice::ExecuteAndWaitForGPUIdle()
|
void VulkanDevice::FlushCommands()
|
||||||
|
{
|
||||||
|
if (InRenderPass())
|
||||||
|
EndRenderPass();
|
||||||
|
|
||||||
|
SubmitCommandBuffer(false);
|
||||||
|
TrimTexturePool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanDevice::WaitForGPUIdle()
|
||||||
{
|
{
|
||||||
if (InRenderPass())
|
if (InRenderPass())
|
||||||
EndRenderPass();
|
EndRenderPass();
|
||||||
@ -2316,39 +2266,7 @@ void VulkanDevice::ExecuteAndWaitForGPUIdle()
|
|||||||
SubmitCommandBuffer(true);
|
SubmitCommandBuffer(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDevice::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle)
|
GPUDevice::PresentResult VulkanDevice::BeginPresent(GPUSwapChain* swap_chain, u32 clear_color)
|
||||||
{
|
|
||||||
m_allow_present_throttle = allow_present_throttle;
|
|
||||||
if (!m_swap_chain)
|
|
||||||
{
|
|
||||||
// For when it is re-created.
|
|
||||||
m_vsync_mode = mode;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkPresentModeKHR present_mode;
|
|
||||||
if (!VulkanSwapChain::SelectPresentMode(m_swap_chain->GetSurface(), &mode, &present_mode))
|
|
||||||
{
|
|
||||||
ERROR_LOG("Ignoring vsync mode change.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actually changed? If using a fallback, it might not have.
|
|
||||||
if (m_vsync_mode == mode)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_vsync_mode = mode;
|
|
||||||
|
|
||||||
// This swap chain should not be used by the current buffer, thus safe to destroy.
|
|
||||||
WaitForGPUIdle();
|
|
||||||
if (!m_swap_chain->SetPresentMode(present_mode))
|
|
||||||
{
|
|
||||||
Panic("Failed to update swap chain present mode.");
|
|
||||||
m_swap_chain.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GPUDevice::PresentResult VulkanDevice::BeginPresent(u32 clear_color)
|
|
||||||
{
|
{
|
||||||
if (InRenderPass())
|
if (InRenderPass())
|
||||||
EndRenderPass();
|
EndRenderPass();
|
||||||
@ -2356,37 +2274,30 @@ GPUDevice::PresentResult VulkanDevice::BeginPresent(u32 clear_color)
|
|||||||
if (m_device_was_lost) [[unlikely]]
|
if (m_device_was_lost) [[unlikely]]
|
||||||
return PresentResult::DeviceLost;
|
return PresentResult::DeviceLost;
|
||||||
|
|
||||||
// If we're running surfaceless, kick the command buffer so we don't run out of descriptors.
|
VulkanSwapChain* const SC = static_cast<VulkanSwapChain*>(swap_chain);
|
||||||
if (!m_swap_chain)
|
VkResult res = SC->AcquireNextImage();
|
||||||
{
|
|
||||||
SubmitCommandBuffer(false);
|
|
||||||
TrimTexturePool();
|
|
||||||
return PresentResult::SkipPresent;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkResult res = m_swap_chain->AcquireNextImage();
|
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkAcquireNextImageKHR() failed: ");
|
LOG_VULKAN_ERROR(res, "vkAcquireNextImageKHR() failed: ");
|
||||||
m_swap_chain->ReleaseCurrentImage();
|
SC->ReleaseCurrentImage();
|
||||||
|
|
||||||
if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
|
if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
|
||||||
{
|
{
|
||||||
ResizeWindow(0, 0, m_window_info.surface_scale);
|
Error error;
|
||||||
res = m_swap_chain->AcquireNextImage();
|
if (!SC->ResizeBuffers(0, 0, SC->GetScale(), &error)) [[unlikely]]
|
||||||
|
WARNING_LOG("Failed to resize buffers: {}", error.GetDescription());
|
||||||
|
else
|
||||||
|
res = SC->AcquireNextImage();
|
||||||
}
|
}
|
||||||
else if (res == VK_ERROR_SURFACE_LOST_KHR)
|
else if (res == VK_ERROR_SURFACE_LOST_KHR)
|
||||||
{
|
{
|
||||||
WARNING_LOG("Surface lost, attempting to recreate");
|
WARNING_LOG("Surface lost, attempting to recreate");
|
||||||
if (!m_swap_chain->RecreateSurface(m_window_info))
|
|
||||||
{
|
|
||||||
ERROR_LOG("Failed to recreate surface after loss");
|
|
||||||
SubmitCommandBuffer(false);
|
|
||||||
TrimTexturePool();
|
|
||||||
return PresentResult::SkipPresent;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = m_swap_chain->AcquireNextImage();
|
Error error;
|
||||||
|
if (!SC->RecreateSurface(&error))
|
||||||
|
ERROR_LOG("Failed to recreate surface after loss: {}", error.GetDescription());
|
||||||
|
else
|
||||||
|
res = SC->AcquireNextImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This can happen when multiple resize events happen in quick succession.
|
// This can happen when multiple resize events happen in quick succession.
|
||||||
@ -2400,33 +2311,38 @@ GPUDevice::PresentResult VulkanDevice::BeginPresent(u32 clear_color)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BeginSwapChainRenderPass(clear_color);
|
BeginSwapChainRenderPass(SC, clear_color);
|
||||||
return PresentResult::OK;
|
return PresentResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDevice::EndPresent(bool explicit_present, u64 present_time)
|
void VulkanDevice::EndPresent(GPUSwapChain* swap_chain, bool explicit_present, u64 present_time)
|
||||||
{
|
{
|
||||||
|
VulkanSwapChain* const SC = static_cast<VulkanSwapChain*>(swap_chain);
|
||||||
|
|
||||||
DebugAssert(present_time == 0);
|
DebugAssert(present_time == 0);
|
||||||
DebugAssert(InRenderPass() && m_num_current_render_targets == 0 && !m_current_depth_target);
|
DebugAssert(InRenderPass() && m_num_current_render_targets == 0 && !m_current_depth_target);
|
||||||
EndRenderPass();
|
EndRenderPass();
|
||||||
|
|
||||||
|
DebugAssert(SC == m_current_swap_chain);
|
||||||
|
m_current_swap_chain = nullptr;
|
||||||
|
|
||||||
VkCommandBuffer cmdbuf = GetCurrentCommandBuffer();
|
VkCommandBuffer cmdbuf = GetCurrentCommandBuffer();
|
||||||
VulkanTexture::TransitionSubresourcesToLayout(cmdbuf, m_swap_chain->GetCurrentImage(), GPUTexture::Type::RenderTarget,
|
VulkanTexture::TransitionSubresourcesToLayout(cmdbuf, SC->GetCurrentImage(), GPUTexture::Type::RenderTarget, 0, 1, 0,
|
||||||
0, 1, 0, 1, VulkanTexture::Layout::ColorAttachment,
|
1, VulkanTexture::Layout::ColorAttachment,
|
||||||
VulkanTexture::Layout::PresentSrc);
|
VulkanTexture::Layout::PresentSrc);
|
||||||
EndAndSubmitCommandBuffer(m_swap_chain.get(), explicit_present);
|
EndAndSubmitCommandBuffer(SC, explicit_present);
|
||||||
MoveToNextCommandBuffer();
|
MoveToNextCommandBuffer();
|
||||||
InvalidateCachedState();
|
InvalidateCachedState();
|
||||||
TrimTexturePool();
|
TrimTexturePool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDevice::SubmitPresent()
|
void VulkanDevice::SubmitPresent(GPUSwapChain* swap_chain)
|
||||||
{
|
{
|
||||||
DebugAssert(m_swap_chain);
|
DebugAssert(swap_chain);
|
||||||
if (m_device_was_lost) [[unlikely]]
|
if (m_device_was_lost) [[unlikely]]
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QueuePresent(m_swap_chain.get());
|
QueuePresent(static_cast<VulkanSwapChain*>(swap_chain));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
@ -2515,7 +2431,6 @@ u32 VulkanDevice::GetMaxMultisamples(VkPhysicalDevice physical_device, const VkP
|
|||||||
void VulkanDevice::SetFeatures(FeatureMask disabled_features, const VkPhysicalDeviceFeatures& vk_features)
|
void VulkanDevice::SetFeatures(FeatureMask disabled_features, const VkPhysicalDeviceFeatures& vk_features)
|
||||||
{
|
{
|
||||||
const u32 store_api_version = std::min(m_device_properties.apiVersion, VK_API_VERSION_1_1);
|
const u32 store_api_version = std::min(m_device_properties.apiVersion, VK_API_VERSION_1_1);
|
||||||
m_render_api = RenderAPI::Vulkan;
|
|
||||||
m_render_api_version = (VK_API_VERSION_MAJOR(store_api_version) * 100u) +
|
m_render_api_version = (VK_API_VERSION_MAJOR(store_api_version) * 100u) +
|
||||||
(VK_API_VERSION_MINOR(store_api_version) * 10u) + (VK_API_VERSION_PATCH(store_api_version));
|
(VK_API_VERSION_MINOR(store_api_version) * 10u) + (VK_API_VERSION_PATCH(store_api_version));
|
||||||
m_max_texture_size =
|
m_max_texture_size =
|
||||||
@ -3076,9 +2991,9 @@ void VulkanDevice::DestroyPersistentDescriptorSets()
|
|||||||
FreePersistentDescriptorSet(m_ubo_descriptor_set);
|
FreePersistentDescriptorSet(m_ubo_descriptor_set);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDevice::RenderBlankFrame()
|
void VulkanDevice::RenderBlankFrame(VulkanSwapChain* swap_chain)
|
||||||
{
|
{
|
||||||
VkResult res = m_swap_chain->AcquireNextImage();
|
VkResult res = swap_chain->AcquireNextImage();
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
ERROR_LOG("Failed to acquire image for blank frame present");
|
ERROR_LOG("Failed to acquire image for blank frame present");
|
||||||
@ -3087,7 +3002,7 @@ void VulkanDevice::RenderBlankFrame()
|
|||||||
|
|
||||||
VkCommandBuffer cmdbuf = GetCurrentCommandBuffer();
|
VkCommandBuffer cmdbuf = GetCurrentCommandBuffer();
|
||||||
|
|
||||||
const VkImage image = m_swap_chain->GetCurrentImage();
|
const VkImage image = swap_chain->GetCurrentImage();
|
||||||
static constexpr VkImageSubresourceRange srr = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
static constexpr VkImageSubresourceRange srr = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||||
static constexpr VkClearColorValue clear_color = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
static constexpr VkClearColorValue clear_color = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
||||||
VulkanTexture::TransitionSubresourcesToLayout(cmdbuf, image, GPUTexture::Type::RenderTarget, 0, 1, 0, 1,
|
VulkanTexture::TransitionSubresourcesToLayout(cmdbuf, image, GPUTexture::Type::RenderTarget, 0, 1, 0, 1,
|
||||||
@ -3096,7 +3011,7 @@ void VulkanDevice::RenderBlankFrame()
|
|||||||
VulkanTexture::TransitionSubresourcesToLayout(cmdbuf, image, GPUTexture::Type::RenderTarget, 0, 1, 0, 1,
|
VulkanTexture::TransitionSubresourcesToLayout(cmdbuf, image, GPUTexture::Type::RenderTarget, 0, 1, 0, 1,
|
||||||
VulkanTexture::Layout::TransferDst, VulkanTexture::Layout::PresentSrc);
|
VulkanTexture::Layout::TransferDst, VulkanTexture::Layout::PresentSrc);
|
||||||
|
|
||||||
EndAndSubmitCommandBuffer(m_swap_chain.get(), false);
|
EndAndSubmitCommandBuffer(swap_chain, false);
|
||||||
MoveToNextCommandBuffer();
|
MoveToNextCommandBuffer();
|
||||||
|
|
||||||
InvalidateCachedState();
|
InvalidateCachedState();
|
||||||
@ -3363,7 +3278,7 @@ void VulkanDevice::BeginRenderPass()
|
|||||||
VkRenderingAttachmentInfo& ai = attachments[0];
|
VkRenderingAttachmentInfo& ai = attachments[0];
|
||||||
ai.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
|
ai.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
|
||||||
ai.pNext = nullptr;
|
ai.pNext = nullptr;
|
||||||
ai.imageView = m_swap_chain->GetCurrentImageView();
|
ai.imageView = m_current_swap_chain->GetCurrentImageView();
|
||||||
ai.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
ai.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||||
ai.resolveMode = VK_RESOLVE_MODE_NONE_KHR;
|
ai.resolveMode = VK_RESOLVE_MODE_NONE_KHR;
|
||||||
ai.resolveImageView = VK_NULL_HANDLE;
|
ai.resolveImageView = VK_NULL_HANDLE;
|
||||||
@ -3373,7 +3288,7 @@ void VulkanDevice::BeginRenderPass()
|
|||||||
|
|
||||||
ri.colorAttachmentCount = 1;
|
ri.colorAttachmentCount = 1;
|
||||||
ri.pColorAttachments = attachments.data();
|
ri.pColorAttachments = attachments.data();
|
||||||
ri.renderArea = {{}, {m_swap_chain->GetWidth(), m_swap_chain->GetHeight()}};
|
ri.renderArea = {{}, {m_current_swap_chain->GetWidth(), m_current_swap_chain->GetHeight()}};
|
||||||
}
|
}
|
||||||
|
|
||||||
m_current_render_pass = DYNAMIC_RENDERING_RENDER_PASS;
|
m_current_render_pass = DYNAMIC_RENDERING_RENDER_PASS;
|
||||||
@ -3434,10 +3349,10 @@ void VulkanDevice::BeginRenderPass()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Re-rendering to swap chain.
|
// Re-rendering to swap chain.
|
||||||
bi.framebuffer = m_swap_chain->GetCurrentFramebuffer();
|
bi.framebuffer = m_current_swap_chain->GetCurrentFramebuffer();
|
||||||
bi.renderPass = m_current_render_pass =
|
bi.renderPass = m_current_render_pass =
|
||||||
GetSwapChainRenderPass(m_swap_chain->GetWindowInfo().surface_format, VK_ATTACHMENT_LOAD_OP_LOAD);
|
GetSwapChainRenderPass(m_current_swap_chain->GetFormat(), VK_ATTACHMENT_LOAD_OP_LOAD);
|
||||||
bi.renderArea.extent = {m_swap_chain->GetWidth(), m_swap_chain->GetHeight()};
|
bi.renderArea.extent = {m_current_swap_chain->GetWidth(), m_current_swap_chain->GetHeight()};
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugAssert(m_current_render_pass);
|
DebugAssert(m_current_render_pass);
|
||||||
@ -3451,12 +3366,12 @@ void VulkanDevice::BeginRenderPass()
|
|||||||
SetInitialPipelineState();
|
SetInitialPipelineState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDevice::BeginSwapChainRenderPass(u32 clear_color)
|
void VulkanDevice::BeginSwapChainRenderPass(VulkanSwapChain* swap_chain, u32 clear_color)
|
||||||
{
|
{
|
||||||
DebugAssert(!InRenderPass());
|
DebugAssert(!InRenderPass());
|
||||||
|
|
||||||
const VkCommandBuffer cmdbuf = GetCurrentCommandBuffer();
|
const VkCommandBuffer cmdbuf = GetCurrentCommandBuffer();
|
||||||
const VkImage swap_chain_image = m_swap_chain->GetCurrentImage();
|
const VkImage swap_chain_image = swap_chain->GetCurrentImage();
|
||||||
|
|
||||||
// Swap chain images start in undefined
|
// Swap chain images start in undefined
|
||||||
VulkanTexture::TransitionSubresourcesToLayout(cmdbuf, swap_chain_image, GPUTexture::Type::RenderTarget, 0, 1, 0, 1,
|
VulkanTexture::TransitionSubresourcesToLayout(cmdbuf, swap_chain_image, GPUTexture::Type::RenderTarget, 0, 1, 0, 1,
|
||||||
@ -3477,7 +3392,7 @@ void VulkanDevice::BeginSwapChainRenderPass(u32 clear_color)
|
|||||||
{
|
{
|
||||||
VkRenderingAttachmentInfo ai = {VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR,
|
VkRenderingAttachmentInfo ai = {VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR,
|
||||||
nullptr,
|
nullptr,
|
||||||
m_swap_chain->GetCurrentImageView(),
|
swap_chain->GetCurrentImageView(),
|
||||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||||
VK_RESOLVE_MODE_NONE_KHR,
|
VK_RESOLVE_MODE_NONE_KHR,
|
||||||
VK_NULL_HANDLE,
|
VK_NULL_HANDLE,
|
||||||
@ -3489,7 +3404,7 @@ void VulkanDevice::BeginSwapChainRenderPass(u32 clear_color)
|
|||||||
const VkRenderingInfoKHR ri = {VK_STRUCTURE_TYPE_RENDERING_INFO_KHR,
|
const VkRenderingInfoKHR ri = {VK_STRUCTURE_TYPE_RENDERING_INFO_KHR,
|
||||||
nullptr,
|
nullptr,
|
||||||
0u,
|
0u,
|
||||||
{{}, {m_swap_chain->GetWidth(), m_swap_chain->GetHeight()}},
|
{{}, {swap_chain->GetWidth(), swap_chain->GetHeight()}},
|
||||||
1u,
|
1u,
|
||||||
0u,
|
0u,
|
||||||
1u,
|
1u,
|
||||||
@ -3503,14 +3418,14 @@ void VulkanDevice::BeginSwapChainRenderPass(u32 clear_color)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_current_render_pass =
|
m_current_render_pass =
|
||||||
GetSwapChainRenderPass(m_swap_chain->GetWindowInfo().surface_format, VK_ATTACHMENT_LOAD_OP_CLEAR);
|
GetSwapChainRenderPass(swap_chain->GetWindowInfo().surface_format, VK_ATTACHMENT_LOAD_OP_CLEAR);
|
||||||
DebugAssert(m_current_render_pass);
|
DebugAssert(m_current_render_pass);
|
||||||
|
|
||||||
const VkRenderPassBeginInfo rp = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
const VkRenderPassBeginInfo rp = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||||
nullptr,
|
nullptr,
|
||||||
m_current_render_pass,
|
m_current_render_pass,
|
||||||
m_swap_chain->GetCurrentFramebuffer(),
|
swap_chain->GetCurrentFramebuffer(),
|
||||||
{{0, 0}, {m_swap_chain->GetWidth(), m_swap_chain->GetHeight()}},
|
{{0, 0}, {swap_chain->GetWidth(), swap_chain->GetHeight()}},
|
||||||
1u,
|
1u,
|
||||||
&clear_value};
|
&clear_value};
|
||||||
vkCmdBeginRenderPass(GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE);
|
vkCmdBeginRenderPass(GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE);
|
||||||
@ -3526,6 +3441,7 @@ void VulkanDevice::BeginSwapChainRenderPass(u32 clear_color)
|
|||||||
std::memset(m_current_render_targets.data(), 0, sizeof(m_current_render_targets));
|
std::memset(m_current_render_targets.data(), 0, sizeof(m_current_render_targets));
|
||||||
m_current_depth_target = nullptr;
|
m_current_depth_target = nullptr;
|
||||||
m_current_framebuffer = VK_NULL_HANDLE;
|
m_current_framebuffer = VK_NULL_HANDLE;
|
||||||
|
m_current_swap_chain = swap_chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanDevice::InRenderPass()
|
bool VulkanDevice::InRenderPass()
|
||||||
|
@ -75,16 +75,16 @@ public:
|
|||||||
static GPUList EnumerateGPUs();
|
static GPUList EnumerateGPUs();
|
||||||
static AdapterInfoList GetAdapterList();
|
static AdapterInfoList GetAdapterList();
|
||||||
|
|
||||||
bool HasSurface() const override;
|
|
||||||
|
|
||||||
bool UpdateWindow() override;
|
|
||||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
|
||||||
void DestroySurface() override;
|
|
||||||
|
|
||||||
std::string GetDriverInfo() const override;
|
std::string GetDriverInfo() const override;
|
||||||
|
|
||||||
void ExecuteAndWaitForGPUIdle() override;
|
void FlushCommands() override;
|
||||||
|
void WaitForGPUIdle() override;
|
||||||
|
|
||||||
|
std::unique_ptr<GPUSwapChain> CreateSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||||
|
bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control,
|
||||||
|
Error* error) override;
|
||||||
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
||||||
GPUTexture::Type type, GPUTexture::Format format,
|
GPUTexture::Type type, GPUTexture::Format format,
|
||||||
const void* data = nullptr, u32 data_stride = 0) override;
|
const void* data = nullptr, u32 data_stride = 0) override;
|
||||||
@ -138,11 +138,9 @@ public:
|
|||||||
bool SetGPUTimingEnabled(bool enabled) override;
|
bool SetGPUTimingEnabled(bool enabled) override;
|
||||||
float GetAndResetAccumulatedGPUTime() override;
|
float GetAndResetAccumulatedGPUTime() override;
|
||||||
|
|
||||||
void SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle) override;
|
PresentResult BeginPresent(GPUSwapChain* swap_chain, u32 clear_color) override;
|
||||||
|
void EndPresent(GPUSwapChain* swap_chain, bool explicit_present, u64 present_time) override;
|
||||||
PresentResult BeginPresent(u32 clear_color) override;
|
void SubmitPresent(GPUSwapChain* swap_chain) override;
|
||||||
void EndPresent(bool explicit_present, u64 present_time) override;
|
|
||||||
void SubmitPresent() override;
|
|
||||||
|
|
||||||
// Global state accessors
|
// Global state accessors
|
||||||
ALWAYS_INLINE static VulkanDevice& GetInstance() { return *static_cast<VulkanDevice*>(g_gpu_device.get()); }
|
ALWAYS_INLINE static VulkanDevice& GetInstance() { return *static_cast<VulkanDevice*>(g_gpu_device.get()); }
|
||||||
@ -167,7 +165,7 @@ public:
|
|||||||
return static_cast<u32>(m_device_properties.limits.optimalBufferCopyRowPitchAlignment);
|
return static_cast<u32>(m_device_properties.limits.optimalBufferCopyRowPitchAlignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaitForGPUIdle();
|
void WaitForAllFences();
|
||||||
|
|
||||||
// Creates a simple render pass.
|
// Creates a simple render pass.
|
||||||
VkRenderPass GetRenderPass(const GPUPipeline::GraphicsConfig& config);
|
VkRenderPass GetRenderPass(const GPUPipeline::GraphicsConfig& config);
|
||||||
@ -219,19 +217,26 @@ public:
|
|||||||
// Also invokes callbacks for completion.
|
// Also invokes callbacks for completion.
|
||||||
void WaitForFenceCounter(u64 fence_counter);
|
void WaitForFenceCounter(u64 fence_counter);
|
||||||
|
|
||||||
|
// Ends a render pass if we're currently in one.
|
||||||
|
// When Bind() is next called, the pass will be restarted.
|
||||||
|
void BeginRenderPass();
|
||||||
|
void EndRenderPass();
|
||||||
|
bool InRenderPass();
|
||||||
|
|
||||||
/// Ends any render pass, executes the command buffer, and invalidates cached state.
|
/// Ends any render pass, executes the command buffer, and invalidates cached state.
|
||||||
void SubmitCommandBuffer(bool wait_for_completion);
|
void SubmitCommandBuffer(bool wait_for_completion);
|
||||||
void SubmitCommandBuffer(bool wait_for_completion, const std::string_view reason);
|
void SubmitCommandBuffer(bool wait_for_completion, const std::string_view reason);
|
||||||
void SubmitCommandBufferAndRestartRenderPass(const std::string_view reason);
|
void SubmitCommandBufferAndRestartRenderPass(const std::string_view reason);
|
||||||
|
|
||||||
void UnbindFramebuffer(VulkanTexture* tex);
|
|
||||||
void UnbindPipeline(VulkanPipeline* pl);
|
void UnbindPipeline(VulkanPipeline* pl);
|
||||||
void UnbindTexture(VulkanTexture* tex);
|
void UnbindTexture(VulkanTexture* tex);
|
||||||
void UnbindTextureBuffer(VulkanTextureBuffer* buf);
|
void UnbindTextureBuffer(VulkanTextureBuffer* buf);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool CreateDevice(std::string_view adapter, std::optional<bool> exclusive_fullscreen_control,
|
bool CreateDeviceAndMainSwapChain(std::string_view adapter, FeatureMask disabled_features, const WindowInfo& wi,
|
||||||
FeatureMask disabled_features, Error* error) override;
|
GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
|
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||||
|
std::optional<bool> exclusive_fullscreen_control, Error* error) override;
|
||||||
void DestroyDevice() override;
|
void DestroyDevice() override;
|
||||||
|
|
||||||
bool ReadPipelineCache(DynamicHeapArray<u8> data, Error* error) override;
|
bool ReadPipelineCache(DynamicHeapArray<u8> data, Error* error) override;
|
||||||
@ -351,7 +356,7 @@ private:
|
|||||||
VkSampler GetSampler(const GPUSampler::Config& config);
|
VkSampler GetSampler(const GPUSampler::Config& config);
|
||||||
void DestroySamplers();
|
void DestroySamplers();
|
||||||
|
|
||||||
void RenderBlankFrame();
|
void RenderBlankFrame(VulkanSwapChain* swap_chain);
|
||||||
|
|
||||||
bool TryImportHostMemory(void* data, size_t data_size, VkBufferUsageFlags buffer_usage, VkDeviceMemory* out_memory,
|
bool TryImportHostMemory(void* data, size_t data_size, VkBufferUsageFlags buffer_usage, VkDeviceMemory* out_memory,
|
||||||
VkBuffer* out_buffer, VkDeviceSize* out_offset);
|
VkBuffer* out_buffer, VkDeviceSize* out_offset);
|
||||||
@ -371,12 +376,7 @@ private:
|
|||||||
bool UpdateDescriptorSetsForLayout(u32 dirty);
|
bool UpdateDescriptorSetsForLayout(u32 dirty);
|
||||||
bool UpdateDescriptorSets(u32 dirty);
|
bool UpdateDescriptorSets(u32 dirty);
|
||||||
|
|
||||||
// Ends a render pass if we're currently in one.
|
void BeginSwapChainRenderPass(VulkanSwapChain* swap_chain, u32 clear_color);
|
||||||
// When Bind() is next called, the pass will be restarted.
|
|
||||||
void BeginRenderPass();
|
|
||||||
void BeginSwapChainRenderPass(u32 clear_color);
|
|
||||||
void EndRenderPass();
|
|
||||||
bool InRenderPass();
|
|
||||||
|
|
||||||
VkRenderPass CreateCachedRenderPass(RenderPassCacheKey key);
|
VkRenderPass CreateCachedRenderPass(RenderPassCacheKey key);
|
||||||
static VkFramebuffer CreateFramebuffer(GPUTexture* const* rts, u32 num_rts, GPUTexture* ds, u32 flags);
|
static VkFramebuffer CreateFramebuffer(GPUTexture* const* rts, u32 num_rts, GPUTexture* ds, u32 flags);
|
||||||
@ -427,7 +427,6 @@ private:
|
|||||||
OptionalExtensions m_optional_extensions = {};
|
OptionalExtensions m_optional_extensions = {};
|
||||||
std::optional<bool> m_exclusive_fullscreen_control;
|
std::optional<bool> m_exclusive_fullscreen_control;
|
||||||
|
|
||||||
std::unique_ptr<VulkanSwapChain> m_swap_chain;
|
|
||||||
std::unique_ptr<VulkanTexture> m_null_texture;
|
std::unique_ptr<VulkanTexture> m_null_texture;
|
||||||
|
|
||||||
VkDescriptorSetLayout m_ubo_ds_layout = VK_NULL_HANDLE;
|
VkDescriptorSetLayout m_ubo_ds_layout = VK_NULL_HANDLE;
|
||||||
@ -468,4 +467,5 @@ private:
|
|||||||
VulkanTextureBuffer* m_current_texture_buffer = nullptr;
|
VulkanTextureBuffer* m_current_texture_buffer = nullptr;
|
||||||
GSVector4i m_current_viewport = GSVector4i::cxpr(0, 0, 1, 1);
|
GSVector4i m_current_viewport = GSVector4i::cxpr(0, 0, 1, 1);
|
||||||
GSVector4i m_current_scissor = GSVector4i::cxpr(0, 0, 1, 1);
|
GSVector4i m_current_scissor = GSVector4i::cxpr(0, 0, 1, 1);
|
||||||
|
VulkanSwapChain* m_current_swap_chain = nullptr;
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
LOG_CHANNEL(VulkanDevice);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include "common/heap_array.h"
|
#include "common/heap_array.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
|
|
||||||
LOG_CHANNEL(VulkanDevice);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
VulkanShader::VulkanShader(GPUShaderStage stage, VkShaderModule mod) : GPUShader(stage), m_module(mod)
|
VulkanShader::VulkanShader(GPUShaderStage stage, VkShaderModule mod) : GPUShader(stage), m_module(mod)
|
||||||
{
|
{
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/bitutils.h"
|
#include "common/bitutils.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
LOG_CHANNEL(VulkanDevice);
|
|
||||||
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
VulkanStreamBuffer::VulkanStreamBuffer() = default;
|
VulkanStreamBuffer::VulkanStreamBuffer() = default;
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "vulkan_device.h"
|
#include "vulkan_device.h"
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/error.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -20,9 +21,7 @@
|
|||||||
#include "util/metal_layer.h"
|
#include "util/metal_layer.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LOG_CHANNEL(VulkanDevice);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
static_assert(VulkanSwapChain::NUM_SEMAPHORES == (VulkanDevice::NUM_COMMAND_BUFFERS + 1));
|
|
||||||
|
|
||||||
static VkFormat GetLinearFormat(VkFormat format)
|
static VkFormat GetLinearFormat(VkFormat format)
|
||||||
{
|
{
|
||||||
@ -72,170 +71,142 @@ static const char* PresentModeToString(VkPresentModeKHR mode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanSwapChain::VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR present_mode,
|
VulkanSwapChain::VulkanSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
std::optional<bool> exclusive_fullscreen_control)
|
std::optional<bool> exclusive_fullscreen_control)
|
||||||
: m_window_info(wi), m_surface(surface), m_present_mode(present_mode),
|
: GPUSwapChain(wi, vsync_mode, allow_present_throttle), m_exclusive_fullscreen_control(exclusive_fullscreen_control)
|
||||||
m_exclusive_fullscreen_control(exclusive_fullscreen_control)
|
|
||||||
{
|
{
|
||||||
|
static_assert(NUM_SEMAPHORES == (VulkanDevice::NUM_COMMAND_BUFFERS + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanSwapChain::~VulkanSwapChain()
|
VulkanSwapChain::~VulkanSwapChain()
|
||||||
{
|
{
|
||||||
DestroySwapChainImages();
|
Destroy(VulkanDevice::GetInstance(), true);
|
||||||
DestroySwapChain();
|
|
||||||
DestroySurface();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VkSurfaceKHR VulkanSwapChain::CreateVulkanSurface(VkInstance instance, VkPhysicalDevice physical_device, WindowInfo* wi)
|
bool VulkanSwapChain::CreateSurface(VkInstance instance, VkPhysicalDevice physical_device, Error* error)
|
||||||
{
|
{
|
||||||
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||||
if (wi->type == WindowInfo::Type::Win32)
|
if (m_window_info.type == WindowInfo::Type::Win32)
|
||||||
{
|
{
|
||||||
VkWin32SurfaceCreateInfoKHR surface_create_info = {
|
const VkWin32SurfaceCreateInfoKHR surface_create_info = {.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
|
||||||
VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, // VkStructureType sType
|
.hwnd = static_cast<HWND>(m_window_info.window_handle)};
|
||||||
nullptr, // const void* pNext
|
const VkResult res = vkCreateWin32SurfaceKHR(instance, &surface_create_info, nullptr, &m_surface);
|
||||||
0, // VkWin32SurfaceCreateFlagsKHR flags
|
|
||||||
nullptr, // HINSTANCE hinstance
|
|
||||||
reinterpret_cast<HWND>(wi->window_handle) // HWND hwnd
|
|
||||||
};
|
|
||||||
|
|
||||||
VkSurfaceKHR surface;
|
|
||||||
VkResult res = vkCreateWin32SurfaceKHR(instance, &surface_create_info, nullptr, &surface);
|
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkCreateWin32SurfaceKHR failed: ");
|
Vulkan::SetErrorObject(error, "vkCreateWin32SurfaceKHR() failed: ", res);
|
||||||
return VK_NULL_HANDLE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return surface;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(VK_USE_PLATFORM_METAL_EXT)
|
#if defined(VK_USE_PLATFORM_METAL_EXT)
|
||||||
if (wi->type == WindowInfo::Type::MacOS)
|
if (m_window_info.type == WindowInfo::Type::MacOS)
|
||||||
{
|
{
|
||||||
// TODO: FIXME
|
m_metal_layer = CocoaTools::CreateMetalLayer(m_window_info, error);
|
||||||
if (!wi->surface_handle && !CocoaTools::CreateMetalLayer(wi))
|
if (!m_metal_layer)
|
||||||
return VK_NULL_HANDLE;
|
return false;
|
||||||
|
|
||||||
VkMetalSurfaceCreateInfoEXT surface_create_info = {VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT, nullptr, 0,
|
const VkMetalSurfaceCreateInfoEXT surface_create_info = {.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT,
|
||||||
static_cast<const CAMetalLayer*>(wi->surface_handle)};
|
.pLayer = static_cast<const CAMetalLayer*>(m_metal_layer)};
|
||||||
|
const VkResult res = vkCreateMetalSurfaceEXT(instance, &surface_create_info, nullptr, &m_surface);
|
||||||
VkSurfaceKHR surface;
|
|
||||||
VkResult res = vkCreateMetalSurfaceEXT(instance, &surface_create_info, nullptr, &surface);
|
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkCreateMetalSurfaceEXT failed: ");
|
Vulkan::SetErrorObject(error, "vkCreateMetalSurfaceEXT failed: ", res);
|
||||||
return VK_NULL_HANDLE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return surface;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
|
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||||
if (wi->type == WindowInfo::Type::Android)
|
if (m_window_info.type == WindowInfo::Type::Android)
|
||||||
{
|
{
|
||||||
VkAndroidSurfaceCreateInfoKHR surface_create_info = {
|
const VkAndroidSurfaceCreateInfoKHR surface_create_info = {
|
||||||
VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, // VkStructureType sType
|
.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
|
||||||
nullptr, // const void* pNext
|
.window = static_cast<ANativeWindow*>(m_window_info.window_handle)};
|
||||||
0, // VkAndroidSurfaceCreateFlagsKHR flags
|
const VkResult res = vkCreateAndroidSurfaceKHR(instance, &surface_create_info, nullptr, &m_surface);
|
||||||
reinterpret_cast<ANativeWindow*>(wi->window_handle) // ANativeWindow* window
|
|
||||||
};
|
|
||||||
|
|
||||||
VkSurfaceKHR surface;
|
|
||||||
VkResult res = vkCreateAndroidSurfaceKHR(instance, &surface_create_info, nullptr, &surface);
|
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkCreateAndroidSurfaceKHR failed: ");
|
Vulkan::SetErrorObject(error, "vkCreateAndroidSurfaceKHR failed: ", res);
|
||||||
return VK_NULL_HANDLE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return surface;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
||||||
if (wi->type == WindowInfo::Type::X11)
|
if (m_window_info.type == WindowInfo::Type::X11)
|
||||||
{
|
{
|
||||||
VkXlibSurfaceCreateInfoKHR surface_create_info = {
|
const VkXlibSurfaceCreateInfoKHR surface_create_info = {
|
||||||
VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, // VkStructureType sType
|
.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
|
||||||
nullptr, // const void* pNext
|
.dpy = static_cast<Display*>(m_window_info.display_connection),
|
||||||
0, // VkXlibSurfaceCreateFlagsKHR flags
|
.window = reinterpret_cast<Window>(m_window_info.window_handle)};
|
||||||
static_cast<Display*>(wi->display_connection), // Display* dpy
|
const VkResult res = vkCreateXlibSurfaceKHR(instance, &surface_create_info, nullptr, &m_surface);
|
||||||
reinterpret_cast<Window>(wi->window_handle) // Window window
|
|
||||||
};
|
|
||||||
|
|
||||||
VkSurfaceKHR surface;
|
|
||||||
VkResult res = vkCreateXlibSurfaceKHR(instance, &surface_create_info, nullptr, &surface);
|
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkCreateXlibSurfaceKHR failed: ");
|
Vulkan::SetErrorObject(error, "vkCreateXlibSurfaceKHR failed: ", res);
|
||||||
return VK_NULL_HANDLE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return surface;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||||
if (wi->type == WindowInfo::Type::Wayland)
|
if (m_window_info.type == WindowInfo::Type::Wayland)
|
||||||
{
|
{
|
||||||
VkWaylandSurfaceCreateInfoKHR surface_create_info = {VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, nullptr, 0,
|
const VkWaylandSurfaceCreateInfoKHR surface_create_info = {
|
||||||
static_cast<struct wl_display*>(wi->display_connection),
|
VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, nullptr, 0,
|
||||||
static_cast<struct wl_surface*>(wi->window_handle)};
|
static_cast<struct wl_display*>(m_window_info.display_connection),
|
||||||
|
static_cast<struct wl_surface*>(m_window_info.window_handle)};
|
||||||
VkSurfaceKHR surface;
|
VkResult res = vkCreateWaylandSurfaceKHR(instance, &surface_create_info, nullptr, &m_surface);
|
||||||
VkResult res = vkCreateWaylandSurfaceKHR(instance, &surface_create_info, nullptr, &surface);
|
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkCreateWaylandSurfaceEXT failed: ");
|
Vulkan::SetErrorObject(error, "vkCreateWaylandSurfaceEXT failed: ", res);
|
||||||
return VK_NULL_HANDLE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return surface;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return VK_NULL_HANDLE;
|
Error::SetStringFmt(error, "Unhandled window type: {}", static_cast<unsigned>(m_window_info.type));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanSwapChain::DestroyVulkanSurface(VkInstance instance, WindowInfo* wi, VkSurfaceKHR surface)
|
void VulkanSwapChain::DestroySurface()
|
||||||
{
|
{
|
||||||
vkDestroySurfaceKHR(VulkanDevice::GetInstance().GetVulkanInstance(), surface, nullptr);
|
if (m_surface != VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
vkDestroySurfaceKHR(VulkanDevice::GetInstance().GetVulkanInstance(), m_surface, nullptr);
|
||||||
|
m_surface = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
if (wi->type == WindowInfo::Type::MacOS && wi->surface_handle)
|
if (m_metal_layer)
|
||||||
CocoaTools::DestroyMetalLayer(wi);
|
{
|
||||||
|
CocoaTools::DestroyMetalLayer(m_window_info, m_metal_layer);
|
||||||
|
m_metal_layer = nullptr;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<VulkanSwapChain> VulkanSwapChain::Create(const WindowInfo& wi, VkSurfaceKHR surface,
|
std::optional<VkSurfaceFormatKHR> VulkanSwapChain::SelectSurfaceFormat(VkPhysicalDevice physdev, Error* error)
|
||||||
VkPresentModeKHR present_mode,
|
|
||||||
std::optional<bool> exclusive_fullscreen_control)
|
|
||||||
{
|
{
|
||||||
std::unique_ptr<VulkanSwapChain> swap_chain =
|
|
||||||
std::unique_ptr<VulkanSwapChain>(new VulkanSwapChain(wi, surface, present_mode, exclusive_fullscreen_control));
|
|
||||||
if (!swap_chain->CreateSwapChain())
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
return swap_chain;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<VkSurfaceFormatKHR> VulkanSwapChain::SelectSurfaceFormat(VkSurfaceKHR surface)
|
|
||||||
{
|
|
||||||
VulkanDevice& dev = VulkanDevice::GetInstance();
|
|
||||||
u32 format_count;
|
u32 format_count;
|
||||||
VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(dev.GetVulkanPhysicalDevice(), surface, &format_count, nullptr);
|
VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(physdev, m_surface, &format_count, nullptr);
|
||||||
if (res != VK_SUCCESS || format_count == 0)
|
if (res != VK_SUCCESS || format_count == 0)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceFormatsKHR failed: ");
|
Vulkan::SetErrorObject(error, "vkGetPhysicalDeviceSurfaceFormatsKHR failed: ", res);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<VkSurfaceFormatKHR> surface_formats(format_count);
|
std::vector<VkSurfaceFormatKHR> surface_formats(format_count);
|
||||||
res =
|
res = vkGetPhysicalDeviceSurfaceFormatsKHR(physdev, m_surface, &format_count, surface_formats.data());
|
||||||
vkGetPhysicalDeviceSurfaceFormatsKHR(dev.GetVulkanPhysicalDevice(), surface, &format_count, surface_formats.data());
|
|
||||||
Assert(res == VK_SUCCESS);
|
Assert(res == VK_SUCCESS);
|
||||||
|
|
||||||
// If there is a single undefined surface format, the device doesn't care, so we'll just use RGBA
|
// If there is a single undefined surface format, the device doesn't care, so we'll just use RGBA
|
||||||
@ -255,28 +226,28 @@ std::optional<VkSurfaceFormatKHR> VulkanSwapChain::SelectSurfaceFormat(VkSurface
|
|||||||
return VkSurfaceFormatKHR{format, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR};
|
return VkSurfaceFormatKHR{format, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR};
|
||||||
}
|
}
|
||||||
|
|
||||||
ERROR_LOG("Failed to find a suitable format for swap chain buffers. Available formats were:");
|
SmallString errormsg = "Failed to find a suitable format for swap chain buffers. Available formats were:";
|
||||||
for (const VkSurfaceFormatKHR& sf : surface_formats)
|
for (const VkSurfaceFormatKHR& sf : surface_formats)
|
||||||
ERROR_LOG(" {}", static_cast<unsigned>(sf.format));
|
errormsg.append_format(" {}", static_cast<unsigned>(sf.format));
|
||||||
|
Error::SetStringView(error, errormsg);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanSwapChain::SelectPresentMode(VkSurfaceKHR surface, GPUVSyncMode* vsync_mode, VkPresentModeKHR* present_mode)
|
std::optional<VkPresentModeKHR> VulkanSwapChain::SelectPresentMode(VkPhysicalDevice physdev, GPUVSyncMode& vsync_mode,
|
||||||
|
Error* error)
|
||||||
{
|
{
|
||||||
VulkanDevice& dev = VulkanDevice::GetInstance();
|
|
||||||
VkResult res;
|
VkResult res;
|
||||||
u32 mode_count;
|
u32 mode_count;
|
||||||
res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev.GetVulkanPhysicalDevice(), surface, &mode_count, nullptr);
|
res = vkGetPhysicalDeviceSurfacePresentModesKHR(physdev, m_surface, &mode_count, nullptr);
|
||||||
if (res != VK_SUCCESS || mode_count == 0)
|
if (res != VK_SUCCESS || mode_count == 0)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceFormatsKHR failed: ");
|
Vulkan::SetErrorObject(error, "vkGetPhysicalDeviceSurfaceFormatsKHR failed: ", res);
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<VkPresentModeKHR> present_modes(mode_count);
|
std::vector<VkPresentModeKHR> present_modes(mode_count);
|
||||||
res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev.GetVulkanPhysicalDevice(), surface, &mode_count,
|
res = vkGetPhysicalDeviceSurfacePresentModesKHR(physdev, m_surface, &mode_count, present_modes.data());
|
||||||
present_modes.data());
|
|
||||||
Assert(res == VK_SUCCESS);
|
Assert(res == VK_SUCCESS);
|
||||||
|
|
||||||
// Checks if a particular mode is supported, if it is, returns that mode.
|
// Checks if a particular mode is supported, if it is, returns that mode.
|
||||||
@ -286,25 +257,25 @@ bool VulkanSwapChain::SelectPresentMode(VkSurfaceKHR surface, GPUVSyncMode* vsyn
|
|||||||
return it != present_modes.end();
|
return it != present_modes.end();
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (*vsync_mode)
|
switch (vsync_mode)
|
||||||
{
|
{
|
||||||
case GPUVSyncMode::Disabled:
|
case GPUVSyncMode::Disabled:
|
||||||
{
|
{
|
||||||
// Prefer immediate > mailbox > fifo.
|
// Prefer immediate > mailbox > fifo.
|
||||||
if (CheckForMode(VK_PRESENT_MODE_IMMEDIATE_KHR))
|
if (CheckForMode(VK_PRESENT_MODE_IMMEDIATE_KHR))
|
||||||
{
|
{
|
||||||
*present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
return VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||||
}
|
}
|
||||||
else if (CheckForMode(VK_PRESENT_MODE_MAILBOX_KHR))
|
else if (CheckForMode(VK_PRESENT_MODE_MAILBOX_KHR))
|
||||||
{
|
{
|
||||||
WARNING_LOG("Immediate not supported for vsync-disabled, using mailbox.");
|
WARNING_LOG("Immediate not supported for vsync-disabled, using mailbox.");
|
||||||
*present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
|
return VK_PRESENT_MODE_MAILBOX_KHR;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WARNING_LOG("Mailbox not supported for vsync-disabled, using FIFO.");
|
WARNING_LOG("Mailbox not supported for vsync-disabled, using FIFO.");
|
||||||
*present_mode = VK_PRESENT_MODE_FIFO_KHR;
|
vsync_mode = GPUVSyncMode::FIFO;
|
||||||
*vsync_mode = GPUVSyncMode::FIFO;
|
return VK_PRESENT_MODE_FIFO_KHR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -312,7 +283,7 @@ bool VulkanSwapChain::SelectPresentMode(VkSurfaceKHR surface, GPUVSyncMode* vsyn
|
|||||||
case GPUVSyncMode::FIFO:
|
case GPUVSyncMode::FIFO:
|
||||||
{
|
{
|
||||||
// FIFO is always available.
|
// FIFO is always available.
|
||||||
*present_mode = VK_PRESENT_MODE_FIFO_KHR;
|
return VK_PRESENT_MODE_FIFO_KHR;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -321,48 +292,50 @@ bool VulkanSwapChain::SelectPresentMode(VkSurfaceKHR surface, GPUVSyncMode* vsyn
|
|||||||
// Mailbox > fifo.
|
// Mailbox > fifo.
|
||||||
if (CheckForMode(VK_PRESENT_MODE_MAILBOX_KHR))
|
if (CheckForMode(VK_PRESENT_MODE_MAILBOX_KHR))
|
||||||
{
|
{
|
||||||
*present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
|
return VK_PRESENT_MODE_MAILBOX_KHR;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WARNING_LOG("Mailbox not supported for vsync-mailbox, using FIFO.");
|
WARNING_LOG("Mailbox not supported for vsync-mailbox, using FIFO.");
|
||||||
*present_mode = VK_PRESENT_MODE_FIFO_KHR;
|
vsync_mode = GPUVSyncMode::FIFO;
|
||||||
*vsync_mode = GPUVSyncMode::FIFO;
|
return VK_PRESENT_MODE_FIFO_KHR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
DefaultCaseIsUnreachable()
|
DefaultCaseIsUnreachable()
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanSwapChain::CreateSwapChain()
|
bool VulkanSwapChain::CreateSwapChain(VulkanDevice& dev, Error* error)
|
||||||
{
|
{
|
||||||
VulkanDevice& dev = VulkanDevice::GetInstance();
|
const VkPhysicalDevice physdev = dev.GetVulkanPhysicalDevice();
|
||||||
|
|
||||||
// Select swap chain format
|
// Select swap chain format
|
||||||
std::optional<VkSurfaceFormatKHR> surface_format = SelectSurfaceFormat(m_surface);
|
std::optional<VkSurfaceFormatKHR> surface_format = SelectSurfaceFormat(physdev, error);
|
||||||
if (!surface_format.has_value())
|
if (!surface_format.has_value())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
const std::optional<VkPresentModeKHR> present_mode = SelectPresentMode(physdev, m_vsync_mode, error);
|
||||||
|
if (!present_mode.has_value())
|
||||||
|
return false;
|
||||||
|
|
||||||
// Look up surface properties to determine image count and dimensions
|
// Look up surface properties to determine image count and dimensions
|
||||||
VkSurfaceCapabilitiesKHR surface_capabilities;
|
VkSurfaceCapabilitiesKHR surface_capabilities;
|
||||||
VkResult res =
|
VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physdev, m_surface, &surface_capabilities);
|
||||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev.GetVulkanPhysicalDevice(), m_surface, &surface_capabilities);
|
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed: ");
|
Vulkan::SetErrorObject(error, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed: ", res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select number of images in swap chain, we prefer one buffer in the background to work on in triple-buffered mode.
|
// Select number of images in swap chain, we prefer one buffer in the background to work on in triple-buffered mode.
|
||||||
// maxImageCount can be zero, in which case there isn't an upper limit on the number of buffers.
|
// maxImageCount can be zero, in which case there isn't an upper limit on the number of buffers.
|
||||||
u32 image_count = std::clamp<u32>(
|
u32 image_count = std::clamp<u32>(
|
||||||
(m_present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ? 3 : 2, surface_capabilities.minImageCount,
|
(present_mode.value() == VK_PRESENT_MODE_MAILBOX_KHR) ? 3 : 2, surface_capabilities.minImageCount,
|
||||||
(surface_capabilities.maxImageCount == 0) ? std::numeric_limits<u32>::max() : surface_capabilities.maxImageCount);
|
(surface_capabilities.maxImageCount == 0) ? std::numeric_limits<u32>::max() : surface_capabilities.maxImageCount);
|
||||||
DEV_LOG("Creating a swap chain with {} images in present mode {}", image_count, PresentModeToString(m_present_mode));
|
DEV_LOG("Creating a swap chain with {} images in present mode {}", image_count,
|
||||||
|
PresentModeToString(present_mode.value()));
|
||||||
|
|
||||||
// Determine the dimensions of the swap chain. Values of -1 indicate the size we specify here
|
// Determine the dimensions of the swap chain. Values of -1 indicate the size we specify here
|
||||||
// determines window size? Android sometimes lags updating currentExtent, so don't use it.
|
// determines window size? Android sometimes lags updating currentExtent, so don't use it.
|
||||||
@ -396,7 +369,7 @@ bool VulkanSwapChain::CreateSwapChain()
|
|||||||
VkImageUsageFlags image_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
VkImageUsageFlags image_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||||
if ((surface_capabilities.supportedUsageFlags & image_usage) != image_usage)
|
if ((surface_capabilities.supportedUsageFlags & image_usage) != image_usage)
|
||||||
{
|
{
|
||||||
ERROR_LOG("Vulkan: Swap chain does not support usage as color attachment");
|
Error::SetStringView(error, "Swap chain does not support usage as color attachment");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,25 +379,21 @@ bool VulkanSwapChain::CreateSwapChain()
|
|||||||
m_swap_chain = VK_NULL_HANDLE;
|
m_swap_chain = VK_NULL_HANDLE;
|
||||||
|
|
||||||
// Now we can actually create the swap chain
|
// Now we can actually create the swap chain
|
||||||
VkSwapchainCreateInfoKHR swap_chain_info = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
VkSwapchainCreateInfoKHR swap_chain_info = {.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||||||
nullptr,
|
.surface = m_surface,
|
||||||
0,
|
.minImageCount = image_count,
|
||||||
m_surface,
|
.imageFormat = surface_format->format,
|
||||||
image_count,
|
.imageColorSpace = surface_format->colorSpace,
|
||||||
surface_format->format,
|
.imageExtent = size,
|
||||||
surface_format->colorSpace,
|
.imageArrayLayers = 1u,
|
||||||
size,
|
.imageUsage = image_usage,
|
||||||
1u,
|
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||||
image_usage,
|
.preTransform = transform,
|
||||||
VK_SHARING_MODE_EXCLUSIVE,
|
.compositeAlpha = alpha,
|
||||||
0,
|
.presentMode = present_mode.value(),
|
||||||
nullptr,
|
.clipped = VK_TRUE,
|
||||||
transform,
|
.oldSwapchain = old_swap_chain};
|
||||||
alpha,
|
const std::array<u32, 2> queue_indices = {{
|
||||||
m_present_mode,
|
|
||||||
VK_TRUE,
|
|
||||||
old_swap_chain};
|
|
||||||
std::array<uint32_t, 2> indices = {{
|
|
||||||
dev.GetGraphicsQueueFamilyIndex(),
|
dev.GetGraphicsQueueFamilyIndex(),
|
||||||
dev.GetPresentQueueFamilyIndex(),
|
dev.GetPresentQueueFamilyIndex(),
|
||||||
}};
|
}};
|
||||||
@ -432,7 +401,7 @@ bool VulkanSwapChain::CreateSwapChain()
|
|||||||
{
|
{
|
||||||
swap_chain_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
swap_chain_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||||
swap_chain_info.queueFamilyIndexCount = 2;
|
swap_chain_info.queueFamilyIndexCount = 2;
|
||||||
swap_chain_info.pQueueFamilyIndices = indices.data();
|
swap_chain_info.pQueueFamilyIndices = queue_indices.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -466,81 +435,101 @@ bool VulkanSwapChain::CreateSwapChain()
|
|||||||
ERROR_LOG("Exclusive fullscreen control requested, but is not supported on this platform.");
|
ERROR_LOG("Exclusive fullscreen control requested, but is not supported on this platform.");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
res = vkCreateSwapchainKHR(dev.GetVulkanDevice(), &swap_chain_info, nullptr, &m_swap_chain);
|
const VkDevice vkdev = dev.GetVulkanDevice();
|
||||||
|
res = vkCreateSwapchainKHR(vkdev, &swap_chain_info, nullptr, &m_swap_chain);
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkCreateSwapchainKHR failed: ");
|
Vulkan::SetErrorObject(error, "vkCreateSwapchainKHR failed: ", res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now destroy the old swap chain, since it's been recreated.
|
// Now destroy the old swap chain, since it's been recreated.
|
||||||
// We can do this immediately since all work should have been completed before calling resize.
|
// We can do this immediately since all work should have been completed before calling resize.
|
||||||
if (old_swap_chain != VK_NULL_HANDLE)
|
if (old_swap_chain != VK_NULL_HANDLE)
|
||||||
vkDestroySwapchainKHR(dev.GetVulkanDevice(), old_swap_chain, nullptr);
|
vkDestroySwapchainKHR(vkdev, old_swap_chain, nullptr);
|
||||||
|
|
||||||
m_format = surface_format->format;
|
if (size.width == 0 || size.width > std::numeric_limits<u16>::max() || size.height == 0 ||
|
||||||
m_window_info.surface_width = std::max(1u, size.width);
|
size.height > std::numeric_limits<u16>::max())
|
||||||
m_window_info.surface_height = std::max(1u, size.height);
|
{
|
||||||
|
Error::SetStringFmt(error, "Invalid swap chain dimensions: {}x{}", size.width, size.height);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_present_mode = present_mode.value();
|
||||||
|
m_window_info.surface_width = static_cast<u16>(size.width);
|
||||||
|
m_window_info.surface_height = static_cast<u16>(size.height);
|
||||||
m_window_info.surface_format = VulkanDevice::GetFormatForVkFormat(surface_format->format);
|
m_window_info.surface_format = VulkanDevice::GetFormatForVkFormat(surface_format->format);
|
||||||
if (m_window_info.surface_format == GPUTexture::Format::Unknown)
|
if (m_window_info.surface_format == GPUTexture::Format::Unknown)
|
||||||
{
|
{
|
||||||
ERROR_LOG("Unknown Vulkan surface format {}", static_cast<u32>(surface_format->format));
|
Error::SetStringFmt(error, "Unknown surface format {}", static_cast<u32>(surface_format->format));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanSwapChain::CreateSwapChainImages(VulkanDevice& dev, Error* error)
|
||||||
|
{
|
||||||
|
const VkDevice vkdev = dev.GetVulkanDevice();
|
||||||
|
|
||||||
// Get and create images.
|
// Get and create images.
|
||||||
Assert(m_images.empty());
|
Assert(m_images.empty());
|
||||||
|
|
||||||
res = vkGetSwapchainImagesKHR(dev.GetVulkanDevice(), m_swap_chain, &image_count, nullptr);
|
u32 image_count;
|
||||||
|
VkResult res = vkGetSwapchainImagesKHR(vkdev, m_swap_chain, &image_count, nullptr);
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkGetSwapchainImagesKHR failed: ");
|
Vulkan::SetErrorObject(error, "vkGetSwapchainImagesKHR failed: ", res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<VkImage> images(image_count);
|
std::vector<VkImage> images(image_count);
|
||||||
res = vkGetSwapchainImagesKHR(dev.GetVulkanDevice(), m_swap_chain, &image_count, images.data());
|
res = vkGetSwapchainImagesKHR(vkdev, m_swap_chain, &image_count, images.data());
|
||||||
Assert(res == VK_SUCCESS);
|
Assert(res == VK_SUCCESS);
|
||||||
|
|
||||||
VkRenderPass render_pass = dev.GetSwapChainRenderPass(m_window_info.surface_format, VK_ATTACHMENT_LOAD_OP_CLEAR);
|
const VkRenderPass render_pass =
|
||||||
|
dev.GetSwapChainRenderPass(m_window_info.surface_format, VK_ATTACHMENT_LOAD_OP_CLEAR);
|
||||||
if (render_pass == VK_NULL_HANDLE)
|
if (render_pass == VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
Error::SetStringFmt(error, "Failed to get render pass for format {}",
|
||||||
|
GPUTexture::GetFormatName(m_window_info.surface_format));
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Vulkan::FramebufferBuilder fbb;
|
Vulkan::FramebufferBuilder fbb;
|
||||||
m_images.reserve(image_count);
|
m_images.reserve(image_count);
|
||||||
m_current_image = 0;
|
m_current_image = 0;
|
||||||
for (u32 i = 0; i < image_count; i++)
|
for (u32 i = 0; i < image_count; i++)
|
||||||
{
|
{
|
||||||
Image image = {};
|
Image& image = m_images.emplace_back();
|
||||||
image.image = images[i];
|
image.image = images[i];
|
||||||
|
|
||||||
const VkImageViewCreateInfo view_info = {
|
const VkImageViewCreateInfo view_info = {
|
||||||
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||||
nullptr,
|
.image = images[i],
|
||||||
0,
|
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||||
images[i],
|
.format = VulkanDevice::TEXTURE_FORMAT_MAPPING[static_cast<u8>(m_window_info.surface_format)],
|
||||||
VK_IMAGE_VIEW_TYPE_2D,
|
.components = {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||||
m_format,
|
VK_COMPONENT_SWIZZLE_IDENTITY},
|
||||||
{VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
|
.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u},
|
||||||
VK_COMPONENT_SWIZZLE_IDENTITY},
|
|
||||||
{VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u},
|
|
||||||
};
|
};
|
||||||
if ((res = vkCreateImageView(dev.GetVulkanDevice(), &view_info, nullptr, &image.view)) != VK_SUCCESS)
|
if ((res = vkCreateImageView(vkdev, &view_info, nullptr, &image.view)) != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkCreateImageView() failed: ");
|
Vulkan::SetErrorObject(error, "vkCreateImageView() failed: ", res);
|
||||||
|
m_images.pop_back();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fbb.AddAttachment(image.view);
|
fbb.AddAttachment(image.view);
|
||||||
fbb.SetRenderPass(render_pass);
|
fbb.SetRenderPass(render_pass);
|
||||||
fbb.SetSize(size.width, size.height, 1);
|
fbb.SetSize(m_window_info.surface_width, m_window_info.surface_height, 1);
|
||||||
if ((image.framebuffer = fbb.Create(dev.GetVulkanDevice())) == VK_NULL_HANDLE)
|
if ((image.framebuffer = fbb.Create(vkdev)) == VK_NULL_HANDLE)
|
||||||
{
|
{
|
||||||
vkDestroyImageView(dev.GetVulkanDevice(), image.view, nullptr);
|
Error::SetStringView(error, "Failed to create swap chain image framebuffer.");
|
||||||
|
vkDestroyImageView(vkdev, image.view, nullptr);
|
||||||
|
m_images.pop_back();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_images.push_back(image);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_current_semaphore = 0;
|
m_current_semaphore = 0;
|
||||||
@ -549,18 +538,18 @@ bool VulkanSwapChain::CreateSwapChain()
|
|||||||
ImageSemaphores& sema = m_semaphores[i];
|
ImageSemaphores& sema = m_semaphores[i];
|
||||||
|
|
||||||
const VkSemaphoreCreateInfo semaphore_info = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0};
|
const VkSemaphoreCreateInfo semaphore_info = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0};
|
||||||
res = vkCreateSemaphore(dev.GetVulkanDevice(), &semaphore_info, nullptr, &sema.available_semaphore);
|
res = vkCreateSemaphore(vkdev, &semaphore_info, nullptr, &sema.available_semaphore);
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkCreateSemaphore failed: ");
|
Vulkan::SetErrorObject(error, "vkCreateSemaphore failed: ", res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = vkCreateSemaphore(dev.GetVulkanDevice(), &semaphore_info, nullptr, &sema.rendering_finished_semaphore);
|
res = vkCreateSemaphore(vkdev, &semaphore_info, nullptr, &sema.rendering_finished_semaphore);
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkCreateSemaphore failed: ");
|
Vulkan::SetErrorObject(error, "vkCreateSemaphore failed: ", res);
|
||||||
vkDestroySemaphore(dev.GetVulkanDevice(), sema.available_semaphore, nullptr);
|
vkDestroySemaphore(vkdev, sema.available_semaphore, nullptr);
|
||||||
sema.available_semaphore = VK_NULL_HANDLE;
|
sema.available_semaphore = VK_NULL_HANDLE;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -569,22 +558,39 @@ bool VulkanSwapChain::CreateSwapChain()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VulkanSwapChain::Destroy(VulkanDevice& dev, bool wait_for_idle)
|
||||||
|
{
|
||||||
|
if (!m_swap_chain && !m_surface)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (wait_for_idle)
|
||||||
|
{
|
||||||
|
if (dev.InRenderPass())
|
||||||
|
dev.EndRenderPass();
|
||||||
|
|
||||||
|
dev.WaitForGPUIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
DestroySwapChain();
|
||||||
|
DestroySurface();
|
||||||
|
}
|
||||||
|
|
||||||
void VulkanSwapChain::DestroySwapChainImages()
|
void VulkanSwapChain::DestroySwapChainImages()
|
||||||
{
|
{
|
||||||
VulkanDevice& dev = VulkanDevice::GetInstance();
|
const VkDevice vkdev = VulkanDevice::GetInstance().GetVulkanDevice();
|
||||||
for (const auto& it : m_images)
|
for (const auto& it : m_images)
|
||||||
{
|
{
|
||||||
// don't defer view destruction, images are no longer valid
|
// don't defer view destruction, images are no longer valid
|
||||||
vkDestroyFramebuffer(dev.GetVulkanDevice(), it.framebuffer, nullptr);
|
vkDestroyFramebuffer(vkdev, it.framebuffer, nullptr);
|
||||||
vkDestroyImageView(dev.GetVulkanDevice(), it.view, nullptr);
|
vkDestroyImageView(vkdev, it.view, nullptr);
|
||||||
}
|
}
|
||||||
m_images.clear();
|
m_images.clear();
|
||||||
for (auto& it : m_semaphores)
|
for (const auto& it : m_semaphores)
|
||||||
{
|
{
|
||||||
if (it.rendering_finished_semaphore != VK_NULL_HANDLE)
|
if (it.rendering_finished_semaphore != VK_NULL_HANDLE)
|
||||||
vkDestroySemaphore(dev.GetVulkanDevice(), it.rendering_finished_semaphore, nullptr);
|
vkDestroySemaphore(vkdev, it.rendering_finished_semaphore, nullptr);
|
||||||
if (it.available_semaphore != VK_NULL_HANDLE)
|
if (it.available_semaphore != VK_NULL_HANDLE)
|
||||||
vkDestroySemaphore(dev.GetVulkanDevice(), it.available_semaphore, nullptr);
|
vkDestroySemaphore(vkdev, it.available_semaphore, nullptr);
|
||||||
}
|
}
|
||||||
m_semaphores = {};
|
m_semaphores = {};
|
||||||
|
|
||||||
@ -595,13 +601,11 @@ void VulkanSwapChain::DestroySwapChain()
|
|||||||
{
|
{
|
||||||
DestroySwapChainImages();
|
DestroySwapChainImages();
|
||||||
|
|
||||||
if (m_swap_chain == VK_NULL_HANDLE)
|
if (m_swap_chain != VK_NULL_HANDLE)
|
||||||
return;
|
{
|
||||||
|
vkDestroySwapchainKHR(VulkanDevice::GetInstance().GetVulkanDevice(), m_swap_chain, nullptr);
|
||||||
vkDestroySwapchainKHR(VulkanDevice::GetInstance().GetVulkanDevice(), m_swap_chain, nullptr);
|
m_swap_chain = VK_NULL_HANDLE;
|
||||||
m_swap_chain = VK_NULL_HANDLE;
|
}
|
||||||
m_window_info.surface_width = 0;
|
|
||||||
m_window_info.surface_height = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VkResult VulkanSwapChain::AcquireNextImage()
|
VkResult VulkanSwapChain::AcquireNextImage()
|
||||||
@ -649,20 +653,27 @@ void VulkanSwapChain::ResetImageAcquireResult()
|
|||||||
m_image_acquire_result.reset();
|
m_image_acquire_result.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanSwapChain::ResizeSwapChain(u32 new_width, u32 new_height, float new_scale)
|
bool VulkanSwapChain::ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error)
|
||||||
{
|
{
|
||||||
|
m_window_info.surface_scale = new_scale;
|
||||||
|
if (m_window_info.surface_width == new_width && m_window_info.surface_height == new_height)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
VulkanDevice& dev = VulkanDevice::GetInstance();
|
||||||
|
if (dev.InRenderPass())
|
||||||
|
dev.EndRenderPass();
|
||||||
|
dev.SubmitCommandBuffer(true);
|
||||||
|
|
||||||
ReleaseCurrentImage();
|
ReleaseCurrentImage();
|
||||||
DestroySwapChainImages();
|
DestroySwapChainImages();
|
||||||
|
|
||||||
if (new_width != 0 && new_height != 0)
|
if (new_width != 0 && new_height != 0)
|
||||||
{
|
{
|
||||||
m_window_info.surface_width = new_width;
|
m_window_info.surface_width = static_cast<u16>(new_width);
|
||||||
m_window_info.surface_height = new_height;
|
m_window_info.surface_height = static_cast<u16>(new_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_window_info.surface_scale = new_scale;
|
if (!CreateSwapChain(VulkanDevice::GetInstance(), error) || !CreateSwapChainImages(dev, error))
|
||||||
|
|
||||||
if (!CreateSwapChain())
|
|
||||||
{
|
{
|
||||||
DestroySwapChain();
|
DestroySwapChain();
|
||||||
return false;
|
return false;
|
||||||
@ -671,18 +682,31 @@ bool VulkanSwapChain::ResizeSwapChain(u32 new_width, u32 new_height, float new_s
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanSwapChain::SetPresentMode(VkPresentModeKHR present_mode)
|
bool VulkanSwapChain::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error)
|
||||||
{
|
{
|
||||||
if (m_present_mode == present_mode)
|
m_allow_present_throttle = allow_present_throttle;
|
||||||
|
|
||||||
|
VulkanDevice& dev = VulkanDevice::GetInstance();
|
||||||
|
const std::optional<VkPresentModeKHR> new_present_mode =
|
||||||
|
SelectPresentMode(dev.GetVulkanPhysicalDevice(), mode, error);
|
||||||
|
if (!new_present_mode.has_value())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// High-level mode could change without the actual backend mode changing.
|
||||||
|
m_vsync_mode = mode;
|
||||||
|
if (m_present_mode == new_present_mode.value())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
m_present_mode = present_mode;
|
if (dev.InRenderPass())
|
||||||
|
dev.EndRenderPass();
|
||||||
|
dev.SubmitCommandBuffer(true);
|
||||||
|
|
||||||
|
// TODO: Use the maintenance extension to change it without recreating...
|
||||||
// Recreate the swap chain with the new present mode.
|
// Recreate the swap chain with the new present mode.
|
||||||
VERBOSE_LOG("Recreating swap chain to change present mode.");
|
VERBOSE_LOG("Recreating swap chain to change present mode.");
|
||||||
ReleaseCurrentImage();
|
ReleaseCurrentImage();
|
||||||
DestroySwapChainImages();
|
DestroySwapChainImages();
|
||||||
if (!CreateSwapChain())
|
if (!CreateSwapChain(dev, error) || !CreateSwapChainImages(dev, error))
|
||||||
{
|
{
|
||||||
DestroySwapChain();
|
DestroySwapChain();
|
||||||
return false;
|
return false;
|
||||||
@ -691,18 +715,19 @@ bool VulkanSwapChain::SetPresentMode(VkPresentModeKHR present_mode)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanSwapChain::RecreateSurface(const WindowInfo& new_wi)
|
bool VulkanSwapChain::RecreateSurface(Error* error)
|
||||||
{
|
{
|
||||||
VulkanDevice& dev = VulkanDevice::GetInstance();
|
VulkanDevice& dev = VulkanDevice::GetInstance();
|
||||||
|
if (dev.InRenderPass())
|
||||||
|
dev.EndRenderPass();
|
||||||
|
dev.SubmitCommandBuffer(true);
|
||||||
|
|
||||||
// Destroy the old swap chain, images, and surface.
|
// Destroy the old swap chain, images, and surface.
|
||||||
DestroySwapChain();
|
DestroySwapChain();
|
||||||
DestroySurface();
|
DestroySurface();
|
||||||
|
|
||||||
// Re-create the surface with the new native handle
|
// Re-create the surface with the new native handle
|
||||||
m_window_info = new_wi;
|
if (!CreateSurface(dev.GetVulkanInstance(), dev.GetVulkanPhysicalDevice(), error))
|
||||||
m_surface = CreateVulkanSurface(dev.GetVulkanInstance(), dev.GetVulkanPhysicalDevice(), &m_window_info);
|
|
||||||
if (m_surface == VK_NULL_HANDLE)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// The validation layers get angry at us if we don't call this before creating the swapchain.
|
// The validation layers get angry at us if we don't call this before creating the swapchain.
|
||||||
@ -711,17 +736,13 @@ bool VulkanSwapChain::RecreateSurface(const WindowInfo& new_wi)
|
|||||||
m_surface, &present_supported);
|
m_surface, &present_supported);
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceSupportKHR failed: ");
|
Vulkan::SetErrorObject(error, "vkGetPhysicalDeviceSurfaceSupportKHR failed: ", res);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!present_supported)
|
|
||||||
{
|
|
||||||
Panic("Recreated surface does not support presenting.");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
AssertMsg(present_supported, "Recreated surface does not support presenting.");
|
||||||
|
|
||||||
// Finally re-create the swap chain
|
// Finally re-create the swap chain
|
||||||
if (!CreateSwapChain())
|
if (!CreateSwapChain(dev, error) || !CreateSwapChainImages(dev, error))
|
||||||
{
|
{
|
||||||
DestroySwapChain();
|
DestroySwapChain();
|
||||||
return false;
|
return false;
|
||||||
@ -729,12 +750,3 @@ bool VulkanSwapChain::RecreateSurface(const WindowInfo& new_wi)
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanSwapChain::DestroySurface()
|
|
||||||
{
|
|
||||||
if (m_surface == VK_NULL_HANDLE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
DestroyVulkanSurface(VulkanDevice::GetInstance().GetVulkanInstance(), &m_window_info, m_surface);
|
|
||||||
m_surface = VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "gpu_device.h"
|
||||||
#include "vulkan_loader.h"
|
#include "vulkan_loader.h"
|
||||||
#include "vulkan_texture.h"
|
#include "vulkan_texture.h"
|
||||||
#include "window_info.h"
|
#include "window_info.h"
|
||||||
@ -14,41 +15,19 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class VulkanSwapChain
|
class VulkanSwapChain final : public GPUSwapChain
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// We don't actually need +1 semaphores, or, more than one really.
|
VulkanSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode, bool allow_present_throttle,
|
||||||
// But, the validation layer gets cranky if we don't fence wait before the next image acquire.
|
std::optional<bool> exclusive_fullscreen_control);
|
||||||
// So, add an additional semaphore to ensure that we're never acquiring before fence waiting.
|
~VulkanSwapChain() override;
|
||||||
static constexpr u32 NUM_SEMAPHORES = 4; // Should be command buffers + 1
|
|
||||||
|
|
||||||
~VulkanSwapChain();
|
|
||||||
|
|
||||||
// Creates a vulkan-renderable surface for the specified window handle.
|
|
||||||
static VkSurfaceKHR CreateVulkanSurface(VkInstance instance, VkPhysicalDevice physical_device, WindowInfo* wi);
|
|
||||||
|
|
||||||
// Destroys a previously-created surface.
|
|
||||||
static void DestroyVulkanSurface(VkInstance instance, WindowInfo* wi, VkSurfaceKHR surface);
|
|
||||||
|
|
||||||
// Create a new swap chain from a pre-existing surface.
|
|
||||||
static std::unique_ptr<VulkanSwapChain> Create(const WindowInfo& wi, VkSurfaceKHR surface,
|
|
||||||
VkPresentModeKHR present_mode,
|
|
||||||
std::optional<bool> exclusive_fullscreen_control);
|
|
||||||
|
|
||||||
// Determines present mode to use.
|
|
||||||
static bool SelectPresentMode(VkSurfaceKHR surface, GPUVSyncMode* vsync_mode, VkPresentModeKHR* present_mode);
|
|
||||||
|
|
||||||
ALWAYS_INLINE VkSurfaceKHR GetSurface() const { return m_surface; }
|
ALWAYS_INLINE VkSurfaceKHR GetSurface() const { return m_surface; }
|
||||||
ALWAYS_INLINE VkSwapchainKHR GetSwapChain() const { return m_swap_chain; }
|
ALWAYS_INLINE VkSwapchainKHR GetSwapChain() const { return m_swap_chain; }
|
||||||
ALWAYS_INLINE const VkSwapchainKHR* GetSwapChainPtr() const { return &m_swap_chain; }
|
ALWAYS_INLINE const VkSwapchainKHR* GetSwapChainPtr() const { return &m_swap_chain; }
|
||||||
ALWAYS_INLINE const WindowInfo& GetWindowInfo() const { return m_window_info; }
|
|
||||||
ALWAYS_INLINE u32 GetWidth() const { return m_window_info.surface_width; }
|
|
||||||
ALWAYS_INLINE u32 GetHeight() const { return m_window_info.surface_height; }
|
|
||||||
ALWAYS_INLINE float GetScale() const { return m_window_info.surface_scale; }
|
|
||||||
ALWAYS_INLINE u32 GetCurrentImageIndex() const { return m_current_image; }
|
ALWAYS_INLINE u32 GetCurrentImageIndex() const { return m_current_image; }
|
||||||
ALWAYS_INLINE const u32* GetCurrentImageIndexPtr() const { return &m_current_image; }
|
ALWAYS_INLINE const u32* GetCurrentImageIndexPtr() const { return &m_current_image; }
|
||||||
ALWAYS_INLINE u32 GetImageCount() const { return static_cast<u32>(m_images.size()); }
|
ALWAYS_INLINE u32 GetImageCount() const { return static_cast<u32>(m_images.size()); }
|
||||||
ALWAYS_INLINE VkFormat GetImageFormat() const { return m_format; }
|
|
||||||
ALWAYS_INLINE VkImage GetCurrentImage() const { return m_images[m_current_image].image; }
|
ALWAYS_INLINE VkImage GetCurrentImage() const { return m_images[m_current_image].image; }
|
||||||
ALWAYS_INLINE VkImageView GetCurrentImageView() const { return m_images[m_current_image].view; }
|
ALWAYS_INLINE VkImageView GetCurrentImageView() const { return m_images[m_current_image].view; }
|
||||||
ALWAYS_INLINE VkFramebuffer GetCurrentFramebuffer() const { return m_images[m_current_image].framebuffer; }
|
ALWAYS_INLINE VkFramebuffer GetCurrentFramebuffer() const { return m_images[m_current_image].framebuffer; }
|
||||||
@ -69,27 +48,31 @@ public:
|
|||||||
return &m_semaphores[m_current_semaphore].rendering_finished_semaphore;
|
return &m_semaphores[m_current_semaphore].rendering_finished_semaphore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CreateSurface(VkInstance instance, VkPhysicalDevice physical_device, Error* error);
|
||||||
|
bool CreateSwapChain(VulkanDevice& dev, Error* error);
|
||||||
|
bool CreateSwapChainImages(VulkanDevice& dev, Error* error);
|
||||||
|
void Destroy(VulkanDevice& dev, bool wait_for_idle);
|
||||||
|
|
||||||
VkResult AcquireNextImage();
|
VkResult AcquireNextImage();
|
||||||
void ReleaseCurrentImage();
|
void ReleaseCurrentImage();
|
||||||
void ResetImageAcquireResult();
|
void ResetImageAcquireResult();
|
||||||
|
|
||||||
bool RecreateSurface(const WindowInfo& new_wi);
|
bool RecreateSurface(Error* error);
|
||||||
bool ResizeSwapChain(u32 new_width = 0, u32 new_height = 0, float new_scale = 1.0f);
|
|
||||||
|
|
||||||
// Change vsync enabled state. This may fail as it causes a swapchain recreation.
|
bool ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error) override;
|
||||||
bool SetPresentMode(VkPresentModeKHR present_mode);
|
bool SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR present_mode,
|
// We don't actually need +1 semaphores, or, more than one really.
|
||||||
std::optional<bool> exclusive_fullscreen_control);
|
// But, the validation layer gets cranky if we don't fence wait before the next image acquire.
|
||||||
|
// So, add an additional semaphore to ensure that we're never acquiring before fence waiting.
|
||||||
|
static constexpr u32 NUM_SEMAPHORES = 4; // Should be command buffers + 1
|
||||||
|
|
||||||
static std::optional<VkSurfaceFormatKHR> SelectSurfaceFormat(VkSurfaceKHR surface);
|
std::optional<VkSurfaceFormatKHR> SelectSurfaceFormat(VkPhysicalDevice physdev, Error* error);
|
||||||
|
std::optional<VkPresentModeKHR> SelectPresentMode(VkPhysicalDevice physdev, GPUVSyncMode& vsync_mode, Error* error);
|
||||||
bool CreateSwapChain();
|
|
||||||
void DestroySwapChain();
|
|
||||||
|
|
||||||
void DestroySwapChainImages();
|
void DestroySwapChainImages();
|
||||||
|
void DestroySwapChain();
|
||||||
void DestroySurface();
|
void DestroySurface();
|
||||||
|
|
||||||
struct Image
|
struct Image
|
||||||
@ -105,9 +88,13 @@ private:
|
|||||||
VkSemaphore rendering_finished_semaphore;
|
VkSemaphore rendering_finished_semaphore;
|
||||||
};
|
};
|
||||||
|
|
||||||
WindowInfo m_window_info;
|
|
||||||
|
|
||||||
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
|
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// On MacOS, we need to store a pointer to the metal layer as well.
|
||||||
|
void* m_metal_layer = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
VkSwapchainKHR m_swap_chain = VK_NULL_HANDLE;
|
VkSwapchainKHR m_swap_chain = VK_NULL_HANDLE;
|
||||||
|
|
||||||
std::vector<Image> m_images;
|
std::vector<Image> m_images;
|
||||||
@ -116,7 +103,6 @@ private:
|
|||||||
u32 m_current_image = 0;
|
u32 m_current_image = 0;
|
||||||
u32 m_current_semaphore = 0;
|
u32 m_current_semaphore = 0;
|
||||||
|
|
||||||
VkFormat m_format = VK_FORMAT_UNDEFINED;
|
|
||||||
VkPresentModeKHR m_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
VkPresentModeKHR m_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||||
|
|
||||||
std::optional<VkResult> m_image_acquire_result;
|
std::optional<VkResult> m_image_acquire_result;
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#include "common/bitutils.h"
|
#include "common/bitutils.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
|
|
||||||
LOG_CHANNEL(VulkanDevice);
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
static constexpr const VkComponentMapping s_identity_swizzle{
|
static constexpr const VkComponentMapping s_identity_swizzle{
|
||||||
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
|
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||||
|
@ -11,21 +11,6 @@
|
|||||||
|
|
||||||
LOG_CHANNEL(WindowInfo);
|
LOG_CHANNEL(WindowInfo);
|
||||||
|
|
||||||
void WindowInfo::SetSurfaceless()
|
|
||||||
{
|
|
||||||
type = Type::Surfaceless;
|
|
||||||
window_handle = nullptr;
|
|
||||||
surface_width = 0;
|
|
||||||
surface_height = 0;
|
|
||||||
surface_refresh_rate = 0.0f;
|
|
||||||
surface_scale = 1.0f;
|
|
||||||
surface_format = GPUTexture::Format::Unknown;
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
surface_handle = nullptr;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
|
||||||
#include "common/windows_headers.h"
|
#include "common/windows_headers.h"
|
||||||
@ -318,4 +303,4 @@ std::optional<float> WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi)
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -3,15 +3,15 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "gpu_texture.h"
|
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
#include "gpu_texture.h"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
// Contains the information required to create a graphics context in a window.
|
// Contains the information required to create a graphics context in a window.
|
||||||
struct WindowInfo
|
struct WindowInfo
|
||||||
{
|
{
|
||||||
enum class Type
|
enum class Type : u8
|
||||||
{
|
{
|
||||||
Surfaceless,
|
Surfaceless,
|
||||||
Win32,
|
Win32,
|
||||||
@ -19,27 +19,18 @@ struct WindowInfo
|
|||||||
Wayland,
|
Wayland,
|
||||||
MacOS,
|
MacOS,
|
||||||
Android,
|
Android,
|
||||||
Display,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Type type = Type::Surfaceless;
|
Type type = Type::Surfaceless;
|
||||||
void* display_connection = nullptr;
|
GPUTexture::Format surface_format = GPUTexture::Format::Unknown;
|
||||||
void* window_handle = nullptr;
|
u16 surface_width = 0;
|
||||||
u32 surface_width = 0;
|
u16 surface_height = 0;
|
||||||
u32 surface_height = 0;
|
|
||||||
float surface_refresh_rate = 0.0f;
|
float surface_refresh_rate = 0.0f;
|
||||||
float surface_scale = 1.0f;
|
float surface_scale = 1.0f;
|
||||||
GPUTexture::Format surface_format = GPUTexture::Format::Unknown;
|
void* display_connection = nullptr;
|
||||||
|
void* window_handle = nullptr;
|
||||||
// Needed for macOS.
|
|
||||||
#ifdef __APPLE__
|
|
||||||
void* surface_handle = nullptr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ALWAYS_INLINE bool IsSurfaceless() const { return type == Type::Surfaceless; }
|
ALWAYS_INLINE bool IsSurfaceless() const { return type == Type::Surfaceless; }
|
||||||
|
|
||||||
// Changes the window to be surfaceless (i.e. no handle/size/etc).
|
|
||||||
void SetSurfaceless();
|
|
||||||
|
|
||||||
static std::optional<float> QueryRefreshRateForWindow(const WindowInfo& wi);
|
static std::optional<float> QueryRefreshRateForWindow(const WindowInfo& wi);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user