mirror of
https://github.com/hedge-dev/UnleashedRecomp.git
synced 2025-04-28 21:37:58 +03:00
Sync render thread present with main thread. (#62)
* Experimenting with syncing the render thread. * Separate wait functions for swap chains. * Sync render thread present to the main thread. * Move present to main thread and frame limit after presenting. * g_next -> s_next * Fix Vulkan validation errors. * Make max frame latency configurable. * Fix loading thread breaking waitable swap chain order.
This commit is contained in:
parent
c1cafacf22
commit
fd009bcada
14 changed files with 242 additions and 275 deletions
|
@ -1,4 +1,5 @@
|
||||||
#include <app.h>
|
#include <app.h>
|
||||||
|
#include <gpu/video.h>
|
||||||
#include <install/installer.h>
|
#include <install/installer.h>
|
||||||
#include <kernel/function.h>
|
#include <kernel/function.h>
|
||||||
#include <ui/game_window.h>
|
#include <ui/game_window.h>
|
||||||
|
@ -38,6 +39,8 @@ PPC_FUNC(sub_824EB490)
|
||||||
PPC_FUNC_IMPL(__imp__sub_822C1130);
|
PPC_FUNC_IMPL(__imp__sub_822C1130);
|
||||||
PPC_FUNC(sub_822C1130)
|
PPC_FUNC(sub_822C1130)
|
||||||
{
|
{
|
||||||
|
Video::WaitOnSwapChain();
|
||||||
|
|
||||||
// Correct small delta time errors.
|
// Correct small delta time errors.
|
||||||
if (Config::FPS >= FPS_MIN && Config::FPS < FPS_MAX)
|
if (Config::FPS >= FPS_MIN && Config::FPS < FPS_MAX)
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,59 +7,6 @@
|
||||||
#include <kernel/xdbf.h>
|
#include <kernel/xdbf.h>
|
||||||
#include <app.h>
|
#include <app.h>
|
||||||
|
|
||||||
void ImDrawDataSnapshot::Clear()
|
|
||||||
{
|
|
||||||
for (int n = 0; n < Cache.GetMapSize(); n++)
|
|
||||||
if (ImDrawDataSnapshotEntry* entry = Cache.TryGetMapData(n))
|
|
||||||
IM_DELETE(entry->OurCopy);
|
|
||||||
Cache.Clear();
|
|
||||||
DrawData.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImDrawDataSnapshot::SnapUsingSwap(ImDrawData* src, double current_time)
|
|
||||||
{
|
|
||||||
ImDrawData* dst = &DrawData;
|
|
||||||
IM_ASSERT(src != dst && src->Valid);
|
|
||||||
|
|
||||||
// Copy all fields except CmdLists[]
|
|
||||||
ImVector<ImDrawList*> backup_draw_list;
|
|
||||||
backup_draw_list.swap(src->CmdLists);
|
|
||||||
IM_ASSERT(src->CmdLists.Data == NULL);
|
|
||||||
*dst = *src;
|
|
||||||
backup_draw_list.swap(src->CmdLists);
|
|
||||||
|
|
||||||
// Swap and mark as used
|
|
||||||
for (ImDrawList* src_list : src->CmdLists)
|
|
||||||
{
|
|
||||||
ImDrawDataSnapshotEntry* entry = GetOrAddEntry(src_list);
|
|
||||||
if (entry->OurCopy == NULL)
|
|
||||||
{
|
|
||||||
entry->SrcCopy = src_list;
|
|
||||||
entry->OurCopy = IM_NEW(ImDrawList)(src_list->_Data);
|
|
||||||
}
|
|
||||||
IM_ASSERT(entry->SrcCopy == src_list);
|
|
||||||
entry->SrcCopy->CmdBuffer.swap(entry->OurCopy->CmdBuffer); // Cheap swap
|
|
||||||
entry->SrcCopy->IdxBuffer.swap(entry->OurCopy->IdxBuffer);
|
|
||||||
entry->SrcCopy->VtxBuffer.swap(entry->OurCopy->VtxBuffer);
|
|
||||||
entry->SrcCopy->CmdBuffer.reserve(entry->OurCopy->CmdBuffer.Capacity); // Preserve bigger size to avoid reallocs for two consecutive frames
|
|
||||||
entry->SrcCopy->IdxBuffer.reserve(entry->OurCopy->IdxBuffer.Capacity);
|
|
||||||
entry->SrcCopy->VtxBuffer.reserve(entry->OurCopy->VtxBuffer.Capacity);
|
|
||||||
entry->LastUsedTime = current_time;
|
|
||||||
dst->CmdLists.push_back(entry->OurCopy);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup unused data
|
|
||||||
const double gc_threshold = current_time - MemoryCompactTimer;
|
|
||||||
for (int n = 0; n < Cache.GetMapSize(); n++)
|
|
||||||
if (ImDrawDataSnapshotEntry* entry = Cache.TryGetMapData(n))
|
|
||||||
{
|
|
||||||
if (entry->LastUsedTime > gc_threshold)
|
|
||||||
continue;
|
|
||||||
IM_DELETE(entry->OurCopy);
|
|
||||||
Cache.Remove(GetDrawListID(entry->SrcCopy), entry);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T1, typename T2>
|
template<typename T1, typename T2>
|
||||||
void ImFontAtlasSnapshot::SnapPointer(size_t offset, const T1& value, const T2& ptr, size_t count)
|
void ImFontAtlasSnapshot::SnapPointer(size_t offset, const T1& value, const T2& ptr, size_t count)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,36 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// https://github.com/ocornut/imgui/issues/1860#issuecomment-1927630727
|
|
||||||
|
|
||||||
// Usage:
|
|
||||||
// static ImDrawDataSnapshot snapshot; // Important: make persistent accross frames to reuse buffers.
|
|
||||||
// snapshot.SnapUsingSwap(ImGui::GetDrawData(), ImGui::GetTime());
|
|
||||||
// [...]
|
|
||||||
// ImGui_ImplDX11_RenderDrawData(&snapshot.DrawData);
|
|
||||||
|
|
||||||
struct ImDrawDataSnapshotEntry
|
|
||||||
{
|
|
||||||
ImDrawList* SrcCopy = NULL;
|
|
||||||
ImDrawList* OurCopy = NULL;
|
|
||||||
double LastUsedTime = 0.0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ImDrawDataSnapshot
|
|
||||||
{
|
|
||||||
// Members
|
|
||||||
ImDrawData DrawData;
|
|
||||||
ImPool<ImDrawDataSnapshotEntry> Cache;
|
|
||||||
float MemoryCompactTimer = 20.0f; // Discard unused data after 20 seconds
|
|
||||||
|
|
||||||
~ImDrawDataSnapshot() { Clear(); }
|
|
||||||
void Clear();
|
|
||||||
void SnapUsingSwap(ImDrawData* src, double current_time); // Efficient snapshot by swapping data, meaning "src_list" is unusable.
|
|
||||||
|
|
||||||
// Internals
|
|
||||||
ImGuiID GetDrawListID(ImDrawList* src_list) { return ImHashData(&src_list, sizeof(src_list)); } // Hash pointer
|
|
||||||
ImDrawDataSnapshotEntry* GetOrAddEntry(ImDrawList* src_list) { return Cache.GetOrAddByKey(GetDrawListID(src_list)); }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Undefine this to generate a font atlas file in working directory.
|
// Undefine this to generate a font atlas file in working directory.
|
||||||
// You also need to do this if you are testing localization, as only
|
// You also need to do this if you are testing localization, as only
|
||||||
// characters in the locale get added to the atlas.
|
// characters in the locale get added to the atlas.
|
||||||
|
|
|
@ -1191,7 +1191,7 @@ namespace plume {
|
||||||
|
|
||||||
// D3D12SwapChain
|
// D3D12SwapChain
|
||||||
|
|
||||||
D3D12SwapChain::D3D12SwapChain(D3D12CommandQueue *commandQueue, RenderWindow renderWindow, uint32_t textureCount, RenderFormat format) {
|
D3D12SwapChain::D3D12SwapChain(D3D12CommandQueue *commandQueue, RenderWindow renderWindow, uint32_t textureCount, RenderFormat format, uint32_t maxFrameLatency) {
|
||||||
assert(commandQueue != nullptr);
|
assert(commandQueue != nullptr);
|
||||||
assert(renderWindow != 0);
|
assert(renderWindow != 0);
|
||||||
|
|
||||||
|
@ -1199,6 +1199,7 @@ namespace plume {
|
||||||
this->renderWindow = renderWindow;
|
this->renderWindow = renderWindow;
|
||||||
this->textureCount = textureCount;
|
this->textureCount = textureCount;
|
||||||
this->format = format;
|
this->format = format;
|
||||||
|
this->maxFrameLatency = maxFrameLatency;
|
||||||
|
|
||||||
// Store the native format representation.
|
// Store the native format representation.
|
||||||
nativeFormat = toDXGI(format);
|
nativeFormat = toDXGI(format);
|
||||||
|
@ -1230,7 +1231,7 @@ namespace plume {
|
||||||
}
|
}
|
||||||
|
|
||||||
d3d = static_cast<IDXGISwapChain3 *>(swapChain1);
|
d3d = static_cast<IDXGISwapChain3 *>(swapChain1);
|
||||||
d3d->SetMaximumFrameLatency(1);
|
d3d->SetMaximumFrameLatency(maxFrameLatency);
|
||||||
waitableObject = d3d->GetFrameLatencyWaitableObject();
|
waitableObject = d3d->GetFrameLatencyWaitableObject();
|
||||||
|
|
||||||
textures.resize(textureCount);
|
textures.resize(textureCount);
|
||||||
|
@ -1264,16 +1265,18 @@ namespace plume {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D12SwapChain::present(uint32_t textureIndex, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount) {
|
bool D3D12SwapChain::present(uint32_t textureIndex, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount) {
|
||||||
if (waitableObject != NULL) {
|
|
||||||
WaitForSingleObject(waitableObject, INFINITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
UINT syncInterval = vsyncEnabled ? 1 : 0;
|
UINT syncInterval = vsyncEnabled ? 1 : 0;
|
||||||
UINT flags = !vsyncEnabled ? DXGI_PRESENT_ALLOW_TEARING : 0;
|
UINT flags = !vsyncEnabled ? DXGI_PRESENT_ALLOW_TEARING : 0;
|
||||||
HRESULT res = d3d->Present(syncInterval, flags);
|
HRESULT res = d3d->Present(syncInterval, flags);
|
||||||
return SUCCEEDED(res);
|
return SUCCEEDED(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void D3D12SwapChain::wait() {
|
||||||
|
if (waitableObject != NULL) {
|
||||||
|
WaitForSingleObject(waitableObject, INFINITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool D3D12SwapChain::resize() {
|
bool D3D12SwapChain::resize() {
|
||||||
getWindowSize(width, height);
|
getWindowSize(width, height);
|
||||||
|
|
||||||
|
@ -2197,8 +2200,8 @@ namespace plume {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<RenderSwapChain> D3D12CommandQueue::createSwapChain(RenderWindow renderWindow, uint32_t bufferCount, RenderFormat format) {
|
std::unique_ptr<RenderSwapChain> D3D12CommandQueue::createSwapChain(RenderWindow renderWindow, uint32_t bufferCount, RenderFormat format, uint32_t maxFrameLatency) {
|
||||||
return std::make_unique<D3D12SwapChain>(this, renderWindow, bufferCount, format);
|
return std::make_unique<D3D12SwapChain>(this, renderWindow, bufferCount, format, maxFrameLatency);
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D12CommandQueue::executeCommandLists(const RenderCommandList **commandLists, uint32_t commandListCount, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount, RenderCommandSemaphore **signalSemaphores, uint32_t signalSemaphoreCount, RenderCommandFence *signalFence) {
|
void D3D12CommandQueue::executeCommandLists(const RenderCommandList **commandLists, uint32_t commandListCount, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount, RenderCommandSemaphore **signalSemaphores, uint32_t signalSemaphoreCount, RenderCommandFence *signalFence) {
|
||||||
|
|
|
@ -107,10 +107,12 @@ namespace plume {
|
||||||
uint32_t height = 0;
|
uint32_t height = 0;
|
||||||
uint32_t refreshRate = 0;
|
uint32_t refreshRate = 0;
|
||||||
bool vsyncEnabled = true;
|
bool vsyncEnabled = true;
|
||||||
|
uint32_t maxFrameLatency = 0;
|
||||||
|
|
||||||
D3D12SwapChain(D3D12CommandQueue *commandQueue, RenderWindow renderWindow, uint32_t textureCount, RenderFormat format);
|
D3D12SwapChain(D3D12CommandQueue *commandQueue, RenderWindow renderWindow, uint32_t textureCount, RenderFormat format, uint32_t maxFrameLatency);
|
||||||
~D3D12SwapChain() override;
|
~D3D12SwapChain() override;
|
||||||
bool present(uint32_t textureIndex, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount) override;
|
bool present(uint32_t textureIndex, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount) override;
|
||||||
|
void wait() override;
|
||||||
bool resize() override;
|
bool resize() override;
|
||||||
bool needsResize() const override;
|
bool needsResize() const override;
|
||||||
void setVsyncEnabled(bool vsyncEnabled) override;
|
void setVsyncEnabled(bool vsyncEnabled) override;
|
||||||
|
@ -230,7 +232,7 @@ namespace plume {
|
||||||
|
|
||||||
D3D12CommandQueue(D3D12Device *device, RenderCommandListType type);
|
D3D12CommandQueue(D3D12Device *device, RenderCommandListType type);
|
||||||
~D3D12CommandQueue() override;
|
~D3D12CommandQueue() override;
|
||||||
std::unique_ptr<RenderSwapChain> createSwapChain(RenderWindow renderWindow, uint32_t textureCount, RenderFormat format) override;
|
std::unique_ptr<RenderSwapChain> createSwapChain(RenderWindow renderWindow, uint32_t textureCount, RenderFormat format, uint32_t newFrameLatency) override;
|
||||||
void executeCommandLists(const RenderCommandList **commandLists, uint32_t commandListCount, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount, RenderCommandSemaphore **signalSemaphores, uint32_t signalSemaphoreCount, RenderCommandFence *signalFence) override;
|
void executeCommandLists(const RenderCommandList **commandLists, uint32_t commandListCount, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount, RenderCommandSemaphore **signalSemaphores, uint32_t signalSemaphoreCount, RenderCommandFence *signalFence) override;
|
||||||
void waitForCommandFence(RenderCommandFence *fence) override;
|
void waitForCommandFence(RenderCommandFence *fence) override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -87,6 +87,7 @@ namespace plume {
|
||||||
struct RenderSwapChain {
|
struct RenderSwapChain {
|
||||||
virtual ~RenderSwapChain() { }
|
virtual ~RenderSwapChain() { }
|
||||||
virtual bool present(uint32_t textureIndex, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount) = 0;
|
virtual bool present(uint32_t textureIndex, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount) = 0;
|
||||||
|
virtual void wait() = 0;
|
||||||
virtual bool resize() = 0;
|
virtual bool resize() = 0;
|
||||||
virtual bool needsResize() const = 0;
|
virtual bool needsResize() const = 0;
|
||||||
virtual void setVsyncEnabled(bool vsyncEnabled) = 0;
|
virtual void setVsyncEnabled(bool vsyncEnabled) = 0;
|
||||||
|
@ -190,7 +191,7 @@ namespace plume {
|
||||||
|
|
||||||
struct RenderCommandQueue {
|
struct RenderCommandQueue {
|
||||||
virtual ~RenderCommandQueue() { }
|
virtual ~RenderCommandQueue() { }
|
||||||
virtual std::unique_ptr<RenderSwapChain> createSwapChain(RenderWindow renderWindow, uint32_t textureCount, RenderFormat format) = 0;
|
virtual std::unique_ptr<RenderSwapChain> createSwapChain(RenderWindow renderWindow, uint32_t textureCount, RenderFormat format, uint32_t maxFrameLatency) = 0;
|
||||||
virtual void executeCommandLists(const RenderCommandList **commandLists, uint32_t commandListCount, RenderCommandSemaphore **waitSemaphores = nullptr, uint32_t waitSemaphoreCount = 0, RenderCommandSemaphore **signalSemaphores = nullptr, uint32_t signalSemaphoreCount = 0, RenderCommandFence *signalFence = nullptr) = 0;
|
virtual void executeCommandLists(const RenderCommandList **commandLists, uint32_t commandListCount, RenderCommandSemaphore **waitSemaphores = nullptr, uint32_t waitSemaphoreCount = 0, RenderCommandSemaphore **signalSemaphores = nullptr, uint32_t signalSemaphoreCount = 0, RenderCommandFence *signalFence = nullptr) = 0;
|
||||||
virtual void waitForCommandFence(RenderCommandFence *fence) = 0;
|
virtual void waitForCommandFence(RenderCommandFence *fence) = 0;
|
||||||
|
|
||||||
|
|
|
@ -1966,13 +1966,14 @@ namespace plume {
|
||||||
|
|
||||||
// VulkanSwapChain
|
// VulkanSwapChain
|
||||||
|
|
||||||
VulkanSwapChain::VulkanSwapChain(VulkanCommandQueue *commandQueue, RenderWindow renderWindow, uint32_t textureCount, RenderFormat format) {
|
VulkanSwapChain::VulkanSwapChain(VulkanCommandQueue *commandQueue, RenderWindow renderWindow, uint32_t textureCount, RenderFormat format, uint32_t maxFrameLatency) {
|
||||||
assert(commandQueue != nullptr);
|
assert(commandQueue != nullptr);
|
||||||
assert(textureCount > 0);
|
assert(textureCount > 0);
|
||||||
|
|
||||||
this->commandQueue = commandQueue;
|
this->commandQueue = commandQueue;
|
||||||
this->renderWindow = renderWindow;
|
this->renderWindow = renderWindow;
|
||||||
this->format = format;
|
this->format = format;
|
||||||
|
this->maxFrameLatency = maxFrameLatency;
|
||||||
|
|
||||||
VkResult res;
|
VkResult res;
|
||||||
|
|
||||||
|
@ -2131,12 +2132,6 @@ namespace plume {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanSwapChain::present(uint32_t textureIndex, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount) {
|
bool VulkanSwapChain::present(uint32_t textureIndex, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount) {
|
||||||
constexpr uint64_t MaxFrameDelay = 1;
|
|
||||||
if (commandQueue->device->capabilities.presentWait && (currentPresentId > MaxFrameDelay)) {
|
|
||||||
constexpr uint64_t waitTimeout = 100000000;
|
|
||||||
vkWaitForPresentKHR(commandQueue->device->vk, vk, currentPresentId - MaxFrameDelay, waitTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
thread_local std::vector<VkSemaphore> waitSemaphoresVector;
|
thread_local std::vector<VkSemaphore> waitSemaphoresVector;
|
||||||
waitSemaphoresVector.clear();
|
waitSemaphoresVector.clear();
|
||||||
for (uint32_t i = 0; i < waitSemaphoreCount; i++) {
|
for (uint32_t i = 0; i < waitSemaphoreCount; i++) {
|
||||||
|
@ -2175,6 +2170,13 @@ namespace plume {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VulkanSwapChain::wait() {
|
||||||
|
if (commandQueue->device->capabilities.presentWait && (currentPresentId >= maxFrameLatency)) {
|
||||||
|
constexpr uint64_t waitTimeout = 100000000;
|
||||||
|
vkWaitForPresentKHR(commandQueue->device->vk, vk, currentPresentId - (maxFrameLatency - 1), waitTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool VulkanSwapChain::resize() {
|
bool VulkanSwapChain::resize() {
|
||||||
getWindowSize(width, height);
|
getWindowSize(width, height);
|
||||||
|
|
||||||
|
@ -3274,8 +3276,8 @@ namespace plume {
|
||||||
device->queueFamilies[familyIndex].remove(this);
|
device->queueFamilies[familyIndex].remove(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<RenderSwapChain> VulkanCommandQueue::createSwapChain(RenderWindow renderWindow, uint32_t bufferCount, RenderFormat format) {
|
std::unique_ptr<RenderSwapChain> VulkanCommandQueue::createSwapChain(RenderWindow renderWindow, uint32_t bufferCount, RenderFormat format, uint32_t maxFrameLatency) {
|
||||||
return std::make_unique<VulkanSwapChain>(this, renderWindow, bufferCount, format);
|
return std::make_unique<VulkanSwapChain>(this, renderWindow, bufferCount, format, maxFrameLatency);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanCommandQueue::executeCommandLists(const RenderCommandList **commandLists, uint32_t commandListCount, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount, RenderCommandSemaphore **signalSemaphores, uint32_t signalSemaphoreCount, RenderCommandFence *signalFence) {
|
void VulkanCommandQueue::executeCommandLists(const RenderCommandList **commandLists, uint32_t commandListCount, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount, RenderCommandSemaphore **signalSemaphores, uint32_t signalSemaphoreCount, RenderCommandFence *signalFence) {
|
||||||
|
|
|
@ -231,10 +231,12 @@ namespace plume {
|
||||||
std::vector<VulkanTexture> textures;
|
std::vector<VulkanTexture> textures;
|
||||||
uint64_t currentPresentId = 0;
|
uint64_t currentPresentId = 0;
|
||||||
bool immediatePresentModeSupported = false;
|
bool immediatePresentModeSupported = false;
|
||||||
|
uint32_t maxFrameLatency = 0;
|
||||||
|
|
||||||
VulkanSwapChain(VulkanCommandQueue *commandQueue, RenderWindow renderWindow, uint32_t textureCount, RenderFormat format);
|
VulkanSwapChain(VulkanCommandQueue *commandQueue, RenderWindow renderWindow, uint32_t textureCount, RenderFormat format, uint32_t maxFrameLatency);
|
||||||
~VulkanSwapChain() override;
|
~VulkanSwapChain() override;
|
||||||
bool present(uint32_t textureIndex, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount) override;
|
bool present(uint32_t textureIndex, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount) override;
|
||||||
|
void wait() override;
|
||||||
bool resize() override;
|
bool resize() override;
|
||||||
bool needsResize() const override;
|
bool needsResize() const override;
|
||||||
void setVsyncEnabled(bool vsyncEnabled) override;
|
void setVsyncEnabled(bool vsyncEnabled) override;
|
||||||
|
@ -346,7 +348,7 @@ namespace plume {
|
||||||
|
|
||||||
VulkanCommandQueue(VulkanDevice *device, RenderCommandListType commandListType);
|
VulkanCommandQueue(VulkanDevice *device, RenderCommandListType commandListType);
|
||||||
~VulkanCommandQueue() override;
|
~VulkanCommandQueue() override;
|
||||||
std::unique_ptr<RenderSwapChain> createSwapChain(RenderWindow renderWindow, uint32_t bufferCount, RenderFormat format) override;
|
std::unique_ptr<RenderSwapChain> createSwapChain(RenderWindow renderWindow, uint32_t bufferCount, RenderFormat format, uint32_t maxFrameLatency) override;
|
||||||
void executeCommandLists(const RenderCommandList **commandLists, uint32_t commandListCount, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount, RenderCommandSemaphore **signalSemaphores, uint32_t signalSemaphoreCount, RenderCommandFence *signalFence) override;
|
void executeCommandLists(const RenderCommandList **commandLists, uint32_t commandListCount, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount, RenderCommandSemaphore **signalSemaphores, uint32_t signalSemaphoreCount, RenderCommandFence *signalFence) override;
|
||||||
void waitForCommandFence(RenderCommandFence *fence) override;
|
void waitForCommandFence(RenderCommandFence *fence) override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -629,7 +629,8 @@ enum class RenderCommandType
|
||||||
UnlockBuffer16,
|
UnlockBuffer16,
|
||||||
UnlockBuffer32,
|
UnlockBuffer32,
|
||||||
DrawImGui,
|
DrawImGui,
|
||||||
Present,
|
ExecuteCommandList,
|
||||||
|
BeginCommandList,
|
||||||
StretchRect,
|
StretchRect,
|
||||||
SetRenderTarget,
|
SetRenderTarget,
|
||||||
SetDepthStencilSurface,
|
SetDepthStencilSurface,
|
||||||
|
@ -1114,7 +1115,6 @@ static constexpr size_t SAMPLER_DESCRIPTOR_SIZE = 1024;
|
||||||
static std::unique_ptr<GuestTexture> g_imFontTexture;
|
static std::unique_ptr<GuestTexture> g_imFontTexture;
|
||||||
static std::unique_ptr<RenderPipelineLayout> g_imPipelineLayout;
|
static std::unique_ptr<RenderPipelineLayout> g_imPipelineLayout;
|
||||||
static std::unique_ptr<RenderPipeline> g_imPipeline;
|
static std::unique_ptr<RenderPipeline> g_imPipeline;
|
||||||
static ImDrawDataSnapshot g_imSnapshot;
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static void ExecuteCopyCommandList(const T& function)
|
static void ExecuteCopyCommandList(const T& function)
|
||||||
|
@ -1309,7 +1309,91 @@ static void CreateImGuiBackend()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BeginCommandList();
|
static void CheckSwapChain()
|
||||||
|
{
|
||||||
|
g_swapChain->setVsyncEnabled(Config::VSync);
|
||||||
|
g_swapChainValid &= !g_swapChain->needsResize();
|
||||||
|
|
||||||
|
if (!g_swapChainValid)
|
||||||
|
{
|
||||||
|
Video::WaitForGPU();
|
||||||
|
g_backBuffer->framebuffers.clear();
|
||||||
|
g_swapChainValid = g_swapChain->resize();
|
||||||
|
g_needsResize = g_swapChainValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_swapChainValid)
|
||||||
|
g_swapChainValid = g_swapChain->acquireTexture(g_acquireSemaphores[g_frame].get(), &g_backBufferIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BeginCommandList()
|
||||||
|
{
|
||||||
|
g_renderTarget = g_backBuffer;
|
||||||
|
g_depthStencil = nullptr;
|
||||||
|
g_framebuffer = nullptr;
|
||||||
|
|
||||||
|
g_pipelineState.renderTargetFormat = BACKBUFFER_FORMAT;
|
||||||
|
g_pipelineState.depthStencilFormat = RenderFormat::UNKNOWN;
|
||||||
|
|
||||||
|
if (g_swapChainValid)
|
||||||
|
{
|
||||||
|
bool applyingGammaCorrection = Config::XboxColorCorrection || abs(Config::Brightness - 0.5f) > 0.001f;
|
||||||
|
|
||||||
|
if (applyingGammaCorrection)
|
||||||
|
{
|
||||||
|
uint32_t width = g_swapChain->getWidth();
|
||||||
|
uint32_t height = g_swapChain->getHeight();
|
||||||
|
|
||||||
|
if (g_intermediaryBackBufferTextureWidth != width ||
|
||||||
|
g_intermediaryBackBufferTextureHeight != height)
|
||||||
|
{
|
||||||
|
if (g_intermediaryBackBufferTextureDescriptorIndex == NULL)
|
||||||
|
g_intermediaryBackBufferTextureDescriptorIndex = g_textureDescriptorAllocator.allocate();
|
||||||
|
|
||||||
|
Video::WaitForGPU(); // Fine to wait for GPU, this'll only happen during resize.
|
||||||
|
|
||||||
|
g_intermediaryBackBufferTexture = g_device->createTexture(RenderTextureDesc::Texture2D(width, height, 1, BACKBUFFER_FORMAT, RenderTextureFlag::RENDER_TARGET));
|
||||||
|
g_textureDescriptorSet->setTexture(g_intermediaryBackBufferTextureDescriptorIndex, g_intermediaryBackBufferTexture.get(), RenderTextureLayout::SHADER_READ);
|
||||||
|
|
||||||
|
g_intermediaryBackBufferTextureWidth = width;
|
||||||
|
g_intermediaryBackBufferTextureHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_backBuffer->texture = g_intermediaryBackBufferTexture.get();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_backBuffer->texture = g_swapChain->getTexture(g_backBufferIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_backBuffer->texture = g_backBuffer->textureHolder.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
g_backBuffer->layout = RenderTextureLayout::UNKNOWN;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
g_sharedConstants.texture2DIndices[i] = TEXTURE_DESCRIPTOR_NULL_TEXTURE_2D;
|
||||||
|
g_sharedConstants.texture3DIndices[i] = TEXTURE_DESCRIPTOR_NULL_TEXTURE_3D;
|
||||||
|
g_sharedConstants.textureCubeIndices[i] = TEXTURE_DESCRIPTOR_NULL_TEXTURE_CUBE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config::GITextureFiltering == EGITextureFiltering::Bicubic)
|
||||||
|
g_pipelineState.specConstants |= SPEC_CONSTANT_BICUBIC_GI_FILTER;
|
||||||
|
else
|
||||||
|
g_pipelineState.specConstants &= ~SPEC_CONSTANT_BICUBIC_GI_FILTER;
|
||||||
|
|
||||||
|
auto& commandList = g_commandLists[g_frame];
|
||||||
|
|
||||||
|
commandList->begin();
|
||||||
|
commandList->setGraphicsPipelineLayout(g_pipelineLayout.get());
|
||||||
|
commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 0);
|
||||||
|
commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 1);
|
||||||
|
commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 2);
|
||||||
|
commandList->setGraphicsDescriptorSet(g_samplerDescriptorSet.get(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
void Video::CreateHostDevice(const char *sdlVideoDriver)
|
void Video::CreateHostDevice(const char *sdlVideoDriver)
|
||||||
{
|
{
|
||||||
|
@ -1380,7 +1464,7 @@ void Video::CreateHostDevice(const char *sdlVideoDriver)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_swapChain = g_queue->createSwapChain(GameWindow::s_renderWindow, bufferCount, BACKBUFFER_FORMAT);
|
g_swapChain = g_queue->createSwapChain(GameWindow::s_renderWindow, bufferCount, BACKBUFFER_FORMAT, Config::MaxFrameLatency);
|
||||||
g_swapChain->setVsyncEnabled(Config::VSync);
|
g_swapChain->setVsyncEnabled(Config::VSync);
|
||||||
g_swapChainValid = !g_swapChain->needsResize();
|
g_swapChainValid = !g_swapChain->needsResize();
|
||||||
|
|
||||||
|
@ -1544,6 +1628,7 @@ void Video::CreateHostDevice(const char *sdlVideoDriver)
|
||||||
g_backBuffer->format = BACKBUFFER_FORMAT;
|
g_backBuffer->format = BACKBUFFER_FORMAT;
|
||||||
g_backBuffer->textureHolder = g_device->createTexture(RenderTextureDesc::Texture2D(1, 1, 1, BACKBUFFER_FORMAT, RenderTextureFlag::RENDER_TARGET));
|
g_backBuffer->textureHolder = g_device->createTexture(RenderTextureDesc::Texture2D(1, 1, 1, BACKBUFFER_FORMAT, RenderTextureFlag::RENDER_TARGET));
|
||||||
|
|
||||||
|
CheckSwapChain();
|
||||||
BeginCommandList();
|
BeginCommandList();
|
||||||
|
|
||||||
RenderTextureBarrier blankTextureBarriers[TEXTURE_DESCRIPTOR_NULL_COUNT];
|
RenderTextureBarrier blankTextureBarriers[TEXTURE_DESCRIPTOR_NULL_COUNT];
|
||||||
|
@ -1574,96 +1659,6 @@ void Video::WaitForGPU()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::atomic<bool> g_pendingRenderThread;
|
|
||||||
|
|
||||||
static void WaitForRenderThread()
|
|
||||||
{
|
|
||||||
g_pendingRenderThread.wait(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void BeginCommandList()
|
|
||||||
{
|
|
||||||
g_renderTarget = g_backBuffer;
|
|
||||||
g_depthStencil = nullptr;
|
|
||||||
g_framebuffer = nullptr;
|
|
||||||
|
|
||||||
g_pipelineState.renderTargetFormat = BACKBUFFER_FORMAT;
|
|
||||||
g_pipelineState.depthStencilFormat = RenderFormat::UNKNOWN;
|
|
||||||
|
|
||||||
g_swapChain->setVsyncEnabled(Config::VSync);
|
|
||||||
g_swapChainValid &= !g_swapChain->needsResize();
|
|
||||||
|
|
||||||
if (!g_swapChainValid)
|
|
||||||
{
|
|
||||||
Video::WaitForGPU();
|
|
||||||
g_backBuffer->framebuffers.clear();
|
|
||||||
g_swapChainValid = g_swapChain->resize();
|
|
||||||
g_needsResize = g_swapChainValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_swapChainValid)
|
|
||||||
g_swapChainValid = g_swapChain->acquireTexture(g_acquireSemaphores[g_frame].get(), &g_backBufferIndex);
|
|
||||||
|
|
||||||
if (g_swapChainValid)
|
|
||||||
{
|
|
||||||
bool applyingGammaCorrection = Config::XboxColorCorrection || abs(Config::Brightness - 0.5f) > 0.001f;
|
|
||||||
|
|
||||||
if (applyingGammaCorrection)
|
|
||||||
{
|
|
||||||
uint32_t width = g_swapChain->getWidth();
|
|
||||||
uint32_t height = g_swapChain->getHeight();
|
|
||||||
|
|
||||||
if (g_intermediaryBackBufferTextureWidth != width ||
|
|
||||||
g_intermediaryBackBufferTextureHeight != height)
|
|
||||||
{
|
|
||||||
if (g_intermediaryBackBufferTextureDescriptorIndex == NULL)
|
|
||||||
g_intermediaryBackBufferTextureDescriptorIndex = g_textureDescriptorAllocator.allocate();
|
|
||||||
|
|
||||||
Video::WaitForGPU(); // Fine to wait for GPU, this'll only happen during resize.
|
|
||||||
|
|
||||||
g_intermediaryBackBufferTexture = g_device->createTexture(RenderTextureDesc::Texture2D(width, height, 1, BACKBUFFER_FORMAT, RenderTextureFlag::RENDER_TARGET));
|
|
||||||
g_textureDescriptorSet->setTexture(g_intermediaryBackBufferTextureDescriptorIndex, g_intermediaryBackBufferTexture.get(), RenderTextureLayout::SHADER_READ);
|
|
||||||
|
|
||||||
g_intermediaryBackBufferTextureWidth = width;
|
|
||||||
g_intermediaryBackBufferTextureHeight = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_backBuffer->texture = g_intermediaryBackBufferTexture.get();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
g_backBuffer->texture = g_swapChain->getTexture(g_backBufferIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
g_backBuffer->texture = g_backBuffer->textureHolder.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
g_backBuffer->layout = RenderTextureLayout::UNKNOWN;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
g_sharedConstants.texture2DIndices[i] = TEXTURE_DESCRIPTOR_NULL_TEXTURE_2D;
|
|
||||||
g_sharedConstants.texture3DIndices[i] = TEXTURE_DESCRIPTOR_NULL_TEXTURE_3D;
|
|
||||||
g_sharedConstants.textureCubeIndices[i] = TEXTURE_DESCRIPTOR_NULL_TEXTURE_CUBE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config::GITextureFiltering == EGITextureFiltering::Bicubic)
|
|
||||||
g_pipelineState.specConstants |= SPEC_CONSTANT_BICUBIC_GI_FILTER;
|
|
||||||
else
|
|
||||||
g_pipelineState.specConstants &= ~SPEC_CONSTANT_BICUBIC_GI_FILTER;
|
|
||||||
|
|
||||||
auto& commandList = g_commandLists[g_frame];
|
|
||||||
|
|
||||||
commandList->begin();
|
|
||||||
commandList->setGraphicsPipelineLayout(g_pipelineLayout.get());
|
|
||||||
commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 0);
|
|
||||||
commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 1);
|
|
||||||
commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 2);
|
|
||||||
commandList->setGraphicsDescriptorSet(g_samplerDescriptorSet.get(), 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t CreateDevice(uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5, be<uint32_t>* a6)
|
static uint32_t CreateDevice(uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5, be<uint32_t>* a6)
|
||||||
{
|
{
|
||||||
g_xdbfTextureCache = std::unordered_map<uint16_t, GuestTexture *>();
|
g_xdbfTextureCache = std::unordered_map<uint16_t, GuestTexture *>();
|
||||||
|
@ -1906,6 +1901,12 @@ struct Profiler
|
||||||
value = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start).count();
|
value = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Reset()
|
||||||
|
{
|
||||||
|
End();
|
||||||
|
Begin();
|
||||||
|
}
|
||||||
|
|
||||||
double UpdateAndReturnAverage()
|
double UpdateAndReturnAverage()
|
||||||
{
|
{
|
||||||
values[g_profilerValueIndex] = value;
|
values[g_profilerValueIndex] = value;
|
||||||
|
@ -1914,6 +1915,7 @@ struct Profiler
|
||||||
};
|
};
|
||||||
|
|
||||||
static double g_applicationValues[PROFILER_VALUE_COUNT];
|
static double g_applicationValues[PROFILER_VALUE_COUNT];
|
||||||
|
static Profiler g_presentProfiler;
|
||||||
static Profiler g_renderDirectorProfiler;
|
static Profiler g_renderDirectorProfiler;
|
||||||
|
|
||||||
static bool g_profilerVisible;
|
static bool g_profilerVisible;
|
||||||
|
@ -1939,6 +1941,9 @@ static void DrawProfiler()
|
||||||
if (ImGui::Begin("Profiler", &g_profilerVisible))
|
if (ImGui::Begin("Profiler", &g_profilerVisible))
|
||||||
{
|
{
|
||||||
g_applicationValues[g_profilerValueIndex] = App::s_deltaTime * 1000.0;
|
g_applicationValues[g_profilerValueIndex] = App::s_deltaTime * 1000.0;
|
||||||
|
|
||||||
|
const double applicationAvg = std::accumulate(g_applicationValues, g_applicationValues + PROFILER_VALUE_COUNT, 0.0) / PROFILER_VALUE_COUNT;
|
||||||
|
double presentAvg = g_presentProfiler.UpdateAndReturnAverage();
|
||||||
double renderDirectorAvg = g_renderDirectorProfiler.UpdateAndReturnAverage();
|
double renderDirectorAvg = g_renderDirectorProfiler.UpdateAndReturnAverage();
|
||||||
|
|
||||||
if (ImPlot::BeginPlot("Frame Time"))
|
if (ImPlot::BeginPlot("Frame Time"))
|
||||||
|
@ -1946,18 +1951,23 @@ static void DrawProfiler()
|
||||||
ImPlot::SetupAxisLimits(ImAxis_Y1, 0.0, 20.0);
|
ImPlot::SetupAxisLimits(ImAxis_Y1, 0.0, 20.0);
|
||||||
ImPlot::SetupAxis(ImAxis_Y1, "ms", ImPlotAxisFlags_None);
|
ImPlot::SetupAxis(ImAxis_Y1, "ms", ImPlotAxisFlags_None);
|
||||||
ImPlot::PlotLine<double>("Application", g_applicationValues, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex);
|
ImPlot::PlotLine<double>("Application", g_applicationValues, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex);
|
||||||
|
ImPlot::PlotLine<double>("Present", g_presentProfiler.values, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex);
|
||||||
ImPlot::PlotLine<double>("Render Director", g_renderDirectorProfiler.values, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex);
|
ImPlot::PlotLine<double>("Render Director", g_renderDirectorProfiler.values, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex);
|
||||||
|
|
||||||
ImPlot::EndPlot();
|
ImPlot::EndPlot();
|
||||||
}
|
}
|
||||||
|
|
||||||
g_profilerValueIndex = (g_profilerValueIndex + 1) % PROFILER_VALUE_COUNT;
|
g_profilerValueIndex = (g_profilerValueIndex + 1) % PROFILER_VALUE_COUNT;
|
||||||
|
|
||||||
const double applicationAvg = std::accumulate(g_applicationValues, g_applicationValues + PROFILER_VALUE_COUNT, 0.0) / PROFILER_VALUE_COUNT;
|
ImGui::Text("Current Application: %g ms (%g FPS)", App::s_deltaTime * 1000.0, 1.0 / App::s_deltaTime);
|
||||||
|
ImGui::Text("Current Present: %g ms (%g FPS)", g_presentProfiler.value.load(), 1000.0 / g_presentProfiler.value.load());
|
||||||
|
ImGui::Text("Current Render Director: %g ms (%g FPS)", g_renderDirectorProfiler.value.load(), 1000.0 / g_renderDirectorProfiler.value.load());
|
||||||
|
ImGui::NewLine();
|
||||||
|
|
||||||
ImGui::Text("Average Application: %g ms (%g FPS)", applicationAvg, 1000.0 / applicationAvg);
|
ImGui::Text("Average Application: %g ms (%g FPS)", applicationAvg, 1000.0 / applicationAvg);
|
||||||
|
ImGui::Text("Average Present: %g ms (%g FPS)", presentAvg, 1000.0 / presentAvg);
|
||||||
ImGui::Text("Average Render Director: %g ms (%g FPS)", renderDirectorAvg, 1000.0 / renderDirectorAvg);
|
ImGui::Text("Average Render Director: %g ms (%g FPS)", renderDirectorAvg, 1000.0 / renderDirectorAvg);
|
||||||
|
ImGui::NewLine();
|
||||||
|
|
||||||
O1HeapDiagnostics diagnostics, physicalDiagnostics;
|
O1HeapDiagnostics diagnostics, physicalDiagnostics;
|
||||||
{
|
{
|
||||||
std::lock_guard lock(g_userHeap.mutex);
|
std::lock_guard lock(g_userHeap.mutex);
|
||||||
|
@ -1970,10 +1980,12 @@ static void DrawProfiler()
|
||||||
|
|
||||||
ImGui::Text("Heap Allocated: %d MB", int32_t(diagnostics.allocated / (1024 * 1024)));
|
ImGui::Text("Heap Allocated: %d MB", int32_t(diagnostics.allocated / (1024 * 1024)));
|
||||||
ImGui::Text("Physical Heap Allocated: %d MB", int32_t(physicalDiagnostics.allocated / (1024 * 1024)));
|
ImGui::Text("Physical Heap Allocated: %d MB", int32_t(physicalDiagnostics.allocated / (1024 * 1024)));
|
||||||
|
ImGui::NewLine();
|
||||||
|
|
||||||
auto capabilities = g_device->getCapabilities();
|
auto capabilities = g_device->getCapabilities();
|
||||||
ImGui::Text("Present Wait: %s", capabilities.presentWait ? "Supported" : "Unsupported");
|
ImGui::Text("Present Wait: %s", capabilities.presentWait ? "Supported" : "Unsupported");
|
||||||
ImGui::Text("Triangle Fan: %s", capabilities.triangleFan ? "Supported" : "Unsupported");
|
ImGui::Text("Triangle Fan: %s", capabilities.triangleFan ? "Supported" : "Unsupported");
|
||||||
|
ImGui::NewLine();
|
||||||
|
|
||||||
const char* sdlVideoDriver = SDL_GetCurrentVideoDriver();
|
const char* sdlVideoDriver = SDL_GetCurrentVideoDriver();
|
||||||
if (sdlVideoDriver != nullptr)
|
if (sdlVideoDriver != nullptr)
|
||||||
|
@ -2023,8 +2035,6 @@ static void DrawImGui()
|
||||||
auto drawData = ImGui::GetDrawData();
|
auto drawData = ImGui::GetDrawData();
|
||||||
if (drawData->CmdListsCount != 0)
|
if (drawData->CmdListsCount != 0)
|
||||||
{
|
{
|
||||||
g_imSnapshot.SnapUsingSwap(drawData, ImGui::GetTime());
|
|
||||||
|
|
||||||
RenderCommand cmd;
|
RenderCommand cmd;
|
||||||
cmd.type = RenderCommandType::DrawImGui;
|
cmd.type = RenderCommandType::DrawImGui;
|
||||||
g_renderQueue.enqueue(cmd);
|
g_renderQueue.enqueue(cmd);
|
||||||
|
@ -2047,7 +2057,7 @@ static void ProcDrawImGui(const RenderCommand& cmd)
|
||||||
commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 0);
|
commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 0);
|
||||||
commandList->setGraphicsDescriptorSet(g_samplerDescriptorSet.get(), 1);
|
commandList->setGraphicsDescriptorSet(g_samplerDescriptorSet.get(), 1);
|
||||||
|
|
||||||
auto& drawData = g_imSnapshot.DrawData;
|
auto& drawData = *ImGui::GetDrawData();
|
||||||
commandList->setViewports(RenderViewport(drawData.DisplayPos.x, drawData.DisplayPos.y, drawData.DisplaySize.x, drawData.DisplaySize.y));
|
commandList->setViewports(RenderViewport(drawData.DisplayPos.x, drawData.DisplayPos.y, drawData.DisplaySize.x, drawData.DisplaySize.y));
|
||||||
|
|
||||||
ImGuiPushConstants pushConstants{};
|
ImGuiPushConstants pushConstants{};
|
||||||
|
@ -2163,17 +2173,34 @@ static void ProcDrawImGui(const RenderCommand& cmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool g_shouldPrecompilePipelines = false;
|
// We have to check for this to properly handle the following situation:
|
||||||
|
// 1. Wait on swap chain.
|
||||||
|
// 2. Create loading thread.
|
||||||
|
// 3. Loading thread also waits on swap chain.
|
||||||
|
// 4. Loading thread presents and quits.
|
||||||
|
// 5. After the loading thread quits, application also presents.
|
||||||
|
static bool g_pendingWaitOnSwapChain = true;
|
||||||
|
|
||||||
void Video::HostPresent()
|
void Video::WaitOnSwapChain()
|
||||||
|
{
|
||||||
|
if (g_pendingWaitOnSwapChain)
|
||||||
|
{
|
||||||
|
if (g_swapChainValid)
|
||||||
|
g_swapChain->wait();
|
||||||
|
|
||||||
|
g_pendingWaitOnSwapChain = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool g_shouldPrecompilePipelines;
|
||||||
|
static std::atomic<bool> g_executedCommandList;
|
||||||
|
|
||||||
|
void Video::Present()
|
||||||
{
|
{
|
||||||
WaitForRenderThread();
|
|
||||||
DrawImGui();
|
DrawImGui();
|
||||||
|
|
||||||
g_pendingRenderThread.store(true);
|
|
||||||
|
|
||||||
RenderCommand cmd;
|
RenderCommand cmd;
|
||||||
cmd.type = RenderCommandType::Present;
|
cmd.type = RenderCommandType::ExecuteCommandList;
|
||||||
g_renderQueue.enqueue(cmd);
|
g_renderQueue.enqueue(cmd);
|
||||||
|
|
||||||
// All the shaders are available at this point. We can precompile embedded PSOs then.
|
// All the shaders are available at this point. We can precompile embedded PSOs then.
|
||||||
|
@ -2187,6 +2214,64 @@ void Video::HostPresent()
|
||||||
|
|
||||||
g_shouldPrecompilePipelines = false;
|
g_shouldPrecompilePipelines = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_executedCommandList.wait(false);
|
||||||
|
g_executedCommandList = false;
|
||||||
|
|
||||||
|
if (g_swapChainValid)
|
||||||
|
{
|
||||||
|
if (g_pendingWaitOnSwapChain)
|
||||||
|
g_swapChain->wait(); // Never gonna happen outside loading threads as explained above.
|
||||||
|
|
||||||
|
RenderCommandSemaphore* signalSemaphores[] = { g_renderSemaphores[g_frame].get() };
|
||||||
|
g_swapChainValid = g_swapChain->present(g_backBufferIndex, signalSemaphores, std::size(signalSemaphores));
|
||||||
|
}
|
||||||
|
|
||||||
|
g_pendingWaitOnSwapChain = true;
|
||||||
|
|
||||||
|
g_frame = g_nextFrame;
|
||||||
|
g_nextFrame = (g_frame + 1) % NUM_FRAMES;
|
||||||
|
|
||||||
|
if (g_commandListStates[g_frame])
|
||||||
|
{
|
||||||
|
g_queue->waitForCommandFence(g_commandFences[g_frame].get());
|
||||||
|
g_commandListStates[g_frame] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_dirtyStates = DirtyStates(true);
|
||||||
|
g_uploadAllocators[g_frame].reset();
|
||||||
|
g_triangleFanIndexData.reset();
|
||||||
|
g_quadIndexData.reset();
|
||||||
|
|
||||||
|
CheckSwapChain();
|
||||||
|
|
||||||
|
cmd.type = RenderCommandType::BeginCommandList;
|
||||||
|
g_renderQueue.enqueue(cmd);
|
||||||
|
|
||||||
|
if (Config::FPS >= FPS_MIN && Config::FPS < FPS_MAX)
|
||||||
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
static std::chrono::steady_clock::time_point s_next;
|
||||||
|
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
if (now < s_next)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::floor<std::chrono::milliseconds>(s_next - now - 2ms));
|
||||||
|
|
||||||
|
while ((now = std::chrono::steady_clock::now()) < s_next)
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_next = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_next += 1000000000ns / Config::FPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_presentProfiler.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Video::StartPipelinePrecompilation()
|
void Video::StartPipelinePrecompilation()
|
||||||
|
@ -2194,11 +2279,6 @@ void Video::StartPipelinePrecompilation()
|
||||||
g_shouldPrecompilePipelines = true;
|
g_shouldPrecompilePipelines = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GuestPresent()
|
|
||||||
{
|
|
||||||
Video::HostPresent();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SetRootDescriptor(const UploadAllocation& allocation, size_t index)
|
static void SetRootDescriptor(const UploadAllocation& allocation, size_t index)
|
||||||
{
|
{
|
||||||
auto& commandList = g_commandLists[g_frame];
|
auto& commandList = g_commandLists[g_frame];
|
||||||
|
@ -2209,7 +2289,7 @@ static void SetRootDescriptor(const UploadAllocation& allocation, size_t index)
|
||||||
commandList->setGraphicsRootDescriptor(allocation.buffer->at(allocation.offset), index);
|
commandList->setGraphicsRootDescriptor(allocation.buffer->at(allocation.offset), index);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ProcPresent(const RenderCommand& cmd)
|
static void ProcExecuteCommandList(const RenderCommand& cmd)
|
||||||
{
|
{
|
||||||
if (g_swapChainValid)
|
if (g_swapChainValid)
|
||||||
{
|
{
|
||||||
|
@ -2292,8 +2372,6 @@ static void ProcPresent(const RenderCommand& cmd)
|
||||||
waitSemaphores, std::size(waitSemaphores),
|
waitSemaphores, std::size(waitSemaphores),
|
||||||
signalSemaphores, std::size(signalSemaphores),
|
signalSemaphores, std::size(signalSemaphores),
|
||||||
g_commandFences[g_frame].get());
|
g_commandFences[g_frame].get());
|
||||||
|
|
||||||
g_swapChainValid = g_swapChain->present(g_backBufferIndex, signalSemaphores, std::size(signalSemaphores));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -2302,25 +2380,14 @@ static void ProcPresent(const RenderCommand& cmd)
|
||||||
|
|
||||||
g_commandListStates[g_frame] = true;
|
g_commandListStates[g_frame] = true;
|
||||||
|
|
||||||
g_frame = g_nextFrame;
|
g_executedCommandList = true;
|
||||||
g_nextFrame = (g_frame + 1) % NUM_FRAMES;
|
g_executedCommandList.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
if (g_commandListStates[g_frame])
|
static void ProcBeginCommandList(const RenderCommand& cmd)
|
||||||
{
|
{
|
||||||
g_queue->waitForCommandFence(g_commandFences[g_frame].get());
|
|
||||||
g_commandListStates[g_frame] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_dirtyStates = DirtyStates(true);
|
|
||||||
g_uploadAllocators[g_frame].reset();
|
|
||||||
DestructTempResources();
|
DestructTempResources();
|
||||||
g_triangleFanIndexData.reset();
|
|
||||||
g_quadIndexData.reset();
|
|
||||||
|
|
||||||
BeginCommandList();
|
BeginCommandList();
|
||||||
|
|
||||||
g_pendingRenderThread.store(false);
|
|
||||||
g_pendingRenderThread.notify_all();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static GuestSurface* GetBackBuffer()
|
static GuestSurface* GetBackBuffer()
|
||||||
|
@ -3387,8 +3454,6 @@ static void FlushRenderStateForMainThread(GuestDevice* device, LocalRenderComman
|
||||||
|
|
||||||
if (g_dirtyStates.vertexShaderConstants || device->dirtyFlags[0] != 0)
|
if (g_dirtyStates.vertexShaderConstants || device->dirtyFlags[0] != 0)
|
||||||
{
|
{
|
||||||
WaitForRenderThread();
|
|
||||||
|
|
||||||
auto& cmd = queue.enqueue();
|
auto& cmd = queue.enqueue();
|
||||||
cmd.type = RenderCommandType::SetVertexShaderConstants;
|
cmd.type = RenderCommandType::SetVertexShaderConstants;
|
||||||
cmd.setVertexShaderConstants.allocation = g_uploadAllocators[g_frame].allocate<true>(device->vertexShaderFloatConstants, 0x1000, 0x100);
|
cmd.setVertexShaderConstants.allocation = g_uploadAllocators[g_frame].allocate<true>(device->vertexShaderFloatConstants, 0x1000, 0x100);
|
||||||
|
@ -3398,8 +3463,6 @@ static void FlushRenderStateForMainThread(GuestDevice* device, LocalRenderComman
|
||||||
|
|
||||||
if (g_dirtyStates.pixelShaderConstants || device->dirtyFlags[1] != 0)
|
if (g_dirtyStates.pixelShaderConstants || device->dirtyFlags[1] != 0)
|
||||||
{
|
{
|
||||||
WaitForRenderThread();
|
|
||||||
|
|
||||||
auto& cmd = queue.enqueue();
|
auto& cmd = queue.enqueue();
|
||||||
cmd.type = RenderCommandType::SetPixelShaderConstants;
|
cmd.type = RenderCommandType::SetPixelShaderConstants;
|
||||||
cmd.setPixelShaderConstants.allocation = g_uploadAllocators[g_frame].allocate<true>(device->pixelShaderFloatConstants, 0xE00, 0x100);
|
cmd.setPixelShaderConstants.allocation = g_uploadAllocators[g_frame].allocate<true>(device->pixelShaderFloatConstants, 0xE00, 0x100);
|
||||||
|
@ -3640,7 +3703,6 @@ static void DrawPrimitiveUP(GuestDevice* device, uint32_t primitiveType, uint32_
|
||||||
{
|
{
|
||||||
LocalRenderCommandQueue queue;
|
LocalRenderCommandQueue queue;
|
||||||
FlushRenderStateForMainThread(device, queue);
|
FlushRenderStateForMainThread(device, queue);
|
||||||
WaitForRenderThread();
|
|
||||||
|
|
||||||
auto& cmd = queue.enqueue();
|
auto& cmd = queue.enqueue();
|
||||||
cmd.type = RenderCommandType::DrawPrimitiveUP;
|
cmd.type = RenderCommandType::DrawPrimitiveUP;
|
||||||
|
@ -4191,7 +4253,8 @@ static std::thread g_renderThread([]
|
||||||
case RenderCommandType::UnlockBuffer16: ProcUnlockBuffer16(cmd); break;
|
case RenderCommandType::UnlockBuffer16: ProcUnlockBuffer16(cmd); break;
|
||||||
case RenderCommandType::UnlockBuffer32: ProcUnlockBuffer32(cmd); break;
|
case RenderCommandType::UnlockBuffer32: ProcUnlockBuffer32(cmd); break;
|
||||||
case RenderCommandType::DrawImGui: ProcDrawImGui(cmd); break;
|
case RenderCommandType::DrawImGui: ProcDrawImGui(cmd); break;
|
||||||
case RenderCommandType::Present: ProcPresent(cmd); break;
|
case RenderCommandType::ExecuteCommandList: ProcExecuteCommandList(cmd); break;
|
||||||
|
case RenderCommandType::BeginCommandList: ProcBeginCommandList(cmd); break;
|
||||||
case RenderCommandType::StretchRect: ProcStretchRect(cmd); break;
|
case RenderCommandType::StretchRect: ProcStretchRect(cmd); break;
|
||||||
case RenderCommandType::SetRenderTarget: ProcSetRenderTarget(cmd); break;
|
case RenderCommandType::SetRenderTarget: ProcSetRenderTarget(cmd); break;
|
||||||
case RenderCommandType::SetDepthStencilSurface: ProcSetDepthStencilSurface(cmd); break;
|
case RenderCommandType::SetDepthStencilSurface: ProcSetDepthStencilSurface(cmd); break;
|
||||||
|
@ -6025,7 +6088,7 @@ GUEST_FUNCTION_HOOK(sub_82BE96F0, GetSurfaceDesc);
|
||||||
GUEST_FUNCTION_HOOK(sub_82BE04B0, GetVertexDeclaration);
|
GUEST_FUNCTION_HOOK(sub_82BE04B0, GetVertexDeclaration);
|
||||||
GUEST_FUNCTION_HOOK(sub_82BE0530, HashVertexDeclaration);
|
GUEST_FUNCTION_HOOK(sub_82BE0530, HashVertexDeclaration);
|
||||||
|
|
||||||
GUEST_FUNCTION_HOOK(sub_82BDA8C0, GuestPresent);
|
GUEST_FUNCTION_HOOK(sub_82BDA8C0, Video::Present);
|
||||||
GUEST_FUNCTION_HOOK(sub_82BDD330, GetBackBuffer);
|
GUEST_FUNCTION_HOOK(sub_82BDD330, GetBackBuffer);
|
||||||
|
|
||||||
GUEST_FUNCTION_HOOK(sub_82BE9498, CreateTexture);
|
GUEST_FUNCTION_HOOK(sub_82BE9498, CreateTexture);
|
||||||
|
|
|
@ -15,7 +15,8 @@ using namespace plume;
|
||||||
struct Video
|
struct Video
|
||||||
{
|
{
|
||||||
static void CreateHostDevice(const char *sdlVideoDriver);
|
static void CreateHostDevice(const char *sdlVideoDriver);
|
||||||
static void HostPresent();
|
static void WaitOnSwapChain();
|
||||||
|
static void Present();
|
||||||
static void StartPipelinePrecompilation();
|
static void StartPipelinePrecompilation();
|
||||||
static void WaitForGPU();
|
static void WaitForGPU();
|
||||||
};
|
};
|
||||||
|
|
|
@ -79,30 +79,6 @@ void Camera2DSlopeLerpFixMidAsmHook(PPCRegister& t, PPCRegister& deltaTime)
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
static std::chrono::steady_clock::time_point g_next;
|
|
||||||
|
|
||||||
void ApplicationUpdateMidAsmHook()
|
|
||||||
{
|
|
||||||
if (Config::FPS >= FPS_MIN && Config::FPS < FPS_MAX)
|
|
||||||
{
|
|
||||||
auto now = std::chrono::steady_clock::now();
|
|
||||||
|
|
||||||
if (now < g_next)
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for(std::chrono::floor<std::chrono::milliseconds>(g_next - now - 2ms));
|
|
||||||
|
|
||||||
while ((now = std::chrono::steady_clock::now()) < g_next)
|
|
||||||
std::this_thread::yield();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
g_next = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_next += 1000000000ns / Config::FPS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::chrono::steady_clock::time_point g_prev;
|
static std::chrono::steady_clock::time_point g_prev;
|
||||||
|
|
||||||
bool LoadingUpdateMidAsmHook(PPCRegister& r31)
|
bool LoadingUpdateMidAsmHook(PPCRegister& r31)
|
||||||
|
|
|
@ -1474,10 +1474,11 @@ bool InstallerWizard::Run(std::filesystem::path installPath, bool skipGame)
|
||||||
|
|
||||||
while (s_isVisible)
|
while (s_isVisible)
|
||||||
{
|
{
|
||||||
|
Video::WaitOnSwapChain();
|
||||||
SDL_PumpEvents();
|
SDL_PumpEvents();
|
||||||
SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
|
SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
|
||||||
GameWindow::Update();
|
GameWindow::Update();
|
||||||
Video::HostPresent();
|
Video::Present();
|
||||||
}
|
}
|
||||||
|
|
||||||
GameWindow::SetFullscreenCursorVisibility(false);
|
GameWindow::SetFullscreenCursorVisibility(false);
|
||||||
|
|
|
@ -647,10 +647,11 @@ public:
|
||||||
CONFIG_DEFINE_LOCALISED("Video", bool, VSync, true);
|
CONFIG_DEFINE_LOCALISED("Video", bool, VSync, true);
|
||||||
CONFIG_DEFINE_ENUM("Video", ETripleBuffering, TripleBuffering, ETripleBuffering::Auto);
|
CONFIG_DEFINE_ENUM("Video", ETripleBuffering, TripleBuffering, ETripleBuffering::Auto);
|
||||||
CONFIG_DEFINE_LOCALISED("Video", int32_t, FPS, 60);
|
CONFIG_DEFINE_LOCALISED("Video", int32_t, FPS, 60);
|
||||||
|
CONFIG_DEFINE("Video", uint32_t, MaxFrameLatency, 2);
|
||||||
CONFIG_DEFINE_LOCALISED("Video", float, Brightness, 0.5f);
|
CONFIG_DEFINE_LOCALISED("Video", float, Brightness, 0.5f);
|
||||||
CONFIG_DEFINE_ENUM_LOCALISED("Video", EAntiAliasing, AntiAliasing, EAntiAliasing::MSAA4x);
|
CONFIG_DEFINE_ENUM_LOCALISED("Video", EAntiAliasing, AntiAliasing, EAntiAliasing::MSAA4x);
|
||||||
CONFIG_DEFINE_LOCALISED("Video", bool, TransparencyAntiAliasing, true);
|
CONFIG_DEFINE_LOCALISED("Video", bool, TransparencyAntiAliasing, true);
|
||||||
CONFIG_DEFINE("Video", size_t, AnisotropicFiltering, 16);
|
CONFIG_DEFINE("Video", uint32_t, AnisotropicFiltering, 16);
|
||||||
CONFIG_DEFINE_ENUM_LOCALISED("Video", EShadowResolution, ShadowResolution, EShadowResolution::x4096);
|
CONFIG_DEFINE_ENUM_LOCALISED("Video", EShadowResolution, ShadowResolution, EShadowResolution::x4096);
|
||||||
CONFIG_DEFINE_ENUM_LOCALISED("Video", EGITextureFiltering, GITextureFiltering, EGITextureFiltering::Bicubic);
|
CONFIG_DEFINE_ENUM_LOCALISED("Video", EGITextureFiltering, GITextureFiltering, EGITextureFiltering::Bicubic);
|
||||||
CONFIG_DEFINE_ENUM("Video", EDepthOfFieldQuality, DepthOfFieldQuality, EDepthOfFieldQuality::Auto);
|
CONFIG_DEFINE_ENUM("Video", EDepthOfFieldQuality, DepthOfFieldQuality, EDepthOfFieldQuality::Auto);
|
||||||
|
|
|
@ -584,13 +584,9 @@ name = "PostureDPadSupportMidAsmHook"
|
||||||
address = 0x823CDA2C
|
address = 0x823CDA2C
|
||||||
registers = ["r3"]
|
registers = ["r3"]
|
||||||
|
|
||||||
[[midasm_hook]]
|
|
||||||
name = "ApplicationUpdateMidAsmHook"
|
|
||||||
address = 0x822C0EC8
|
|
||||||
|
|
||||||
[[midasm_hook]]
|
[[midasm_hook]]
|
||||||
name = "LoadingUpdateMidAsmHook"
|
name = "LoadingUpdateMidAsmHook"
|
||||||
address = 0x825360C8
|
address = 0x825360C8
|
||||||
registers = ["r31"]
|
registers = ["r31"]
|
||||||
jump_address_on_true = 0x825360C8
|
jump_address_on_true = 0x825360C8
|
||||||
jump_address_on_false = 0x82536140
|
jump_address_on_false = 0x82536140
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue