Merge branch 'develop' into 3DProjectiles

This commit is contained in:
Baoulettes
2022-05-29 23:02:19 +02:00
committed by GitHub
26 changed files with 970 additions and 367 deletions
@@ -96,7 +96,6 @@ static struct {
uint32_t msaa_num_quality_levels[D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT];
ComPtr<ID3D11Device> device;
ComPtr<IDXGISwapChain1> swap_chain;
ComPtr<ID3D11DeviceContext> context;
ComPtr<ID3D11RasterizerState> rasterizer_state;
ComPtr<ID3D11DepthStencilState> depth_stencil_state;
@@ -252,7 +251,24 @@ static void gfx_d3d11_init(void) {
});
// Create the swap chain
d3d.swap_chain = gfx_dxgi_create_swap_chain(d3d.device.Get());
gfx_dxgi_create_swap_chain(d3d.device.Get(), []() {
d3d.framebuffers[0].render_target_view.Reset();
d3d.textures[d3d.framebuffers[0].texture_id].texture.Reset();
d3d.context->ClearState();
d3d.context->Flush();
d3d.last_shader_program = nullptr;
d3d.last_vertex_buffer_stride = 0;
d3d.last_blend_state.Reset();
d3d.last_resource_views[0].Reset();
d3d.last_resource_views[1].Reset();
d3d.last_sampler_states[0].Reset();
d3d.last_sampler_states[1].Reset();
d3d.last_depth_test = -1;
d3d.last_depth_mask = -1;
d3d.last_zmode_decal = -1;
d3d.last_primitive_topology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
});
// Create D3D Debug device if in debug mode
@@ -266,7 +282,7 @@ static void gfx_d3d11_init(void) {
// Check the size of the window
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc;
ThrowIfFailed(d3d.swap_chain->GetDesc1(&swap_chain_desc));
ThrowIfFailed(gfx_dxgi_get_swap_chain()->GetDesc1(&swap_chain_desc));
d3d.textures[fb.texture_id].width = swap_chain_desc.Width;
d3d.textures[fb.texture_id].height = swap_chain_desc.Height;
fb.msaa_level = 1;
@@ -303,8 +319,6 @@ static void gfx_d3d11_init(void) {
ThrowIfFailed(d3d.device->CreateBuffer(&constant_buffer_desc, nullptr, d3d.per_frame_cb.GetAddressOf()),
gfx_dxgi_get_h_wnd(), "Failed to create per-frame constant buffer.");
d3d.context->PSSetConstantBuffers(0, 1, d3d.per_frame_cb.GetAddressOf());
// Create per-draw constant buffer
constant_buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
@@ -316,8 +330,6 @@ static void gfx_d3d11_init(void) {
ThrowIfFailed(d3d.device->CreateBuffer(&constant_buffer_desc, nullptr, d3d.per_draw_cb.GetAddressOf()),
gfx_dxgi_get_h_wnd(), "Failed to create per-draw constant buffer.");
d3d.context->PSSetConstantBuffers(1, 1, d3d.per_draw_cb.GetAddressOf());
// Create compute shader that can be used to retrieve depth buffer values
const char* shader_source = R"(
@@ -737,6 +749,8 @@ static void gfx_d3d11_on_resize(void) {
static void gfx_d3d11_start_frame(void) {
// Set per-frame constant buffer
ID3D11Buffer* buffers[2] = { d3d.per_frame_cb.Get(), d3d.per_draw_cb.Get() };
d3d.context->PSSetConstantBuffers(0, 2, buffers);
d3d.per_frame_cb_data.noise_frame++;
if (d3d.per_frame_cb_data.noise_frame > 150) {
@@ -803,15 +817,16 @@ static void gfx_d3d11_update_framebuffer_parameters(int fb_id, uint32_t width, u
if (msaa_level <= 1) {
ThrowIfFailed(d3d.device->CreateShaderResourceView(tex.texture.Get(), nullptr, tex.resource_view.ReleaseAndGetAddressOf()));
}
} else if (diff) {
} else if (diff || (render_target && tex.texture.Get() == nullptr)) {
DXGI_SWAP_CHAIN_DESC1 desc1;
ThrowIfFailed(d3d.swap_chain->GetDesc1(&desc1));
IDXGISwapChain1* swap_chain = gfx_dxgi_get_swap_chain();
ThrowIfFailed(swap_chain->GetDesc1(&desc1));
if (desc1.Width != width || desc1.Height != height) {
fb.render_target_view.Reset();
tex.texture.Reset();
ThrowIfFailed(d3d.swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, desc1.Flags));
ThrowIfFailed(swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, desc1.Flags));
}
ThrowIfFailed(d3d.swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *)tex.texture.ReleaseAndGetAddressOf()));
ThrowIfFailed(swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *)tex.texture.ReleaseAndGetAddressOf()));
}
if (render_target) {
ThrowIfFailed(d3d.device->CreateRenderTargetView(tex.texture.Get(), nullptr, fb.render_target_view.ReleaseAndGetAddressOf()));
@@ -34,15 +34,8 @@
#define WINCLASS_NAME L"N64GAME"
#define GFX_API_NAME "DirectX"
#ifdef VERSION_EU
#define FRAME_INTERVAL_US_NUMERATOR_ 60000
#define FRAME_INTERVAL_US_DENOMINATOR 3
#else
#define FRAME_INTERVAL_US_NUMERATOR_ 50000
#define FRAME_INTERVAL_US_DENOMINATOR 3
#endif
#define FRAME_INTERVAL_US_NUMERATOR (FRAME_INTERVAL_US_NUMERATOR_ * dxgi.frame_divisor)
#define FRAME_INTERVAL_NS_NUMERATOR 1000000000
#define FRAME_INTERVAL_NS_DENOMINATOR (dxgi.target_fps)
using namespace Microsoft::WRL; // For ComPtr
@@ -66,14 +59,19 @@ static struct {
ComPtr<IDXGIFactory2> factory;
ComPtr<IDXGISwapChain1> swap_chain;
HANDLE waitable_object;
ComPtr<IUnknown> swap_chain_device; // D3D11 Device or D3D12 Command Queue
std::function<void()> before_destroy_swap_chain_fn;
uint64_t qpc_init, qpc_freq;
uint64_t frame_timestamp; // in units of 1/FRAME_INTERVAL_US_DENOMINATOR microseconds
uint64_t frame_timestamp; // in units of 1/FRAME_INTERVAL_NS_DENOMINATOR nanoseconds
std::map<UINT, DXGI_FRAME_STATISTICS> frame_stats;
std::set<std::pair<UINT, UINT>> pending_frame_stats;
bool dropped_frame;
bool zero_latency;
float detected_hz;
UINT length_in_vsync_frames;
uint32_t frame_divisor;
uint32_t target_fps;
uint32_t maximum_frame_latency;
uint32_t applied_maximum_frame_latency;
HANDLE timer;
bool use_timer;
LARGE_INTEGER previous_present_time;
@@ -143,6 +141,22 @@ static void run_as_dpi_aware(Fun f) {
}
}
static void apply_maximum_frame_latency(bool first) {
ComPtr<IDXGISwapChain2> swap_chain2;
if (dxgi.swap_chain->QueryInterface(__uuidof(IDXGISwapChain2), &swap_chain2) == S_OK) {
ThrowIfFailed(swap_chain2->SetMaximumFrameLatency(dxgi.maximum_frame_latency));
if (first) {
dxgi.waitable_object = swap_chain2->GetFrameLatencyWaitableObject();
WaitForSingleObject(dxgi.waitable_object, INFINITE);
}
} else {
ComPtr<IDXGIDevice1> device1;
ThrowIfFailed(dxgi.swap_chain->GetDevice(__uuidof(IDXGIDevice1), &device1));
ThrowIfFailed(device1->SetMaximumFrameLatency(dxgi.maximum_frame_latency));
}
dxgi.applied_maximum_frame_latency = dxgi.maximum_frame_latency;
}
static void toggle_borderless_window_full_screen(bool enable, bool call_callback) {
// Windows 7 + flip mode + waitable object can't go to exclusive fullscreen,
// so do borderless instead. If DWM is enabled, this means we get one monitor
@@ -271,7 +285,8 @@ void gfx_dxgi_init(const char *game_name, bool start_in_fullscreen) {
dxgi.qpc_init = qpc_init.QuadPart;
dxgi.qpc_freq = qpc_freq.QuadPart;
dxgi.frame_divisor = 1;
dxgi.target_fps = 60;
dxgi.maximum_frame_latency = 1;
dxgi.timer = CreateWaitableTimer(nullptr, false, nullptr);
// Prepare window title
@@ -367,8 +382,8 @@ static void gfx_dxgi_handle_events(void) {
}*/
}
static uint64_t qpc_to_us(uint64_t qpc) {
return qpc / dxgi.qpc_freq * 1000000 + qpc % dxgi.qpc_freq * 1000000 / dxgi.qpc_freq;
static uint64_t qpc_to_ns(uint64_t qpc) {
return qpc / dxgi.qpc_freq * 1000000000 + qpc % dxgi.qpc_freq * 1000000000 / dxgi.qpc_freq;
}
static uint64_t qpc_to_100ns(uint64_t qpc) {
@@ -406,7 +421,7 @@ static bool gfx_dxgi_start_frame(void) {
dxgi.use_timer = false;
dxgi.frame_timestamp += FRAME_INTERVAL_US_NUMERATOR;
dxgi.frame_timestamp += FRAME_INTERVAL_NS_NUMERATOR;
if (dxgi.frame_stats.size() >= 2) {
DXGI_FRAME_STATISTICS *first = &dxgi.frame_stats.begin()->second;
@@ -421,14 +436,16 @@ static bool gfx_dxgi_start_frame(void) {
}
double estimated_vsync_interval = (double)sync_qpc_diff / (double)sync_vsync_diff;
uint64_t estimated_vsync_interval_us = qpc_to_us(estimated_vsync_interval);
//printf("Estimated vsync_interval: %d\n", (int)estimated_vsync_interval_us);
if (estimated_vsync_interval_us < 2 || estimated_vsync_interval_us > 1000000) {
uint64_t estimated_vsync_interval_ns = qpc_to_ns(estimated_vsync_interval);
//printf("Estimated vsync_interval: %d\n", (int)estimated_vsync_interval_ns);
if (estimated_vsync_interval_ns < 2000 || estimated_vsync_interval_ns > 1000000000) {
// Unreasonable, maybe a monitor change
estimated_vsync_interval_us = 16666;
estimated_vsync_interval = estimated_vsync_interval_us * dxgi.qpc_freq / 1000000;
estimated_vsync_interval_ns = 16666666;
estimated_vsync_interval = estimated_vsync_interval_ns * dxgi.qpc_freq / 1000000000;
}
dxgi.detected_hz = (float)((double)1000000000 / (double)estimated_vsync_interval_ns);
UINT queued_vsyncs = 0;
bool is_first = true;
for (const std::pair<UINT, UINT>& p : dxgi.pending_frame_stats) {
@@ -440,21 +457,21 @@ static bool gfx_dxgi_start_frame(void) {
}
uint64_t last_frame_present_end_qpc = (last->SyncQPCTime.QuadPart - dxgi.qpc_init) + estimated_vsync_interval * queued_vsyncs;
uint64_t last_end_us = qpc_to_us(last_frame_present_end_qpc);
uint64_t last_end_ns = qpc_to_ns(last_frame_present_end_qpc);
double vsyncs_to_wait = (double)(int64_t)(dxgi.frame_timestamp / FRAME_INTERVAL_US_DENOMINATOR - last_end_us) / estimated_vsync_interval_us;
//printf("ts: %llu, last_end_us: %llu, Init v: %f\n", dxgi.frame_timestamp / 3, last_end_us, vsyncs_to_wait);
double vsyncs_to_wait = (double)(int64_t)(dxgi.frame_timestamp / FRAME_INTERVAL_NS_DENOMINATOR - last_end_ns) / estimated_vsync_interval_ns;
//printf("ts: %llu, last_end_ns: %llu, Init v: %f\n", dxgi.frame_timestamp / 3, last_end_ns, vsyncs_to_wait);
if (vsyncs_to_wait <= 0) {
// Too late
if ((int64_t)(dxgi.frame_timestamp / FRAME_INTERVAL_US_DENOMINATOR - last_end_us) < -66666) {
if ((int64_t)(dxgi.frame_timestamp / FRAME_INTERVAL_NS_DENOMINATOR - last_end_ns) < -66666666) {
// The application must have been paused or similar
vsyncs_to_wait = round(((double)FRAME_INTERVAL_US_NUMERATOR / FRAME_INTERVAL_US_DENOMINATOR) / estimated_vsync_interval_us);
vsyncs_to_wait = round(((double)FRAME_INTERVAL_NS_NUMERATOR / FRAME_INTERVAL_NS_DENOMINATOR) / estimated_vsync_interval_ns);
if (vsyncs_to_wait < 1) {
vsyncs_to_wait = 1;
}
dxgi.frame_timestamp = FRAME_INTERVAL_US_DENOMINATOR * (last_end_us + vsyncs_to_wait * estimated_vsync_interval_us);
dxgi.frame_timestamp = FRAME_INTERVAL_NS_DENOMINATOR * (last_end_ns + vsyncs_to_wait * estimated_vsync_interval_ns);
} else {
// Drop frame
//printf("Dropping frame\n");
@@ -464,9 +481,9 @@ static bool gfx_dxgi_start_frame(void) {
}
double orig_wait = vsyncs_to_wait;
if (floor(vsyncs_to_wait) != vsyncs_to_wait) {
uint64_t left = last_end_us + floor(vsyncs_to_wait) * estimated_vsync_interval_us;
uint64_t right = last_end_us + ceil(vsyncs_to_wait) * estimated_vsync_interval_us;
uint64_t adjusted_desired_time = dxgi.frame_timestamp / FRAME_INTERVAL_US_DENOMINATOR + (last_end_us + (FRAME_INTERVAL_US_NUMERATOR / FRAME_INTERVAL_US_DENOMINATOR) > dxgi.frame_timestamp / FRAME_INTERVAL_US_DENOMINATOR ? 2000 : -2000);
uint64_t left = last_end_ns + floor(vsyncs_to_wait) * estimated_vsync_interval_ns;
uint64_t right = last_end_ns + ceil(vsyncs_to_wait) * estimated_vsync_interval_ns;
uint64_t adjusted_desired_time = dxgi.frame_timestamp / FRAME_INTERVAL_NS_DENOMINATOR + (last_end_ns + (FRAME_INTERVAL_NS_NUMERATOR / FRAME_INTERVAL_NS_DENOMINATOR) > dxgi.frame_timestamp / FRAME_INTERVAL_NS_DENOMINATOR ? 2000000 : -2000000);
int64_t diff_left = adjusted_desired_time - left;
int64_t diff_right = right - adjusted_desired_time;
if (diff_left < 0) {
@@ -506,7 +523,7 @@ static void gfx_dxgi_swap_buffers_begin(void) {
LARGE_INTEGER t;
if (dxgi.use_timer) {
QueryPerformanceCounter(&t);
int64_t next = qpc_to_100ns(dxgi.previous_present_time.QuadPart) + 10 * FRAME_INTERVAL_US_NUMERATOR / FRAME_INTERVAL_US_DENOMINATOR;
int64_t next = qpc_to_100ns(dxgi.previous_present_time.QuadPart) + FRAME_INTERVAL_NS_NUMERATOR / (FRAME_INTERVAL_NS_DENOMINATOR * 100);
int64_t left = next - qpc_to_100ns(t.QuadPart);
if (left > 0) {
LARGE_INTEGER li;
@@ -531,6 +548,32 @@ static void gfx_dxgi_swap_buffers_end(void) {
QueryPerformanceCounter(&t0);
QueryPerformanceCounter(&t1);
if (dxgi.applied_maximum_frame_latency > dxgi.maximum_frame_latency) {
// There seems to be a bug that if latency is decreased, there is no effect of that operation, so recreate swap chain
if (dxgi.waitable_object != nullptr) {
if (!dxgi.dropped_frame) {
// Wait the last time on this swap chain
WaitForSingleObject(dxgi.waitable_object, INFINITE);
}
CloseHandle(dxgi.waitable_object);
dxgi.waitable_object = nullptr;
}
dxgi.before_destroy_swap_chain_fn();
dxgi.swap_chain.Reset();
gfx_dxgi_create_swap_chain(dxgi.swap_chain_device.Get(), move(dxgi.before_destroy_swap_chain_fn));
dxgi.frame_timestamp = 0;
dxgi.frame_stats.clear();
dxgi.pending_frame_stats.clear();
return; // Make sure we don't wait a second time on the waitable object, since that would hang the program
} else if (dxgi.applied_maximum_frame_latency != dxgi.maximum_frame_latency) {
apply_maximum_frame_latency(false);
}
if (!dxgi.dropped_frame) {
if (dxgi.waitable_object != nullptr) {
WaitForSingleObject(dxgi.waitable_object, INFINITE);
@@ -554,8 +597,20 @@ static double gfx_dxgi_get_time(void) {
return (double)(t.QuadPart - dxgi.qpc_init) / dxgi.qpc_freq;
}
static void gfx_dxgi_set_frame_divisor(int divisor) {
dxgi.frame_divisor = divisor;
static void gfx_dxgi_set_target_fps(int fps) {
uint32_t old_fps = dxgi.target_fps;
uint64_t t0 = dxgi.frame_timestamp / old_fps;
uint32_t t1 = dxgi.frame_timestamp % old_fps;
dxgi.target_fps = fps;
dxgi.frame_timestamp = t0 * dxgi.target_fps + t1 * dxgi.target_fps / old_fps;
}
static void gfx_dxgi_set_maximum_frame_latency(int latency) {
dxgi.maximum_frame_latency = latency;
}
static float gfx_dxgi_get_detected_hz() {
return dxgi.detected_hz;
}
void gfx_dxgi_create_factory_and_device(bool debug, int d3d_version, bool (*create_device_fn)(IDXGIAdapter1 *adapter, bool test_only)) {
@@ -592,12 +647,12 @@ void gfx_dxgi_create_factory_and_device(bool debug, int d3d_version, bool (*crea
SetWindowTextW(dxgi.h_wnd, w_title);
}
ComPtr<IDXGISwapChain1> gfx_dxgi_create_swap_chain(IUnknown *device) {
void gfx_dxgi_create_swap_chain(IUnknown *device, std::function<void()>&& before_destroy_fn) {
bool win8 = IsWindows8OrGreater(); // DXGI_SCALING_NONE is only supported on Win8 and beyond
bool dxgi_13 = dxgi.CreateDXGIFactory2 != nullptr; // DXGI 1.3 introduced waitable object
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
swap_chain_desc.BufferCount = 2;
swap_chain_desc.BufferCount = 3;
swap_chain_desc.Width = 0;
swap_chain_desc.Height = 0;
swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
@@ -617,28 +672,24 @@ ComPtr<IDXGISwapChain1> gfx_dxgi_create_swap_chain(IUnknown *device) {
});
ThrowIfFailed(dxgi.factory->MakeWindowAssociation(dxgi.h_wnd, DXGI_MWA_NO_ALT_ENTER));
ComPtr<IDXGISwapChain2> swap_chain2;
if (dxgi.swap_chain->QueryInterface(__uuidof(IDXGISwapChain2), &swap_chain2) == S_OK) {
ThrowIfFailed(swap_chain2->SetMaximumFrameLatency(1));
dxgi.waitable_object = swap_chain2->GetFrameLatencyWaitableObject();
WaitForSingleObject(dxgi.waitable_object, INFINITE);
} else {
ComPtr<IDXGIDevice1> device1;
ThrowIfFailed(device->QueryInterface(IID_PPV_ARGS(&device1)));
ThrowIfFailed(device1->SetMaximumFrameLatency(1));
}
apply_maximum_frame_latency(true);
ThrowIfFailed(dxgi.swap_chain->GetDesc1(&swap_chain_desc));
dxgi.current_width = swap_chain_desc.Width;
dxgi.current_height = swap_chain_desc.Height;
return dxgi.swap_chain;
dxgi.swap_chain_device = device;
dxgi.before_destroy_swap_chain_fn = std::move(before_destroy_fn);
}
HWND gfx_dxgi_get_h_wnd(void) {
return dxgi.h_wnd;
}
IDXGISwapChain1* gfx_dxgi_get_swap_chain() {
return dxgi.swap_chain.Get();
}
void ThrowIfFailed(HRESULT res) {
if (FAILED(res)) {
fprintf(stderr, "Error: 0x%08X\n", res);
@@ -668,7 +719,9 @@ extern "C" struct GfxWindowManagerAPI gfx_dxgi_api = {
gfx_dxgi_swap_buffers_begin,
gfx_dxgi_swap_buffers_end,
gfx_dxgi_get_time,
gfx_dxgi_set_frame_divisor,
gfx_dxgi_set_target_fps,
gfx_dxgi_set_maximum_frame_latency,
gfx_dxgi_get_detected_hz,
};
#endif
@@ -4,9 +4,15 @@
#include "gfx_rendering_api.h"
#ifdef DECLARE_GFX_DXGI_FUNCTIONS
#include <functional>
#include <dxgi1_2.h>
void gfx_dxgi_create_factory_and_device(bool debug, int d3d_version, bool (*create_device_fn)(IDXGIAdapter1 *adapter, bool test_only));
Microsoft::WRL::ComPtr<IDXGISwapChain1> gfx_dxgi_create_swap_chain(IUnknown *device);
void gfx_dxgi_create_swap_chain(IUnknown *device, std::function<void()>&& before_destroy_fn);
HWND gfx_dxgi_get_h_wnd(void);
IDXGISwapChain1* gfx_dxgi_get_swap_chain();
void ThrowIfFailed(HRESULT res);
void ThrowIfFailed(HRESULT res, HWND h_wnd, const char *message);
#endif
@@ -2789,6 +2789,9 @@ void gfx_run(Gfx *commands, const std::unordered_map<Mtx *, MtxF>& mtx_replaceme
gfx_rapi->start_frame();
gfx_rapi->start_draw_to_framebuffer(game_renders_to_framebuffer ? game_framebuffer : 0, (float)gfx_current_dimensions.height / SCREEN_HEIGHT);
gfx_rapi->clear_framebuffer();
rdp.viewport_or_scissor_changed = true;
rendering_state.viewport = {};
rendering_state.scissor = {};
gfx_run_dl(commands);
gfx_flush();
SohUtils::saveEnvironmentVar("framebuffer", string());
@@ -2825,8 +2828,16 @@ void gfx_end_frame(void) {
}
}
void gfx_set_framedivisor(int divisor) {
gfx_wapi->set_frame_divisor(divisor);
void gfx_set_target_fps(int fps) {
gfx_wapi->set_target_fps(fps);
}
void gfx_set_maximum_frame_latency(int latency) {
gfx_wapi->set_maximum_frame_latency(latency);
}
float gfx_get_detected_hz(void) {
return gfx_wapi->get_detected_hz();
}
int gfx_create_framebuffer(uint32_t width, uint32_t height) {
@@ -67,7 +67,9 @@ struct GfxRenderingAPI* gfx_get_current_rendering_api(void);
void gfx_start_frame(void);
void gfx_run(Gfx* commands, const std::unordered_map<Mtx*, MtxF>& mtx_replacements);
void gfx_end_frame(void);
void gfx_set_framedivisor(int);
void gfx_set_target_fps(int);
void gfx_set_maximum_frame_latency(int latency);
float gfx_get_detected_hz(void);
void gfx_texture_cache_clear();
extern "C" int gfx_create_framebuffer(uint32_t width, uint32_t height);
void gfx_get_pixel_depth_prepare(float x, float y);
@@ -123,11 +123,10 @@ static uint64_t previous_time;
static HANDLE timer;
#endif
static int frameDivisor = 1;
static int target_fps = 60;
#define FRAME_INTERVAL_US_NUMERATOR_ 50000
#define FRAME_INTERVAL_US_DENOMINATOR 3
#define FRAME_INTERVAL_US_NUMERATOR (FRAME_INTERVAL_US_NUMERATOR_ * frameDivisor)
#define FRAME_INTERVAL_US_NUMERATOR 1000000
#define FRAME_INTERVAL_US_DENOMINATOR (target_fps)
static void gfx_sdl_init(const char *game_name, bool start_in_fullscreen) {
SDL_Init(SDL_INIT_VIDEO);
@@ -250,6 +249,7 @@ static void gfx_sdl_handle_events(void) {
}
break;
case SDL_QUIT:
SDL_Quit(); // bandaid fix for linux window closing issue
exit(0);
}
}
@@ -266,15 +266,16 @@ static uint64_t qpc_to_100ns(uint64_t qpc) {
static inline void sync_framerate_with_timer(void) {
uint64_t t;
t = SDL_GetPerformanceCounter();
t = qpc_to_100ns(SDL_GetPerformanceCounter());
const int64_t next = qpc_to_100ns(previous_time) + 10 * FRAME_INTERVAL_US_NUMERATOR / FRAME_INTERVAL_US_DENOMINATOR;
const int64_t left = next - qpc_to_100ns(t);
const int64_t next = previous_time + 10 * FRAME_INTERVAL_US_NUMERATOR / FRAME_INTERVAL_US_DENOMINATOR;
const int64_t left = next - t;
if (left > 0) {
#ifdef __linux__
const timespec spec = { 0, left * 100 };
nanosleep(&spec, nullptr);
#else
// The accuracy of this timer seems to usually be within +- 1.0 ms
LARGE_INTEGER li;
li.QuadPart = -left;
SetWaitableTimer(timer, &li, 0, nullptr, nullptr, false);
@@ -282,7 +283,13 @@ static inline void sync_framerate_with_timer(void) {
#endif
}
t = SDL_GetPerformanceCounter();
t = qpc_to_100ns(SDL_GetPerformanceCounter());
if (left > 0 && t - next < 10000) {
// In case it takes some time for the application to wake up after sleep,
// or inaccurate timer,
// don't let that slow down the framerate.
t = next;
}
previous_time = t;
}
@@ -299,9 +306,16 @@ static double gfx_sdl_get_time(void) {
return 0.0;
}
static void gfx_sdl_set_framedivisor(int divisor)
{
frameDivisor = divisor;
static void gfx_sdl_set_target_fps(int fps) {
target_fps = fps;
}
static void gfx_sdl_set_maximum_frame_latency(int latency) {
// Not supported by SDL :(
}
static float gfx_sdl_get_detected_hz(void) {
return 0;
}
struct GfxWindowManagerAPI gfx_sdl = {
@@ -317,7 +331,9 @@ struct GfxWindowManagerAPI gfx_sdl = {
gfx_sdl_swap_buffers_begin,
gfx_sdl_swap_buffers_end,
gfx_sdl_get_time,
gfx_sdl_set_framedivisor
gfx_sdl_set_target_fps,
gfx_sdl_set_maximum_frame_latency,
gfx_sdl_get_detected_hz
};
#endif
@@ -17,7 +17,9 @@ struct GfxWindowManagerAPI {
void (*swap_buffers_begin)(void);
void (*swap_buffers_end)(void);
double (*get_time)(void); // For debug
void (*set_frame_divisor)(int);
void (*set_target_fps)(int fps);
void (*set_maximum_frame_latency)(int latency);
float (*get_detected_hz)(void);
};
#endif
File diff suppressed because it is too large Load Diff
+12 -7
View File
@@ -63,12 +63,14 @@ namespace SohImGui {
extern bool needs_save;
void Init(WindowImpl window_impl);
void Update(EventImpl event);
void EnhancementRadioButton(std::string text, std::string cvarName, int value);
void EnhancementCheckbox(std::string text, std::string cvarName);
void EnhancementSliderInt(std::string text, std::string id, std::string cvarName, int min, int max, std::string format);
void EnhancementSliderFloat(std::string text, std::string id, std::string cvarName, float min, float max, std::string format, float defaultValue);
void EnhancementColor3(std::string text, std::string cvarName, float ColorRGB[3], bool TitleSameLine);
void Tooltip(const char* text);
void EnhancementRadioButton(const char* text, const char* cvarName, int id);
void EnhancementCheckbox(const char* text, const char* cvarName);
void EnhancementButton(const char* text, const char* cvarName);
void EnhancementSliderInt(const char* text, const char* id, const char* cvarName, int min, int max, const char* format);
void EnhancementSliderFloat(const char* text, const char* id, const char* cvarName, float min, float max, const char* format, float defaultValue, bool isPercentage);
void EnhancementColor(const char* text, const char* cvarName, ImVec4 ColorRGBA, ImVec4 default_colors, bool allow_rainbow = true, bool has_alpha=false, bool TitleSameLine=false);
void DrawMainMenuAndCalculateGameSize(void);
@@ -79,7 +81,10 @@ namespace SohImGui {
void BindCmd(const std::string& cmd, CommandEntry entry);
void AddWindow(const std::string& category, const std::string& name, WindowDrawFunc drawFunc);
void LoadResource(const std::string& name, const std::string& path, const ImVec4& tint = ImVec4(1, 1, 1, 1));
void LoadInterfaceEditor();
void LoadPickersColors(ImVec4& ColorArray, const char* cvarname, const ImVec4& default_colors, bool has_alpha=false);
void RandomizeColor(const char* cvarName, ImVec4* colors);
void RainbowColor(const char* cvarName, ImVec4* colors);
void ResetColor(const char* cvarName, ImVec4* colors, ImVec4 defaultcolors, bool has_alpha);
ImTextureID GetTextureByID(int id);
ImTextureID GetTextureByName(const std::string& name);
}
+6 -5
View File
@@ -272,13 +272,14 @@ namespace Ship {
gfx_run(Commands, m);
gfx_end_frame();
}
gfx_run(Commands, {});
gfx_end_frame();
}
void Window::SetFrameDivisor(int divisor) {
gfx_set_framedivisor(divisor);
//gfx_set_framedivisor(0);
void Window::SetTargetFps(int fps) {
gfx_set_target_fps(fps);
}
void Window::SetMaximumFrameLatency(int latency) {
gfx_set_maximum_frame_latency(latency);
}
void Window::GetPixelDepthPrepare(float x, float y) {
+2 -1
View File
@@ -20,7 +20,8 @@ namespace Ship {
void Init();
void StartFrame();
void RunCommands(Gfx* Commands, const std::vector<std::unordered_map<Mtx*, MtxF>>& mtx_replacements);
void SetFrameDivisor(int divisor);
void SetTargetFps(int fps);
void SetMaximumFrameLatency(int latency);
void GetPixelDepthPrepare(float x, float y);
uint16_t GetPixelDepth(float x, float y);
void ToggleFullscreen();
+2 -2
View File
@@ -18,8 +18,8 @@ WARN := \
-funsigned-char \
-m32 -mhard-float -fno-stack-protector -fno-common -fno-zero-initialized-in-bss -fno-strict-aliasing -fno-inline-functions -fno-inline-small-functions -fno-toplevel-reorder -ffreestanding -fwrapv \
CXXFLAGS := $(WARN) -std=c++20 -D_GNU_SOURCE -fpermissive -no-pie -nostdlib -march=i386
CFLAGS := $(WARN) -std=c99 -D_GNU_SOURCE -no-pie -nostdlib -march=i386
CXXFLAGS := $(WARN) -std=c++20 -D_GNU_SOURCE -fpermissive -no-pie -nostdlib -march=i386 -msse2 -mfpmath=sse
CFLAGS := $(WARN) -std=c99 -D_GNU_SOURCE -no-pie -nostdlib -march=i386 -msse2 -mfpmath=sse
LDFLAGS := -m32
CPPFLAGS := -MMD
@@ -1,6 +1,6 @@
<Root>
<File Name="object_triforce_spot" Segment="6">
<Array Name="gTriforceVtx" Count="32" Offset="0x0000">
<Array Name="gTriforceVtx" Count="96" Offset="0x0000">
<Vtx/>
</Array>
+34 -6
View File
@@ -175,8 +175,6 @@ extern "C" void Graph_StartFrame() {
// C->C++ Bridge
extern "C" void Graph_ProcessGfxCommands(Gfx* commands) {
OTRGlobals::Instance->context->GetWindow()->SetFrameDivisor(CVar_GetS32("g60FPS", 0) == 0 ? R_UPDATE_RATE : 1);
if (!audio.initialized) {
audio.initialized = true;
std::thread([]() {
@@ -226,15 +224,45 @@ extern "C" void Graph_ProcessGfxCommands(Gfx* commands) {
audio.cv_to_thread.notify_one();
std::vector<std::unordered_map<Mtx*, MtxF>> mtx_replacements;
if (CVar_GetS32("g60FPS", 0) != 0) {
int to = R_UPDATE_RATE;
for (int i = 1; i < to; i++) {
mtx_replacements.push_back(FrameInterpolation_Interpolate(i / (float)to));
int target_fps = CVar_GetS32("gInterpolationFPS", 20);
static int last_fps;
static int last_update_rate;
static int time;
int fps = target_fps;
int original_fps = 60 / R_UPDATE_RATE;
if (target_fps == 20 || original_fps > target_fps) {
fps = original_fps;
}
if (last_fps != fps || last_update_rate != R_UPDATE_RATE) {
time = 0;
}
// time_base = fps * original_fps (one second)
int next_original_frame = fps;
while (time + original_fps <= next_original_frame) {
time += original_fps;
if (time != next_original_frame) {
mtx_replacements.push_back(FrameInterpolation_Interpolate((float)time / next_original_frame));
} else {
mtx_replacements.emplace_back();
}
}
time -= fps;
OTRGlobals::Instance->context->GetWindow()->SetTargetFps(fps);
int threshold = CVar_GetS32("gExtraLatencyThreshold", 80);
OTRGlobals::Instance->context->GetWindow()->SetMaximumFrameLatency(threshold > 0 && target_fps >= threshold ? 2 : 1);
OTRGlobals::Instance->context->GetWindow()->RunCommands(commands, mtx_replacements);
last_fps = fps;
last_update_rate = R_UPDATE_RATE;
{
std::unique_lock<std::mutex> Lock(audio.mutex);
while (audio.processing) {
+1 -1
View File
@@ -451,7 +451,7 @@ void FrameInterpolation_StartRecord(void) {
current_recording = {};
current_path.clear();
current_path.push_back(&current_recording.root_path);
if (CVar_GetS32("g60FPS", 0) != 0) {
if (CVar_GetS32("gInterpolationFPS", 20) != 20) {
is_recording = true;
}
}
+42 -1
View File
@@ -19,9 +19,50 @@ MessageTableEntry* OTRMessage_LoadTable(const char* filePath, bool isNES) {
if (file == nullptr)
return nullptr;
MessageTableEntry* table = (MessageTableEntry*)malloc(sizeof(MessageTableEntry) * file->messages.size());
// Allocate room for an additional message
MessageTableEntry* table = (MessageTableEntry*)malloc(sizeof(MessageTableEntry) * (file->messages.size() + 1));
for (int i = 0; i < file->messages.size(); i++) {
// Look for Owl Text
if (file->messages[i].id == 0x2066) {
// Create a new message based on the Owl Text
char* kaeporaPatch = (char*)malloc(sizeof(char) * file->messages[i].msg.size());
file->messages[i].msg.copy(kaeporaPatch, file->messages[i].msg.size(), 0);
// Swap the order of yes and no in this new message
if (filePath == "text/nes_message_data_static/nes_message_data_static") {
kaeporaPatch[26] = 'Y';
kaeporaPatch[27] = 'e';
kaeporaPatch[28] = 's';
kaeporaPatch[29] = 1;
kaeporaPatch[30] = 'N';
kaeporaPatch[31] = 'o';
} else if (filePath == "text/ger_message_data_static/ger_message_data_static") {
kaeporaPatch[30] = 'J';
kaeporaPatch[31] = 'a';
kaeporaPatch[32] = '!';
kaeporaPatch[33] = 1;
kaeporaPatch[34] = 'N';
kaeporaPatch[35] = 'e';
kaeporaPatch[36] = 'i';
kaeporaPatch[37] = 'n';
} else {
kaeporaPatch[26] = 'O';
kaeporaPatch[27] = 'u';
kaeporaPatch[28] = 'i';
kaeporaPatch[29] = 1;
kaeporaPatch[30] = 'N';
kaeporaPatch[31] = 'o';
kaeporaPatch[32] = 'n';
}
// load data into message
table[file->messages.size()].textId = 0x71B3;
table[file->messages.size()].typePos = (file->messages[i].textboxType << 4) | file->messages[i].textboxYPos;
table[file->messages.size()].segment = kaeporaPatch;
table[file->messages.size()].msgSize = file->messages[i].msg.size();
}
table[i].textId = file->messages[i].id;
table[i].typePos = (file->messages[i].textboxType << 4) | file->messages[i].textboxYPos;
table[i].segment = file->messages[i].msg.c_str();
+1 -1
View File
@@ -2290,8 +2290,8 @@ void Actor_DrawFaroresWindPointer(GlobalContext* globalCtx) {
((void)0, gSaveContext.respawn[RESPAWN_MODE_TOP].pos.y) + yOffset,
((void)0, gSaveContext.respawn[RESPAWN_MODE_TOP].pos.z), 255, 255, 255, lightRadius);
CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 5474);
}
CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 5474);
}
void func_80030488(GlobalContext* globalCtx) {
+28 -21
View File
@@ -128,6 +128,13 @@ void HealthMeter_Init(GlobalContext* globalCtx) {
HeartDDOutline[0] = CVar_GetS32("gDDCCHeartsPrimR", 90);
HeartDDOutline[1] = CVar_GetS32("gDDCCHeartsPrimG", 90);
HeartDDOutline[2] = CVar_GetS32("gDDCCHeartsPrimB", 90);
} else {
HeartInner[0] = HEARTS_PRIM_R;
HeartInner[1] = HEARTS_PRIM_G;
HeartInner[2] = HEARTS_PRIM_B;
HeartDDOutline[0] = HEARTS_DD_PRIM_R;
HeartDDOutline[1] = HEARTS_DD_PRIM_G;
HeartDDOutline[2] = HEARTS_DD_PRIM_B;
}
interfaceCtx->unk_228 = 0x140;
@@ -194,12 +201,12 @@ void HealthMeter_Update(GlobalContext* globalCtx) {
HeartDDOutline[1] = CVar_GetS32("gDDCCHeartsPrimG", sHeartsDDPrim[0][1]);
HeartDDOutline[2] = CVar_GetS32("gDDCCHeartsPrimB", sHeartsDDPrim[0][2]);
} else {
HeartInner[0] = sHeartsPrimColors[0][0];
HeartInner[1] = sHeartsPrimColors[0][1];
HeartInner[2] = sHeartsPrimColors[0][2];
HeartDDOutline[0] = sHeartsDDPrim[0][0];
HeartDDOutline[1] = sHeartsDDPrim[0][1];
HeartDDOutline[2] = sHeartsDDPrim[0][2];
HeartInner[0] = HEARTS_PRIM_R;
HeartInner[1] = HEARTS_PRIM_G;
HeartInner[2] = HEARTS_PRIM_B;
HeartDDOutline[0] = HEARTS_DD_PRIM_R;
HeartDDOutline[1] = HEARTS_DD_PRIM_G;
HeartDDOutline[2] = HEARTS_DD_PRIM_B;
}
if (interfaceCtx->unk_200 != 0) {
@@ -299,33 +306,33 @@ void HealthMeter_Update(GlobalContext* globalCtx) {
sBeatingHeartsDDEnv[1] = (u8)(gFactor + HeartDDInner[1]) & 0xFF;
sBeatingHeartsDDEnv[2] = (u8)(bFactor + HeartDDInner[2]) & 0xFF;
} else {
sHeartsDDPrim[2][0] = HeartInner[0];
sHeartsDDPrim[2][1] = HeartInner[1];
sHeartsDDPrim[2][2] = HeartInner[2];
sHeartsDDPrim[2][0] = HEARTS_PRIM_R;
sHeartsDDPrim[2][1] = HEARTS_PRIM_G;
sHeartsDDPrim[2][2] = HEARTS_PRIM_B;
sHeartsDDPrim[1][0] = sHeartsDDPrimColors[ddType][0];
sHeartsDDPrim[1][1] = sHeartsDDPrimColors[ddType][1];
sHeartsDDPrim[1][2] = sHeartsDDPrimColors[ddType][2];
sHeartsDDPrim[1][0] = HEARTS_DD_PRIM_R;
sHeartsDDPrim[1][1] = HEARTS_DD_PRIM_G;
sHeartsDDPrim[1][2] = HEARTS_DD_PRIM_B;
sHeartsDDEnv[1][0] = sHeartsDDEnvColors[ddType][0];
sHeartsDDEnv[1][1] = sHeartsDDEnvColors[ddType][1];
sHeartsDDEnv[1][2] = sHeartsDDEnvColors[ddType][2];
sHeartsDDEnv[1][0] = HEARTS_PRIM_R;
sHeartsDDEnv[1][1] = HEARTS_PRIM_G;
sHeartsDDEnv[1][2] = HEARTS_PRIM_B;
rFactor = sHeartsDDPrimFactors[ddType][0] * ddFactor;
gFactor = sHeartsDDPrimFactors[ddType][1] * ddFactor;
bFactor = sHeartsDDPrimFactors[ddType][2] * ddFactor;
sBeatingHeartsDDPrim[0] = (u8)(rFactor + HeartDDOutline[0]) & 0xFF;
sBeatingHeartsDDPrim[1] = (u8)(gFactor + HeartDDOutline[1]) & 0xFF;
sBeatingHeartsDDPrim[2] = (u8)(bFactor + HeartDDOutline[2]) & 0xFF;
sBeatingHeartsDDPrim[0] = (u8)(rFactor + HEARTS_DD_PRIM_R) & 0xFF;
sBeatingHeartsDDPrim[1] = (u8)(gFactor + HEARTS_DD_PRIM_G) & 0xFF;
sBeatingHeartsDDPrim[2] = (u8)(bFactor + HEARTS_DD_PRIM_B) & 0xFF;
rFactor = sHeartsDDEnvFactors[ddType][0] * ddFactor;
gFactor = sHeartsDDEnvFactors[ddType][1] * ddFactor;
bFactor = sHeartsDDEnvFactors[ddType][2] * ddFactor;
sBeatingHeartsDDEnv[0] = (u8)(rFactor + HeartDDInner[0]) & 0xFF;
sBeatingHeartsDDEnv[1] = (u8)(gFactor + HeartDDInner[1]) & 0xFF;
sBeatingHeartsDDEnv[2] = (u8)(bFactor + HeartDDInner[2]) & 0xFF;
sBeatingHeartsDDEnv[0] = (u8)(rFactor + HEARTS_PRIM_R) & 0xFF;
sBeatingHeartsDDEnv[1] = (u8)(gFactor + HEARTS_PRIM_G) & 0xFF;
sBeatingHeartsDDEnv[2] = (u8)(bFactor + HEARTS_PRIM_B) & 0xFF;
}
}
+10 -3
View File
@@ -272,6 +272,13 @@ void Message_FindMessage(GlobalContext* globalCtx, u16 textId) {
const char** languageSegmentTable;
Font* font;
const char* seg;
u16 bufferId = textId;
// Use the better owl message if better owl is enabled
if (CVar_GetS32("gBetterOwl", 0) != 0 && (bufferId == 0x2066 || bufferId == 0x607B ||
bufferId == 0x10C2 || bufferId == 0x10C6 || bufferId == 0x206A))
{
bufferId = 0x71B3;
}
if (gSaveContext.language == LANGUAGE_GER)
messageTableEntry = sGerMessageEntryTablePtr;
@@ -287,7 +294,7 @@ void Message_FindMessage(GlobalContext* globalCtx, u16 textId) {
while (messageTableEntry->textId != 0xFFFF) {
font = &globalCtx->msgCtx.font;
if (messageTableEntry->textId == textId) {
if (messageTableEntry->textId == bufferId) {
foundSeg = messageTableEntry->segment;
font->charTexBuf[0] = messageTableEntry->typePos;
@@ -298,14 +305,14 @@ void Message_FindMessage(GlobalContext* globalCtx, u16 textId) {
// "Message found!!!"
osSyncPrintf(" メッセージが,見つかった!!! = %x "
"(data=%x) (data0=%x) (data1=%x) (data2=%x) (data3=%x)\n",
textId, font->msgOffset, font->msgLength, foundSeg, seg, nextSeg);
bufferId, font->msgOffset, font->msgLength, foundSeg, seg, nextSeg);
return;
}
messageTableEntry++;
}
// "Message not found!!!"
osSyncPrintf(" メッセージが,見つからなかった!!! = %x\n", textId);
osSyncPrintf(" メッセージが,見つからなかった!!! = %x\n", bufferId);
font = &globalCtx->msgCtx.font;
messageTableEntry = sNesMessageEntryTablePtr;
+20 -1
View File
@@ -2884,7 +2884,26 @@ void Interface_DrawItemButtons(GlobalContext* globalCtx) {
gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 0);
gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0,
PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0);
//There is probably a more elegant way to do it.
char* doAction = actionsTbl[3];
char newName[512];
if (gSaveContext.language != LANGUAGE_ENG) {
size_t length = strlen(doAction);
strcpy(newName, doAction);
if (gSaveContext.language == LANGUAGE_FRA) {
newName[length - 6] = 'F';
newName[length - 5] = 'R';
newName[length - 4] = 'A';
} else if (gSaveContext.language == LANGUAGE_GER) {
newName[length - 6] = 'G';
newName[length - 5] = 'E';
newName[length - 4] = 'R';
}
doAction = newName;
}
memcpy(interfaceCtx->doActionSegment + DO_ACTION_TEX_SIZE * 2, ResourceMgr_LoadTexByName(doAction), DO_ACTION_TEX_SIZE);
gDPLoadTextureBlock_4b(OVERLAY_DISP++, interfaceCtx->doActionSegment + DO_ACTION_TEX_SIZE * 2, G_IM_FMT_IA,
DO_ACTION_TEX_WIDTH, DO_ACTION_TEX_HEIGHT, 0, G_TX_NOMIRROR | G_TX_WRAP,
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD);

Some files were not shown because too many files have changed in this diff Show More