mirror of
https://github.com/Takiiiiiiii/strato.git
synced 2025-07-17 08:46:39 +00:00
Implement Exceptional Signal Handler + Fix Destruction Behavior
An exceptional signal handler allows us to convert an OS signal into a C++ exception, this allows us to alleviate a lot of crashes that would otherwise occur from signals being thrown during execution of games and be able to handle them gracefully.
This commit is contained in:
@ -1,117 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <common.h>
|
||||
|
||||
namespace skyline::gpu {
|
||||
/**
|
||||
* @brief An efficient consumer-producer structure with internal synchronization
|
||||
*/
|
||||
template<typename Type>
|
||||
class CircularQueue {
|
||||
private:
|
||||
std::vector<u8> vector; //!< The internal vector holding the circular queue's data, we use a byte vector due to the default item construction/destruction semantics not being appropriate for a circular buffer
|
||||
Type *start{reinterpret_cast<Type *>(vector.begin().base())}; //!< The start/oldest element of the queue
|
||||
Type *end{reinterpret_cast<Type *>(vector.begin().base())}; //!< The end/newest element of the queue
|
||||
std::mutex consumptionMutex;
|
||||
std::condition_variable consumeCondition;
|
||||
std::mutex productionMutex;
|
||||
std::condition_variable produceCondition;
|
||||
|
||||
public:
|
||||
CircularQueue(size_t size) : vector(size * sizeof(Type)) {}
|
||||
|
||||
~CircularQueue() {
|
||||
ssize_t size{};
|
||||
if (start < end)
|
||||
size = end - start;
|
||||
else
|
||||
size = (reinterpret_cast<Type *>(vector.end().base()) - start) + (end - reinterpret_cast<Type *>(vector.begin().base()));
|
||||
|
||||
while (size--) {
|
||||
std::destroy_at(start);
|
||||
if (start + 1 == reinterpret_cast<Type *>(vector.end().base()))
|
||||
start = reinterpret_cast<Type *>(vector.begin().base());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A blocking for-each that runs on every item and waits till new items to run on them as well
|
||||
* @param function A function that is called for each item (with the only parameter as a reference to that item)
|
||||
*/
|
||||
template<typename F>
|
||||
[[noreturn]] inline void Process(F function) {
|
||||
while (true) {
|
||||
if (start == end) {
|
||||
std::unique_lock lock(productionMutex);
|
||||
produceCondition.wait(lock, [this]() { return start != end; });
|
||||
}
|
||||
|
||||
ssize_t size{};
|
||||
if (start < end)
|
||||
size = end - start;
|
||||
else
|
||||
size = (reinterpret_cast<Type *>(vector.end().base()) - start) + (end - reinterpret_cast<Type *>(vector.begin().base()));
|
||||
|
||||
while (size--) {
|
||||
function(*start);
|
||||
if (start + 1 != reinterpret_cast<Type *>(vector.end().base()))
|
||||
start++;
|
||||
else
|
||||
start = reinterpret_cast<Type *>(vector.begin().base());
|
||||
}
|
||||
|
||||
consumeCondition.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
inline void Push(const Type &item) {
|
||||
std::unique_lock lock(productionMutex);
|
||||
auto next{end + 1};
|
||||
next = (next == reinterpret_cast<Type *>(vector.end().base())) ? reinterpret_cast<Type *>(vector.begin().base()) : next;
|
||||
if (next == start) {
|
||||
std::unique_lock consumeLock(consumptionMutex);
|
||||
consumeCondition.wait(consumeLock, [=]() { return next != start; });
|
||||
}
|
||||
*next = item;
|
||||
end = next;
|
||||
produceCondition.notify_one();
|
||||
}
|
||||
|
||||
inline void Append(span <Type> buffer) {
|
||||
std::unique_lock lock(productionMutex);
|
||||
for (auto &item : buffer) {
|
||||
auto next{end + 1};
|
||||
next = (next == reinterpret_cast<Type *>(vector.end().base())) ? reinterpret_cast<Type *>(vector.begin().base()) : next;
|
||||
if (next == start) {
|
||||
std::unique_lock consumeLock(consumptionMutex);
|
||||
consumeCondition.wait(consumeLock, [=]() { return next != start; });
|
||||
}
|
||||
*next = item;
|
||||
end = next;
|
||||
}
|
||||
produceCondition.notify_one();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Appends a buffer with an alternative input type while supplied transformation function
|
||||
* @param tranformation A function that takes in an item of TransformedType as input and returns an item of Type
|
||||
*/
|
||||
template<typename TransformedType, typename Transformation>
|
||||
inline void AppendTranform(span <TransformedType> buffer, Transformation transformation) {
|
||||
std::unique_lock lock(productionMutex);
|
||||
auto next{end};
|
||||
for (auto &item : buffer) {
|
||||
end = (end == reinterpret_cast<Type *>(vector.end().base())) ? reinterpret_cast<Type *>(vector.begin().base()) : end;
|
||||
if (start == end + 1) {
|
||||
std::unique_lock consumeLock(consumptionMutex);
|
||||
consumeCondition.wait(consumeLock, [=]() { return start != end + 1; });
|
||||
}
|
||||
*(end++) = transformation(item);
|
||||
}
|
||||
produceCondition.notify_one();
|
||||
}
|
||||
};
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <common/signal.h>
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include <gpu.h>
|
||||
#include <gpu/engines/maxwell_3d.h>
|
||||
#include "gpfifo.h"
|
||||
@ -86,19 +88,38 @@ namespace skyline::gpu::gpfifo {
|
||||
|
||||
void GPFIFO::Run() {
|
||||
pthread_setname_np(pthread_self(), "GPFIFO");
|
||||
pushBuffers->Process([this](PushBuffer& pushBuffer){
|
||||
if (pushBuffer.segment.empty())
|
||||
pushBuffer.Fetch(state.gpu->memoryManager);
|
||||
Process(pushBuffer.segment);
|
||||
});
|
||||
try {
|
||||
signal::SetSignalHandler({SIGINT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}, signal::ExceptionalSignalHandler);
|
||||
pushBuffers->Process([this](PushBuffer &pushBuffer) {
|
||||
if (pushBuffer.segment.empty())
|
||||
pushBuffer.Fetch(state.gpu->memoryManager);
|
||||
Process(pushBuffer.segment);
|
||||
});
|
||||
} catch (const signal::SignalException &e) {
|
||||
if (e.signal != SIGINT) {
|
||||
state.logger->Write(Logger::LogLevel::Error, e.what());
|
||||
signal::BlockSignal({SIGINT});
|
||||
state.process->mainThread->Kill(false);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
state.logger->Write(Logger::LogLevel::Error, e.what());
|
||||
state.process->mainThread->Kill(false);
|
||||
}
|
||||
}
|
||||
|
||||
void GPFIFO::Push(span<GpEntry> entries) {
|
||||
bool beforeBarrier{true};
|
||||
pushBuffers->AppendTranform(entries, [&beforeBarrier, this](const GpEntry& entry){
|
||||
pushBuffers->AppendTranform(entries, [&beforeBarrier, this](const GpEntry &entry) {
|
||||
if (entry.sync == GpEntry::Sync::Wait)
|
||||
beforeBarrier = false;
|
||||
return PushBuffer(entry, state.gpu->memoryManager, beforeBarrier);
|
||||
});
|
||||
}
|
||||
|
||||
GPFIFO::~GPFIFO() {
|
||||
if (thread.joinable()) {
|
||||
pthread_kill(thread.native_handle(), SIGINT);
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "circular_queue.h"
|
||||
#include <common/circular_queue.h>
|
||||
#include "engines/gpfifo.h"
|
||||
#include "memory_manager.h"
|
||||
|
||||
@ -163,6 +163,8 @@ namespace skyline::gpu {
|
||||
public:
|
||||
GPFIFO(const DeviceState &state) : state(state), gpfifoEngine(state) {}
|
||||
|
||||
~GPFIFO();
|
||||
|
||||
/**
|
||||
* @param numBuffers The amount of push-buffers to allocate in the circular buffer
|
||||
*/
|
||||
|
@ -5,8 +5,8 @@
|
||||
#include "jvm.h"
|
||||
#include "presentation_engine.h"
|
||||
|
||||
extern skyline::u16 fps;
|
||||
extern skyline::u32 frametime;
|
||||
extern skyline::u16 Fps;
|
||||
extern skyline::u32 FrameTime;
|
||||
|
||||
namespace skyline::gpu {
|
||||
PresentationEngine::PresentationEngine(const DeviceState &state) : state(state), vsyncEvent(std::make_shared<kernel::type::KEvent>(state)), bufferEvent(std::make_shared<kernel::type::KEvent>(state)) {}
|
||||
@ -29,7 +29,7 @@ namespace skyline::gpu {
|
||||
if (!env->IsSameObject(newSurface, nullptr))
|
||||
surface = env->NewGlobalRef(newSurface);
|
||||
if (surface) {
|
||||
window = ANativeWindow_fromSurface(state.jvm->GetEnv(), surface);
|
||||
window = ANativeWindow_fromSurface(env, surface);
|
||||
ANativeWindow_acquire(window);
|
||||
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window));
|
||||
resolution.height = static_cast<u32>(ANativeWindow_getHeight(window));
|
||||
@ -73,8 +73,8 @@ namespace skyline::gpu {
|
||||
if (frameTimestamp) {
|
||||
auto now{util::GetTimeNs()};
|
||||
|
||||
frametime = static_cast<u32>((now - frameTimestamp) / 10000); // frametime / 100 is the real ms value, this is to retain the first two decimals
|
||||
fps = static_cast<u16>(constant::NsInSecond / (now - frameTimestamp));
|
||||
FrameTime = static_cast<u32>((now - frameTimestamp) / 10000); // frametime / 100 is the real ms value, this is to retain the first two decimals
|
||||
Fps = static_cast<u16>(constant::NsInSecond / (now - frameTimestamp));
|
||||
|
||||
frameTimestamp = now;
|
||||
} else {
|
||||
|
@ -14,7 +14,7 @@ namespace skyline::gpu {
|
||||
const DeviceState &state;
|
||||
std::mutex windowMutex;
|
||||
std::condition_variable windowConditional;
|
||||
jobject surface; //!< The Surface object backing the ANativeWindow
|
||||
jobject surface{}; //!< The Surface object backing the ANativeWindow
|
||||
u64 frameTimestamp{}; //!< The timestamp of the last frame being shown
|
||||
|
||||
public:
|
||||
|
Reference in New Issue
Block a user