Graphics backend enhancements etc. (#163)

This commit is contained in:
Emill 2022-04-18 11:37:47 +02:00 committed by GitHub
parent fe6dbd2a5b
commit ceef4a9453
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 872 additions and 588 deletions

View File

@ -52,6 +52,11 @@ struct SoHConfigType {
bool moon_jump_on_l = false;
bool super_tunic = false;
} cheats;
// Graphics
struct {
bool show = false;
} graphics;
};
enum SeqPlayers {

View File

@ -37,9 +37,8 @@ namespace {
struct PerFrameCB {
uint32_t noise_frame;
float noise_scale_x;
float noise_scale_y;
uint32_t padding;
float noise_scale;
uint32_t padding[2]; // constant buffers must be multiples of 16 bytes in size
};
struct PerDrawCB {
@ -51,12 +50,12 @@ struct PerDrawCB {
} textures[2];
};
struct CoordCB {
float x, y;
float padding[2]; // structure size must be multiple of 16
struct Coord {
int x, y;
};
struct TextureData {
ComPtr<ID3D11Texture2D> texture;
ComPtr<ID3D11ShaderResourceView> resource_view;
ComPtr<ID3D11SamplerState> sampler_state;
uint32_t width;
@ -64,10 +63,13 @@ struct TextureData {
bool linear_filtering;
};
struct FramebufferData {
struct Framebuffer {
ComPtr<ID3D11RenderTargetView> render_target_view;
ComPtr<ID3D11DepthStencilView> depth_stencil_view;
ComPtr<ID3D11ShaderResourceView> depth_stencil_srv;
uint32_t texture_id;
bool has_depth_buffer;
uint32_t msaa_level;
};
struct ShaderProgramD3D11 {
@ -91,34 +93,30 @@ static struct {
pD3DCompile D3DCompile;
D3D_FEATURE_LEVEL feature_level;
uint32_t msaa_num_quality_levels[D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT];
ComPtr<ID3D11Device> device;
ComPtr<IDXGISwapChain1> swap_chain;
ComPtr<ID3D11DeviceContext> context;
ComPtr<ID3D11RenderTargetView> backbuffer_view;
ComPtr<ID3D11DepthStencilView> depth_stencil_view;
ComPtr<ID3D11ShaderResourceView> depth_stencil_srv;
ComPtr<ID3D11RasterizerState> rasterizer_state;
ComPtr<ID3D11DepthStencilState> depth_stencil_state;
ComPtr<ID3D11Buffer> vertex_buffer;
ComPtr<ID3D11Buffer> per_frame_cb;
ComPtr<ID3D11Buffer> per_draw_cb;
ComPtr<ID3D11Texture2D> depth_stencil_texture;
ComPtr<ID3D11Texture2D> depth_stencil_copy_texture;
ComPtr<ID3D11Buffer> coord_buffer;
ComPtr<ID3D11ShaderResourceView> coord_buffer_srv;
ComPtr<ID3D11Buffer> depth_value_output_buffer;
ComPtr<ID3D11Buffer> depth_value_output_buffer_copy;
ComPtr<ID3D11UnorderedAccessView> depth_value_output_uav;
ComPtr<ID3D11SamplerState> depth_value_sampler;
ComPtr<ID3D11ComputeShader> compute_shader;
bool copied_depth_buffer;
ComPtr<ID3D11ComputeShader> compute_shader_msaa;
ComPtr<ID3DBlob> compute_shader_msaa_blob;
size_t coord_buffer_size;
#if DEBUG_D3D
ComPtr<ID3D11Debug> debug;
#endif
DXGI_SAMPLE_DESC sample_description;
PerFrameCB per_frame_cb_data;
PerDrawCB per_draw_cb_data;
@ -128,14 +126,15 @@ static struct {
int current_tile;
uint32_t current_texture_ids[2];
std::vector<FramebufferData> framebuffers;
std::vector<Framebuffer> framebuffers;
// Current state
struct ShaderProgramD3D11 *shader_program;
uint32_t current_width, current_height;
//uint32_t current_width, current_height;
uint32_t render_target_height;
int current_framebuffer;
int8_t depth_test;
int8_t depth_mask;
@ -156,7 +155,9 @@ static struct {
static LARGE_INTEGER last_time, accumulated_time, frequency;
void create_depth_stencil_objects(uint32_t width, uint32_t height, ID3D11Texture2D **texture, ID3D11DepthStencilView **view, ID3D11ShaderResourceView **srv) {
int gfx_d3d11_create_framebuffer(void);
void create_depth_stencil_objects(uint32_t width, uint32_t height, uint32_t msaa_count, ID3D11DepthStencilView **view, ID3D11ShaderResourceView **srv) {
D3D11_TEXTURE2D_DESC texture_desc;
texture_desc.Width = width;
texture_desc.Height = height;
@ -164,93 +165,42 @@ void create_depth_stencil_objects(uint32_t width, uint32_t height, ID3D11Texture
texture_desc.ArraySize = 1;
texture_desc.Format = d3d.feature_level >= D3D_FEATURE_LEVEL_10_0 ?
DXGI_FORMAT_R32_TYPELESS : DXGI_FORMAT_R24G8_TYPELESS;
texture_desc.SampleDesc.Count = 1;
texture_desc.SampleDesc.Count = msaa_count;
texture_desc.SampleDesc.Quality = 0;
texture_desc.Usage = D3D11_USAGE_DEFAULT;
texture_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL | (srv != nullptr ? D3D11_BIND_SHADER_RESOURCE : 0);
texture_desc.CPUAccessFlags = 0;
texture_desc.MiscFlags = 0;
ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, nullptr, texture));
ComPtr<ID3D11Texture2D> texture;
ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, nullptr, texture.GetAddressOf()));
D3D11_DEPTH_STENCIL_VIEW_DESC view_desc;
view_desc.Format = d3d.feature_level >= D3D_FEATURE_LEVEL_10_0 ?
DXGI_FORMAT_D32_FLOAT : DXGI_FORMAT_D24_UNORM_S8_UINT;
view_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
view_desc.Flags = 0;
if (msaa_count > 1) {
view_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
view_desc.Texture2DMS.UnusedField_NothingToDefine = 0;
} else {
view_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
view_desc.Texture2D.MipSlice = 0;
}
ThrowIfFailed(d3d.device->CreateDepthStencilView(*texture, &view_desc, view));
ThrowIfFailed(d3d.device->CreateDepthStencilView(texture.Get(), &view_desc, view));
if (srv != nullptr) {
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc;
srv_desc.Format = d3d.feature_level >= D3D_FEATURE_LEVEL_10_0 ?
DXGI_FORMAT_R32_FLOAT : DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srv_desc.ViewDimension = msaa_count > 1 ? D3D11_SRV_DIMENSION_TEXTURE2DMS : D3D11_SRV_DIMENSION_TEXTURE2D;
srv_desc.Texture2D.MostDetailedMip = 0;
srv_desc.Texture2D.MipLevels = -1;
ThrowIfFailed(d3d.device->CreateShaderResourceView(*texture, &srv_desc, srv));
ThrowIfFailed(d3d.device->CreateShaderResourceView(texture.Get(), &srv_desc, srv));
}
}
static void create_render_target_views(bool is_resize) {
DXGI_SWAP_CHAIN_DESC1 desc1;
if (is_resize) {
// Release previous stuff (if any)
d3d.backbuffer_view.Reset();
d3d.depth_stencil_texture.Reset();
d3d.depth_stencil_view.Reset();
d3d.depth_stencil_srv.Reset();
d3d.depth_stencil_copy_texture.Reset();
// Resize swap chain buffers
ThrowIfFailed(d3d.swap_chain->GetDesc1(&desc1));
ThrowIfFailed(d3d.swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, desc1.Flags),
gfx_dxgi_get_h_wnd(), "Failed to resize IDXGISwapChain buffers.");
}
// Get new size
ThrowIfFailed(d3d.swap_chain->GetDesc1(&desc1));
// Create back buffer
ComPtr<ID3D11Texture2D> backbuffer_texture;
ThrowIfFailed(d3d.swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *) backbuffer_texture.GetAddressOf()),
gfx_dxgi_get_h_wnd(), "Failed to get backbuffer from IDXGISwapChain.");
ThrowIfFailed(d3d.device->CreateRenderTargetView(backbuffer_texture.Get(), nullptr, d3d.backbuffer_view.GetAddressOf()),
gfx_dxgi_get_h_wnd(), "Failed to create render target view.");
// Create depth buffer
create_depth_stencil_objects(desc1.Width, desc1.Height, d3d.depth_stencil_texture.GetAddressOf(), d3d.depth_stencil_view.GetAddressOf(), d3d.depth_stencil_srv.GetAddressOf());
// Create texture that can be used to retrieve depth value
D3D11_TEXTURE2D_DESC depth_texture = {};
depth_texture.Width = desc1.Width;
depth_texture.Height = desc1.Height;
depth_texture.MipLevels = 1;
depth_texture.ArraySize = 1;
depth_texture.Format = DXGI_FORMAT_D32_FLOAT;
depth_texture.SampleDesc.Count = 1;
depth_texture.SampleDesc.Quality = 0;
depth_texture.Usage = D3D11_USAGE_STAGING;
depth_texture.BindFlags = 0;
depth_texture.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
depth_texture.MiscFlags = 0;
ThrowIfFailed(d3d.device->CreateTexture2D(&depth_texture, nullptr, d3d.depth_stencil_copy_texture.GetAddressOf()));
// Save resolution
d3d.current_width = desc1.Width;
d3d.current_height = desc1.Height;
}
static void gfx_d3d11_init(void) {
// Load d3d11.dll
d3d.d3d11_module = LoadLibraryW(L"d3d11.dll");
@ -300,11 +250,6 @@ static void gfx_d3d11_init(void) {
}
});
// Sample description to be used in back buffer and depth buffer
d3d.sample_description.Count = 1;
d3d.sample_description.Quality = 0;
// Create the swap chain
d3d.swap_chain = gfx_dxgi_create_swap_chain(d3d.device.Get());
@ -315,9 +260,19 @@ static void gfx_d3d11_init(void) {
gfx_dxgi_get_h_wnd(), "Failed to get ID3D11Debug device.");
#endif
// Create views
// Create the default framebuffer which represents the window
Framebuffer& fb = d3d.framebuffers[gfx_d3d11_create_framebuffer()];
create_render_target_views(false);
// Check the size of the window
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc;
ThrowIfFailed(d3d.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;
for (uint32_t sample_count = 1; sample_count <= D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT; sample_count++) {
ThrowIfFailed(d3d.device->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, sample_count, &d3d.msaa_num_quality_levels[sample_count - 1]));
}
// Create main vertex buffer
@ -364,61 +319,30 @@ static void gfx_d3d11_init(void) {
// Create compute shader that can be used to retrieve depth buffer values
D3D11_BUFFER_DESC coord_cb_desc;
coord_cb_desc.Usage = D3D11_USAGE_DYNAMIC;
coord_cb_desc.ByteWidth = sizeof(CoordCB);
coord_cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
coord_cb_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
coord_cb_desc.MiscFlags = 0;
coord_cb_desc.StructureByteStride = 0;
ThrowIfFailed(d3d.device->CreateBuffer(&coord_cb_desc, nullptr, d3d.coord_buffer.GetAddressOf()));
D3D11_SAMPLER_DESC sampler_desc = {};
sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
sampler_desc.MinLOD = 0;
sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
ThrowIfFailed(d3d.device->CreateSamplerState(&sampler_desc, d3d.depth_value_sampler.GetAddressOf()));
D3D11_BUFFER_DESC output_buffer_desc;
output_buffer_desc.Usage = D3D11_USAGE_DEFAULT;
output_buffer_desc.ByteWidth = sizeof(float);
output_buffer_desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
output_buffer_desc.CPUAccessFlags = 0;
output_buffer_desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
output_buffer_desc.StructureByteStride = sizeof(float);
ThrowIfFailed(d3d.device->CreateBuffer(&output_buffer_desc, nullptr, d3d.depth_value_output_buffer.GetAddressOf()));
D3D11_UNORDERED_ACCESS_VIEW_DESC output_buffer_uav_desc;
output_buffer_uav_desc.Format = DXGI_FORMAT_UNKNOWN;
output_buffer_uav_desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
output_buffer_uav_desc.Buffer.FirstElement = 0;
output_buffer_uav_desc.Buffer.NumElements = 1;
output_buffer_uav_desc.Buffer.Flags = 0;
ThrowIfFailed(d3d.device->CreateUnorderedAccessView(d3d.depth_value_output_buffer.Get(), &output_buffer_uav_desc, d3d.depth_value_output_uav.GetAddressOf()));
output_buffer_desc.Usage = D3D11_USAGE_STAGING;
output_buffer_desc.BindFlags = 0;
output_buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
ThrowIfFailed(d3d.device->CreateBuffer(&output_buffer_desc, nullptr, d3d.depth_value_output_buffer_copy.GetAddressOf()));
const char* shader_source = R"(
sampler my_sampler : register(s0);
Texture2D<float> tex : register(t0);
cbuffer coordCB : register(b0) {
float2 coord;
}
StructuredBuffer<int2> coord : register(t1);
RWStructuredBuffer<float> output : register(u0);
[numthreads(1, 1, 1)]
void CSMain(uint3 DTid : SV_DispatchThreadID) {
output[0] = tex.SampleLevel(my_sampler, coord, 0);
output[DTid.x] = tex.Load(int3(coord[DTid.x], 0));
}
)";
const char* shader_source_msaa = R"(
sampler my_sampler : register(s0);
Texture2DMS<float, 2> tex : register(t0);
StructuredBuffer<int2> coord : register(t1);
RWStructuredBuffer<float> output : register(u0);
[numthreads(1, 1, 1)]
void CSMain(uint3 DTid : SV_DispatchThreadID) {
output[DTid.x] = tex.Load(coord[DTid.x], 0);
}
)";
#if DEBUG_D3D
UINT compile_flags = D3DCOMPILE_DEBUG;
#else
@ -426,7 +350,9 @@ void CSMain(uint3 DTid : SV_DispatchThreadID) {
#endif
ComPtr<ID3DBlob> cs, error_blob;
HRESULT hr = d3d.D3DCompile(shader_source, strlen(shader_source), nullptr, nullptr, nullptr, "CSMain", "cs_4_0", compile_flags, 0, cs.GetAddressOf(), error_blob.GetAddressOf());
HRESULT hr;
hr = d3d.D3DCompile(shader_source, strlen(shader_source), nullptr, nullptr, nullptr, "CSMain", "cs_4_0", compile_flags, 0, cs.GetAddressOf(), error_blob.GetAddressOf());
if (FAILED(hr)) {
char* err = (char*)error_blob->GetBufferPointer();
@ -436,6 +362,14 @@ void CSMain(uint3 DTid : SV_DispatchThreadID) {
ThrowIfFailed(d3d.device->CreateComputeShader(cs->GetBufferPointer(), cs->GetBufferSize(), nullptr, d3d.compute_shader.GetAddressOf()));
hr = d3d.D3DCompile(shader_source_msaa, strlen(shader_source_msaa), nullptr, nullptr, nullptr, "CSMain", "cs_4_1", compile_flags, 0, d3d.compute_shader_msaa_blob.GetAddressOf(), error_blob.ReleaseAndGetAddressOf());
if (FAILED(hr)) {
char* err = (char*)error_blob->GetBufferPointer();
MessageBoxA(gfx_dxgi_get_h_wnd(), err, "Error", MB_OK | MB_ICONERROR);
throw hr;
}
// Create ImGui
SohImGui::WindowImpl window_impl;
@ -445,8 +379,8 @@ void CSMain(uint3 DTid : SV_DispatchThreadID) {
}
static bool gfx_d3d11_z_is_from_0_to_1(void) {
return true;
static struct GfxClipParameters gfx_d3d11_get_clip_parameters(void) {
return { true, false };
}
static void gfx_d3d11_unload_shader(struct ShaderProgram *old_prg) {
@ -531,8 +465,8 @@ static struct ShaderProgram *gfx_d3d11_create_and_load_new_shader(uint64_t shade
blend_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
blend_desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
blend_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;
blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO;
blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE; // We initially clear alpha to 1.0f and want to keep it at 1.0f
blend_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
blend_desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
} else {
@ -592,6 +526,10 @@ static D3D11_TEXTURE_ADDRESS_MODE gfx_cm_to_d3d11(uint32_t val) {
static void gfx_d3d11_upload_texture(const uint8_t *rgba32_buf, uint32_t width, uint32_t height) {
// Create texture
TextureData *texture_data = &d3d.textures[d3d.current_texture_ids[d3d.current_tile]];
texture_data->width = width;
texture_data->height = height;
D3D11_TEXTURE2D_DESC texture_desc;
ZeroMemory(&texture_desc, sizeof(D3D11_TEXTURE2D_DESC));
@ -612,21 +550,11 @@ static void gfx_d3d11_upload_texture(const uint8_t *rgba32_buf, uint32_t width,
resource_data.SysMemPitch = width * 4;
resource_data.SysMemSlicePitch = resource_data.SysMemPitch * height;
ComPtr<ID3D11Texture2D> texture;
ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, &resource_data, texture.GetAddressOf()));
TextureData *texture_data = &d3d.textures[d3d.current_texture_ids[d3d.current_tile]];
texture_data->width = width;
texture_data->height = height;
ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, &resource_data, texture_data->texture.ReleaseAndGetAddressOf()));
// Create shader resource view from texture
if (texture_data->resource_view.Get() != nullptr) {
// Free the previous texture in this slot
texture_data->resource_view.Reset();
}
ThrowIfFailed(d3d.device->CreateShaderResourceView(texture.Get(), nullptr, texture_data->resource_view.GetAddressOf()));
ThrowIfFailed(d3d.device->CreateShaderResourceView(texture_data->texture.Get(), nullptr, texture_data->resource_view.ReleaseAndGetAddressOf()));
}
static void gfx_d3d11_set_sampler_parameters(int tile, bool linear_filter, uint32_t cms, uint32_t cmt) {
@ -803,20 +731,10 @@ static void gfx_d3d11_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_t
}
static void gfx_d3d11_on_resize(void) {
create_render_target_views(true);
//create_render_target_views(true);
}
static void gfx_d3d11_start_frame(void) {
// Set render targets
d3d.context->OMSetRenderTargets(1, d3d.backbuffer_view.GetAddressOf(), d3d.depth_stencil_view.Get());
// Clear render targets
const float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
d3d.context->ClearRenderTargetView(d3d.backbuffer_view.Get(), clearColor);
d3d.context->ClearDepthStencilView(d3d.depth_stencil_view.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0);
// Set per-frame constant buffer
d3d.per_frame_cb_data.noise_frame++;
@ -824,22 +742,9 @@ static void gfx_d3d11_start_frame(void) {
// No high values, as noise starts to look ugly
d3d.per_frame_cb_data.noise_frame = 0;
}
float aspect_ratio = (float) d3d.current_width / (float) d3d.current_height;
d3d.render_target_height = d3d.current_height;
d3d.per_frame_cb_data.noise_scale_x = 120 * aspect_ratio; // 120 = N64 height resolution (240) / 2
d3d.per_frame_cb_data.noise_scale_y = 120;
D3D11_MAPPED_SUBRESOURCE ms;
ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE));
d3d.context->Map(d3d.per_frame_cb.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms);
memcpy(ms.pData, &d3d.per_frame_cb_data, sizeof(PerFrameCB));
d3d.context->Unmap(d3d.per_frame_cb.Get(), 0);
d3d.copied_depth_buffer = false;
}
static void gfx_d3d11_end_frame(void) {
SohImGui::Draw();
d3d.context->Flush();
}
@ -847,43 +752,13 @@ static void gfx_d3d11_finish_render(void) {
d3d.context->Flush();
}
void gfx_d3d11_resize_framebuffer(int fb, uint32_t width, uint32_t height) {
FramebufferData& fd = d3d.framebuffers[fb];
TextureData& td = d3d.textures[fd.texture_id];
ComPtr<ID3D11Texture2D> texture, depth_stencil_texture;
D3D11_TEXTURE2D_DESC texture_desc;
texture_desc.Width = width;
texture_desc.Height = height;
texture_desc.Usage = D3D11_USAGE_DEFAULT;
texture_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
texture_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texture_desc.CPUAccessFlags = 0;
texture_desc.MiscFlags = 0;
texture_desc.ArraySize = 1;
texture_desc.MipLevels = 1;
texture_desc.SampleDesc.Count = 1;
texture_desc.SampleDesc.Quality = 0;
ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, nullptr, texture.GetAddressOf()));
create_depth_stencil_objects(width, height, depth_stencil_texture.GetAddressOf(), fd.depth_stencil_view.ReleaseAndGetAddressOf(), nullptr);
ThrowIfFailed(d3d.device->CreateRenderTargetView(texture.Get(), nullptr, fd.render_target_view.ReleaseAndGetAddressOf()));
ThrowIfFailed(d3d.device->CreateShaderResourceView(texture.Get(), nullptr, td.resource_view.ReleaseAndGetAddressOf()));
td.width = width;
td.height = height;
}
int gfx_d3d11_create_framebuffer(uint32_t width, uint32_t height) {
int gfx_d3d11_create_framebuffer(void) {
uint32_t texture_id = gfx_d3d11_new_texture();
TextureData& t = d3d.textures[texture_id];
t.width = width;
t.height = height;
size_t index = d3d.framebuffers.size();
d3d.framebuffers.resize(d3d.framebuffers.size() + 1);
FramebufferData& data = d3d.framebuffers.back();
Framebuffer& data = d3d.framebuffers.back();
data.texture_id = texture_id;
uint32_t tile = 0;
@ -892,24 +767,109 @@ int gfx_d3d11_create_framebuffer(uint32_t width, uint32_t height) {
gfx_d3d11_set_sampler_parameters(0, true, G_TX_WRAP, G_TX_WRAP);
d3d.current_texture_ids[tile] = saved;
gfx_d3d11_resize_framebuffer(index, width, height);
return (int)index;
}
void gfx_d3d11_set_framebuffer(int fb) {
d3d.render_target_height = d3d.textures[d3d.framebuffers[fb].texture_id].height;
static void gfx_d3d11_update_framebuffer_parameters(int fb_id, uint32_t width, uint32_t height, uint32_t msaa_level, bool opengl_invert_y, bool render_target, bool has_depth_buffer, bool can_extract_depth) {
Framebuffer& fb = d3d.framebuffers[fb_id];
TextureData& tex = d3d.textures[fb.texture_id];
d3d.context->OMSetRenderTargets(1, d3d.framebuffers[fb].render_target_view.GetAddressOf(), d3d.framebuffers[fb].depth_stencil_view.Get());
width = max(width, 1U);
height = max(height, 1U);
while (msaa_level > 1 && d3d.msaa_num_quality_levels[msaa_level - 1] == 0) {
--msaa_level;
}
const float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
d3d.context->ClearRenderTargetView(d3d.framebuffers[fb].render_target_view.Get(), clearColor);
d3d.context->ClearDepthStencilView(d3d.framebuffers[fb].depth_stencil_view.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0);
bool diff = tex.width != width || tex.height != height || fb.msaa_level != msaa_level;
if (diff || (fb.render_target_view.Get() != nullptr) != render_target) {
if (fb_id != 0) {
D3D11_TEXTURE2D_DESC texture_desc;
texture_desc.Width = width;
texture_desc.Height = height;
texture_desc.Usage = D3D11_USAGE_DEFAULT;
texture_desc.BindFlags = (msaa_level <= 1 ? D3D11_BIND_SHADER_RESOURCE : 0) | (render_target ? D3D11_BIND_RENDER_TARGET : 0);
texture_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texture_desc.CPUAccessFlags = 0;
texture_desc.MiscFlags = 0;
texture_desc.ArraySize = 1;
texture_desc.MipLevels = 1;
texture_desc.SampleDesc.Count = msaa_level;
texture_desc.SampleDesc.Quality = 0;
ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, nullptr, tex.texture.ReleaseAndGetAddressOf()));
if (msaa_level <= 1) {
ThrowIfFailed(d3d.device->CreateShaderResourceView(tex.texture.Get(), nullptr, tex.resource_view.ReleaseAndGetAddressOf()));
}
} else if (diff) {
DXGI_SWAP_CHAIN_DESC1 desc1;
ThrowIfFailed(d3d.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(d3d.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()));
}
tex.width = width;
tex.height = height;
}
if (has_depth_buffer && (diff || !fb.has_depth_buffer || (fb.depth_stencil_srv.Get() != nullptr) != can_extract_depth)) {
fb.depth_stencil_srv.Reset();
create_depth_stencil_objects(width, height, msaa_level, fb.depth_stencil_view.ReleaseAndGetAddressOf(), can_extract_depth ? fb.depth_stencil_srv.GetAddressOf() : nullptr);
}
if (!has_depth_buffer) {
fb.depth_stencil_view.Reset();
fb.depth_stencil_srv.Reset();
}
fb.has_depth_buffer = has_depth_buffer;
fb.msaa_level = msaa_level;
}
void gfx_d3d11_reset_framebuffer(void) {
d3d.render_target_height = d3d.current_height;
d3d.context->OMSetRenderTargets(1, d3d.backbuffer_view.GetAddressOf(), d3d.depth_stencil_view.Get());
void gfx_d3d11_start_draw_to_framebuffer(int fb_id, float noise_scale) {
Framebuffer& fb = d3d.framebuffers[fb_id];
d3d.render_target_height = d3d.textures[fb.texture_id].height;
d3d.context->OMSetRenderTargets(1, fb.render_target_view.GetAddressOf(), fb.has_depth_buffer ? fb.depth_stencil_view.Get() : nullptr);
d3d.current_framebuffer = fb_id;
if (noise_scale != 0.0f) {
d3d.per_frame_cb_data.noise_scale = 1.0f / noise_scale;
}
D3D11_MAPPED_SUBRESOURCE ms;
ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE));
d3d.context->Map(d3d.per_frame_cb.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms);
memcpy(ms.pData, &d3d.per_frame_cb_data, sizeof(PerFrameCB));
d3d.context->Unmap(d3d.per_frame_cb.Get(), 0);
}
void gfx_d3d11_clear_framebuffer(void) {
Framebuffer& fb = d3d.framebuffers[d3d.current_framebuffer];
const float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
d3d.context->ClearRenderTargetView(fb.render_target_view.Get(), clearColor);
if (fb.has_depth_buffer) {
d3d.context->ClearDepthStencilView(fb.depth_stencil_view.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0);
}
}
void gfx_d3d11_resolve_msaa_color_buffer(int fb_id_target, int fb_id_source) {
Framebuffer& fb_dst = d3d.framebuffers[fb_id_target];
Framebuffer& fb_src = d3d.framebuffers[fb_id_source];
d3d.context->ResolveSubresource(d3d.textures[fb_dst.texture_id].texture.Get(), 0, d3d.textures[fb_src.texture_id].texture.Get(), 0, DXGI_FORMAT_R8G8B8A8_UNORM);
}
void *gfx_d3d11_get_framebuffer_texture_id(int fb_id) {
return (void *)d3d.textures[d3d.framebuffers[fb_id].texture_id].resource_view.Get();
}
void gfx_d3d11_select_texture_fb(int fbID) {
@ -917,62 +877,107 @@ void gfx_d3d11_select_texture_fb(int fbID) {
gfx_d3d11_select_texture(tile, d3d.framebuffers[fbID].texture_id);
}
uint16_t gfx_d3d11_get_pixel_depth(float x, float y) {
std::map<std::pair<float, float>, uint16_t> gfx_d3d11_get_pixel_depth(int fb_id, const std::set<std::pair<float, float>>& coordinates) {
Framebuffer& fb = d3d.framebuffers[fb_id];
TextureData& td = d3d.textures[fb.texture_id];
if (coordinates.size() > d3d.coord_buffer_size) {
d3d.coord_buffer.Reset();
d3d.coord_buffer_srv.Reset();
d3d.depth_value_output_buffer.Reset();
d3d.depth_value_output_uav.Reset();
d3d.depth_value_output_buffer_copy.Reset();
D3D11_BUFFER_DESC coord_buf_desc;
coord_buf_desc.Usage = D3D11_USAGE_DYNAMIC;
coord_buf_desc.ByteWidth = sizeof(Coord) * coordinates.size();
coord_buf_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
coord_buf_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
coord_buf_desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
coord_buf_desc.StructureByteStride = sizeof(Coord);
ThrowIfFailed(d3d.device->CreateBuffer(&coord_buf_desc, nullptr, d3d.coord_buffer.GetAddressOf()));
D3D11_SHADER_RESOURCE_VIEW_DESC coord_buf_srv_desc;
coord_buf_srv_desc.Format = DXGI_FORMAT_UNKNOWN;
coord_buf_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
coord_buf_srv_desc.Buffer.FirstElement = 0;
coord_buf_srv_desc.Buffer.NumElements = coordinates.size();
ThrowIfFailed(d3d.device->CreateShaderResourceView(d3d.coord_buffer.Get(), &coord_buf_srv_desc, d3d.coord_buffer_srv.GetAddressOf()));
D3D11_BUFFER_DESC output_buffer_desc;
output_buffer_desc.Usage = D3D11_USAGE_DEFAULT;
output_buffer_desc.ByteWidth = sizeof(float) * coordinates.size();
output_buffer_desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
output_buffer_desc.CPUAccessFlags = 0;
output_buffer_desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
output_buffer_desc.StructureByteStride = sizeof(float);
ThrowIfFailed(d3d.device->CreateBuffer(&output_buffer_desc, nullptr, d3d.depth_value_output_buffer.GetAddressOf()));
D3D11_UNORDERED_ACCESS_VIEW_DESC output_buffer_uav_desc;
output_buffer_uav_desc.Format = DXGI_FORMAT_UNKNOWN;
output_buffer_uav_desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
output_buffer_uav_desc.Buffer.FirstElement = 0;
output_buffer_uav_desc.Buffer.NumElements = coordinates.size();
output_buffer_uav_desc.Buffer.Flags = 0;
ThrowIfFailed(d3d.device->CreateUnorderedAccessView(d3d.depth_value_output_buffer.Get(), &output_buffer_uav_desc, d3d.depth_value_output_uav.GetAddressOf()));
output_buffer_desc.Usage = D3D11_USAGE_STAGING;
output_buffer_desc.BindFlags = 0;
output_buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
ThrowIfFailed(d3d.device->CreateBuffer(&output_buffer_desc, nullptr, d3d.depth_value_output_buffer_copy.GetAddressOf()));
d3d.coord_buffer_size = coordinates.size();
}
D3D11_MAPPED_SUBRESOURCE ms;
if (fb.msaa_level > 1 && d3d.compute_shader_msaa.Get() == nullptr) {
ThrowIfFailed(d3d.device->CreateComputeShader(d3d.compute_shader_msaa_blob->GetBufferPointer(), d3d.compute_shader_msaa_blob->GetBufferSize(), nullptr, d3d.compute_shader_msaa.GetAddressOf()));
}
// ImGui overwrites these values, so we cannot set them once at init
d3d.context->CSSetShader(d3d.compute_shader.Get(), nullptr, 0);
d3d.context->CSSetConstantBuffers(0, 1, d3d.coord_buffer.GetAddressOf());
d3d.context->CSSetSamplers(0, 1, d3d.depth_value_sampler.GetAddressOf());
d3d.context->CSSetShader(fb.msaa_level > 1 ? d3d.compute_shader_msaa.Get() : d3d.compute_shader.Get(), nullptr, 0);
d3d.context->CSSetUnorderedAccessViews(0, 1, d3d.depth_value_output_uav.GetAddressOf(), nullptr);
ThrowIfFailed(d3d.context->Map(d3d.coord_buffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms));
CoordCB* coord_cb = (CoordCB*)ms.pData;
coord_cb->x = x / d3d.current_width;
// We invert y because the game assumes OpenGL coordinates (bottom-left corner is origin), while DX's origin is top-left corner
coord_cb->y = 1 - y / d3d.current_height;
Coord *coord_cb = (Coord *)ms.pData;
{
size_t i = 0;
for (const auto& coord : coordinates) {
coord_cb[i].x = coord.first;
// We invert y because the gfx_pc assumes OpenGL coordinates (bottom-left corner is origin), while DX's origin is top-left corner
coord_cb[i].y = td.height - 1 - coord.second;
++i;
}
}
d3d.context->Unmap(d3d.coord_buffer.Get(), 0);
// The depth stencil texture can only have one mapping at a time, so temporarily unbind from the OM
d3d.context->OMSetRenderTargets(1, d3d.backbuffer_view.GetAddressOf(), nullptr);
d3d.context->CSSetShaderResources(0, 1, d3d.depth_stencil_srv.GetAddressOf());
// The depth stencil texture can only have one mapping at a time, so unbind from the OM
ID3D11RenderTargetView* null_arr1[1] = { nullptr };
d3d.context->OMSetRenderTargets(1, null_arr1, nullptr);
d3d.context->Dispatch(1, 1, 1);
ID3D11ShaderResourceView *srvs[] = { fb.depth_stencil_srv.Get(), d3d.coord_buffer_srv.Get() };
d3d.context->CSSetShaderResources(0, 2, srvs);
d3d.context->Dispatch(coordinates.size(), 1, 1);
d3d.context->CopyResource(d3d.depth_value_output_buffer_copy.Get(), d3d.depth_value_output_buffer.Get());
ThrowIfFailed(d3d.context->Map(d3d.depth_value_output_buffer_copy.Get(), 0, D3D11_MAP_READ, 0, &ms));
float res = *(float *)ms.pData;
std::map<std::pair<float, float>, uint16_t> res;
{
size_t i = 0;
for (const auto& coord : coordinates) {
res.emplace(coord, ((float *)ms.pData)[i++] * 65532.0f);
}
}
d3d.context->Unmap(d3d.depth_value_output_buffer_copy.Get(), 0);
ID3D11ShaderResourceView *null_arr[1] = { nullptr };
d3d.context->CSSetShaderResources(0, 1, null_arr);
d3d.context->OMSetRenderTargets(1, d3d.backbuffer_view.GetAddressOf(), d3d.depth_stencil_view.Get());
ID3D11ShaderResourceView *null_arr[2] = { nullptr, nullptr };
d3d.context->CSSetShaderResources(0, 2, null_arr);
return res * 65532.0f;
}
uint16_t gfx_d3d11_get_pixel_depth_old(float x, float y) {
// This approach, compared to using a compute shader, might have better performance on nvidia cards
if (!d3d.copied_depth_buffer) {
d3d.context->CopyResource(d3d.depth_stencil_copy_texture.Get(), d3d.depth_stencil_texture.Get());
d3d.copied_depth_buffer = true;
}
D3D11_MAPPED_SUBRESOURCE mapping_desc;
d3d.context->Map(d3d.depth_stencil_copy_texture.Get(), 0, D3D11_MAP_READ, 0, &mapping_desc);
float res = 0;
if (mapping_desc.pData != nullptr) {
float *addr = (float *)mapping_desc.pData;
uint32_t num_pixels = mapping_desc.DepthPitch / sizeof(float);
uint32_t width = mapping_desc.RowPitch / sizeof(float);
uint32_t height = width == 0 ? 0 : num_pixels / width;
if (x >= 0 && x < width && y >= 0 && y < height) {
res = addr[width * (height - 1 - (int)y) + (int)x];
}
}
d3d.context->Unmap(d3d.depth_stencil_copy_texture.Get(), 0);
return res * 65532.0f;
return res;
}
} // namespace
@ -982,7 +987,7 @@ ImTextureID SohImGui::GetTextureByID(int id) {
}
struct GfxRenderingAPI gfx_direct3d11_api = {
gfx_d3d11_z_is_from_0_to_1,
gfx_d3d11_get_clip_parameters,
gfx_d3d11_unload_shader,
gfx_d3d11_load_shader,
gfx_d3d11_create_and_load_new_shader,
@ -993,7 +998,6 @@ struct GfxRenderingAPI gfx_direct3d11_api = {
gfx_d3d11_upload_texture,
gfx_d3d11_set_sampler_parameters,
gfx_d3d11_set_depth_test_and_mask,
gfx_d3d11_get_pixel_depth,
gfx_d3d11_set_zmode_decal,
gfx_d3d11_set_viewport,
gfx_d3d11_set_scissor,
@ -1005,9 +1009,12 @@ struct GfxRenderingAPI gfx_direct3d11_api = {
gfx_d3d11_end_frame,
gfx_d3d11_finish_render,
gfx_d3d11_create_framebuffer,
gfx_d3d11_resize_framebuffer,
gfx_d3d11_set_framebuffer,
gfx_d3d11_reset_framebuffer,
gfx_d3d11_update_framebuffer_parameters,
gfx_d3d11_start_draw_to_framebuffer,
gfx_d3d11_clear_framebuffer,
gfx_d3d11_resolve_msaa_color_buffer,
gfx_d3d11_get_pixel_depth,
gfx_d3d11_get_framebuffer_texture_id,
gfx_d3d11_select_texture_fb,
gfx_d3d11_delete_texture
};

View File

@ -134,9 +134,6 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f
}
}
}
if (cc_features.opt_alpha && cc_features.opt_noise) {
append_line(buf, &len, " float4 screenPos : TEXCOORD2;");
}
if (cc_features.opt_fog) {
append_line(buf, &len, " float4 fog : FOG;");
num_floats += 4;
@ -163,7 +160,7 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f
if (cc_features.opt_alpha && cc_features.opt_noise) {
append_line(buf, &len, "cbuffer PerFrameCB : register(b0) {");
append_line(buf, &len, " uint noise_frame;");
append_line(buf, &len, " float2 noise_scale;");
append_line(buf, &len, " float noise_scale;");
append_line(buf, &len, "}");
append_line(buf, &len, "float random(in float3 value) {");
@ -217,9 +214,6 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f
append_line(buf, &len, ") {");
append_line(buf, &len, " PSInput result;");
append_line(buf, &len, " result.position = position;");
if (cc_features.opt_alpha && cc_features.opt_noise) {
append_line(buf, &len, " result.screenPos = position;");
}
for (int i = 0; i < 2; i++) {
if (cc_features.used_textures[i]) {
len += sprintf(buf + len, " result.uv%d = uv%d;\r\n", i, i);
@ -244,7 +238,11 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f
if (include_root_signature) {
append_line(buf, &len, "[RootSignature(RS)]");
}
if (cc_features.opt_alpha && cc_features.opt_noise) {
append_line(buf, &len, "float4 PSMain(PSInput input, float4 screenSpace : SV_Position) : SV_TARGET {");
} else {
append_line(buf, &len, "float4 PSMain(PSInput input) : SV_TARGET {");
}
for (int i = 0; i < 2; i++) {
if (cc_features.used_textures[i]) {
len += sprintf(buf + len, " float2 tc%d = input.uv%d;\r\n", i, i);
@ -301,7 +299,7 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f
}
if (cc_features.opt_alpha && cc_features.opt_noise) {
append_line(buf, &len, " float2 coords = (input.screenPos.xy / input.screenPos.w) * noise_scale;");
append_line(buf, &len, " float2 coords = screenSpace.xy * noise_scale;");
append_line(buf, &len, " texel.a *= round(saturate(random(float3(floor(coords), noise_frame)) + texel.a - 0.5));");
}

View File

@ -10,6 +10,7 @@
#include <windows.h>
#include <wrl/client.h>
#include <dxgi1_3.h>
#include <dxgi1_4.h>
#include <versionhelpers.h>
#include <shellscalingapi.h>
@ -61,6 +62,7 @@ static struct {
RECT last_window_rect;
bool is_full_screen, last_maximized_state;
bool dxgi1_4;
ComPtr<IDXGIFactory2> factory;
ComPtr<IDXGISwapChain1> swap_chain;
HANDLE waitable_object;
@ -197,17 +199,6 @@ static void toggle_borderless_window_full_screen(bool enable, bool call_callback
}
}
static void gfx_dxgi_on_resize(void) {
if (dxgi.swap_chain.Get() != nullptr) {
gfx_get_current_rendering_api()->on_resize();
DXGI_SWAP_CHAIN_DESC1 desc1;
ThrowIfFailed(dxgi.swap_chain->GetDesc1(&desc1));
dxgi.current_width = desc1.Width;
dxgi.current_height = desc1.Height;
}
}
static void onkeydown(WPARAM w_param, LPARAM l_param) {
int key = ((l_param >> 16) & 0x1ff);
if (dxgi.on_key_down != nullptr) {
@ -227,7 +218,8 @@ static LRESULT CALLBACK gfx_dxgi_wnd_proc(HWND h_wnd, UINT message, WPARAM w_par
SohImGui::Update(event_impl);
switch (message) {
case WM_SIZE:
gfx_dxgi_on_resize();
dxgi.current_width = (uint32_t)(l_param & 0xffff);
dxgi.current_height = (uint32_t)(l_param >> 16);
break;
case WM_DESTROY:
exit(0);
@ -573,6 +565,13 @@ void gfx_dxgi_create_factory_and_device(bool debug, int d3d_version, bool (*crea
ThrowIfFailed(dxgi.CreateDXGIFactory1(__uuidof(IDXGIFactory2), &dxgi.factory));
}
{
ComPtr<IDXGIFactory4> factory4;
if (dxgi.factory->QueryInterface(__uuidof(IDXGIFactory4), &factory4) == S_OK) {
dxgi.dxgi1_4 = true;
}
}
ComPtr<IDXGIAdapter1> adapter;
for (UINT i = 0; dxgi.factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND; i++) {
DXGI_ADAPTER_DESC1 desc;
@ -604,7 +603,9 @@ ComPtr<IDXGISwapChain1> gfx_dxgi_create_swap_chain(IUnknown *device) {
swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.Scaling = win8 ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH;
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // Apparently this was backported to Win 7 Platform Update
swap_chain_desc.SwapEffect = dxgi.dxgi1_4 ?
DXGI_SWAP_EFFECT_FLIP_DISCARD : // Introduced in DXGI 1.4 and Windows 10
DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // Apparently flip sequential was also backported to Win 7 Platform Update
swap_chain_desc.Flags = dxgi_13 ? DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT : 0;
swap_chain_desc.SampleDesc.Count = 1;

View File

@ -52,19 +52,33 @@ struct ShaderProgram {
uint8_t num_attribs;
bool used_noise;
GLint frame_count_location;
GLint window_height_location;
GLint noise_scale_location;
};
struct Framebuffer {
uint32_t width, height;
bool has_depth_buffer;
uint32_t msaa_level;
bool invert_y;
GLuint fbo, clrbuf, clrbuf_msaa, rbo;
};
static map<pair<uint64_t, uint32_t>, struct ShaderProgram> shader_program_pool;
static GLuint opengl_vbo;
static uint32_t frame_count;
static uint32_t current_height;
static map<int, pair<GLuint, GLuint>> fb2tex;
static bool current_depth_mask;
static bool gfx_opengl_z_is_from_0_to_1(void) {
return false;
static uint32_t frame_count;
static vector<Framebuffer> framebuffers;
static size_t current_framebuffer;
static float current_noise_scale;
GLuint pixel_depth_rb, pixel_depth_fb;
size_t pixel_depth_rb_size;
static struct GfxClipParameters gfx_opengl_get_clip_parameters(void) {
return { false, framebuffers[current_framebuffer].invert_y };
}
static void gfx_opengl_vertex_array_set_attribs(struct ShaderProgram *prg) {
@ -81,7 +95,7 @@ static void gfx_opengl_vertex_array_set_attribs(struct ShaderProgram *prg) {
static void gfx_opengl_set_uniforms(struct ShaderProgram *prg) {
if (prg->used_noise) {
glUniform1i(prg->frame_count_location, frame_count);
glUniform1i(prg->window_height_location, current_height);
glUniform1f(prg->noise_scale_location, current_noise_scale);
}
}
@ -277,7 +291,7 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
if (cc_features.opt_alpha && cc_features.opt_noise) {
append_line(fs_buf, &fs_len, "uniform int frame_count;");
append_line(fs_buf, &fs_len, "uniform int window_height;");
append_line(fs_buf, &fs_len, "uniform float noise_scale;");
append_line(fs_buf, &fs_len, "float random(in vec3 value) {");
append_line(fs_buf, &fs_len, " float random = dot(sin(value), vec3(12.9898, 78.233, 37.719));");
@ -338,7 +352,7 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
}
if (cc_features.opt_alpha && cc_features.opt_noise) {
append_line(fs_buf, &fs_len, "texel.a *= floor(clamp(random(vec3(floor(gl_FragCoord.xy * (240.0 / float(window_height))), float(frame_count))) + texel.a, 0.0, 1.0));");
append_line(fs_buf, &fs_len, "texel.a *= floor(clamp(random(vec3(floor(gl_FragCoord.xy * noise_scale), float(frame_count))) + texel.a, 0.0, 1.0));");
}
if (cc_features.opt_alpha) {
@ -460,7 +474,7 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
if (cc_features.opt_alpha && cc_features.opt_noise) {
prg->frame_count_location = glGetUniformLocation(shader_program, "frame_count");
prg->window_height_location = glGetUniformLocation(shader_program, "window_height");
prg->noise_scale_location = glGetUniformLocation(shader_program, "noise_scale");
prg->used_noise = true;
} else {
prg->used_noise = false;
@ -496,7 +510,7 @@ static void gfx_opengl_select_texture(int tile, GLuint texture_id) {
}
static void gfx_opengl_upload_texture(const uint8_t *rgba32_buf, uint32_t width, uint32_t height) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba32_buf);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba32_buf);
}
static uint32_t gfx_cm_to_opengl(uint32_t val) {
@ -543,7 +557,6 @@ static void gfx_opengl_set_zmode_decal(bool zmode_decal) {
static void gfx_opengl_set_viewport(int x, int y, int width, int height) {
glViewport(x, y, width, height);
current_height = height;
}
static void gfx_opengl_set_scissor(int x, int y, int width, int height) {
@ -564,10 +577,6 @@ static void gfx_opengl_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_
glDrawArrays(GL_TRIANGLES, 0, 3 * buf_vbo_num_tris);
}
static unsigned int framebuffer;
static unsigned int textureColorbuffer;
static unsigned int rbo;
static void gfx_opengl_init(void) {
//#if FOR_WINDOWS
glewInit();
@ -576,143 +585,214 @@ static void gfx_opengl_init(void) {
glGenBuffers(1, &opengl_vbo);
glBindBuffer(GL_ARRAY_BUFFER, opengl_vbo);
glGenFramebuffers(1, &framebuffer);
glGenTextures(1, &textureColorbuffer);
glGenRenderbuffers(1, &rbo);
SohUtils::saveEnvironmentVar("framebuffer", std::to_string(textureColorbuffer));
glDepthFunc(GL_LEQUAL);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
framebuffers.resize(1); // for the default screen buffer
glGenRenderbuffers(1, &pixel_depth_rb);
glBindRenderbuffer(GL_RENDERBUFFER, pixel_depth_rb);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 1, 1);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glGenFramebuffers(1, &pixel_depth_fb);
glBindFramebuffer(GL_FRAMEBUFFER, pixel_depth_fb);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, pixel_depth_rb);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
pixel_depth_rb_size = 1;
}
static void gfx_opengl_on_resize(void) {
}
static void gfx_opengl_start_frame(void) {
GLsizei framebuffer_width = gfx_current_dimensions.width;
GLsizei framebuffer_height = gfx_current_dimensions.height;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
std::shared_ptr<Ship::Window> wnd = Ship::GlobalCtx2::GetInstance()->GetWindow();
glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, framebuffer_width, framebuffer_height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorbuffer, 0);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, framebuffer_width, framebuffer_height); // use a single renderbuffer object for both a depth AND stencil buffer.
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); // now actually attach it
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
frame_count++;
glDisable(GL_SCISSOR_TEST);
glDepthMask(GL_TRUE);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST);
glEnable(GL_DEPTH_CLAMP);
current_depth_mask = true;
}
static void gfx_opengl_end_frame(void) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
GLint last_program;
glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
glUseProgram(0);
SohImGui::Draw();
glUseProgram(last_program);
glFlush();
}
static void gfx_opengl_finish_render(void) {
}
static int gfx_opengl_create_framebuffer(uint32_t width, uint32_t height) {
GLuint textureColorbuffer;
glGenTextures(1, &textureColorbuffer);
glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
static int gfx_opengl_create_framebuffer() {
GLuint clrbuf;
glGenTextures(1, &clrbuf);
glBindTexture(GL_TEXTURE_2D, clrbuf);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
GLuint clrbuf_msaa;
glGenRenderbuffers(1, &clrbuf_msaa);
GLuint rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 1, 1);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
GLuint fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorbuffer, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
size_t i = framebuffers.size();
framebuffers.resize(i + 1);
fb2tex[fbo] = make_pair(textureColorbuffer, rbo);
//glBindFramebuffer(GL_FRAMEBUFFER, 0);
framebuffers[i].fbo = fbo;
framebuffers[i].clrbuf = clrbuf;
framebuffers[i].clrbuf_msaa = clrbuf_msaa;
framebuffers[i].rbo = rbo;
return fbo;
}
static void gfx_opengl_resize_framebuffer(int fb, uint32_t width, uint32_t height) {
glBindTexture(GL_TEXTURE_2D, fb2tex[fb].first);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
static void gfx_opengl_update_framebuffer_parameters(int fb_id, uint32_t width, uint32_t height, uint32_t msaa_level, bool opengl_invert_y, bool render_target, bool has_depth_buffer, bool can_extract_depth) {
Framebuffer& fb = framebuffers[fb_id];
glBindRenderbuffer(GL_RENDERBUFFER, fb2tex[fb].second);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
width = max(width, 1U);
height = max(height, 1U);
glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo);
if (fb_id != 0) {
if (fb.width != width || fb.height != height || fb.msaa_level != msaa_level) {
if (msaa_level <= 1) {
glBindTexture(GL_TEXTURE_2D, fb.clrbuf);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.clrbuf, 0);
} else {
glBindRenderbuffer(GL_RENDERBUFFER, fb.clrbuf_msaa);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa_level, GL_RGB8, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, fb.clrbuf_msaa);
}
}
if (has_depth_buffer && (fb.width != width || fb.height != height || fb.msaa_level != msaa_level || !fb.has_depth_buffer)) {
glBindRenderbuffer(GL_RENDERBUFFER, fb.rbo);
if (msaa_level <= 1) {
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
} else {
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa_level, GL_DEPTH24_STENCIL8, width, height);
}
glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
if (!fb.has_depth_buffer && has_depth_buffer) {
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb.rbo);
} else if (fb.has_depth_buffer && !has_depth_buffer) {
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
}
}
fb.width = width;
fb.height = height;
fb.has_depth_buffer = has_depth_buffer;
fb.msaa_level = msaa_level;
fb.invert_y = opengl_invert_y;
}
void gfx_opengl_set_framebuffer(int fb)
{
if (GLEW_ARB_clip_control || GLEW_VERSION_4_5) {
// Set origin to upper left corner, to match N64 and DX11
// If this function is not supported, the texture will be upside down :(
glClipControl(GL_UPPER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
}
glBindFramebuffer(GL_FRAMEBUFFER_EXT, fb);
void gfx_opengl_start_draw_to_framebuffer(int fb_id, float noise_scale) {
Framebuffer& fb = framebuffers[fb_id];
if (noise_scale != 0.0f) {
current_noise_scale = 1.0f / noise_scale;
}
glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo);
current_framebuffer = fb_id;
}
void gfx_opengl_clear_framebuffer() {
glDisable(GL_SCISSOR_TEST);
glDepthMask(GL_TRUE);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDepthMask(current_depth_mask ? GL_TRUE : GL_FALSE);
glEnable(GL_SCISSOR_TEST);
}
void gfx_opengl_reset_framebuffer(void)
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER_EXT, framebuffer);
if (GLEW_ARB_clip_control || GLEW_VERSION_4_5) {
glClipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
}
void gfx_opengl_resolve_msaa_color_buffer(int fb_id_target, int fb_id_source) {
Framebuffer& fb_dst = framebuffers[fb_id_target];
Framebuffer& fb_src = framebuffers[fb_id_source];
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_dst.fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_src.fbo);
glBlitFramebuffer(0, 0, fb_src.width, fb_src.height, 0, 0, fb_dst.width, fb_dst.height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer);
}
void gfx_opengl_select_texture_fb(int fbID)
{
void *gfx_opengl_get_framebuffer_texture_id(int fb_id) {
return (void *)(uintptr_t)framebuffers[fb_id].clrbuf;
}
void gfx_opengl_select_texture_fb(int fb_id) {
//glDisable(GL_DEPTH_TEST);
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, fb2tex[fbID].first);
glBindTexture(GL_TEXTURE_2D, framebuffers[fb_id].clrbuf);
}
static uint16_t gfx_opengl_get_pixel_depth(float x, float y) {
float depth;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return depth * 65532.0f;
static std::map<std::pair<float, float>, uint16_t> gfx_opengl_get_pixel_depth(int fb_id, const std::set<std::pair<float, float>>& coordinates) {
std::map<std::pair<float, float>, uint16_t> res;
Framebuffer& fb = framebuffers[fb_id];
if (coordinates.size() == 1) {
uint32_t depth_stencil_value;
glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo);
int x = coordinates.begin()->first;
int y = coordinates.begin()->second;
glReadPixels(x, fb.invert_y ? fb.height - y : y, 1, 1, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, &depth_stencil_value);
res.emplace(*coordinates.begin(), (depth_stencil_value >> 18) << 2);
} else {
if (pixel_depth_rb_size < coordinates.size()) {
glBindRenderbuffer(GL_RENDERBUFFER, pixel_depth_rb);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, coordinates.size(), 1);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
pixel_depth_rb_size = coordinates.size();
}
glBindFramebuffer(GL_READ_FRAMEBUFFER, fb.fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, pixel_depth_fb);
glDisable(GL_SCISSOR_TEST); // needed for the blit operation
{
size_t i = 0;
for (const auto& coord : coordinates) {
int x = coord.first;
int y = coord.second;
if (fb.invert_y) {
y = fb.height - y;
}
glBlitFramebuffer(x, y, x + 1, y + 1, i, 0, i + 1, 1, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
++i;
}
}
glBindFramebuffer(GL_READ_FRAMEBUFFER, pixel_depth_fb);
vector<uint32_t> depth_stencil_values(coordinates.size());
glReadPixels(0, 0, coordinates.size(), 1, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, depth_stencil_values.data());
{
size_t i = 0;
for (const auto& coord : coordinates) {
res.emplace(coord, (depth_stencil_values[i++] >> 18) << 2);
}
}
}
glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer);
return res;
}
struct GfxRenderingAPI gfx_opengl_api = {
gfx_opengl_z_is_from_0_to_1,
gfx_opengl_get_clip_parameters,
gfx_opengl_unload_shader,
gfx_opengl_load_shader,
gfx_opengl_create_and_load_new_shader,
@ -723,7 +803,6 @@ struct GfxRenderingAPI gfx_opengl_api = {
gfx_opengl_upload_texture,
gfx_opengl_set_sampler_parameters,
gfx_opengl_set_depth_test_and_mask,
gfx_opengl_get_pixel_depth,
gfx_opengl_set_zmode_decal,
gfx_opengl_set_viewport,
gfx_opengl_set_scissor,
@ -735,9 +814,12 @@ struct GfxRenderingAPI gfx_opengl_api = {
gfx_opengl_end_frame,
gfx_opengl_finish_render,
gfx_opengl_create_framebuffer,
gfx_opengl_resize_framebuffer,
gfx_opengl_set_framebuffer,
gfx_opengl_reset_framebuffer,
gfx_opengl_update_framebuffer_parameters,
gfx_opengl_start_draw_to_framebuffer,
gfx_opengl_clear_framebuffer,
gfx_opengl_resolve_msaa_color_buffer,
gfx_opengl_get_pixel_depth,
gfx_opengl_get_framebuffer_texture_id,
gfx_opengl_select_texture_fb,
gfx_opengl_delete_texture
};

View File

@ -7,6 +7,7 @@
#include <stdio.h>
#include <map>
#include <set>
#include <unordered_map>
#include <vector>
@ -27,6 +28,8 @@
#include "../../luslog.h"
#include "../StrHash64.h"
#include "../../SohImGuiImpl.h"
#include "../../Environment.h"
// OTRTODO: fix header files for these
extern "C" {
@ -71,10 +74,6 @@ struct RGBA {
uint8_t r, g, b, a;
};
struct XYWidthHeight {
uint16_t x, y, width, height;
};
struct LoadedVertex {
float x, y, z, w;
float u, v;
@ -174,8 +173,16 @@ static struct RenderingState {
TextureCacheNode *textures[2];
} rendering_state;
struct GfxDimensions gfx_current_window_dimensions;
struct GfxDimensions gfx_current_dimensions;
static struct GfxDimensions gfx_prev_dimensions;
struct XYWidthHeight gfx_current_game_window_viewport;
static bool game_renders_to_framebuffer;
static int game_framebuffer;
static int game_framebuffer_msaa_resolved;
uint32_t gfx_msaa_level = 1;
static bool dropped_frame;
@ -198,6 +205,9 @@ static bool fbActive = 0;
static map<int, FBInfo>::iterator active_fb;
static map<int, FBInfo> framebuffers;
static set<pair<float, float>> get_pixel_depth_pending;
static map<pair<float, float>, uint16_t> get_pixel_depth_cached;
#ifdef _MSC_VER
// TODO: Properly implement for MSVC
static unsigned long get_time(void)
@ -1326,11 +1336,11 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
gfx_rapi->shader_get_info(prg, &num_inputs, used_textures);
bool z_is_from_0_to_1 = gfx_rapi->z_is_from_0_to_1();
struct GfxClipParameters clip_parameters = gfx_rapi->get_clip_parameters();
for (int i = 0; i < 3; i++) {
float z = v_arr[i]->z, w = v_arr[i]->w;
if (z_is_from_0_to_1) {
if (clip_parameters.z_is_from_0_to_1) {
z = (z + w) / 2.0f;
}
@ -1340,7 +1350,7 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
}
buf_vbo[buf_vbo_len++] = v_arr[i]->x;
buf_vbo[buf_vbo_len++] = v_arr[i]->y;
buf_vbo[buf_vbo_len++] = clip_parameters.invert_y ? -v_arr[i]->y : v_arr[i]->y;
buf_vbo[buf_vbo_len++] = z;
buf_vbo[buf_vbo_len++] = w;
@ -1491,6 +1501,27 @@ static void gfx_sp_geometry_mode(uint32_t clear, uint32_t set) {
rsp.geometry_mode |= set;
}
static void gfx_adjust_viewport_or_scissor(XYWidthHeight *area) {
if (!fbActive) {
area->width *= RATIO_X;
area->height *= RATIO_Y;
area->x *= RATIO_X;
area->y = SCREEN_HEIGHT - area->y;
area->y *= RATIO_Y;
if (!game_renders_to_framebuffer || (gfx_msaa_level > 1 && gfx_current_dimensions.width == gfx_current_game_window_viewport.width && gfx_current_dimensions.height == gfx_current_game_window_viewport.height)) {
area->x += gfx_current_game_window_viewport.x;
area->y += gfx_current_window_dimensions.height - (gfx_current_game_window_viewport.y + gfx_current_game_window_viewport.height);
}
} else {
area->width *= RATIO_Y;
area->height *= RATIO_Y;
area->x *= RATIO_Y;
area->y = active_fb->second.orig_height - area->y;
area->y *= RATIO_Y;
}
}
static void gfx_calc_and_set_viewport(const Vp_t *viewport) {
// 2 bits fraction
float width = 2.0f * viewport->vscale[0] / 4.0f;
@ -1498,25 +1529,13 @@ static void gfx_calc_and_set_viewport(const Vp_t *viewport) {
float x = (viewport->vtrans[0] / 4.0f) - width / 2.0f;
float y = ((viewport->vtrans[1] / 4.0f) + height / 2.0f);
if (!fbActive) {
width *= RATIO_X;
height *= RATIO_Y;
x *= RATIO_X;
y = SCREEN_HEIGHT - y;
y *= RATIO_Y;
} else {
width *= RATIO_Y;
height *= RATIO_Y;
x *= RATIO_Y;
y = active_fb->second.orig_height - y;
y *= RATIO_Y;
}
rdp.viewport.x = x;
rdp.viewport.y = y;
rdp.viewport.width = width;
rdp.viewport.height = height;
gfx_adjust_viewport_or_scissor(&rdp.viewport);
rdp.viewport_or_scissor_changed = true;
}
@ -1585,11 +1604,6 @@ static void gfx_sp_texture(uint16_t sc, uint16_t tc, uint8_t level, uint8_t tile
rdp.textures_changed[1] = true;
}
if (tile > 8)
{
int bp = 0;
}
rdp.first_tile_index = tile;
}
@ -1599,25 +1613,13 @@ static void gfx_dp_set_scissor(uint32_t mode, uint32_t ulx, uint32_t uly, uint32
float width = (lrx - ulx) / 4.0f;
float height = (lry - uly) / 4.0f;
if (!fbActive) {
x *= RATIO_X;
y = SCREEN_HEIGHT - y;
y *= RATIO_Y;
width *= RATIO_X;
height *= RATIO_Y;
} else {
width *= RATIO_Y;
height *= RATIO_Y;
x *= RATIO_Y;
y = active_fb->second.orig_height - y;
y *= RATIO_Y;
}
rdp.scissor.x = x;
rdp.scissor.y = y;
rdp.scissor.width = width;
rdp.scissor.height = height;
gfx_adjust_viewport_or_scissor(&rdp.scissor);
rdp.viewport_or_scissor_changed = true;
}
@ -1887,10 +1889,12 @@ static void gfx_draw_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lr
ur->w = 1.0f;
// The coordinates for texture rectangle shall bypass the viewport setting
struct XYWidthHeight default_viewport = { 0, 0, gfx_current_dimensions.width, gfx_current_dimensions.height };
struct XYWidthHeight default_viewport = { 0, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT };
struct XYWidthHeight viewport_saved = rdp.viewport;
uint32_t geometry_mode_saved = rsp.geometry_mode;
gfx_adjust_viewport_or_scissor(&default_viewport);
rdp.viewport = default_viewport;
rdp.viewport_or_scissor_changed = true;
rsp.geometry_mode = 0;
@ -2456,14 +2460,15 @@ static void gfx_run_dl(Gfx* cmd) {
gfx_flush();
fbActive = 1;
active_fb = framebuffers.find(cmd->words.w1);
gfx_rapi->set_framebuffer(active_fb->first);
gfx_rapi->start_draw_to_framebuffer(active_fb->first, (float)active_fb->second.applied_height / active_fb->second.orig_height);
gfx_rapi->clear_framebuffer();
}
break;
case G_RESETFB:
{
gfx_flush();
fbActive = 0;
gfx_rapi->reset_framebuffer();
gfx_rapi->start_draw_to_framebuffer(game_renders_to_framebuffer ? game_framebuffer : 0, (float)gfx_current_dimensions.height / SCREEN_HEIGHT);
break;
}
break;
@ -2630,9 +2635,12 @@ void gfx_init(struct GfxWindowManagerAPI *wapi, struct GfxRenderingAPI *rapi, co
gfx_rapi = rapi;
gfx_wapi->init(game_name, start_in_fullscreen);
gfx_rapi->init();
gfx_rapi->update_framebuffer_parameters(0, SCREEN_WIDTH, SCREEN_HEIGHT, 1, false, true, true, true);
gfx_current_dimensions.internal_mul = 1;
gfx_current_dimensions.width = SCREEN_WIDTH;
gfx_current_dimensions.height = SCREEN_HEIGHT;
game_framebuffer = gfx_rapi->create_framebuffer();
game_framebuffer_msaa_resolved = gfx_rapi->create_framebuffer();
for (int i = 0; i < 16; i++)
segmentPointers[i] = NULL;
@ -2681,7 +2689,8 @@ struct GfxRenderingAPI *gfx_get_current_rendering_api(void) {
void gfx_start_frame(void) {
gfx_wapi->handle_events();
// gfx_wapi->get_dimensions(&gfx_current_dimensions.width, &gfx_current_dimensions.height);
gfx_wapi->get_dimensions(&gfx_current_window_dimensions.width, &gfx_current_window_dimensions.height);
SohImGui::DrawMainMenuAndCalculateGameSize();
if (gfx_current_dimensions.height == 0) {
// Avoid division by zero
gfx_current_dimensions.height = 1;
@ -2693,7 +2702,7 @@ void gfx_start_frame(void) {
uint32_t width = fb.second.orig_width, height = fb.second.orig_height;
gfx_adjust_width_height_for_scale(width, height);
if (width != fb.second.applied_width || height != fb.second.applied_height) {
gfx_rapi->resize_framebuffer(fb.first, width, height);
gfx_rapi->update_framebuffer_parameters(fb.first, width, height, 1, true, true, true, true);
fb.second.applied_width = width;
fb.second.applied_height = height;
}
@ -2701,6 +2710,22 @@ void gfx_start_frame(void) {
}
gfx_prev_dimensions = gfx_current_dimensions;
bool different_size = gfx_current_dimensions.width != gfx_current_game_window_viewport.width || gfx_current_dimensions.height != gfx_current_game_window_viewport.height;
if (different_size || gfx_msaa_level > 1) {
game_renders_to_framebuffer = true;
if (different_size) {
gfx_rapi->update_framebuffer_parameters(game_framebuffer, gfx_current_dimensions.width, gfx_current_dimensions.height, gfx_msaa_level, true, true, true, true);
} else {
// MSAA framebuffer needs to be resolved to an equally sized target when complete, which must therefore match the window size
gfx_rapi->update_framebuffer_parameters(game_framebuffer, gfx_current_window_dimensions.width, gfx_current_window_dimensions.height, gfx_msaa_level, false, true, true, true);
}
if (gfx_msaa_level > 1 && different_size) {
gfx_rapi->update_framebuffer_parameters(game_framebuffer_msaa_resolved, gfx_current_dimensions.width, gfx_current_dimensions.height, 1, false, false, false, false);
}
} else {
game_renders_to_framebuffer = false;
}
fbActive = 0;
}
@ -2708,17 +2733,44 @@ void gfx_run(Gfx *commands) {
gfx_sp_reset();
//puts("New frame");
get_pixel_depth_pending.clear();
get_pixel_depth_cached.clear();
if (!gfx_wapi->start_frame()) {
dropped_frame = true;
SohImGui::DrawFramebufferAndGameInput();
SohImGui::CancelFrame();
return;
}
dropped_frame = false;
double t0 = gfx_wapi->get_time();
gfx_rapi->update_framebuffer_parameters(0, gfx_current_window_dimensions.width, gfx_current_window_dimensions.height, 1, false, true, true, !game_renders_to_framebuffer);
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();
gfx_run_dl(commands);
gfx_flush();
SohUtils::saveEnvironmentVar("framebuffer", string());
if (game_renders_to_framebuffer) {
gfx_rapi->start_draw_to_framebuffer(0, 1);
gfx_rapi->clear_framebuffer();
if (gfx_msaa_level > 1) {
bool different_size = gfx_current_dimensions.width != gfx_current_game_window_viewport.width || gfx_current_dimensions.height != gfx_current_game_window_viewport.height;
if (different_size) {
gfx_rapi->resolve_msaa_color_buffer(game_framebuffer_msaa_resolved, game_framebuffer);
SohUtils::saveEnvironmentVar("framebuffer", std::to_string((uintptr_t)gfx_rapi->get_framebuffer_texture_id(game_framebuffer_msaa_resolved)));
} else {
gfx_rapi->resolve_msaa_color_buffer(0, game_framebuffer);
}
} else {
SohUtils::saveEnvironmentVar("framebuffer", std::to_string((uintptr_t)gfx_rapi->get_framebuffer_texture_id(game_framebuffer)));
}
}
SohImGui::DrawFramebufferAndGameInput();
SohImGui::Render();
double t1 = gfx_wapi->get_time();
//printf("Process %f %f\n", t1, t1 - t0);
gfx_rapi->end_frame();
@ -2739,21 +2791,47 @@ void gfx_set_framedivisor(int divisor) {
int gfx_create_framebuffer(uint32_t width, uint32_t height) {
uint32_t orig_width = width, orig_height = height;
gfx_adjust_width_height_for_scale(width, height);
int fb = gfx_rapi->create_framebuffer(width, height);
int fb = gfx_rapi->create_framebuffer();
gfx_rapi->update_framebuffer_parameters(fb, width, height, 1, true, true, true, true);
framebuffers[fb] = { orig_width, orig_height, width, height };
return fb;
}
void gfx_set_framebuffer(int fb)
{
gfx_rapi->set_framebuffer(fb);
void gfx_set_framebuffer(int fb, float noise_scale) {
gfx_rapi->start_draw_to_framebuffer(fb, noise_scale);
gfx_rapi->clear_framebuffer();
}
void gfx_reset_framebuffer()
{
gfx_rapi->reset_framebuffer();
void gfx_reset_framebuffer() {
gfx_rapi->start_draw_to_framebuffer(0, (float)gfx_current_dimensions.height / SCREEN_HEIGHT);
}
static void adjust_pixel_depth_coordinates(float& x, float& y) {
x = x * RATIO_Y - (SCREEN_WIDTH * RATIO_Y - gfx_current_dimensions.width) / 2;
y *= RATIO_Y;
if (!game_renders_to_framebuffer || (gfx_msaa_level > 1 && gfx_current_dimensions.width == gfx_current_game_window_viewport.width && gfx_current_dimensions.height == gfx_current_game_window_viewport.height)) {
x += gfx_current_game_window_viewport.x;
y += gfx_current_window_dimensions.height - (gfx_current_game_window_viewport.y + gfx_current_game_window_viewport.height);
}
}
void gfx_get_pixel_depth_prepare(float x, float y) {
adjust_pixel_depth_coordinates(x, y);
get_pixel_depth_pending.emplace(x, y);
}
uint16_t gfx_get_pixel_depth(float x, float y) {
return gfx_rapi->get_pixel_depth(x * RATIO_Y - (SCREEN_WIDTH * RATIO_Y - gfx_current_dimensions.width) / 2, y * RATIO_Y);
adjust_pixel_depth_coordinates(x, y);
if (auto it = get_pixel_depth_cached.find(make_pair(x, y)); it != get_pixel_depth_cached.end()) {
return it->second;
}
get_pixel_depth_pending.emplace(x, y);
map<pair<float, float>, uint16_t> res = gfx_rapi->get_pixel_depth(game_renders_to_framebuffer ? game_framebuffer : 0, get_pixel_depth_pending);
get_pixel_depth_cached.merge(res);
get_pixel_depth_pending.clear();
return get_pixel_depth_cached.find(make_pair(x, y))->second;
}

View File

@ -8,8 +8,11 @@
struct GfxRenderingAPI;
struct GfxWindowManagerAPI;
struct GfxDimensions
{
struct XYWidthHeight {
int16_t x, y, width, height;
};
struct GfxDimensions {
uint32_t internal_mul;
uint32_t width, height;
float aspect_ratio;
@ -50,7 +53,10 @@ struct TextureCacheValue {
#ifdef __cplusplus
extern "C" {
#endif
extern struct GfxDimensions gfx_current_dimensions;
extern struct GfxDimensions gfx_current_window_dimensions; // The dimensions of the window
extern struct GfxDimensions gfx_current_dimensions; // The dimensions of the draw area the game draws to, before scaling (if applicable)
extern struct XYWidthHeight gfx_current_game_window_viewport; // The area of the window the game is drawn to, (0, 0) is top-left corner
extern uint32_t gfx_msaa_level;
void gfx_init(struct GfxWindowManagerAPI* wapi, struct GfxRenderingAPI* rapi, const char* game_name, bool start_in_fullscreen);
struct GfxRenderingAPI* gfx_get_current_rendering_api(void);
@ -60,6 +66,7 @@ void gfx_end_frame(void);
void gfx_set_framedivisor(int);
void gfx_texture_cache_clear();
int gfx_create_framebuffer(uint32_t width, uint32_t height);
void gfx_get_pixel_depth_prepare(float x, float y);
uint16_t gfx_get_pixel_depth(float x, float y);
#ifdef __cplusplus

View File

@ -5,10 +5,18 @@
#include <stdint.h>
#include <stdbool.h>
#include <map>
#include <set>
struct ShaderProgram;
struct GfxClipParameters {
bool z_is_from_0_to_1;
bool invert_y;
};
struct GfxRenderingAPI {
bool (*z_is_from_0_to_1)(void);
struct GfxClipParameters (*get_clip_parameters)(void);
void (*unload_shader)(struct ShaderProgram *old_prg);
void (*load_shader)(struct ShaderProgram *new_prg);
struct ShaderProgram *(*create_and_load_new_shader)(uint64_t shader_id0, uint32_t shader_id1);
@ -19,7 +27,6 @@ struct GfxRenderingAPI {
void (*upload_texture)(const uint8_t *rgba32_buf, uint32_t width, uint32_t height);
void (*set_sampler_parameters)(int sampler, bool linear_filter, uint32_t cms, uint32_t cmt);
void (*set_depth_test_and_mask)(bool depth_test, bool z_upd);
uint16_t (*get_pixel_depth)(float x, float y);
void (*set_zmode_decal)(bool zmode_decal);
void (*set_viewport)(int x, int y, int width, int height);
void (*set_scissor)(int x, int y, int width, int height);
@ -30,11 +37,14 @@ struct GfxRenderingAPI {
void (*start_frame)(void);
void (*end_frame)(void);
void (*finish_render)(void);
int (*create_framebuffer)(uint32_t width, uint32_t height);
void (*resize_framebuffer)(int fb, uint32_t width, uint32_t height);
void (*set_framebuffer)(int fb);
void (*reset_framebuffer)();
void (*select_texture_fb)(int fbID);
int (*create_framebuffer)();
void (*update_framebuffer_parameters)(int fb_id, uint32_t width, uint32_t height, uint32_t msaa_level, bool opengl_invert_y, bool render_target, bool has_depth_buffer, bool can_extract_depth);
void (*start_draw_to_framebuffer)(int fb_id, float noise_scale);
void (*clear_framebuffer)(void);
void (*resolve_msaa_color_buffer)(int fb_id_target, int fb_id_source);
std::map<std::pair<float, float>, uint16_t> (*get_pixel_depth)(int fb_id, const std::set<std::pair<float, float>>& coordinates);
void *(*get_framebuffer_texture_id)(int fb_id);
void (*select_texture_fb)(int fb_id);
void (*delete_texture)(uint32_t texID);
};

View File

@ -502,7 +502,6 @@ namespace ImGui
IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) to easily embed within text
IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size, ImGuiButtonFlags flags = 0); // flexible button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.)
IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); // square button with an arrow shape
IMGUI_API void ImageRotated(ImTextureID tex_id, ImVec2 center, ImVec2 size, float angle);
IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0));
IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding
IMGUI_API bool Checkbox(const char* label, bool* v);

View File

@ -1007,33 +1007,6 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
return held;
}
void ImGui::ImageRotated(ImTextureID tex_id, ImVec2 center, ImVec2 size, float angle) {
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImRect bb(window->DC.CursorPos + ImVec2(size.x / 2, size.y / 2), window->DC.CursorPos + size);
ImVec2 pos[4] =
{
center + bb.Min + ImVec2(-size.x * 0.5f, -size.y * 0.5f),
center + bb.Min + ImVec2(+size.x * 0.5f, -size.y * 0.5f),
center + bb.Min + ImVec2(+size.x * 0.5f, +size.y * 0.5f),
center + bb.Min + ImVec2(-size.x * 0.5f, +size.y * 0.5f)
};
ImVec2 uvs[4] =
{
ImVec2(0.0f, 1.0f),
ImVec2(1.0f, 1.0f),
ImVec2(1.0f, 0.0f),
ImVec2(0.0f, 0.0f)
};
draw_list->AddImageQuad(tex_id, pos[0], pos[1], pos[2], pos[3], uvs[0], uvs[1], uvs[2], uvs[3], IM_COL32_WHITE);
}
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
{
ImGuiWindow* window = GetCurrentWindow();

View File

@ -123,14 +123,6 @@ namespace SohImGui {
}
}
bool UseInternalRes() {
switch (impl.backend) {
case Backend::SDL:
return true;
}
return false;
}
bool UseViewports() {
switch (impl.backend) {
case Backend::DX11:
@ -281,20 +273,16 @@ namespace SohImGui {
}
}
void Draw() {
void DrawMainMenuAndCalculateGameSize() {
console->Update();
ImGuiBackendNewFrame();
ImGuiWMNewFrame();
ImGui::NewFrame();
const std::shared_ptr<Window> wnd = GlobalCtx2::GetInstance()->GetWindow();
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking |
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoResize;
if (UseViewports()) {
window_flags |= ImGuiWindowFlags_NoBackground;
}
if (Game::Settings.debug.menu_bar) window_flags |= ImGuiWindowFlags_MenuBar;
const ImGuiViewport* viewport = ImGui::GetMainViewport();
@ -302,8 +290,12 @@ namespace SohImGui {
ImGui::SetNextWindowSize(ImVec2(wnd->GetCurrentWidth(), wnd->GetCurrentHeight()));
ImGui::SetNextWindowViewport(viewport->ID);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f);
ImGui::Begin("Main - Deck", nullptr, window_flags);
ImGui::PopStyleVar();
ImGui::PopStyleVar(3);
ImVec2 top_left_pos = ImGui::GetWindowPos();
const ImGuiID dockId = ImGui::GetID("main_dock");
@ -421,9 +413,7 @@ namespace SohImGui {
ImGui::Text("Graphics");
ImGui::Separator();
if (UseInternalRes()) {
HOOK(ImGui::Checkbox("N64 Mode", &Game::Settings.debug.n64mode));
}
if (ImGui::Checkbox("Animated Link in Pause Menu", &Game::Settings.enhancements.animated_pause_menu)) {
CVar_SetS32("gPauseLiveLink", Game::Settings.enhancements.animated_pause_menu);
@ -453,6 +443,11 @@ namespace SohImGui {
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Graphics")) {
HOOK(ImGui::MenuItem("Anti-aliasing", nullptr, &Game::Settings.graphics.show));
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Cheats")) {
if (ImGui::Checkbox("Infinite Money", &Game::Settings.cheats.infinite_money)) {
CVar_SetS32("gInfiniteMoney", Game::Settings.cheats.infinite_money);
@ -509,36 +504,6 @@ namespace SohImGui {
ImGui::EndMenuBar();
}
ImGui::End();
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
if (UseViewports()) {
flags |= ImGuiWindowFlags_NoBackground;
}
ImGui::Begin("OoT Master Quest", nullptr, flags);
ImGui::PopStyleVar();
ImGui::PopStyleColor();
ImVec2 main_pos = ImGui::GetWindowPos();
ImVec2 size = ImGui::GetContentRegionAvail();
ImVec2 pos = ImVec2(0, 0);
gfx_current_dimensions.width = size.x * gfx_current_dimensions.internal_mul;
gfx_current_dimensions.height = size.y * gfx_current_dimensions.internal_mul;
if (UseInternalRes()) {
if (Game::Settings.debug.n64mode) {
gfx_current_dimensions.width = 320;
gfx_current_dimensions.height = 240;
const int sw = size.y * 320 / 240;
pos = ImVec2(size.x / 2 - sw / 2, 0);
size = ImVec2(sw, size.y);
}
}
if (UseInternalRes()) {
int fbuf = std::stoi(SohUtils::getEnvironmentVar("framebuffer"));
ImGui::ImageRotated(reinterpret_cast<ImTextureID>(fbuf), pos, size, 0.0f);
}
ImGui::End();
if (Game::Settings.debug.soh) {
@ -548,18 +513,84 @@ namespace SohImGui {
ImGui::Text("Platform: Windows");
ImGui::Text("Status: %.3f ms/frame (%.1f FPS)", 1000.0f / framerate, framerate);
if (UseInternalRes()) {
ImGui::Text("Internal Resolution:");
ImGui::SliderInt("Mul", reinterpret_cast<int*>(&gfx_current_dimensions.internal_mul), 1, 8);
}
ImGui::End();
ImGui::PopStyleColor();
}
if (Game::Settings.graphics.show) {
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
ImGui::Begin("Anti-aliasing settings", nullptr, ImGuiWindowFlags_None);
ImGui::Text("Internal Resolution:");
ImGui::SliderInt("Mul", reinterpret_cast<int*>(&gfx_current_dimensions.internal_mul), 1, 8);
ImGui::Text("MSAA:");
ImGui::SliderInt("MSAA", reinterpret_cast<int*>(&gfx_msaa_level), 1, 8);
ImGui::End();
ImGui::PopStyleColor();
}
console->Draw();
for (auto& windowIter : customWindows) {
CustomWindow& window = windowIter.second;
if (window.drawFunc != nullptr) {
window.drawFunc(window.enabled);
}
}
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f);
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBackground;
ImGui::Begin("OoT Master Quest", nullptr, flags);
ImGui::PopStyleVar(3);
ImGui::PopStyleColor();
ImVec2 main_pos = ImGui::GetWindowPos();
main_pos.x -= top_left_pos.x;
main_pos.y -= top_left_pos.y;
ImVec2 size = ImGui::GetContentRegionAvail();
ImVec2 pos = ImVec2(0, 0);
gfx_current_dimensions.width = size.x * gfx_current_dimensions.internal_mul;
gfx_current_dimensions.height = size.y * gfx_current_dimensions.internal_mul;
gfx_current_game_window_viewport.x = main_pos.x;
gfx_current_game_window_viewport.y = main_pos.y;
gfx_current_game_window_viewport.width = size.x;
gfx_current_game_window_viewport.height = size.y;
if (Game::Settings.debug.n64mode) {
gfx_current_dimensions.width = 320;
gfx_current_dimensions.height = 240;
const int sw = size.y * 320 / 240;
gfx_current_game_window_viewport.x += (size.x - sw) / 2;
gfx_current_game_window_viewport.width = sw;
pos = ImVec2(size.x / 2 - sw / 2, 0);
size = ImVec2(sw, size.y);
}
}
void DrawFramebufferAndGameInput() {
ImVec2 main_pos = ImGui::GetWindowPos();
ImVec2 size = ImGui::GetContentRegionAvail();
ImVec2 pos = ImVec2(0, 0);
if (Game::Settings.debug.n64mode) {
const int sw = size.y * 320 / 240;
pos = ImVec2(size.x / 2 - sw / 2, 0);
size = ImVec2(sw, size.y);
}
std::string fb_str = SohUtils::getEnvironmentVar("framebuffer");
if (!fb_str.empty()) {
uintptr_t fbuf = (uintptr_t)std::stoull(fb_str);
//ImGui::ImageSimple(reinterpret_cast<ImTextureID>(fbuf), pos, size);
ImGui::SetCursorPos(pos);
ImGui::Image(reinterpret_cast<ImTextureID>(fbuf), size);
}
ImGui::End();
const float scale = Game::Settings.controller.input_scale;
ImVec2 BtnPos = ImVec2(160 * scale, 85 * scale);
if(Game::Settings.controller.input_enabled) {
if (Game::Settings.controller.input_enabled) {
ImGui::SetNextWindowSize(BtnPos);
ImGui::SetNextWindowPos(ImVec2(main_pos.x + size.x - BtnPos.x - 20, main_pos.y + size.y - BtnPos.y - 20));
@ -605,16 +636,9 @@ namespace SohImGui {
ImGui::End();
}
}
console->Draw();
for (auto& windowIter : customWindows) {
CustomWindow& window = windowIter.second;
if (window.drawFunc != nullptr) {
window.drawFunc(window.enabled);
}
}
void Render() {
ImGui::Render();
ImGuiRenderDrawData(ImGui::GetDrawData());
if (UseViewports()) {
@ -623,6 +647,13 @@ namespace SohImGui {
}
}
void CancelFrame() {
ImGui::EndFrame();
if (UseViewports()) {
ImGui::UpdatePlatformWindows();
}
}
void BindCmd(const std::string& cmd, CommandEntry entry) {
console->Commands[cmd] = std::move(entry);
}

View File

@ -60,7 +60,10 @@ namespace SohImGui {
extern Console* console;
void Init(WindowImpl window_impl);
void Update(EventImpl event);
void Draw(void);
void DrawMainMenuAndCalculateGameSize(void);
void DrawFramebufferAndGameInput(void);
void Render(void);
void CancelFrame(void);
void ShowCursor(bool hide, Dialogues w);
void BindCmd(const std::string& cmd, CommandEntry entry);
void AddWindow(const std::string& category, const std::string& name, WindowDrawFunc drawFunc);

View File

@ -286,6 +286,10 @@ namespace Ship {
//gfx_set_framedivisor(0);
}
void Window::GetPixelDepthPrepare(float x, float y) {
gfx_get_pixel_depth_prepare(x, y);
}
uint16_t Window::GetPixelDepth(float x, float y) {
return gfx_get_pixel_depth(x, y);
}

View File

@ -20,6 +20,7 @@ namespace Ship {
void Init();
void RunCommands(Gfx* Commands);
void SetFrameDivisor(int divisor);
void GetPixelDepthPrepare(float x, float y);
uint16_t GetPixelDepth(float x, float y);
void ToggleFullscreen();
void SetFullscreen(bool bIsFullscreen);

View File

@ -977,6 +977,7 @@ void LightContext_RemoveLight(GlobalContext* globalCtx, LightContext* lightCtx,
Lights* Lights_NewAndDraw(GraphicsContext* gfxCtx, u8 ambientR, u8 ambientG, u8 ambientB, u8 numLights, u8 r, u8 g,
u8 b, s8 x, s8 y, s8 z);
Lights* Lights_New(GraphicsContext* gfxCtx, u8 ambientR, u8 ambientG, u8 ambientB);
void Lights_GlowCheckPrepare(GlobalContext* globalCtx);
void Lights_GlowCheck(GlobalContext* globalCtx);
void Lights_DrawGlow(GlobalContext* globalCtx);
void ZeldaArena_CheckPointer(void* ptr, size_t size, const char* name, const char* action);

View File

@ -9,6 +9,7 @@ void Graph_ProcessGfxCommands(Gfx* commands);
void OTRLogString(const char* src);
void OTRGfxPrint(const char* str, void* printer, void (*printImpl)(void*, char));
void OTRSetFrameDivisor(int divisor);
void OTRGetPixelDepthPrepare(float x, float y);
uint16_t OTRGetPixelDepth(float x, float y);
int32_t OTRGetLastScancode();
void ResourceMgr_CacheDirectory(const char* resName);

View File

@ -24,9 +24,17 @@
#include "../soh/Enhancements/debugconsole.h"
#include "../soh/Enhancements/debugger/debugger.h"
#include "Utils/BitConverter.h"
#include "variables.h"
OTRGlobals* OTRGlobals::Instance;
static struct {
std::condition_variable cv_to_thread, cv_from_thread;
std::mutex mutex;
bool initialized;
bool processing;
} audio;
OTRGlobals::OTRGlobals() {
context = Ship::GlobalCtx2::CreateInstance("Ship of Harkinian");
context->GetWindow()->Init();
@ -39,6 +47,10 @@ extern uintptr_t clearMtx;
extern "C" Mtx gMtxClear;
extern "C" MtxF gMtxFClear;
extern "C" void OTRMessage_Init();
extern "C" void AudioMgr_CreateNextAudioBuffer(s16* samples, u32 num_samples);
extern "C" void AudioPlayer_Play(const uint8_t* buf, uint32_t len);
extern "C" int AudioPlayer_Buffered(void);
extern "C" int AudioPlayer_GetDesiredBuffered(void);
// C->C++ Bridge
extern "C" void InitOTR() {
@ -80,8 +92,67 @@ extern "C" void Graph_ProcessFrame(void (*run_one_game_iter)(void)) {
// C->C++ Bridge
extern "C" void Graph_ProcessGfxCommands(Gfx* commands) {
OTRGlobals::Instance->context->GetWindow()->SetFrameDivisor(R_UPDATE_RATE);
if (!audio.initialized) {
audio.initialized = true;
std::thread([]() {
for (;;) {
{
std::unique_lock<std::mutex> Lock(audio.mutex);
while (!audio.processing) {
audio.cv_to_thread.wait(Lock);
}
}
//AudioMgr_ThreadEntry(&gAudioMgr);
// 528 and 544 relate to 60 fps at 32 kHz 32000/60 = 533.333..
// in an ideal world, one third of the calls should use num_samples=544 and two thirds num_samples=528
#define SAMPLES_HIGH 560
#define SAMPLES_LOW 528
// PAL values
//#define SAMPLES_HIGH 656
//#define SAMPLES_LOW 624
#define AUDIO_FRAMES_PER_UPDATE (R_UPDATE_RATE > 0 ? R_UPDATE_RATE : 1 )
#define NUM_AUDIO_CHANNELS 2
int samples_left = AudioPlayer_Buffered();
u32 num_audio_samples = samples_left < AudioPlayer_GetDesiredBuffered() ? SAMPLES_HIGH : SAMPLES_LOW;
// printf("Audio samples: %d %u\n", samples_left, num_audio_samples);
// 3 is the maximum authentic frame divisor.
s16 audio_buffer[SAMPLES_HIGH * NUM_AUDIO_CHANNELS * 3];
for (int i = 0; i < AUDIO_FRAMES_PER_UPDATE; i++) {
AudioMgr_CreateNextAudioBuffer(audio_buffer + i * (num_audio_samples * NUM_AUDIO_CHANNELS), num_audio_samples);
}
//for (uint32_t i = 0; i < 2 * num_audio_samples; i++) {
// audio_buffer[i] = Rand_Next() & 0xFF;
//}
// printf("Audio samples before submitting: %d\n", audio_api->buffered());
AudioPlayer_Play((u8*)audio_buffer, num_audio_samples * (sizeof(int16_t) * NUM_AUDIO_CHANNELS * AUDIO_FRAMES_PER_UPDATE));
{
std::unique_lock<std::mutex> Lock(audio.mutex);
audio.processing = false;
}
audio.cv_from_thread.notify_one();
}
}).detach();
}
{
std::unique_lock<std::mutex> Lock(audio.mutex);
audio.processing = true;
}
audio.cv_to_thread.notify_one();
OTRGlobals::Instance->context->GetWindow()->RunCommands(commands);
{
std::unique_lock<std::mutex> Lock(audio.mutex);
while (audio.processing) {
audio.cv_from_thread.wait(Lock);
}
}
// OTRTODO: FIGURE OUT END FRAME POINT
/* if (OTRGlobals::Instance->context->GetWindow()->lastScancode != -1)
OTRGlobals::Instance->context->GetWindow()->lastScancode = -1;*/
@ -90,8 +161,8 @@ extern "C" void Graph_ProcessGfxCommands(Gfx* commands) {
float divisor_num = 0.0f;
extern "C" void OTRSetFrameDivisor(int divisor) {
OTRGlobals::Instance->context->GetWindow()->SetFrameDivisor(divisor);
extern "C" void OTRGetPixelDepthPrepare(float x, float y) {
OTRGlobals::Instance->context->GetWindow()->GetPixelDepthPrepare(x, y);
}
extern "C" uint16_t OTRGetPixelDepth(float x, float y) {

View File

@ -24,7 +24,7 @@ void Graph_ProcessFrame(void (*run_one_game_iter)(void));
void Graph_ProcessGfxCommands(Gfx* commands);
void OTRLogString(const char* src);
void OTRGfxPrint(const char* str, void* printer, void (*printImpl)(void*, char));
void OTRSetFrameDivisor(int divisor);
void OTRGetPixelDepthPrepare(float x, float y);
uint16_t OTRGetPixelDepth(float x, float y);
int32_t OTRGetLastScancode();
uint32_t ResourceMgr_GetGameVersion();

View File

@ -469,35 +469,6 @@ static void RunFrame()
uint64_t ticksA, ticksB;
ticksA = GetPerfCounter();
OTRSetFrameDivisor(R_UPDATE_RATE);
//OTRSetFrameDivisor(0);
//AudioMgr_ThreadEntry(&gAudioMgr);
// 528 and 544 relate to 60 fps at 32 kHz 32000/60 = 533.333..
// in an ideal world, one third of the calls should use num_samples=544 and two thirds num_samples=528
#define SAMPLES_HIGH 560
#define SAMPLES_LOW 528
// PAL values
//#define SAMPLES_HIGH 656
//#define SAMPLES_LOW 624
#define AUDIO_FRAMES_PER_UPDATE (R_UPDATE_RATE > 0 ? R_UPDATE_RATE : 1 )
#define NUM_AUDIO_CHANNELS 2
int samples_left = AudioPlayer_Buffered();
u32 num_audio_samples = samples_left < AudioPlayer_GetDesiredBuffered() ? SAMPLES_HIGH : SAMPLES_LOW;
// printf("Audio samples: %d %u\n", samples_left, num_audio_samples);
// 3 is the maximum authentic frame divisor.
s16 audio_buffer[SAMPLES_HIGH * NUM_AUDIO_CHANNELS * 3];
for (int i = 0; i < AUDIO_FRAMES_PER_UPDATE; i++) {
AudioMgr_CreateNextAudioBuffer(audio_buffer + i * (num_audio_samples * NUM_AUDIO_CHANNELS), num_audio_samples);
}
//for (uint32_t i = 0; i < 2 * num_audio_samples; i++) {
// audio_buffer[i] = Rand_Next() & 0xFF;
//}
// printf("Audio samples before submitting: %d\n", audio_api->buffered());
AudioPlayer_Play((u8*)audio_buffer, num_audio_samples * (sizeof(int16_t) * NUM_AUDIO_CHANNELS * AUDIO_FRAMES_PER_UPDATE));
PadMgr_ThreadEntry(&gPadMgr);

View File

@ -226,6 +226,9 @@ u16 Environment_GetPixelDepth(s32 x, s32 y) {
void Environment_GraphCallback(GraphicsContext* gfxCtx, void* param) {
GlobalContext* globalCtx = (GlobalContext*)param;
OTRGetPixelDepthPrepare(D_8015FD7E, D_8015FD80);
Lights_GlowCheckPrepare(globalCtx);
D_8011FB44 = Environment_GetPixelDepth(D_8015FD7E, D_8015FD80);
Lights_GlowCheck(globalCtx);
}

View File

@ -323,6 +323,44 @@ Lights* Lights_New(GraphicsContext* gfxCtx, u8 ambientR, u8 ambientG, u8 ambient
return lights;
}
void Lights_GlowCheckPrepare(GlobalContext* globalCtx) {
LightNode* node;
LightPoint* params;
Vec3f pos;
Vec3f multDest;
f32 wDest;
f32 wX;
f32 wY;
node = globalCtx->lightCtx.listHead;
while (node != NULL) {
params = &node->info->params.point;
if (node->info->type == LIGHT_POINT_GLOW) {
f32 x, y;
u32 shrink;
uint32_t height;
pos.x = params->x;
pos.y = params->y;
pos.z = params->z;
func_8002BE04(globalCtx, &pos, &multDest, &wDest);
wX = multDest.x * wDest;
wY = multDest.y * wDest;
x = wX * 160 + 160;
y = wY * 120 + 120;
shrink = ShrinkWindow_GetCurrentVal();
if ((multDest.z > 1.0f) && y >= shrink && y <= SCREEN_HEIGHT - shrink) {
OTRGetPixelDepthPrepare(x, y);
}
}
node = node->next;
}
}
void Lights_GlowCheck(GlobalContext* globalCtx) {
LightNode* node;
LightPoint* params;