Compare commits

...

11 commits

Author SHA1 Message Date
Joshua de Reeper
eb1945e826 review changes 2025-01-30 12:31:43 +01:00
Joshua de Reeper
3db85ce0e5 fix typo 2025-01-30 12:18:58 +01:00
Joshua de Reeper
8368875fc0 save config properly 2025-01-30 12:18:58 +01:00
Joshua de Reeper
c796f707f4 make portal audio a setting 2025-01-30 12:18:58 +01:00
Joshua de Reeper
b4ec46ee0d Play portal audio via Cemu output 2025-01-30 12:18:58 +01:00
Exzap
ec2d7c086a coreinit: Clean up time functions
Some checks failed
Generate translation template / generate-pot (push) Failing after 34s
Build check / build (push) Failing after 40s
2025-01-30 03:49:17 +01:00
Exzap
c714e8cb6b coreinit: Time to tick conversion is unsigned
The result is treated as signed in most cases, but the calculation uses unsigned arithmetic.

As a concrete example where this matters, DS VC passes -1 (2^64-1) to OSWaitEventWithTimeout which internally causes an overflow. But only with unsigned arithmetic this will result in a large positive number that behaves like the intended infinite timeout. With signed arithmetic the result is negative and the events will timeout immediately.
2025-01-30 03:32:24 +01:00
goeiecool9999
e834515f43
Vulkan: Improve post-shutdown cleanup and minor improvements (#1401) 2025-01-23 21:20:03 +01:00
Exzap
4f9eea07e0 CI: Update action version 2025-01-23 21:06:07 +01:00
goeiecool9999
372c314f06 fix building with fmt11 and GCC 2025-01-23 21:03:11 +01:00
Exzap
5bd253a1f8 Revert "Fix building against fmt 11.1.0 (#1474)"
Reverting commit 4ac65159ef because game profile enums use the stringifying formatters from config.h and are not supposed to store raw integers
2025-01-23 17:33:06 +01:00
42 changed files with 498 additions and 226 deletions

View file

@ -35,7 +35,7 @@ jobs:
-o cemu.pot -o cemu.pot
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: POT file name: POT file
path: ./cemu.pot path: ./cemu.pot

View file

@ -140,14 +140,14 @@ bool gameProfile_loadEnumOption(IniParser& iniParser, const char* optionName, T&
for(const T& v : T()) for(const T& v : T())
{ {
// test integer option // test integer option
if (boost::iequals(fmt::format("{}", static_cast<typename std::underlying_type<T>::type>(v)), *option_value)) if (boost::iequals(fmt::format("{}", fmt::underlying(v)), *option_value))
{ {
option = v; option = v;
return true; return true;
} }
// test enum name // test enum name
if(boost::iequals(fmt::format("{}", fmt::underlying(v)), *option_value)) if(boost::iequals(fmt::format("{}", v), *option_value))
{ {
option = v; option = v;
return true; return true;

View file

@ -114,13 +114,13 @@ void* ATTR_MS_ABI PPCRecompiler_virtualHLE(PPCInterpreter_t* hCPU, uint32 hleFun
void ATTR_MS_ABI PPCRecompiler_getTBL(PPCInterpreter_t* hCPU, uint32 gprIndex) void ATTR_MS_ABI PPCRecompiler_getTBL(PPCInterpreter_t* hCPU, uint32 gprIndex)
{ {
uint64 coreTime = coreinit::coreinit_getTimerTick(); uint64 coreTime = coreinit::OSGetSystemTime();
hCPU->gpr[gprIndex] = (uint32)(coreTime&0xFFFFFFFF); hCPU->gpr[gprIndex] = (uint32)(coreTime&0xFFFFFFFF);
} }
void ATTR_MS_ABI PPCRecompiler_getTBU(PPCInterpreter_t* hCPU, uint32 gprIndex) void ATTR_MS_ABI PPCRecompiler_getTBU(PPCInterpreter_t* hCPU, uint32 gprIndex)
{ {
uint64 coreTime = coreinit::coreinit_getTimerTick(); uint64 coreTime = coreinit::OSGetSystemTime();
hCPU->gpr[gprIndex] = (uint32)((coreTime>>32)&0xFFFFFFFF); hCPU->gpr[gprIndex] = (uint32)((coreTime>>32)&0xFFFFFFFF);
} }

View file

@ -799,7 +799,7 @@ LatteCMDPtr LatteCP_itHLESampleTimer(LatteCMDPtr cmd, uint32 nWords)
{ {
cemu_assert_debug(nWords == 1); cemu_assert_debug(nWords == 1);
MPTR timerMPTR = (MPTR)LatteReadCMD(); MPTR timerMPTR = (MPTR)LatteReadCMD();
memory_writeU64(timerMPTR, coreinit::coreinit_getTimerTick()); memory_writeU64(timerMPTR, coreinit::OSGetSystemTime());
return cmd; return cmd;
} }

View file

@ -209,7 +209,7 @@ class BootSoundPlayer
try try
{ {
bootSndAudioDev = IAudioAPI::CreateDeviceFromConfig(true, sampleRate, nChannels, samplesPerBlock, bitsPerSample); bootSndAudioDev = IAudioAPI::CreateDeviceFromConfig(IAudioAPI::AudioType::TV, sampleRate, nChannels, samplesPerBlock, bitsPerSample);
if(!bootSndAudioDev) if(!bootSndAudioDev)
return; return;
} }
@ -485,6 +485,9 @@ void LatteShaderCache_Load()
g_renderer->DeleteTexture(g_shaderCacheLoaderState.textureDRCId); g_renderer->DeleteTexture(g_shaderCacheLoaderState.textureDRCId);
g_bootSndPlayer.FadeOutSound(); g_bootSndPlayer.FadeOutSound();
if(Latte_GetStopSignal())
LatteThread_Exit();
} }
void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateFunc, bool isPipelines) void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateFunc, bool isPipelines)
@ -625,8 +628,6 @@ void LatteShaderCache_LoadVulkanPipelineCache(uint64 cacheTitleId)
g_shaderCacheLoaderState.loadedPipelines = 0; g_shaderCacheLoaderState.loadedPipelines = 0;
LatteShaderCache_ShowProgress(LatteShaderCache_updatePipelineLoadingProgress, true); LatteShaderCache_ShowProgress(LatteShaderCache_updatePipelineLoadingProgress, true);
pipelineCache.EndLoading(); pipelineCache.EndLoading();
if(Latte_GetStopSignal())
LatteThread_Exit();
} }
bool LatteShaderCache_updatePipelineLoadingProgress() bool LatteShaderCache_updatePipelineLoadingProgress()

View file

@ -257,6 +257,7 @@ void LatteThread_Exit()
LatteSHRC_UnloadAll(); LatteSHRC_UnloadAll();
// close disk cache // close disk cache
LatteShaderCache_Close(); LatteShaderCache_Close();
RendererOutputShader::ShutdownStatic();
// destroy renderer but make sure that g_renderer remains valid until the destructor has finished // destroy renderer but make sure that g_renderer remains valid until the destructor has finished
if (g_renderer) if (g_renderer)
{ {

View file

@ -118,8 +118,8 @@ RendererOutputShader::RendererOutputShader(const std::string& vertex_source, con
{ {
auto finalFragmentSrc = PrependFragmentPreamble(fragment_source); auto finalFragmentSrc = PrependFragmentPreamble(fragment_source);
m_vertex_shader = g_renderer->shader_create(RendererShader::ShaderType::kVertex, 0, 0, vertex_source, false, false); m_vertex_shader.reset(g_renderer->shader_create(RendererShader::ShaderType::kVertex, 0, 0, vertex_source, false, false));
m_fragment_shader = g_renderer->shader_create(RendererShader::ShaderType::kFragment, 0, 0, finalFragmentSrc, false, false); m_fragment_shader.reset(g_renderer->shader_create(RendererShader::ShaderType::kFragment, 0, 0, finalFragmentSrc, false, false));
m_vertex_shader->PreponeCompilation(true); m_vertex_shader->PreponeCompilation(true);
m_fragment_shader->PreponeCompilation(true); m_fragment_shader->PreponeCompilation(true);
@ -169,8 +169,8 @@ void RendererOutputShader::SetUniformParameters(const LatteTextureView& texture_
shader->SetUniform2fv(locations.m_loc_outputResolution, res, 1); shader->SetUniform2fv(locations.m_loc_outputResolution, res, 1);
} }
}; };
setUniforms(m_vertex_shader, m_uniformLocations[0]); setUniforms(m_vertex_shader.get(), m_uniformLocations[0]);
setUniforms(m_fragment_shader, m_uniformLocations[1]); setUniforms(m_fragment_shader.get(), m_uniformLocations[1]);
} }
RendererOutputShader* RendererOutputShader::s_copy_shader; RendererOutputShader* RendererOutputShader::s_copy_shader;
@ -325,3 +325,15 @@ void RendererOutputShader::InitializeStatic()
s_hermit_shader = new RendererOutputShader(vertex_source, s_hermite_shader_source); s_hermit_shader = new RendererOutputShader(vertex_source, s_hermite_shader_source);
s_hermit_shader_ud = new RendererOutputShader(vertex_source_ud, s_hermite_shader_source); s_hermit_shader_ud = new RendererOutputShader(vertex_source_ud, s_hermite_shader_source);
} }
void RendererOutputShader::ShutdownStatic()
{
delete s_copy_shader;
delete s_copy_shader_ud;
delete s_bicubic_shader;
delete s_bicubic_shader_ud;
delete s_hermit_shader;
delete s_hermit_shader_ud;
}

View file

@ -21,15 +21,16 @@ public:
RendererShader* GetVertexShader() const RendererShader* GetVertexShader() const
{ {
return m_vertex_shader; return m_vertex_shader.get();
} }
RendererShader* GetFragmentShader() const RendererShader* GetFragmentShader() const
{ {
return m_fragment_shader; return m_fragment_shader.get();
} }
static void InitializeStatic(); static void InitializeStatic();
static void ShutdownStatic();
static RendererOutputShader* s_copy_shader; static RendererOutputShader* s_copy_shader;
static RendererOutputShader* s_copy_shader_ud; static RendererOutputShader* s_copy_shader_ud;
@ -46,8 +47,8 @@ public:
static std::string PrependFragmentPreamble(const std::string& shaderSrc); static std::string PrependFragmentPreamble(const std::string& shaderSrc);
protected: protected:
RendererShader* m_vertex_shader; std::unique_ptr<RendererShader> m_vertex_shader;
RendererShader* m_fragment_shader; std::unique_ptr<RendererShader> m_fragment_shader;
struct UniformLocations struct UniformLocations
{ {

View file

@ -211,6 +211,9 @@ RendererShaderVk::~RendererShaderVk()
{ {
while (!list_pipelineInfo.empty()) while (!list_pipelineInfo.empty())
delete list_pipelineInfo[0]; delete list_pipelineInfo[0];
VkDevice vkDev = VulkanRenderer::GetInstance()->GetLogicalDevice();
vkDestroyShaderModule(vkDev, m_shader_module, nullptr);
} }
void RendererShaderVk::Init() void RendererShaderVk::Init()

View file

@ -60,7 +60,7 @@ void SwapchainInfoVk::Create()
VkAttachmentDescription colorAttachment = {}; VkAttachmentDescription colorAttachment = {};
colorAttachment.format = m_surfaceFormat.format; colorAttachment.format = m_surfaceFormat.format;
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;

View file

@ -70,6 +70,7 @@ struct SwapchainInfoVk
VkSurfaceFormatKHR m_surfaceFormat{}; VkSurfaceFormatKHR m_surfaceFormat{};
VkSwapchainKHR m_swapchain{}; VkSwapchainKHR m_swapchain{};
Vector2i m_desiredExtent{}; Vector2i m_desiredExtent{};
VkExtent2D m_actualExtent{};
uint32 swapchainImageIndex = (uint32)-1; uint32 swapchainImageIndex = (uint32)-1;
uint64 m_presentId = 1; uint64 m_presentId = 1;
uint64 m_queueDepth = 0; // number of frames with pending presentation requests uint64 m_queueDepth = 0; // number of frames with pending presentation requests
@ -92,5 +93,4 @@ private:
VkSemaphore m_currentSemaphore = VK_NULL_HANDLE; VkSemaphore m_currentSemaphore = VK_NULL_HANDLE;
std::array<uint32, 2> m_swapchainQueueFamilyIndices; std::array<uint32, 2> m_swapchainQueueFamilyIndices;
VkExtent2D m_actualExtent{};
}; };

View file

@ -4,6 +4,14 @@
/* VKRSynchronizedMemoryBuffer */ /* VKRSynchronizedMemoryBuffer */
VKRSynchronizedRingAllocator::~VKRSynchronizedRingAllocator()
{
for(auto& buf : m_buffers)
{
m_vkrMemMgr->DeleteBuffer(buf.vk_buffer, buf.vk_mem);
}
}
void VKRSynchronizedRingAllocator::addUploadBufferSyncPoint(AllocatorBuffer_t& buffer, uint32 offset) void VKRSynchronizedRingAllocator::addUploadBufferSyncPoint(AllocatorBuffer_t& buffer, uint32 offset)
{ {
auto cmdBufferId = m_vkr->GetCurrentCommandBufferId(); auto cmdBufferId = m_vkr->GetCurrentCommandBufferId();
@ -233,6 +241,15 @@ void VKRSynchronizedHeapAllocator::GetStats(uint32& numBuffers, size_t& totalBuf
/* VkTextureChunkedHeap */ /* VkTextureChunkedHeap */
VkTextureChunkedHeap::~VkTextureChunkedHeap()
{
VkDevice device = VulkanRenderer::GetInstance()->GetLogicalDevice();
for (auto& i : m_list_chunkInfo)
{
vkFreeMemory(device, i.mem, nullptr);
}
}
uint32 VkTextureChunkedHeap::allocateNewChunk(uint32 chunkIndex, uint32 minimumAllocationSize) uint32 VkTextureChunkedHeap::allocateNewChunk(uint32 chunkIndex, uint32 minimumAllocationSize)
{ {
cemu_assert_debug(m_list_chunkInfo.size() == chunkIndex); cemu_assert_debug(m_list_chunkInfo.size() == chunkIndex);
@ -310,11 +327,11 @@ VKRBuffer* VKRBuffer::Create(VKR_BUFFER_TYPE bufferType, size_t bufferSize, VkMe
VkDeviceMemory bufferMemory; VkDeviceMemory bufferMemory;
bool allocSuccess; bool allocSuccess;
if (bufferType == VKR_BUFFER_TYPE::STAGING) if (bufferType == VKR_BUFFER_TYPE::STAGING)
allocSuccess = memMgr->CreateBuffer2(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, properties, buffer, bufferMemory); allocSuccess = memMgr->CreateBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, properties, buffer, bufferMemory);
else if (bufferType == VKR_BUFFER_TYPE::INDEX) else if (bufferType == VKR_BUFFER_TYPE::INDEX)
allocSuccess = memMgr->CreateBuffer2(bufferSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, properties, buffer, bufferMemory); allocSuccess = memMgr->CreateBuffer(bufferSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, properties, buffer, bufferMemory);
else if (bufferType == VKR_BUFFER_TYPE::STRIDE) else if (bufferType == VKR_BUFFER_TYPE::STRIDE)
allocSuccess = memMgr->CreateBuffer2(bufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, properties, buffer, bufferMemory); allocSuccess = memMgr->CreateBuffer(bufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, properties, buffer, bufferMemory);
else else
cemu_assert_debug(false); cemu_assert_debug(false);
if (!allocSuccess) if (!allocSuccess)
@ -363,28 +380,14 @@ uint32 VkBufferChunkedHeap::allocateNewChunk(uint32 chunkIndex, uint32 minimumAl
return allocationSize; return allocationSize;
} }
uint32_t VKRMemoryManager::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) const bool VKRMemoryManager::FindMemoryType(uint32 typeFilter, VkMemoryPropertyFlags properties, uint32& memoryIndex) const
{
VkPhysicalDeviceMemoryProperties memProperties;
vkGetPhysicalDeviceMemoryProperties(m_vkr->GetPhysicalDevice(), &memProperties);
for (uint32 i = 0; i < memProperties.memoryTypeCount; i++)
{
if ((typeFilter & (1 << i)) != 0 && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
return i;
}
m_vkr->UnrecoverableError(fmt::format("failed to find suitable memory type ({0:#08x} {1:#08x})", typeFilter, properties).c_str());
return 0;
}
bool VKRMemoryManager::FindMemoryType2(uint32 typeFilter, VkMemoryPropertyFlags properties, uint32& memoryIndex) const
{ {
VkPhysicalDeviceMemoryProperties memProperties; VkPhysicalDeviceMemoryProperties memProperties;
vkGetPhysicalDeviceMemoryProperties(m_vkr->GetPhysicalDevice(), &memProperties); vkGetPhysicalDeviceMemoryProperties(m_vkr->GetPhysicalDevice(), &memProperties);
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
{ {
if (typeFilter & (1 << i) && memProperties.memoryTypes[i].propertyFlags == properties) if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
{ {
memoryIndex = i; memoryIndex = i;
return true; return true;
@ -455,31 +458,7 @@ size_t VKRMemoryManager::GetTotalMemoryForBufferType(VkBufferUsageFlags usage, V
return total; return total;
} }
void VKRMemoryManager::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) const bool VKRMemoryManager::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) const
{
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.usage = usage;
bufferInfo.size = size;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (vkCreateBuffer(m_vkr->GetLogicalDevice(), &bufferInfo, nullptr, &buffer) != VK_SUCCESS)
m_vkr->UnrecoverableError("Failed to create buffer");
VkMemoryRequirements memRequirements;
vkGetBufferMemoryRequirements(m_vkr->GetLogicalDevice(), buffer, &memRequirements);
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, properties);
if (vkAllocateMemory(m_vkr->GetLogicalDevice(), &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS)
m_vkr->UnrecoverableError("Failed to allocate buffer memory");
if (vkBindBufferMemory(m_vkr->GetLogicalDevice(), buffer, bufferMemory, 0) != VK_SUCCESS)
m_vkr->UnrecoverableError("Failed to bind buffer memory");
}
bool VKRMemoryManager::CreateBuffer2(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) const
{ {
VkBufferCreateInfo bufferInfo{}; VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
@ -488,7 +467,7 @@ bool VKRMemoryManager::CreateBuffer2(VkDeviceSize size, VkBufferUsageFlags usage
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (vkCreateBuffer(m_vkr->GetLogicalDevice(), &bufferInfo, nullptr, &buffer) != VK_SUCCESS) if (vkCreateBuffer(m_vkr->GetLogicalDevice(), &bufferInfo, nullptr, &buffer) != VK_SUCCESS)
{ {
cemuLog_log(LogType::Force, "Failed to create buffer (CreateBuffer2)"); cemuLog_log(LogType::Force, "Failed to create buffer (CreateBuffer)");
return false; return false;
} }
@ -498,7 +477,7 @@ bool VKRMemoryManager::CreateBuffer2(VkDeviceSize size, VkBufferUsageFlags usage
VkMemoryAllocateInfo allocInfo{}; VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size; allocInfo.allocationSize = memRequirements.size;
if (!FindMemoryType2(memRequirements.memoryTypeBits, properties, allocInfo.memoryTypeIndex)) if (!FindMemoryType(memRequirements.memoryTypeBits, properties, allocInfo.memoryTypeIndex))
{ {
vkDestroyBuffer(m_vkr->GetLogicalDevice(), buffer, nullptr); vkDestroyBuffer(m_vkr->GetLogicalDevice(), buffer, nullptr);
return false; return false;
@ -511,7 +490,7 @@ bool VKRMemoryManager::CreateBuffer2(VkDeviceSize size, VkBufferUsageFlags usage
if (vkBindBufferMemory(m_vkr->GetLogicalDevice(), buffer, bufferMemory, 0) != VK_SUCCESS) if (vkBindBufferMemory(m_vkr->GetLogicalDevice(), buffer, bufferMemory, 0) != VK_SUCCESS)
{ {
vkDestroyBuffer(m_vkr->GetLogicalDevice(), buffer, nullptr); vkDestroyBuffer(m_vkr->GetLogicalDevice(), buffer, nullptr);
cemuLog_log(LogType::Force, "Failed to bind buffer (CreateBuffer2)"); cemuLog_log(LogType::Force, "Failed to bind buffer (CreateBuffer)");
return false; return false;
} }
return true; return true;
@ -533,7 +512,7 @@ bool VKRMemoryManager::CreateBufferFromHostMemory(void* hostPointer, VkDeviceSiz
if (vkCreateBuffer(m_vkr->GetLogicalDevice(), &bufferInfo, nullptr, &buffer) != VK_SUCCESS) if (vkCreateBuffer(m_vkr->GetLogicalDevice(), &bufferInfo, nullptr, &buffer) != VK_SUCCESS)
{ {
cemuLog_log(LogType::Force, "Failed to create buffer (CreateBuffer2)"); cemuLog_log(LogType::Force, "Failed to create buffer (CreateBuffer)");
return false; return false;
} }
@ -554,7 +533,7 @@ bool VKRMemoryManager::CreateBufferFromHostMemory(void* hostPointer, VkDeviceSiz
allocInfo.pNext = &importHostMem; allocInfo.pNext = &importHostMem;
if (!FindMemoryType2(memRequirements.memoryTypeBits, properties, allocInfo.memoryTypeIndex)) if (!FindMemoryType(memRequirements.memoryTypeBits, properties, allocInfo.memoryTypeIndex))
{ {
vkDestroyBuffer(m_vkr->GetLogicalDevice(), buffer, nullptr); vkDestroyBuffer(m_vkr->GetLogicalDevice(), buffer, nullptr);
return false; return false;
@ -598,7 +577,7 @@ VkImageMemAllocation* VKRMemoryManager::imageMemoryAllocate(VkImage image)
map_textureHeap.emplace(typeFilter, texHeap); map_textureHeap.emplace(typeFilter, texHeap);
} }
else else
texHeap = it->second; texHeap = it->second.get();
// alloc mem from heap // alloc mem from heap
uint32 allocationSize = (uint32)memRequirements.size; uint32 allocationSize = (uint32)memRequirements.size;

View file

@ -48,6 +48,7 @@ class VkTextureChunkedHeap : private ChunkedHeap<>
{ {
public: public:
VkTextureChunkedHeap(class VKRMemoryManager* memoryManager, uint32 typeFilter) : m_vkrMemoryManager(memoryManager), m_typeFilter(typeFilter) { }; VkTextureChunkedHeap(class VKRMemoryManager* memoryManager, uint32 typeFilter) : m_vkrMemoryManager(memoryManager), m_typeFilter(typeFilter) { };
~VkTextureChunkedHeap();
struct ChunkInfo struct ChunkInfo
{ {
@ -148,6 +149,7 @@ class VKRSynchronizedRingAllocator
public: public:
VKRSynchronizedRingAllocator(class VulkanRenderer* vkRenderer, class VKRMemoryManager* vkMemoryManager, VKR_BUFFER_TYPE bufferType, uint32 minimumBufferAllocSize) : m_vkr(vkRenderer), m_vkrMemMgr(vkMemoryManager), m_bufferType(bufferType), m_minimumBufferAllocSize(minimumBufferAllocSize) {}; VKRSynchronizedRingAllocator(class VulkanRenderer* vkRenderer, class VKRMemoryManager* vkMemoryManager, VKR_BUFFER_TYPE bufferType, uint32 minimumBufferAllocSize) : m_vkr(vkRenderer), m_vkrMemMgr(vkMemoryManager), m_bufferType(bufferType), m_minimumBufferAllocSize(minimumBufferAllocSize) {};
VKRSynchronizedRingAllocator(const VKRSynchronizedRingAllocator&) = delete; // disallow copy VKRSynchronizedRingAllocator(const VKRSynchronizedRingAllocator&) = delete; // disallow copy
~VKRSynchronizedRingAllocator();
struct BufferSyncPoint_t struct BufferSyncPoint_t
{ {
@ -256,7 +258,7 @@ public:
} }
// texture memory management // texture memory management
std::unordered_map<uint32, VkTextureChunkedHeap*> map_textureHeap; // one heap per memory type std::unordered_map<uint32, std::unique_ptr<VkTextureChunkedHeap>> map_textureHeap; // one heap per memory type
std::vector<uint8> m_textureUploadBuffer; std::vector<uint8> m_textureUploadBuffer;
// texture upload buffer // texture upload buffer
@ -286,9 +288,7 @@ public:
m_vertexStrideMetalBuffer.CleanupBuffer(latestFinishedCommandBufferId); m_vertexStrideMetalBuffer.CleanupBuffer(latestFinishedCommandBufferId);
} }
// memory helpers bool FindMemoryType(uint32 typeFilter, VkMemoryPropertyFlags properties, uint32& memoryIndex) const; // searches for exact properties. Can gracefully fail without throwing exception (returns false)
uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) const;
bool FindMemoryType2(uint32 typeFilter, VkMemoryPropertyFlags properties, uint32& memoryIndex) const; // searches for exact properties. Can gracefully fail without throwing exception (returns false)
std::vector<uint32> FindMemoryTypes(uint32_t typeFilter, VkMemoryPropertyFlags properties) const; std::vector<uint32> FindMemoryTypes(uint32_t typeFilter, VkMemoryPropertyFlags properties) const;
// image memory allocation // image memory allocation
@ -298,8 +298,7 @@ public:
// buffer management // buffer management
size_t GetTotalMemoryForBufferType(VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, size_t minimumBufferSize = 16 * 1024 * 1024); size_t GetTotalMemoryForBufferType(VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, size_t minimumBufferSize = 16 * 1024 * 1024);
void CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) const; bool CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) const; // same as CreateBuffer but doesn't throw exception on failure
bool CreateBuffer2(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) const; // same as CreateBuffer but doesn't throw exception on failure
bool CreateBufferFromHostMemory(void* hostPointer, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) const; bool CreateBufferFromHostMemory(void* hostPointer, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) const;
void DeleteBuffer(VkBuffer& buffer, VkDeviceMemory& deviceMem) const; void DeleteBuffer(VkBuffer& buffer, VkDeviceMemory& deviceMem) const;

View file

@ -165,6 +165,7 @@ VKFUNC_DEVICE(vkCmdDraw);
VKFUNC_DEVICE(vkCmdCopyBufferToImage); VKFUNC_DEVICE(vkCmdCopyBufferToImage);
VKFUNC_DEVICE(vkCmdCopyImageToBuffer); VKFUNC_DEVICE(vkCmdCopyImageToBuffer);
VKFUNC_DEVICE(vkCmdClearColorImage); VKFUNC_DEVICE(vkCmdClearColorImage);
VKFUNC_DEVICE(vkCmdClearAttachments);
VKFUNC_DEVICE(vkCmdBindIndexBuffer); VKFUNC_DEVICE(vkCmdBindIndexBuffer);
VKFUNC_DEVICE(vkCmdBindVertexBuffers); VKFUNC_DEVICE(vkCmdBindVertexBuffers);
VKFUNC_DEVICE(vkCmdDrawIndexed); VKFUNC_DEVICE(vkCmdDrawIndexed);
@ -198,6 +199,7 @@ VKFUNC_DEVICE(vkCmdEndTransformFeedbackEXT);
// query // query
VKFUNC_DEVICE(vkCreateQueryPool); VKFUNC_DEVICE(vkCreateQueryPool);
VKFUNC_DEVICE(vkDestroyQueryPool);
VKFUNC_DEVICE(vkCmdResetQueryPool); VKFUNC_DEVICE(vkCmdResetQueryPool);
VKFUNC_DEVICE(vkCmdBeginQuery); VKFUNC_DEVICE(vkCmdBeginQuery);
VKFUNC_DEVICE(vkCmdEndQuery); VKFUNC_DEVICE(vkCmdEndQuery);
@ -236,6 +238,7 @@ VKFUNC_DEVICE(vkAllocateDescriptorSets);
VKFUNC_DEVICE(vkFreeDescriptorSets); VKFUNC_DEVICE(vkFreeDescriptorSets);
VKFUNC_DEVICE(vkUpdateDescriptorSets); VKFUNC_DEVICE(vkUpdateDescriptorSets);
VKFUNC_DEVICE(vkCreateDescriptorPool); VKFUNC_DEVICE(vkCreateDescriptorPool);
VKFUNC_DEVICE(vkDestroyDescriptorPool);
VKFUNC_DEVICE(vkDestroyDescriptorSetLayout); VKFUNC_DEVICE(vkDestroyDescriptorSetLayout);
#undef VKFUNC_INIT #undef VKFUNC_INIT

View file

@ -439,7 +439,7 @@ VulkanRenderer::VulkanRenderer()
GetDeviceFeatures(); GetDeviceFeatures();
// init memory manager // init memory manager
memoryManager = new VKRMemoryManager(this); memoryManager.reset(new VKRMemoryManager(this));
try try
{ {
@ -577,15 +577,15 @@ VulkanRenderer::VulkanRenderer()
void* bufferPtr; void* bufferPtr;
// init ringbuffer for uniform vars // init ringbuffer for uniform vars
m_uniformVarBufferMemoryIsCoherent = false; m_uniformVarBufferMemoryIsCoherent = false;
if (memoryManager->CreateBuffer2(UNIFORMVAR_RINGBUFFER_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, m_uniformVarBuffer, m_uniformVarBufferMemory)) if (memoryManager->CreateBuffer(UNIFORMVAR_RINGBUFFER_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, m_uniformVarBuffer, m_uniformVarBufferMemory))
m_uniformVarBufferMemoryIsCoherent = true; m_uniformVarBufferMemoryIsCoherent = true;
else if (memoryManager->CreateBuffer2(UNIFORMVAR_RINGBUFFER_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, m_uniformVarBuffer, m_uniformVarBufferMemory)) else if (memoryManager->CreateBuffer(UNIFORMVAR_RINGBUFFER_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, m_uniformVarBuffer, m_uniformVarBufferMemory))
m_uniformVarBufferMemoryIsCoherent = true; // unified memory m_uniformVarBufferMemoryIsCoherent = true; // unified memory
else if (memoryManager->CreateBuffer2(UNIFORMVAR_RINGBUFFER_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, m_uniformVarBuffer, m_uniformVarBufferMemory)) else if (memoryManager->CreateBuffer(UNIFORMVAR_RINGBUFFER_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, m_uniformVarBuffer, m_uniformVarBufferMemory))
m_uniformVarBufferMemoryIsCoherent = true; m_uniformVarBufferMemoryIsCoherent = true;
else else
{ {
memoryManager->CreateBuffer2(UNIFORMVAR_RINGBUFFER_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, m_uniformVarBuffer, m_uniformVarBufferMemory); memoryManager->CreateBuffer(UNIFORMVAR_RINGBUFFER_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, m_uniformVarBuffer, m_uniformVarBufferMemory);
} }
if (!m_uniformVarBufferMemoryIsCoherent) if (!m_uniformVarBufferMemoryIsCoherent)
@ -628,6 +628,31 @@ VulkanRenderer::~VulkanRenderer()
m_pipeline_cache_semaphore.notify(); m_pipeline_cache_semaphore.notify();
m_pipeline_cache_save_thread.join(); m_pipeline_cache_save_thread.join();
vkDestroyPipelineCache(m_logicalDevice, m_pipeline_cache, nullptr);
if(!m_backbufferBlitDescriptorSetCache.empty())
{
std::vector<VkDescriptorSet> freeVector;
freeVector.reserve(m_backbufferBlitDescriptorSetCache.size());
std::transform(m_backbufferBlitDescriptorSetCache.begin(), m_backbufferBlitDescriptorSetCache.end(), std::back_inserter(freeVector), [](auto& i) {
return i.second;
});
vkFreeDescriptorSets(m_logicalDevice, m_descriptorPool, freeVector.size(), freeVector.data());
}
vkDestroyDescriptorPool(m_logicalDevice, m_descriptorPool, nullptr);
for(auto& i : m_backbufferBlitPipelineCache)
{
vkDestroyPipeline(m_logicalDevice, i.second, nullptr);
}
m_backbufferBlitPipelineCache = {};
if(m_occlusionQueries.queryPool != VK_NULL_HANDLE)
vkDestroyQueryPool(m_logicalDevice, m_occlusionQueries.queryPool, nullptr);
vkDestroyDescriptorSetLayout(m_logicalDevice, m_swapchainDescriptorSetLayout, nullptr);
// shut down imgui // shut down imgui
ImGui_ImplVulkan_Shutdown(); ImGui_ImplVulkan_Shutdown();
@ -640,10 +665,6 @@ VulkanRenderer::~VulkanRenderer()
memoryManager->DeleteBuffer(m_xfbRingBuffer, m_xfbRingBufferMemory); memoryManager->DeleteBuffer(m_xfbRingBuffer, m_xfbRingBufferMemory);
memoryManager->DeleteBuffer(m_occlusionQueries.bufferQueryResults, m_occlusionQueries.memoryQueryResults); memoryManager->DeleteBuffer(m_occlusionQueries.bufferQueryResults, m_occlusionQueries.memoryQueryResults);
memoryManager->DeleteBuffer(m_bufferCache, m_bufferCacheMemory); memoryManager->DeleteBuffer(m_bufferCache, m_bufferCacheMemory);
// texture memory
// todo
// upload buffers
// todo
m_padSwapchainInfo = nullptr; m_padSwapchainInfo = nullptr;
m_mainSwapchainInfo = nullptr; m_mainSwapchainInfo = nullptr;
@ -666,6 +687,12 @@ VulkanRenderer::~VulkanRenderer()
it = VK_NULL_HANDLE; it = VK_NULL_HANDLE;
} }
for(auto& sem : m_commandBufferSemaphores)
{
vkDestroySemaphore(m_logicalDevice, sem, nullptr);
sem = VK_NULL_HANDLE;
}
if (m_pipelineLayout != VK_NULL_HANDLE) if (m_pipelineLayout != VK_NULL_HANDLE)
vkDestroyPipelineLayout(m_logicalDevice, m_pipelineLayout, nullptr); vkDestroyPipelineLayout(m_logicalDevice, m_pipelineLayout, nullptr);
@ -681,8 +708,11 @@ VulkanRenderer::~VulkanRenderer()
vkDestroyDebugUtilsMessengerEXT(m_instance, m_debugCallback, nullptr); vkDestroyDebugUtilsMessengerEXT(m_instance, m_debugCallback, nullptr);
} }
while(!m_destructionQueue.empty())
ProcessDestructionQueue();
// destroy memory manager // destroy memory manager
delete memoryManager; memoryManager.reset();
// destroy instance, devices // destroy instance, devices
if (m_instance != VK_NULL_HANDLE) if (m_instance != VK_NULL_HANDLE)
@ -825,7 +855,14 @@ void VulkanRenderer::HandleScreenshotRequest(LatteTextureView* texView, bool pad
VkMemoryAllocateInfo allocInfo{}; VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size; allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex = memoryManager->FindMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); uint32 memIndex;
bool foundMemory = memoryManager->FindMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, memIndex);
if(!foundMemory)
{
cemuLog_log(LogType::Force, "Screenshot request failed due to incompatible vulkan memory types.");
return;
}
allocInfo.memoryTypeIndex = memIndex;
if (vkAllocateMemory(m_logicalDevice, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) if (vkAllocateMemory(m_logicalDevice, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS)
{ {
@ -1608,6 +1645,7 @@ void VulkanRenderer::Initialize()
void VulkanRenderer::Shutdown() void VulkanRenderer::Shutdown()
{ {
DeleteFontTextures();
Renderer::Shutdown(); Renderer::Shutdown();
SubmitCommandBuffer(); SubmitCommandBuffer();
WaitDeviceIdle(); WaitDeviceIdle();
@ -1808,7 +1846,6 @@ void VulkanRenderer::ImguiEnd()
vkCmdEndRenderPass(m_state.currentCommandBuffer); vkCmdEndRenderPass(m_state.currentCommandBuffer);
} }
std::vector<LatteTextureVk*> g_imgui_textures; // TODO manage better
ImTextureID VulkanRenderer::GenerateTexture(const std::vector<uint8>& data, const Vector2i& size) ImTextureID VulkanRenderer::GenerateTexture(const std::vector<uint8>& data, const Vector2i& size)
{ {
try try
@ -1838,6 +1875,7 @@ void VulkanRenderer::DeleteTexture(ImTextureID id)
void VulkanRenderer::DeleteFontTextures() void VulkanRenderer::DeleteFontTextures()
{ {
WaitDeviceIdle();
ImGui_ImplVulkan_DestroyFontsTexture(); ImGui_ImplVulkan_DestroyFontsTexture();
} }
@ -1876,7 +1914,7 @@ void VulkanRenderer::InitFirstCommandBuffer()
vkResetFences(m_logicalDevice, 1, &m_cmd_buffer_fences[m_commandBufferIndex]); vkResetFences(m_logicalDevice, 1, &m_cmd_buffer_fences[m_commandBufferIndex]);
VkCommandBufferBeginInfo beginInfo{}; VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(m_state.currentCommandBuffer, &beginInfo); vkBeginCommandBuffer(m_state.currentCommandBuffer, &beginInfo);
vkCmdSetViewport(m_state.currentCommandBuffer, 0, 1, &m_state.currentViewport); vkCmdSetViewport(m_state.currentCommandBuffer, 0, 1, &m_state.currentViewport);
@ -1998,7 +2036,7 @@ void VulkanRenderer::SubmitCommandBuffer(VkSemaphore signalSemaphore, VkSemaphor
VkCommandBufferBeginInfo beginInfo{}; VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(m_state.currentCommandBuffer, &beginInfo); vkBeginCommandBuffer(m_state.currentCommandBuffer, &beginInfo);
// make sure some states are set for this command buffer // make sure some states are set for this command buffer
@ -2519,9 +2557,8 @@ VkPipeline VulkanRenderer::backbufferBlit_createGraphicsPipeline(VkDescriptorSet
hash += (uint64)(chainInfo.m_usesSRGB); hash += (uint64)(chainInfo.m_usesSRGB);
hash += ((uint64)padView) << 1; hash += ((uint64)padView) << 1;
static std::unordered_map<uint64, VkPipeline> s_pipeline_cache; const auto it = m_backbufferBlitPipelineCache.find(hash);
const auto it = s_pipeline_cache.find(hash); if (it != m_backbufferBlitPipelineCache.cend())
if (it != s_pipeline_cache.cend())
return it->second; return it->second;
std::vector<VkPipelineShaderStageCreateInfo> shaderStages; std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
@ -2625,7 +2662,7 @@ VkPipeline VulkanRenderer::backbufferBlit_createGraphicsPipeline(VkDescriptorSet
throw std::runtime_error(fmt::format("Failed to create graphics pipeline: {}", result)); throw std::runtime_error(fmt::format("Failed to create graphics pipeline: {}", result));
} }
s_pipeline_cache[hash] = pipeline; m_backbufferBlitPipelineCache[hash] = pipeline;
m_pipeline_cache_semaphore.notify(); m_pipeline_cache_semaphore.notify();
return pipeline; return pipeline;
@ -2922,9 +2959,6 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu
LatteTextureViewVk* texViewVk = (LatteTextureViewVk*)texView; LatteTextureViewVk* texViewVk = (LatteTextureViewVk*)texView;
draw_endRenderPass(); draw_endRenderPass();
if (clearBackground)
ClearColorbuffer(padView);
// barrier for input texture // barrier for input texture
VkMemoryBarrier memoryBarrier{}; VkMemoryBarrier memoryBarrier{};
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
@ -2961,6 +2995,16 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu
vkCmdBeginRenderPass(m_state.currentCommandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); vkCmdBeginRenderPass(m_state.currentCommandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
if (clearBackground)
{
VkClearAttachment clearAttachment{};
clearAttachment.clearValue = {0,0,0,0};
clearAttachment.colorAttachment = 0;
clearAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
VkClearRect clearExtent = {{{0,0},chainInfo.m_actualExtent}, 0, 1};
vkCmdClearAttachments(m_state.currentCommandBuffer, 1, &clearAttachment, 1, &clearExtent);
}
vkCmdBindPipeline(m_state.currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); vkCmdBindPipeline(m_state.currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
m_state.currentPipeline = pipeline; m_state.currentPipeline = pipeline;
@ -3025,9 +3069,8 @@ VkDescriptorSet VulkanRenderer::backbufferBlit_createDescriptorSet(VkDescriptorS
hash += (uint64)texViewVk->GetViewRGBA(); hash += (uint64)texViewVk->GetViewRGBA();
hash += (uint64)texViewVk->GetDefaultTextureSampler(useLinearTexFilter); hash += (uint64)texViewVk->GetDefaultTextureSampler(useLinearTexFilter);
static std::unordered_map<uint64, VkDescriptorSet> s_set_cache; const auto it = m_backbufferBlitDescriptorSetCache.find(hash);
const auto it = s_set_cache.find(hash); if (it != m_backbufferBlitDescriptorSetCache.cend())
if (it != s_set_cache.cend())
return it->second; return it->second;
VkDescriptorSetAllocateInfo allocInfo = {}; VkDescriptorSetAllocateInfo allocInfo = {};
@ -3058,7 +3101,7 @@ VkDescriptorSet VulkanRenderer::backbufferBlit_createDescriptorSet(VkDescriptorS
vkUpdateDescriptorSets(m_logicalDevice, 1, &descriptorWrites, 0, nullptr); vkUpdateDescriptorSets(m_logicalDevice, 1, &descriptorWrites, 0, nullptr);
performanceMonitor.vk.numDescriptorSamplerTextures.increment(); performanceMonitor.vk.numDescriptorSamplerTextures.increment();
s_set_cache[hash] = result; m_backbufferBlitDescriptorSetCache[hash] = result;
return result; return result;
} }
@ -3191,7 +3234,8 @@ VkDescriptorSetInfo::~VkDescriptorSetInfo()
performanceMonitor.vk.numDescriptorDynUniformBuffers.decrement(statsNumDynUniformBuffers); performanceMonitor.vk.numDescriptorDynUniformBuffers.decrement(statsNumDynUniformBuffers);
performanceMonitor.vk.numDescriptorStorageBuffers.decrement(statsNumStorageBuffers); performanceMonitor.vk.numDescriptorStorageBuffers.decrement(statsNumStorageBuffers);
VulkanRenderer::GetInstance()->ReleaseDestructibleObject(m_vkObjDescriptorSet); auto renderer = VulkanRenderer::GetInstance();
renderer->ReleaseDestructibleObject(m_vkObjDescriptorSet);
m_vkObjDescriptorSet = nullptr; m_vkObjDescriptorSet = nullptr;
} }

View file

@ -137,8 +137,8 @@ class VulkanRenderer : public Renderer
public: public:
// memory management // memory management
VKRMemoryManager* memoryManager{}; std::unique_ptr<VKRMemoryManager> memoryManager;
VKRMemoryManager* GetMemoryManager() const { return memoryManager; }; VKRMemoryManager* GetMemoryManager() const { return memoryManager.get(); };
VkSupportedFormatInfo_t m_supportedFormatInfo; VkSupportedFormatInfo_t m_supportedFormatInfo;
@ -583,6 +583,8 @@ private:
std::shared_mutex m_pipeline_cache_save_mutex; std::shared_mutex m_pipeline_cache_save_mutex;
std::thread m_pipeline_cache_save_thread; std::thread m_pipeline_cache_save_thread;
VkPipelineCache m_pipeline_cache{ nullptr }; VkPipelineCache m_pipeline_cache{ nullptr };
std::unordered_map<uint64, VkPipeline> m_backbufferBlitPipelineCache;
std::unordered_map<uint64, VkDescriptorSet> m_backbufferBlitDescriptorSetCache;
VkPipelineLayout m_pipelineLayout{nullptr}; VkPipelineLayout m_pipelineLayout{nullptr};
VkCommandPool m_commandPool{ nullptr }; VkCommandPool m_commandPool{ nullptr };
@ -860,7 +862,7 @@ private:
memBarrier.pNext = nullptr; memBarrier.pNext = nullptr;
VkPipelineStageFlags srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT; VkPipelineStageFlags srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
VkPipelineStageFlags dstStages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; VkPipelineStageFlags dstStages = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
memBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; memBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
memBarrier.dstAccessMask = 0; memBarrier.dstAccessMask = 0;

View file

@ -900,6 +900,7 @@ VkDescriptorSetInfo* VulkanRenderer::draw_getOrCreateDescriptorSet(PipelineInfo*
} }
} }
} }
VKRObjectSampler* samplerObj = VKRObjectSampler::GetOrCreateSampler(&samplerInfo); VKRObjectSampler* samplerObj = VKRObjectSampler::GetOrCreateSampler(&samplerInfo);
vkObjDS->addRef(samplerObj); vkObjDS->addRef(samplerObj);
info.sampler = samplerObj->GetSampler(); info.sampler = samplerObj->GetSampler();
@ -1163,28 +1164,17 @@ void VulkanRenderer::draw_prepareDescriptorSets(PipelineInfo* pipeline_info, VkD
const auto geometryShader = LatteSHRC_GetActiveGeometryShader(); const auto geometryShader = LatteSHRC_GetActiveGeometryShader();
const auto pixelShader = LatteSHRC_GetActivePixelShader(); const auto pixelShader = LatteSHRC_GetActivePixelShader();
auto prepareShaderDescriptors = [this, &pipeline_info](LatteDecompilerShader* shader) -> VkDescriptorSetInfo* {
if (vertexShader) if (!shader)
{ return nullptr;
auto descriptorSetInfo = draw_getOrCreateDescriptorSet(pipeline_info, vertexShader); auto descriptorSetInfo = draw_getOrCreateDescriptorSet(pipeline_info, shader);
descriptorSetInfo->m_vkObjDescriptorSet->flagForCurrentCommandBuffer(); descriptorSetInfo->m_vkObjDescriptorSet->flagForCurrentCommandBuffer();
vertexDS = descriptorSetInfo; return descriptorSetInfo;
} };
if (pixelShader) vertexDS = prepareShaderDescriptors(vertexShader);
{ pixelDS = prepareShaderDescriptors(pixelShader);
auto descriptorSetInfo = draw_getOrCreateDescriptorSet(pipeline_info, pixelShader); geometryDS = prepareShaderDescriptors(geometryShader);
descriptorSetInfo->m_vkObjDescriptorSet->flagForCurrentCommandBuffer();
pixelDS = descriptorSetInfo;
}
if (geometryShader)
{
auto descriptorSetInfo = draw_getOrCreateDescriptorSet(pipeline_info, geometryShader);
descriptorSetInfo->m_vkObjDescriptorSet->flagForCurrentCommandBuffer();
geometryDS = descriptorSetInfo;
}
} }
void VulkanRenderer::draw_updateVkBlendConstants() void VulkanRenderer::draw_updateVkBlendConstants()

View file

@ -76,6 +76,30 @@ struct CopySurfacePipelineInfo
CopySurfacePipelineInfo() = default; CopySurfacePipelineInfo() = default;
CopySurfacePipelineInfo(VkDevice device) : m_device(device) {} CopySurfacePipelineInfo(VkDevice device) : m_device(device) {}
CopySurfacePipelineInfo(const CopySurfacePipelineInfo& info) = delete; CopySurfacePipelineInfo(const CopySurfacePipelineInfo& info) = delete;
~CopySurfacePipelineInfo()
{
auto renderer = VulkanRenderer::GetInstance();
renderer->ReleaseDestructibleObject(vkObjRenderPass);
renderer->ReleaseDestructibleObject(vkObjPipeline);
for(auto& i : map_framebuffers)
{
for(auto& fb : i.second.m_array)
{
renderer->ReleaseDestructibleObject(fb->vkObjFramebuffer);
renderer->ReleaseDestructibleObject(fb->vkObjImageView);
}
}
for(auto& i : map_descriptors)
{
for(auto& descriptor : i.second.m_array)
{
renderer->ReleaseDestructibleObject(descriptor->vkObjImageView);
renderer->ReleaseDestructibleObject(descriptor->vkObjDescriptorSet);
}
}
}
VkDevice m_device = nullptr; VkDevice m_device = nullptr;
@ -842,5 +866,9 @@ void VulkanRenderer::surfaceCopy_notifyTextureRelease(LatteTextureVk* hostTextur
void VulkanRenderer::surfaceCopy_cleanup() void VulkanRenderer::surfaceCopy_cleanup()
{ {
// todo - release m_copySurfacePipelineCache etc for(auto& i : m_copySurfacePipelineCache)
{
delete i.second;
}
m_copySurfacePipelineCache = {};
} }

View file

@ -469,7 +469,7 @@ namespace iosu
entry->ukn0C = 0; entry->ukn0C = 0;
entry->sizeA = _swapEndianU64(0); // ukn entry->sizeA = _swapEndianU64(0); // ukn
entry->sizeB = _swapEndianU64(dirSize); entry->sizeB = _swapEndianU64(dirSize);
entry->time = _swapEndianU64((coreinit::coreinit_getOSTime() / ESPRESSO_TIMER_CLOCK)); entry->time = _swapEndianU64((coreinit::OSGetTime() / ESPRESSO_TIMER_CLOCK));
sprintf(entry->path, "%susr/save/%08x/%08x/meta/", devicePath, (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF)); sprintf(entry->path, "%susr/save/%08x/%08x/meta/", devicePath, (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF));
count++; count++;
} }
@ -504,7 +504,7 @@ namespace iosu
entry->ukn0C = 0; entry->ukn0C = 0;
entry->sizeA = _swapEndianU64(0); entry->sizeA = _swapEndianU64(0);
entry->sizeB = _swapEndianU64(0); entry->sizeB = _swapEndianU64(0);
entry->time = _swapEndianU64((coreinit::coreinit_getOSTime() / ESPRESSO_TIMER_CLOCK)); entry->time = _swapEndianU64((coreinit::OSGetTime() / ESPRESSO_TIMER_CLOCK));
sprintf(entry->path, "%susr/save/%08x/%08x/meta/", devicePath, (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF)); sprintf(entry->path, "%susr/save/%08x/%08x/meta/", devicePath, (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF));
count++; count++;
} }
@ -584,7 +584,7 @@ namespace iosu
uint64 _ACPGetTimestamp() uint64 _ACPGetTimestamp()
{ {
return coreinit::coreinit_getOSTime() / ESPRESSO_TIMER_CLOCK; return coreinit::OSGetTime() / ESPRESSO_TIMER_CLOCK;
} }
nnResult ACPUpdateSaveTimeStamp(uint32 persistentId, uint64 titleId, ACPDeviceType deviceType) nnResult ACPUpdateSaveTimeStamp(uint32 persistentId, uint64 titleId, ACPDeviceType deviceType)

View file

@ -186,7 +186,7 @@ namespace camera
if (g_cameraCounter == 0) if (g_cameraCounter == 0)
{ {
coreinit::OSCreateAlarm(g_alarm_camera.GetPtr()); coreinit::OSCreateAlarm(g_alarm_camera.GetPtr());
coreinit::OSSetPeriodicAlarm(g_alarm_camera.GetPtr(), coreinit::coreinit_getOSTime(), (uint64)ESPRESSO_TIMER_CLOCK / 60ull, RPLLoader_MakePPCCallable(ppcCAMUpdate60)); coreinit::OSSetPeriodicAlarm(g_alarm_camera.GetPtr(), coreinit::OSGetTime(), (uint64)ESPRESSO_TIMER_CLOCK / 60ull, RPLLoader_MakePPCCallable(ppcCAMUpdate60));
} }
g_cameraCounter++; g_cameraCounter++;

View file

@ -166,7 +166,7 @@ namespace coreinit
void alarm_update() void alarm_update()
{ {
cemu_assert_debug(!__OSHasSchedulerLock()); cemu_assert_debug(!__OSHasSchedulerLock());
uint64 currentTick = coreinit::coreinit_getOSTime(); uint64 currentTick = coreinit::OSGetTime();
if (!OSHostAlarm::quickCheckForAlarm(currentTick)) if (!OSHostAlarm::quickCheckForAlarm(currentTick))
return; return;
__OSLockScheduler(); __OSLockScheduler();
@ -233,7 +233,7 @@ namespace coreinit
if (period == 0) if (period == 0)
return; return;
uint64 currentTime = coreinit_getOSTime(); uint64 currentTime = OSGetTime();
uint64 ticksSinceStart = currentTime - startTime; uint64 ticksSinceStart = currentTime - startTime;
uint64 numPeriods = ticksSinceStart / period; uint64 numPeriods = ticksSinceStart / period;
@ -267,7 +267,7 @@ namespace coreinit
void OSSetAlarm(OSAlarm_t* alarm, uint64 delayInTicks, MPTR handlerFunc) void OSSetAlarm(OSAlarm_t* alarm, uint64 delayInTicks, MPTR handlerFunc)
{ {
__OSLockScheduler(); __OSLockScheduler();
__OSInitiateAlarm(alarm, coreinit_getOSTime() + delayInTicks, 0, handlerFunc, false); __OSInitiateAlarm(alarm, OSGetTime() + delayInTicks, 0, handlerFunc, false);
__OSUnlockScheduler(); __OSUnlockScheduler();
} }
@ -310,7 +310,7 @@ namespace coreinit
while( true ) while( true )
{ {
OSWaitEvent(g_alarmEvent.GetPtr()); OSWaitEvent(g_alarmEvent.GetPtr());
uint64 currentTick = coreinit_getOSTime(); uint64 currentTick = OSGetTime();
while (true) while (true)
{ {
// get alarm to fire // get alarm to fire

View file

@ -86,11 +86,11 @@ namespace coreinit
else else
{ {
// loop until lock acquired or timeout occurred // loop until lock acquired or timeout occurred
uint64 timeoutValue = coreinit_getTimerTick() + coreinit::EspressoTime::ConvertNsToTimerTicks(timeout); uint64 timeoutValue = OSGetSystemTime() + coreinit::EspressoTime::ConvertNsToTimerTicks(timeout);
while (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread)) while (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread))
{ {
OSYieldThread(); OSYieldThread();
if (coreinit_getTimerTick() >= timeoutValue) if (OSGetSystemTime() >= timeoutValue)
{ {
return false; return false;
} }
@ -182,11 +182,11 @@ namespace coreinit
else else
{ {
// loop until lock acquired or timeout occurred // loop until lock acquired or timeout occurred
uint64 timeoutValue = coreinit_getTimerTick() + coreinit::EspressoTime::ConvertNsToTimerTicks(timeout); uint64 timeoutValue = OSGetSystemTime() + coreinit::EspressoTime::ConvertNsToTimerTicks(timeout);
while (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread)) while (!spinlock->ownerThread.atomic_compare_exchange(nullptr, currentThread))
{ {
OSYieldThread(); OSYieldThread();
if (coreinit_getTimerTick() >= timeoutValue) if (OSGetSystemTime() >= timeoutValue)
{ {
return false; return false;
} }

View file

@ -73,8 +73,6 @@ namespace coreinit
} }
} }
uint64 coreinit_getOSTime();
bool OSWaitEventWithTimeout(OSEvent* event, uint64 timeout) bool OSWaitEventWithTimeout(OSEvent* event, uint64 timeout)
{ {
__OSLockScheduler(); __OSLockScheduler();
@ -95,14 +93,14 @@ namespace coreinit
// workaround for a bad implementation in some Unity games (like Qube Directors Cut, see FEventWiiU::Wait) // workaround for a bad implementation in some Unity games (like Qube Directors Cut, see FEventWiiU::Wait)
// where the the return value of OSWaitEventWithTimeout is ignored and instead the game measures the elapsed time to determine if a timeout occurred // where the the return value of OSWaitEventWithTimeout is ignored and instead the game measures the elapsed time to determine if a timeout occurred
timeout = timeout * 98ULL / 100ULL; // 98% (we want the function to return slightly before the actual timeout) if (timeout < 0x00FFFFFFFFFFFFFFULL)
timeout = timeout * 98ULL / 100ULL; // 98% (we want the function to return slightly before the actual timeout)
WaitEventWithTimeoutData data; WaitEventWithTimeoutData data;
data.thread = OSGetCurrentThread(); data.thread = OSGetCurrentThread();
data.threadQueue = &event->threadQueue; data.threadQueue = &event->threadQueue;
data.hasTimeout = false; data.hasTimeout = false;
auto hostAlarm = coreinit::OSHostAlarmCreate(OSGetTime() + coreinit::EspressoTime::ConvertNsToTimerTicks(timeout), 0, _OSWaitEventWithTimeoutHandler, &data);
auto hostAlarm = coreinit::OSHostAlarmCreate(coreinit::coreinit_getOSTime() + coreinit::EspressoTime::ConvertNsToTimerTicks(timeout), 0, _OSWaitEventWithTimeoutHandler, &data);
event->threadQueue.queueAndWait(OSGetCurrentThread()); event->threadQueue.queueAndWait(OSGetCurrentThread());
coreinit::OSHostAlarmDestroy(hostAlarm); coreinit::OSHostAlarmDestroy(hostAlarm);
if (data.hasTimeout) if (data.hasTimeout)

View file

@ -655,7 +655,7 @@ namespace coreinit
StackAllocator<OSThreadQueue> _threadQueue; StackAllocator<OSThreadQueue> _threadQueue;
OSInitThreadQueue(_threadQueue.GetPointer()); OSInitThreadQueue(_threadQueue.GetPointer());
__OSLockScheduler(); __OSLockScheduler();
OSHostAlarm* hostAlarm = OSHostAlarmCreate(coreinit_getOSTime() + ticks, 0, _OSSleepTicks_alarmHandler, _threadQueue.GetPointer()); OSHostAlarm* hostAlarm = OSHostAlarmCreate(OSGetTime() + ticks, 0, _OSSleepTicks_alarmHandler, _threadQueue.GetPointer());
_threadQueue.GetPointer()->queueAndWait(OSGetCurrentThread()); _threadQueue.GetPointer()->queueAndWait(OSGetCurrentThread());
OSHostAlarmDestroy(hostAlarm); OSHostAlarmDestroy(hostAlarm);
__OSUnlockScheduler(); __OSUnlockScheduler();

View file

@ -3,38 +3,32 @@
namespace coreinit namespace coreinit
{ {
uint64 coreinit_GetMFTB()
uint64 coreinit_getTimerTick()
{ {
// bus clock is 1/5th of core clock // bus clock is 1/5th of core clock
// timer clock is 1/4th of bus clock // timer clock is 1/4th of bus clock
return PPCInterpreter_getMainCoreCycleCounter() / 20ULL; return PPCInterpreter_getMainCoreCycleCounter() / 20ULL;
} }
uint64 coreinit_getOSTime() uint64 OSGetSystemTime()
{ {
return coreinit_getTimerTick() + ppcCyclesSince2000TimerClock; return coreinit_GetMFTB();
}
void export_OSGetTick(PPCInterpreter_t* hCPU)
{
uint64 osTime = coreinit_getOSTime();
osLib_returnFromFunction(hCPU, (uint32)osTime);
} }
uint64 OSGetTime() uint64 OSGetTime()
{ {
return coreinit_getOSTime(); return OSGetSystemTime() + ppcCyclesSince2000TimerClock;
} }
void export_OSGetSystemTime(PPCInterpreter_t* hCPU) uint32 OSGetSystemTick()
{ {
osLib_returnFromFunction64(hCPU, coreinit_getTimerTick()); return static_cast<uint32>(coreinit_GetMFTB());
} }
void export_OSGetSystemTick(PPCInterpreter_t* hCPU) uint32 OSGetTick()
{ {
osLib_returnFromFunction(hCPU, (uint32)coreinit_getTimerTick()); uint64 osTime = OSGetTime();
return static_cast<uint32>(osTime);
} }
uint32 getLeapDaysUntilYear(uint32 year) uint32 getLeapDaysUntilYear(uint32 year)
@ -360,14 +354,13 @@ namespace coreinit
void InitializeTimeAndCalendar() void InitializeTimeAndCalendar()
{ {
cafeExportRegister("coreinit", OSGetTime, LogType::Placeholder); cafeExportRegister("coreinit", OSGetTime, LogType::Placeholder);
osLib_addFunction("coreinit", "OSGetSystemTime", export_OSGetSystemTime); cafeExportRegister("coreinit", OSGetSystemTime, LogType::Placeholder);
osLib_addFunction("coreinit", "OSGetTick", export_OSGetTick); cafeExportRegister("coreinit", OSGetTick, LogType::Placeholder);
osLib_addFunction("coreinit", "OSGetSystemTick", export_OSGetSystemTick); cafeExportRegister("coreinit", OSGetSystemTick, LogType::Placeholder);
cafeExportRegister("coreinit", OSTicksToCalendarTime, LogType::Placeholder); cafeExportRegister("coreinit", OSTicksToCalendarTime, LogType::Placeholder);
cafeExportRegister("coreinit", OSCalendarTimeToTicks, LogType::Placeholder); cafeExportRegister("coreinit", OSCalendarTimeToTicks, LogType::Placeholder);
//timeTest(); //timeTest();
} }
}; };

View file

@ -40,25 +40,21 @@ namespace coreinit
inline TimerTicks ConvertNsToTimerTicks(uint64 ns) inline TimerTicks ConvertNsToTimerTicks(uint64 ns)
{ {
return ((GetTimerClock() / 31250LL) * ((TimerTicks)ns) / 32000LL); return static_cast<TimerTicks>((static_cast<uint64>(GetTimerClock()) / 31250ULL) * (ns) / 32000ULL);
} }
inline TimerTicks ConvertMsToTimerTicks(uint64 ms) inline TimerTicks ConvertMsToTimerTicks(uint64 ms)
{ {
return (TimerTicks)ms * GetTimerClock() / 1000LL; return static_cast<TimerTicks>(ms * static_cast<uint64>(GetTimerClock()) / 1000ULL);
} }
}; };
void OSTicksToCalendarTime(uint64 ticks, OSCalendarTime_t* calenderStruct); void OSTicksToCalendarTime(uint64 ticks, OSCalendarTime_t* calenderStruct);
uint64 OSGetSystemTime();
uint64 OSGetTime(); uint64 OSGetTime();
uint32 OSGetSystemTick();
uint64 coreinit_getOSTime(); uint32 OSGetTick();
uint64 coreinit_getTimerTick();
static uint64 OSGetSystemTime()
{
return coreinit_getTimerTick();
}
void InitializeTimeAndCalendar(); void InitializeTimeAndCalendar();
}; };

View file

@ -11,7 +11,7 @@ uint64 dmaeRetiredTimestamp = 0;
uint64 dmae_getTimestamp() uint64 dmae_getTimestamp()
{ {
return coreinit::coreinit_getTimerTick(); return coreinit::OSGetSystemTime();
} }
void dmae_setRetiredTimestamp(uint64 timestamp) void dmae_setRetiredTimestamp(uint64 timestamp)

View file

@ -322,7 +322,7 @@ uint64 _prevReturnedGPUTime = 0;
uint64 Latte_GetTime() uint64 Latte_GetTime()
{ {
uint64 gpuTime = coreinit::coreinit_getTimerTick(); uint64 gpuTime = coreinit::OSGetSystemTime();
gpuTime *= 20000ULL; gpuTime *= 20000ULL;
if (gpuTime <= _prevReturnedGPUTime) if (gpuTime <= _prevReturnedGPUTime)
gpuTime = _prevReturnedGPUTime + 1; // avoid ever returning identical timestamps gpuTime = _prevReturnedGPUTime + 1; // avoid ever returning identical timestamps

View file

@ -54,7 +54,7 @@ void gx2Export_GX2GetGPUTimeout(PPCInterpreter_t* hCPU)
void gx2Export_GX2SampleTopGPUCycle(PPCInterpreter_t* hCPU) void gx2Export_GX2SampleTopGPUCycle(PPCInterpreter_t* hCPU)
{ {
cemuLog_log(LogType::GX2, "GX2SampleTopGPUCycle(0x{:08x})", hCPU->gpr[3]); cemuLog_log(LogType::GX2, "GX2SampleTopGPUCycle(0x{:08x})", hCPU->gpr[3]);
memory_writeU64(hCPU->gpr[3], coreinit::coreinit_getTimerTick()); memory_writeU64(hCPU->gpr[3], coreinit::OSGetSystemTime());
osLib_returnFromFunction(hCPU, 0); osLib_returnFromFunction(hCPU, 0);
} }

View file

@ -315,7 +315,7 @@ namespace acp
ppcDefineParamU32BEPtr(timestamp64, 0); ppcDefineParamU32BEPtr(timestamp64, 0);
ppcDefineParamU32BEPtr(ukn, 1); // probably timezone or offset? Could also be a bool for success/failed ppcDefineParamU32BEPtr(ukn, 1); // probably timezone or offset? Could also be a bool for success/failed
uint64 t = coreinit::coreinit_getOSTime() + (uint64)((sint64)(ppcCyclesSince2000_UTC - ppcCyclesSince2000) / 20LL); uint64 t = coreinit::OSGetTime() + (uint64)((sint64)(ppcCyclesSince2000_UTC - ppcCyclesSince2000) / 20LL);
timestamp64[0] = (uint32)(t >> 32); timestamp64[0] = (uint32)(t >> 32);
timestamp64[1] = (uint32)(t & 0xFFFFFFFF); timestamp64[1] = (uint32)(t & 0xFFFFFFFF);

View file

@ -6,6 +6,8 @@
#include "Backend.h" #include "Backend.h"
#include "Common/FileStream.h" #include "Common/FileStream.h"
#include "audio/IAudioAPI.h"
#include "config/CemuConfig.h"
namespace nsyshid namespace nsyshid
{ {
@ -558,6 +560,26 @@ namespace nsyshid
Device::WriteResult SkylanderPortalDevice::Write(WriteMessage* message) Device::WriteResult SkylanderPortalDevice::Write(WriteMessage* message)
{ {
if (message->length != 64) {
cemu_assert_error();
}
if (!g_portalAudio)
{
// Portal audio is mono channel, 16 bit audio.
// Audio is unsigned 16 bit, supplied as 64 bytes which is 32 samples per block
g_portalAudio = IAudioAPI::CreateDeviceFromConfig(IAudioAPI::AudioType::Portal, 8000, 32, 16);
}
std::array<sint16, 32> mono_samples;
for (unsigned int i = 0; i < mono_samples.size(); ++i)
{
sint16 sample = static_cast<uint16>(message->data[i * 2 + 1]) << 8 | static_cast<uint16>(message->data[i * 2]);
mono_samples[i] = sample;
}
if (g_portalAudio)
{
g_portalAudio->FeedBlock(mono_samples.data());
}
message->bytesWritten = message->length; message->bytesWritten = message->length;
return Device::WriteResult::Success; return Device::WriteResult::Success;
} }
@ -604,20 +626,20 @@ namespace nsyshid
*(uint16be*)(currentWritePtr + 7) = 0x001D; // wDescriptorLength *(uint16be*)(currentWritePtr + 7) = 0x001D; // wDescriptorLength
currentWritePtr = currentWritePtr + 9; currentWritePtr = currentWritePtr + 9;
// endpoint descriptor 1 // endpoint descriptor 1
*(uint8*)(currentWritePtr + 0) = 7; // bLength *(uint8*)(currentWritePtr + 0) = 7; // bLength
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType *(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
*(uint8*)(currentWritePtr + 2) = 0x81; // bEndpointAddress *(uint8*)(currentWritePtr + 2) = 0x81; // bEndpointAddress
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes *(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
*(uint16be*)(currentWritePtr + 4) = 0x0040; // wMaxPacketSize *(uint16be*)(currentWritePtr + 4) = 0x0040; // wMaxPacketSize
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval *(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
currentWritePtr = currentWritePtr + 7; currentWritePtr = currentWritePtr + 7;
// endpoint descriptor 2 // endpoint descriptor 2
*(uint8*)(currentWritePtr + 0) = 7; // bLength *(uint8*)(currentWritePtr + 0) = 7; // bLength
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType *(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
*(uint8*)(currentWritePtr + 2) = 0x02; // bEndpointAddress *(uint8*)(currentWritePtr + 2) = 0x02; // bEndpointAddress
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes *(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
*(uint16be*)(currentWritePtr + 4) = 0x0040; // wMaxPacketSize *(uint16be*)(currentWritePtr + 4) = 0x0040; // wMaxPacketSize
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval *(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
currentWritePtr = currentWritePtr + 7; currentWritePtr = currentWritePtr + 7;
cemu_assert_debug((currentWritePtr - configurationDescriptor) == 0x29); cemu_assert_debug((currentWritePtr - configurationDescriptor) == 0x29);
@ -628,8 +650,8 @@ namespace nsyshid
} }
bool SkylanderPortalDevice::SetIdle(uint8 ifIndex, bool SkylanderPortalDevice::SetIdle(uint8 ifIndex,
uint8 reportId, uint8 reportId,
uint8 duration) uint8 duration)
{ {
return true; return true;
} }

View file

@ -760,7 +760,7 @@ namespace padscore
void start() void start()
{ {
OSCreateAlarm(&g_padscore.alarm); OSCreateAlarm(&g_padscore.alarm);
const uint64 start_tick = coreinit::coreinit_getOSTime(); const uint64 start_tick = coreinit::OSGetTime();
const uint64 period_tick = coreinit::EspressoTime::GetTimerClock() / 200; // every 5ms const uint64 period_tick = coreinit::EspressoTime::GetTimerClock() / 200; // every 5ms
MPTR handler = PPCInterpreter_makeCallableExportDepr(TickFunction); MPTR handler = PPCInterpreter_makeCallableExportDepr(TickFunction);
OSSetPeriodicAlarm(&g_padscore.alarm, start_tick, period_tick, handler); OSSetPeriodicAlarm(&g_padscore.alarm, start_tick, period_tick, handler);

View file

@ -404,7 +404,7 @@ namespace snd_core
{ {
try try
{ {
g_tvAudio = IAudioAPI::CreateDeviceFromConfig(true, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16); g_tvAudio = IAudioAPI::CreateDeviceFromConfig(IAudioAPI::AudioType::TV, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
} }
catch (std::runtime_error& ex) catch (std::runtime_error& ex)
{ {
@ -417,7 +417,7 @@ namespace snd_core
{ {
try try
{ {
g_padAudio = IAudioAPI::CreateDeviceFromConfig(false, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16); g_padAudio = IAudioAPI::CreateDeviceFromConfig(IAudioAPI::AudioType::Gamepad, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
if(g_padAudio) if(g_padAudio)
g_padVolume = g_padAudio->GetVolume(); g_padVolume = g_padAudio->GetVolume();
} }
@ -442,6 +442,11 @@ namespace snd_core
g_padAudio->Stop(); g_padAudio->Stop();
g_padAudio.reset(); g_padAudio.reset();
} }
if (g_portalAudio)
{
g_portalAudio->Stop();
g_portalAudio.reset();
}
} }
void AXOut_updateDevicePlayState(bool isPlaying) void AXOut_updateDevicePlayState(bool isPlaying)
@ -462,6 +467,14 @@ namespace snd_core
else else
g_padAudio->Stop(); g_padAudio->Stop();
} }
if (g_portalAudio)
{
if (isPlaying)
g_portalAudio->Play();
else
g_portalAudio->Stop();
}
} }
// called periodically to check for AX updates // called periodically to check for AX updates

View file

@ -267,7 +267,7 @@ namespace vpad
{ {
if (channel <= 1 && vpadDelayEnabled) if (channel <= 1 && vpadDelayEnabled)
{ {
uint64 currentTime = coreinit::coreinit_getOSTime(); uint64 currentTime = coreinit::OSGetTime();
const auto dif = currentTime - vpad::g_vpad.controller_data[channel].drcLastCallTime; const auto dif = currentTime - vpad::g_vpad.controller_data[channel].drcLastCallTime;
if (dif <= (ESPRESSO_TIMER_CLOCK / 60ull)) if (dif <= (ESPRESSO_TIMER_CLOCK / 60ull))
{ {
@ -1149,7 +1149,7 @@ namespace vpad
void start() void start()
{ {
coreinit::OSCreateAlarm(&g_vpad.alarm); coreinit::OSCreateAlarm(&g_vpad.alarm);
const uint64 start_tick = coreinit::coreinit_getOSTime(); const uint64 start_tick = coreinit::OSGetTime();
const uint64 period_tick = coreinit::EspressoTime::GetTimerClock() * 5 / 1000; const uint64 period_tick = coreinit::EspressoTime::GetTimerClock() * 5 / 1000;
const MPTR handler = PPCInterpreter_makeCallableExportDepr(TickFunction); const MPTR handler = PPCInterpreter_makeCallableExportDepr(TickFunction);
coreinit::OSSetPeriodicAlarm(&g_vpad.alarm, start_tick, period_tick, handler); coreinit::OSSetPeriodicAlarm(&g_vpad.alarm, start_tick, period_tick, handler);

View file

@ -13,13 +13,14 @@
std::shared_mutex g_audioMutex; std::shared_mutex g_audioMutex;
AudioAPIPtr g_tvAudio; AudioAPIPtr g_tvAudio;
AudioAPIPtr g_padAudio; AudioAPIPtr g_padAudio;
AudioAPIPtr g_portalAudio;
std::atomic_int32_t g_padVolume = 0; std::atomic_int32_t g_padVolume = 0;
uint32 IAudioAPI::s_audioDelay = 2; uint32 IAudioAPI::s_audioDelay = 2;
std::array<bool, IAudioAPI::AudioAPIEnd> IAudioAPI::s_availableApis{}; std::array<bool, IAudioAPI::AudioAPIEnd> IAudioAPI::s_availableApis{};
IAudioAPI::IAudioAPI(uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample) IAudioAPI::IAudioAPI(uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample)
: m_samplerate(samplerate), m_channels(channels), m_samplesPerBlock(samples_per_block), m_bitsPerSample(bits_per_sample) : m_samplerate(samplerate), m_channels(channels), m_samplesPerBlock(samples_per_block), m_bitsPerSample(bits_per_sample)
{ {
m_bytesPerBlock = samples_per_block * channels * (bits_per_sample / 8); m_bytesPerBlock = samples_per_block * channels * (bits_per_sample / 8);
InitWFX(m_samplerate, m_channels, m_bitsPerSample); InitWFX(m_samplerate, m_channels, m_bitsPerSample);
@ -80,7 +81,7 @@ void IAudioAPI::InitializeStatic()
#if BOOST_OS_WINDOWS #if BOOST_OS_WINDOWS
s_availableApis[DirectSound] = true; s_availableApis[DirectSound] = true;
s_availableApis[XAudio2] = XAudio2API::InitializeStatic(); s_availableApis[XAudio2] = XAudio2API::InitializeStatic();
if(!s_availableApis[XAudio2]) // don't try to initialize the older lib if the newer version is available if (!s_availableApis[XAudio2]) // don't try to initialize the older lib if the newer version is available
s_availableApis[XAudio27] = XAudio27API::InitializeStatic(); s_availableApis[XAudio27] = XAudio27API::InitializeStatic();
#endif #endif
#if HAS_CUBEB #if HAS_CUBEB
@ -97,30 +98,29 @@ bool IAudioAPI::IsAudioAPIAvailable(AudioAPI api)
return false; return false;
} }
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(bool TV, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample) AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample)
{ {
auto& config = GetConfig(); sint32 channels = CemuConfig::AudioChannelsToNChannels(AudioTypeToChannels(type));
sint32 channels = CemuConfig::AudioChannelsToNChannels(TV ? config.tv_channels : config.pad_channels);
return CreateDeviceFromConfig(TV, rate, channels, samples_per_block, bits_per_sample); return CreateDeviceFromConfig(TV, rate, channels, samples_per_block, bits_per_sample);
} }
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(bool TV, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample) AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample)
{ {
AudioAPIPtr audioAPIDev; AudioAPIPtr audioAPIDev;
auto& config = GetConfig(); auto& config = GetConfig();
const auto audio_api = (IAudioAPI::AudioAPI)config.audio_api; const auto audio_api = (IAudioAPI::AudioAPI)config.audio_api;
auto& selectedDevice = TV ? config.tv_device : config.pad_device; auto selectedDevice = GetDeviceFromType(type);
if(selectedDevice.empty()) if (selectedDevice.empty())
return {}; return {};
IAudioAPI::DeviceDescriptionPtr device_description; IAudioAPI::DeviceDescriptionPtr device_description;
if (IAudioAPI::IsAudioAPIAvailable(audio_api)) if (IAudioAPI::IsAudioAPIAvailable(audio_api))
{ {
auto devices = IAudioAPI::GetDevices(audio_api); auto devices = IAudioAPI::GetDevices(audio_api);
const auto it = std::find_if(devices.begin(), devices.end(), [&selectedDevice](const auto& d) {return d->GetIdentifier() == selectedDevice; }); const auto it = std::find_if(devices.begin(), devices.end(), [&selectedDevice](const auto& d) { return d->GetIdentifier() == selectedDevice; });
if (it != devices.end()) if (it != devices.end())
device_description = *it; device_description = *it;
} }
@ -129,6 +129,7 @@ AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(bool TV, sint32 rate, sint32 chann
audioAPIDev = CreateDevice(audio_api, device_description, rate, channels, samples_per_block, bits_per_sample); audioAPIDev = CreateDevice(audio_api, device_description, rate, channels, samples_per_block, bits_per_sample);
audioAPIDev->SetVolume(TV ? config.tv_volume : config.pad_volume); audioAPIDev->SetVolume(TV ? config.tv_volume : config.pad_volume);
return audioAPIDev; return audioAPIDev;
} }
@ -137,7 +138,7 @@ AudioAPIPtr IAudioAPI::CreateDevice(AudioAPI api, const DeviceDescriptionPtr& de
if (!IsAudioAPIAvailable(api)) if (!IsAudioAPIAvailable(api))
return {}; return {};
switch(api) switch (api)
{ {
#if BOOST_OS_WINDOWS #if BOOST_OS_WINDOWS
case DirectSound: case DirectSound:
@ -157,11 +158,11 @@ AudioAPIPtr IAudioAPI::CreateDevice(AudioAPI api, const DeviceDescriptionPtr& de
} }
#endif #endif
#if HAS_CUBEB #if HAS_CUBEB
case Cubeb: case Cubeb:
{ {
const auto tmp = std::dynamic_pointer_cast<CubebAPI::CubebDeviceDescription>(device); const auto tmp = std::dynamic_pointer_cast<CubebAPI::CubebDeviceDescription>(device);
return std::make_unique<CubebAPI>(tmp->GetDeviceId(), samplerate, channels, samples_per_block, bits_per_sample); return std::make_unique<CubebAPI>(tmp->GetDeviceId(), samplerate, channels, samples_per_block, bits_per_sample);
} }
#endif #endif
default: default:
throw std::runtime_error(fmt::format("invalid audio api: {}", api)); throw std::runtime_error(fmt::format("invalid audio api: {}", api));
@ -172,8 +173,8 @@ std::vector<IAudioAPI::DeviceDescriptionPtr> IAudioAPI::GetDevices(AudioAPI api)
{ {
if (!IsAudioAPIAvailable(api)) if (!IsAudioAPIAvailable(api))
return {}; return {};
switch(api) switch (api)
{ {
#if BOOST_OS_WINDOWS #if BOOST_OS_WINDOWS
case DirectSound: case DirectSound:
@ -209,3 +210,51 @@ uint32 IAudioAPI::GetAudioDelay() const
{ {
return m_audioDelayOverride > 0 ? m_audioDelayOverride : s_audioDelay; return m_audioDelayOverride > 0 ? m_audioDelayOverride : s_audioDelay;
} }
AudioChannels IAudioAPI::AudioTypeToChannels(AudioType type)
{
auto& config = GetConfig();
switch (type)
{
case TV:
return config.tv_channels;
case Gamepad:
return config.pad_channels;
case Portal:
return kMono;
default:
return kMono;
}
}
std::wstring IAudioAPI::GetDeviceFromType(AudioType type)
{
auto& config = GetConfig();
switch (type)
{
case TV:
return config.tv_device;
case Gamepad:
return config.pad_device;
case Portal:
return config.portal_device;
default:
return L"";
}
}
sint32 IAudioAPI::GetVolumeFromType(AudioType type)
{
auto& config = GetConfig();
switch (type)
{
case TV:
return config.tv_volume;
case Gamepad:
return config.pad_volume;
case Portal:
return config.portal_volume;
default:
return 0;
}
}

View file

@ -4,6 +4,8 @@
#include <mmreg.h> #include <mmreg.h>
#endif #endif
#include "config/CemuConfig.h"
class IAudioAPI class IAudioAPI
{ {
friend class GeneralSettings2; friend class GeneralSettings2;
@ -30,6 +32,13 @@ public:
using DeviceDescriptionPtr = std::shared_ptr<DeviceDescription>; using DeviceDescriptionPtr = std::shared_ptr<DeviceDescription>;
enum AudioType
{
TV = 0,
Gamepad,
Portal
};
enum AudioAPI enum AudioAPI
{ {
DirectSound = 0, DirectSound = 0,
@ -62,8 +71,8 @@ public:
static void InitializeStatic(); static void InitializeStatic();
static bool IsAudioAPIAvailable(AudioAPI api); static bool IsAudioAPIAvailable(AudioAPI api);
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(bool TV, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample); static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample);
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(bool TV, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample); static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
static std::unique_ptr<IAudioAPI> CreateDevice(AudioAPI api, const DeviceDescriptionPtr& device, sint32 samplerate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample); static std::unique_ptr<IAudioAPI> CreateDevice(AudioAPI api, const DeviceDescriptionPtr& device, sint32 samplerate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
static std::vector<DeviceDescriptionPtr> GetDevices(AudioAPI api); static std::vector<DeviceDescriptionPtr> GetDevices(AudioAPI api);
@ -84,6 +93,9 @@ protected:
private: private:
static uint32 s_audioDelay; static uint32 s_audioDelay;
void InitWFX(sint32 samplerate, sint32 channels, sint32 bits_per_sample); void InitWFX(sint32 samplerate, sint32 channels, sint32 bits_per_sample);
static AudioChannels AudioTypeToChannels(AudioType type);
static std::wstring GetDeviceFromType(AudioType type);
static sint32 GetVolumeFromType(AudioType type);
}; };
@ -93,3 +105,5 @@ extern AudioAPIPtr g_tvAudio;
extern AudioAPIPtr g_padAudio; extern AudioAPIPtr g_padAudio;
extern std::atomic_int32_t g_padVolume; extern std::atomic_int32_t g_padVolume;
extern AudioAPIPtr g_portalAudio;

View file

@ -278,6 +278,7 @@ void CemuConfig::Load(XMLConfigParser& parser)
tv_volume = audio.get("TVVolume", 20); tv_volume = audio.get("TVVolume", 20);
pad_volume = audio.get("PadVolume", 0); pad_volume = audio.get("PadVolume", 0);
input_volume = audio.get("InputVolume", 20); input_volume = audio.get("InputVolume", 20);
portal_volume = audio.get("PortalVolume", 20);
const auto tv = audio.get("TVDevice", ""); const auto tv = audio.get("TVDevice", "");
try try
@ -309,6 +310,16 @@ void CemuConfig::Load(XMLConfigParser& parser)
cemuLog_log(LogType::Force, "config load error: can't load input device: {}", input_device_name); cemuLog_log(LogType::Force, "config load error: can't load input device: {}", input_device_name);
} }
const auto portal_device_name = audio.get("PortalDevice", "");
try
{
portal_device = boost::nowide::widen(portal_device_name);
}
catch (const std::exception&)
{
cemuLog_log(LogType::Force, "config load error: can't load input device: {}", portal_device_name);
}
// account // account
auto acc = parser.get("Account"); auto acc = parser.get("Account");
account.m_persistent_id = acc.get("PersistentId", account.m_persistent_id); account.m_persistent_id = acc.get("PersistentId", account.m_persistent_id);
@ -511,9 +522,11 @@ void CemuConfig::Save(XMLConfigParser& parser)
audio.set("TVVolume", tv_volume); audio.set("TVVolume", tv_volume);
audio.set("PadVolume", pad_volume); audio.set("PadVolume", pad_volume);
audio.set("InputVolume", input_volume); audio.set("InputVolume", input_volume);
audio.set("PortalVolume", portal_volume);
audio.set("TVDevice", boost::nowide::narrow(tv_device).c_str()); audio.set("TVDevice", boost::nowide::narrow(tv_device).c_str());
audio.set("PadDevice", boost::nowide::narrow(pad_device).c_str()); audio.set("PadDevice", boost::nowide::narrow(pad_device).c_str());
audio.set("InputDevice", boost::nowide::narrow(input_device).c_str()); audio.set("InputDevice", boost::nowide::narrow(input_device).c_str());
audio.set("PortalDevice", boost::nowide::narrow(portal_device).c_str());
// account // account
auto acc = config.set("Account"); auto acc = config.set("Account");

View file

@ -192,7 +192,7 @@ ENABLE_ENUM_ITERATORS(CrashDump, CrashDump::Disabled, CrashDump::Enabled);
#endif #endif
template <> template <>
struct fmt::formatter<PrecompiledShaderOption> : formatter<string_view> { struct fmt::formatter<const PrecompiledShaderOption> : formatter<string_view> {
template <typename FormatContext> template <typename FormatContext>
auto format(const PrecompiledShaderOption c, FormatContext &ctx) const { auto format(const PrecompiledShaderOption c, FormatContext &ctx) const {
string_view name; string_view name;
@ -207,7 +207,7 @@ struct fmt::formatter<PrecompiledShaderOption> : formatter<string_view> {
} }
}; };
template <> template <>
struct fmt::formatter<AccurateShaderMulOption> : formatter<string_view> { struct fmt::formatter<const AccurateShaderMulOption> : formatter<string_view> {
template <typename FormatContext> template <typename FormatContext>
auto format(const AccurateShaderMulOption c, FormatContext &ctx) const { auto format(const AccurateShaderMulOption c, FormatContext &ctx) const {
string_view name; string_view name;
@ -221,7 +221,7 @@ struct fmt::formatter<AccurateShaderMulOption> : formatter<string_view> {
} }
}; };
template <> template <>
struct fmt::formatter<CPUMode> : formatter<string_view> { struct fmt::formatter<const CPUMode> : formatter<string_view> {
template <typename FormatContext> template <typename FormatContext>
auto format(const CPUMode c, FormatContext &ctx) const { auto format(const CPUMode c, FormatContext &ctx) const {
string_view name; string_view name;
@ -238,7 +238,7 @@ struct fmt::formatter<CPUMode> : formatter<string_view> {
} }
}; };
template <> template <>
struct fmt::formatter<CPUModeLegacy> : formatter<string_view> { struct fmt::formatter<const CPUModeLegacy> : formatter<string_view> {
template <typename FormatContext> template <typename FormatContext>
auto format(const CPUModeLegacy c, FormatContext &ctx) const { auto format(const CPUModeLegacy c, FormatContext &ctx) const {
string_view name; string_view name;
@ -255,7 +255,7 @@ struct fmt::formatter<CPUModeLegacy> : formatter<string_view> {
} }
}; };
template <> template <>
struct fmt::formatter<CafeConsoleRegion> : formatter<string_view> { struct fmt::formatter<const CafeConsoleRegion> : formatter<string_view> {
template <typename FormatContext> template <typename FormatContext>
auto format(const CafeConsoleRegion v, FormatContext &ctx) const { auto format(const CafeConsoleRegion v, FormatContext &ctx) const {
string_view name; string_view name;
@ -276,7 +276,7 @@ struct fmt::formatter<CafeConsoleRegion> : formatter<string_view> {
} }
}; };
template <> template <>
struct fmt::formatter<CafeConsoleLanguage> : formatter<string_view> { struct fmt::formatter<const CafeConsoleLanguage> : formatter<string_view> {
template <typename FormatContext> template <typename FormatContext>
auto format(const CafeConsoleLanguage v, FormatContext &ctx) { auto format(const CafeConsoleLanguage v, FormatContext &ctx) {
string_view name; string_view name;
@ -302,7 +302,7 @@ struct fmt::formatter<CafeConsoleLanguage> : formatter<string_view> {
#if BOOST_OS_WINDOWS #if BOOST_OS_WINDOWS
template <> template <>
struct fmt::formatter<CrashDump> : formatter<string_view> { struct fmt::formatter<const CrashDump> : formatter<string_view> {
template <typename FormatContext> template <typename FormatContext>
auto format(const CrashDump v, FormatContext &ctx) { auto format(const CrashDump v, FormatContext &ctx) {
string_view name; string_view name;
@ -319,7 +319,7 @@ struct fmt::formatter<CrashDump> : formatter<string_view> {
}; };
#elif BOOST_OS_UNIX #elif BOOST_OS_UNIX
template <> template <>
struct fmt::formatter<CrashDump> : formatter<string_view> { struct fmt::formatter<const CrashDump> : formatter<string_view> {
template <typename FormatContext> template <typename FormatContext>
auto format(const CrashDump v, FormatContext &ctx) { auto format(const CrashDump v, FormatContext &ctx) {
string_view name; string_view name;
@ -480,8 +480,8 @@ struct CemuConfig
sint32 audio_api = 0; sint32 audio_api = 0;
sint32 audio_delay = 2; sint32 audio_delay = 2;
AudioChannels tv_channels = kStereo, pad_channels = kStereo, input_channels = kMono; AudioChannels tv_channels = kStereo, pad_channels = kStereo, input_channels = kMono;
sint32 tv_volume = 50, pad_volume = 0, input_volume = 50; sint32 tv_volume = 50, pad_volume = 0, input_volume = 50, portal_volume = 50;
std::wstring tv_device{ L"default" }, pad_device, input_device; std::wstring tv_device{ L"default" }, pad_device, input_device, portal_device;
// account // account
struct struct

View file

@ -542,6 +542,36 @@ wxPanel* GeneralSettings2::AddAudioPage(wxNotebook* notebook)
audio_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); audio_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
} }
{
auto box = new wxStaticBox(audio_panel, wxID_ANY, _("Trap Team Portal"));
auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
auto portal_audio_row = new wxFlexGridSizer(0, 3, 0, 0);
portal_audio_row->SetFlexibleDirection(wxBOTH);
portal_audio_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
portal_audio_row->Add(new wxStaticText(box, wxID_ANY, _("Device")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
m_portal_device = new wxChoice(box, wxID_ANY, wxDefaultPosition);
m_portal_device->SetMinSize(wxSize(300, -1));
m_portal_device->SetToolTip(_("Select the active audio output device for Wii U GamePad"));
portal_audio_row->Add(m_portal_device, 0, wxEXPAND | wxALL, 5);
portal_audio_row->AddSpacer(0);
m_portal_device->Bind(wxEVT_CHOICE, &GeneralSettings2::OnAudioDeviceSelected, this);
portal_audio_row->Add(new wxStaticText(box, wxID_ANY, _("Volume")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
m_portal_volume = new wxSlider(box, wxID_ANY, 100, 0, 100);
portal_audio_row->Add(m_portal_volume, 0, wxEXPAND | wxALL, 5);
auto audio_pad_volume_text = new wxStaticText(box, wxID_ANY, "100%");
portal_audio_row->Add(audio_pad_volume_text, 0, wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_RIGHT, 5);
m_portal_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnSliderChangedPercent, this, wxID_ANY, wxID_ANY, new wxControlObject(audio_pad_volume_text));
m_portal_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnVolumeChanged, this);
box_sizer->Add(portal_audio_row, 1, wxEXPAND, 5);
audio_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
}
audio_panel->SetSizerAndFit(audio_panel_sizer); audio_panel->SetSizerAndFit(audio_panel_sizer);
return audio_panel; return audio_panel;
} }
@ -993,6 +1023,7 @@ void GeneralSettings2::StoreConfig()
config.tv_volume = m_tv_volume->GetValue(); config.tv_volume = m_tv_volume->GetValue();
config.pad_volume = m_pad_volume->GetValue(); config.pad_volume = m_pad_volume->GetValue();
config.input_volume = m_input_volume->GetValue(); config.input_volume = m_input_volume->GetValue();
config.portal_volume = m_portal_volume->GetValue();
config.tv_device.clear(); config.tv_device.clear();
const auto tv_device = m_tv_device->GetSelection(); const auto tv_device = m_tv_device->GetSelection();
@ -1021,6 +1052,15 @@ void GeneralSettings2::StoreConfig()
config.input_device = device_description->GetDescription()->GetIdentifier(); config.input_device = device_description->GetDescription()->GetIdentifier();
} }
config.portal_device.clear();
const auto portal_device = m_portal_device->GetSelection();
if (portal_device != wxNOT_FOUND && portal_device != 0 && m_portal_device->HasClientObjectData())
{
const auto* device_description = (wxDeviceDescription*)m_portal_device->GetClientObject(portal_device);
if (device_description)
config.portal_device = device_description->GetDescription()->GetIdentifier();
}
// graphics // graphics
config.graphic_api = (GraphicAPI)m_graphic_api->GetSelection(); config.graphic_api = (GraphicAPI)m_graphic_api->GetSelection();
@ -1131,11 +1171,16 @@ void GeneralSettings2::OnVolumeChanged(wxCommandEvent& event)
g_padVolume = event.GetInt(); g_padVolume = event.GetInt();
} }
} }
else else if (event.GetEventObject() == m_tv_volume)
{ {
if (g_tvAudio) if (g_tvAudio)
g_tvAudio->SetVolume(event.GetInt()); g_tvAudio->SetVolume(event.GetInt());
} }
else
{
if(g_portalAudio)
g_portalAudio->SetVolume(event.GetInt());
}
} }
@ -1195,10 +1240,12 @@ void GeneralSettings2::UpdateAudioDeviceList()
m_tv_device->Clear(); m_tv_device->Clear();
m_pad_device->Clear(); m_pad_device->Clear();
m_input_device->Clear(); m_input_device->Clear();
m_portal_device->Clear();
m_tv_device->Append(_("Disabled")); m_tv_device->Append(_("Disabled"));
m_pad_device->Append(_("Disabled")); m_pad_device->Append(_("Disabled"));
m_input_device->Append(_("Disabled")); m_input_device->Append(_("Disabled"));
m_portal_device->Append(_("Disabled"));
const auto audio_api = (IAudioAPI::AudioAPI)GetConfig().audio_api; const auto audio_api = (IAudioAPI::AudioAPI)GetConfig().audio_api;
const auto devices = IAudioAPI::GetDevices(audio_api); const auto devices = IAudioAPI::GetDevices(audio_api);
@ -1206,6 +1253,7 @@ void GeneralSettings2::UpdateAudioDeviceList()
{ {
m_tv_device->Append(device->GetName(), new wxDeviceDescription(device)); m_tv_device->Append(device->GetName(), new wxDeviceDescription(device));
m_pad_device->Append(device->GetName(), new wxDeviceDescription(device)); m_pad_device->Append(device->GetName(), new wxDeviceDescription(device));
m_portal_device->Append(device->GetName(), new wxDeviceDescription(device));
} }
const auto input_audio_api = IAudioInputAPI::Cubeb; //(IAudioAPI::AudioAPI)GetConfig().input_audio_api; const auto input_audio_api = IAudioInputAPI::Cubeb; //(IAudioAPI::AudioAPI)GetConfig().input_audio_api;
@ -1225,6 +1273,8 @@ void GeneralSettings2::UpdateAudioDeviceList()
m_input_device->SetSelection(0); m_input_device->SetSelection(0);
m_portal_device->SetSelection(0);
// todo reset global instance of audio device // todo reset global instance of audio device
} }
@ -1708,6 +1758,22 @@ void GeneralSettings2::ApplyConfig()
else else
m_input_device->SetSelection(0); m_input_device->SetSelection(0);
SendSliderEvent(m_portal_volume, config.portal_volume);
if (!config.portal_device.empty() && m_portal_device->HasClientObjectData())
{
for (uint32 i = 0; i < m_portal_device->GetCount(); ++i)
{
const auto device_description = (wxDeviceDescription*)m_portal_device->GetClientObject(i);
if (device_description && config.portal_device == device_description->GetDescription()->GetIdentifier())
{
m_portal_device->SetSelection(i);
break;
}
}
}
else
m_portal_device->SetSelection(0);
// account // account
UpdateOnlineAccounts(); UpdateOnlineAccounts();
m_active_account->SetSelection(0); m_active_account->SetSelection(0);
@ -1866,6 +1932,42 @@ void GeneralSettings2::UpdateAudioDevice()
} }
} }
} }
// skylander portal audio device
{
const auto selection = m_portal_device->GetSelection();
if (selection == wxNOT_FOUND)
{
cemu_assert_debug(false);
return;
}
g_portalAudio.reset();
if (m_portal_device->HasClientObjectData())
{
const auto description = (wxDeviceDescription*)m_portal_device->GetClientObject(selection);
if (description)
{
sint32 channels;
if (m_game_launched && g_portalAudio)
channels = g_portalAudio->GetChannels();
else
channels = 1;
try
{
g_portalAudio = IAudioAPI::CreateDevice((IAudioAPI::AudioAPI)config.audio_api, description->GetDescription(), 8000, 1, 32, 16);
g_portalAudio->SetVolume(m_portal_volume->GetValue());
}
catch (std::runtime_error& ex)
{
cemuLog_log(LogType::Force, "can't initialize portal audio: {}", ex.what());
}
}
}
}
} }
void GeneralSettings2::OnAudioDeviceSelected(wxCommandEvent& event) void GeneralSettings2::OnAudioDeviceSelected(wxCommandEvent& event)

View file

@ -63,9 +63,9 @@ private:
// Audio // Audio
wxChoice* m_audio_api; wxChoice* m_audio_api;
wxSlider *m_audio_latency; wxSlider *m_audio_latency;
wxSlider *m_tv_volume, *m_pad_volume, *m_input_volume; wxSlider *m_tv_volume, *m_pad_volume, *m_input_volume, *m_portal_volume;
wxChoice *m_tv_channels, *m_pad_channels, *m_input_channels; wxChoice *m_tv_channels, *m_pad_channels, *m_input_channels;
wxChoice *m_tv_device, *m_pad_device, *m_input_device; wxChoice *m_tv_device, *m_pad_device, *m_input_device, *m_portal_device;
// Account // Account
wxButton* m_create_account, * m_delete_account; wxButton* m_create_account, * m_delete_account;

View file

@ -304,7 +304,7 @@ void SaveImportWindow::OnImport(wxCommandEvent& event)
auto new_node = info_node.append_child("account"); auto new_node = info_node.append_child("account");
new_node.append_attribute("persistentId").set_value(new_persistend_id_string.c_str()); new_node.append_attribute("persistentId").set_value(new_persistend_id_string.c_str());
auto timestamp = new_node.append_child("timestamp"); auto timestamp = new_node.append_child("timestamp");
timestamp.text().set(fmt::format("{:016x}", coreinit::coreinit_getOSTime() / ESPRESSO_TIMER_CLOCK).c_str()); // TODO time not initialized yet? timestamp.text().set(fmt::format("{:016x}", coreinit::OSGetTime() / ESPRESSO_TIMER_CLOCK).c_str()); // TODO time not initialized yet?
if(!doc.save_file(saveinfo.c_str())) if(!doc.save_file(saveinfo.c_str()))
cemuLog_log(LogType::Force, "couldn't insert save entry in saveinfo.xml: {}", _pathToUtf8(saveinfo)); cemuLog_log(LogType::Force, "couldn't insert save entry in saveinfo.xml: {}", _pathToUtf8(saveinfo));

View file

@ -465,6 +465,15 @@ void ImGui_ImplVulkan_DestroyFontsTexture()
if (g_FontView) { vkDestroyImageView(v->Device, g_FontView, v->Allocator); g_FontView = VK_NULL_HANDLE; } if (g_FontView) { vkDestroyImageView(v->Device, g_FontView, v->Allocator); g_FontView = VK_NULL_HANDLE; }
if (g_FontImage) { vkDestroyImage(v->Device, g_FontImage, v->Allocator); g_FontImage = VK_NULL_HANDLE; } if (g_FontImage) { vkDestroyImage(v->Device, g_FontImage, v->Allocator); g_FontImage = VK_NULL_HANDLE; }
if (g_FontMemory) { vkFreeMemory(v->Device, g_FontMemory, v->Allocator); g_FontMemory = VK_NULL_HANDLE; } if (g_FontMemory) { vkFreeMemory(v->Device, g_FontMemory, v->Allocator); g_FontMemory = VK_NULL_HANDLE; }
ImGuiIO& io = ImGui::GetIO();
auto texture = io.Fonts->TexID;
if(texture != (ImTextureID)nullptr)
{
ImGui_ImplVulkan_DeleteTexture(texture);
delete (ImGuiTexture*)texture;
io.Fonts->TexID = nullptr;
}
} }
bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer)