From ceef4a94537a9b6fd14b5ff1b58f4605fd4c64fd Mon Sep 17 00:00:00 2001 From: Emill Date: Mon, 18 Apr 2022 11:37:47 +0200 Subject: [PATCH] Graphics backend enhancements etc. (#163) --- libultraship/libultraship/GameSettings.h | 5 + .../Lib/Fast3D/gfx_direct3d11.cpp | 541 +++++++++--------- .../Lib/Fast3D/gfx_direct3d_common.cpp | 16 +- .../libultraship/Lib/Fast3D/gfx_dxgi.cpp | 27 +- .../libultraship/Lib/Fast3D/gfx_opengl.cpp | 292 ++++++---- .../libultraship/Lib/Fast3D/gfx_pc.cpp | 186 ++++-- libultraship/libultraship/Lib/Fast3D/gfx_pc.h | 13 +- .../Lib/Fast3D/gfx_rendering_api.h | 24 +- libultraship/libultraship/Lib/ImGui/imgui.h | 1 - .../libultraship/Lib/ImGui/imgui_widgets.cpp | 27 - libultraship/libultraship/SohImGuiImpl.cpp | 169 +++--- libultraship/libultraship/SohImGuiImpl.h | 5 +- libultraship/libultraship/Window.cpp | 4 + libultraship/libultraship/Window.h | 1 + soh/include/functions.h | 1 + soh/soh/GbiWrap.cpp | 1 + soh/soh/OTRGlobals.cpp | 75 ++- soh/soh/OTRGlobals.h | 2 +- soh/src/code/graph.c | 29 - soh/src/code/z_kankyo.c | 3 + soh/src/code/z_lights.c | 38 ++ 21 files changed, 872 insertions(+), 588 deletions(-) diff --git a/libultraship/libultraship/GameSettings.h b/libultraship/libultraship/GameSettings.h index 340e93d26..fe6087494 100644 --- a/libultraship/libultraship/GameSettings.h +++ b/libultraship/libultraship/GameSettings.h @@ -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 { diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_direct3d11.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_direct3d11.cpp index 9c2f46422..04d777e7e 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_direct3d11.cpp +++ b/libultraship/libultraship/Lib/Fast3D/gfx_direct3d11.cpp @@ -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 texture; ComPtr resource_view; ComPtr sampler_state; uint32_t width; @@ -64,10 +63,13 @@ struct TextureData { bool linear_filtering; }; -struct FramebufferData { +struct Framebuffer { ComPtr render_target_view; ComPtr depth_stencil_view; + ComPtr 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 device; ComPtr swap_chain; ComPtr context; - ComPtr backbuffer_view; - ComPtr depth_stencil_view; - ComPtr depth_stencil_srv; ComPtr rasterizer_state; ComPtr depth_stencil_state; ComPtr vertex_buffer; ComPtr per_frame_cb; ComPtr per_draw_cb; - ComPtr depth_stencil_texture; - ComPtr depth_stencil_copy_texture; ComPtr coord_buffer; + ComPtr coord_buffer_srv; ComPtr depth_value_output_buffer; ComPtr depth_value_output_buffer_copy; ComPtr depth_value_output_uav; - ComPtr depth_value_sampler; ComPtr compute_shader; - bool copied_depth_buffer; + ComPtr compute_shader_msaa; + ComPtr compute_shader_msaa_blob; + size_t coord_buffer_size; #if DEBUG_D3D ComPtr 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 framebuffers; + std::vector 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 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; - view_desc.Texture2D.MipSlice = 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 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 tex : register(t0); -cbuffer coordCB : register(b0) { - float2 coord; -} - +StructuredBuffer coord : register(t1); RWStructuredBuffer 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 tex : register(t0); +StructuredBuffer coord : register(t1); +RWStructuredBuffer 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 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 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 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, uint16_t> gfx_d3d11_get_pixel_depth(int fb_id, const std::set>& 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; - 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()); - - 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]; + std::map, 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_stencil_copy_texture.Get(), 0); - return res * 65532.0f; + d3d.context->Unmap(d3d.depth_value_output_buffer_copy.Get(), 0); + + ID3D11ShaderResourceView *null_arr[2] = { nullptr, nullptr }; + d3d.context->CSSetShaderResources(0, 2, null_arr); + + 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 }; diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_direct3d_common.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_direct3d_common.cpp index ca60aef8b..f778812d1 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_direct3d_common.cpp +++ b/libultraship/libultraship/Lib/Fast3D/gfx_direct3d_common.cpp @@ -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)]"); } - append_line(buf, &len, "float4 PSMain(PSInput input) : SV_TARGET {"); + 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));"); } diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_dxgi.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_dxgi.cpp index 6cfc3cf32..49abe0b28 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_dxgi.cpp +++ b/libultraship/libultraship/Lib/Fast3D/gfx_dxgi.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,7 @@ static struct { RECT last_window_rect; bool is_full_screen, last_maximized_state; + bool dxgi1_4; ComPtr factory; ComPtr 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 factory4; + if (dxgi.factory->QueryInterface(__uuidof(IDXGIFactory4), &factory4) == S_OK) { + dxgi.dxgi1_4 = true; + } + } + ComPtr adapter; for (UINT i = 0; dxgi.factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND; i++) { DXGI_ADAPTER_DESC1 desc; @@ -604,7 +603,9 @@ ComPtr 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; diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_opengl.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_opengl.cpp index 16e750d14..5373f8799 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_opengl.cpp +++ b/libultraship/libultraship/Lib/Fast3D/gfx_opengl.cpp @@ -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, struct ShaderProgram> shader_program_pool; static GLuint opengl_vbo; - -static uint32_t frame_count; -static uint32_t current_height; -static map> 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 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 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); - glBindRenderbuffer(GL_RENDERBUFFER, 0); + 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, uint16_t> gfx_opengl_get_pixel_depth(int fb_id, const std::set>& coordinates) { + std::map, 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 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 }; diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp index 28debd120..5918ccaf7 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp +++ b/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -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::iterator active_fb; static map framebuffers; +static set> get_pixel_depth_pending; +static map, 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; @@ -2547,7 +2552,7 @@ static void gfx_run_dl(Gfx* cmd) { gfx_dp_texture_rectangle(ulx, uly, lrx, lry, tile, uls, ult, dsdx, dtdy, opcode == G_TEXRECTFLIP); break; } - case G_TEXRECT_WIDE: + case G_TEXRECT_WIDE: { int32_t lrx, lry, tile, ulx, uly; uint32_t uls, ult, dsdx, dtdy; @@ -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, 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; } \ No newline at end of file diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_pc.h b/libultraship/libultraship/Lib/Fast3D/gfx_pc.h index 446c0b6a4..d682991d9 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_pc.h +++ b/libultraship/libultraship/Lib/Fast3D/gfx_pc.h @@ -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 diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_rendering_api.h b/libultraship/libultraship/Lib/Fast3D/gfx_rendering_api.h index 9283ee236..84247fe60 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_rendering_api.h +++ b/libultraship/libultraship/Lib/Fast3D/gfx_rendering_api.h @@ -5,10 +5,18 @@ #include #include +#include +#include + 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, uint16_t> (*get_pixel_depth)(int fb_id, const std::set>& coordinates); + void *(*get_framebuffer_texture_id)(int fb_id); + void (*select_texture_fb)(int fb_id); void (*delete_texture)(uint32_t texID); }; diff --git a/libultraship/libultraship/Lib/ImGui/imgui.h b/libultraship/libultraship/Lib/ImGui/imgui.h index fa2dadcda..323832071 100644 --- a/libultraship/libultraship/Lib/ImGui/imgui.h +++ b/libultraship/libultraship/Lib/ImGui/imgui.h @@ -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); diff --git a/libultraship/libultraship/Lib/ImGui/imgui_widgets.cpp b/libultraship/libultraship/Lib/ImGui/imgui_widgets.cpp index 3d165580b..8e4210002 100644 --- a/libultraship/libultraship/Lib/ImGui/imgui_widgets.cpp +++ b/libultraship/libultraship/Lib/ImGui/imgui_widgets.cpp @@ -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(); diff --git a/libultraship/libultraship/SohImGuiImpl.cpp b/libultraship/libultraship/SohImGuiImpl.cpp index 93dcd2492..860720433 100644 --- a/libultraship/libultraship/SohImGuiImpl.cpp +++ b/libultraship/libultraship/SohImGuiImpl.cpp @@ -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 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)); - } + 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); @@ -441,10 +431,10 @@ namespace SohImGui { if (ImGui::BeginMenu("Developer Tools")) { HOOK(ImGui::MenuItem("Stats", nullptr, &Game::Settings.debug.soh)); HOOK(ImGui::MenuItem("Console", nullptr, &console->opened)); - + ImGui::Text("Debug"); ImGui::Separator(); - + if (ImGui::Checkbox("Debug Mode", &Game::Settings.cheats.debug_mode)) { CVar_SetS32("gDebugEnabled", Game::Settings.cheats.debug_mode); needs_save = true; @@ -452,7 +442,12 @@ 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); @@ -473,22 +468,22 @@ namespace SohImGui { CVar_SetS32("gInfiniteMagic", Game::Settings.cheats.infinite_magic); needs_save = true; } - + if (ImGui::Checkbox("No Clip", &Game::Settings.cheats.no_clip)) { CVar_SetS32("gNoClip", Game::Settings.cheats.no_clip); needs_save = true; } - + if (ImGui::Checkbox("Climb Everything", &Game::Settings.cheats.climb_everything)) { CVar_SetS32("gClimbEverything", Game::Settings.cheats.climb_everything); needs_save = true; } - + if (ImGui::Checkbox("Moon Jump on L", &Game::Settings.cheats.moon_jump_on_l)) { CVar_SetS32("gMoonJumpOnL", Game::Settings.cheats.moon_jump_on_l); needs_save = true; } - + if (ImGui::Checkbox("Super Tunic", &Game::Settings.cheats.super_tunic)) { CVar_SetS32("gSuperTunic", Game::Settings.cheats.super_tunic); needs_save = true; @@ -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(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(&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(&gfx_current_dimensions.internal_mul), 1, 8); + ImGui::Text("MSAA:"); + ImGui::SliderInt("MSAA", reinterpret_cast(&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(fbuf), pos, size); + ImGui::SetCursorPos(pos); + ImGui::Image(reinterpret_cast(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); } diff --git a/libultraship/libultraship/SohImGuiImpl.h b/libultraship/libultraship/SohImGuiImpl.h index 58dac7a1b..dc6e24ee1 100644 --- a/libultraship/libultraship/SohImGuiImpl.h +++ b/libultraship/libultraship/SohImGuiImpl.h @@ -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); diff --git a/libultraship/libultraship/Window.cpp b/libultraship/libultraship/Window.cpp index 665483b35..2b57c547e 100644 --- a/libultraship/libultraship/Window.cpp +++ b/libultraship/libultraship/Window.cpp @@ -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); } diff --git a/libultraship/libultraship/Window.h b/libultraship/libultraship/Window.h index 6046ca4ae..0d12211b0 100644 --- a/libultraship/libultraship/Window.h +++ b/libultraship/libultraship/Window.h @@ -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); diff --git a/soh/include/functions.h b/soh/include/functions.h index a4b1a9a99..13e3a7055 100644 --- a/soh/include/functions.h +++ b/soh/include/functions.h @@ -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); diff --git a/soh/soh/GbiWrap.cpp b/soh/soh/GbiWrap.cpp index 1f6a8003c..c7f69f1a9 100644 --- a/soh/soh/GbiWrap.cpp +++ b/soh/soh/GbiWrap.cpp @@ -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); diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 007afd702..fca6d6b7d 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -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 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 Lock(audio.mutex); + audio.processing = false; + } + audio.cv_from_thread.notify_one(); + } + }).detach(); + } + + { + std::unique_lock Lock(audio.mutex); + audio.processing = true; + } + audio.cv_to_thread.notify_one(); + OTRGlobals::Instance->context->GetWindow()->RunCommands(commands); + { + std::unique_lock 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) { diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index 4f62f0694..20080fcb1 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -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(); diff --git a/soh/src/code/graph.c b/soh/src/code/graph.c index 9d2886ef1..34283f342 100644 --- a/soh/src/code/graph.c +++ b/soh/src/code/graph.c @@ -468,35 +468,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); diff --git a/soh/src/code/z_kankyo.c b/soh/src/code/z_kankyo.c index afcd0f4fa..f3ac083ab 100644 --- a/soh/src/code/z_kankyo.c +++ b/soh/src/code/z_kankyo.c @@ -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); } diff --git a/soh/src/code/z_lights.c b/soh/src/code/z_lights.c index 2c7140c73..c9b60ceca 100644 --- a/soh/src/code/z_lights.c +++ b/soh/src/code/z_lights.c @@ -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;