diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2bdc8e9ee..9c73f6473 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -55,5 +55,11 @@ add_library(core target_include_directories(core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..") target_include_directories(core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..") -target_link_libraries(core Threads::Threads YBaseLib common imgui stb) +target_link_libraries(core PRIVATE Threads::Threads YBaseLib common imgui glad stb) +if(WIN32) + target_sources(core PRIVATE + gpu_hw_d3d11.cpp + gpu_hw_d3d11.h + ) +endif() diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index 4497e24df..21bc7b334 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -41,6 +41,7 @@ + @@ -65,6 +66,7 @@ + @@ -74,6 +76,7 @@ + diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index 70ddf1b56..d16e5cefb 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -24,6 +24,7 @@ + @@ -52,6 +53,8 @@ + + diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 1339ddc45..8d9a3cfcd 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -17,8 +17,10 @@ GPU::GPU() = default; GPU::~GPU() = default; -bool GPU::Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers) +bool GPU::Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller, + Timers* timers) { + m_host_display = host_display; m_system = system; m_dma = dma; m_interrupt_controller = interrupt_controller; @@ -26,10 +28,7 @@ bool GPU::Initialize(System* system, DMA* dma, InterruptController* interrupt_co return true; } -void GPU::UpdateSettings() -{ - -} +void GPU::UpdateSettings() {} void GPU::Reset() { diff --git a/src/core/gpu.h b/src/core/gpu.h index 99170c246..85d292f39 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -10,6 +10,8 @@ class StateWrapper; +class HostDisplay; + class System; class DMA; class InterruptController; @@ -90,7 +92,8 @@ public: GPU(); virtual ~GPU(); - virtual bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers); + virtual bool Initialize(HostDisplay* host_display, System* system, DMA* dma, + InterruptController* interrupt_controller, Timers* timers); virtual void Reset(); virtual bool DoState(StateWrapper& sw); @@ -116,6 +119,9 @@ public: // Ticks for hblank/vblank. void Execute(TickCount ticks); + // gpu_hw_d3d11.cpp + static std::unique_ptr CreateHardwareD3D11Renderer(); + // gpu_hw_opengl.cpp static std::unique_ptr CreateHardwareOpenGLRenderer(); @@ -292,6 +298,7 @@ protected: virtual void DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32* command_ptr); virtual void FlushRender(); + HostDisplay* m_host_display = nullptr; System* m_system = nullptr; DMA* m_dma = nullptr; InterruptController* m_interrupt_controller = nullptr; diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index a0730d3f7..e4bb786c7 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -19,9 +19,10 @@ void GPU_HW::Reset() m_batch_ubo_dirty = true; } -bool GPU_HW::Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers) +bool GPU_HW::Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller, + Timers* timers) { - if (!GPU::Initialize(system, dma, interrupt_controller, timers)) + if (!GPU::Initialize(host_display, system, dma, interrupt_controller, timers)) return false; m_resolution_scale = std::clamp(m_system->GetSettings().gpu_resolution_scale, 1, m_max_resolution_scale); @@ -260,7 +261,7 @@ void GPU_HW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32 rc.transparency_enable ? m_render_state.transparency_mode : TransparencyMode::Disabled; const BatchPrimitive rc_primitive = GetPrimitiveForCommand(rc); const bool dithering_enable = (!m_true_color && rc.IsDitheringEnabled()) ? m_GPUSTAT.dither_enable : false; - const u32 max_added_vertices = num_vertices + 2; + const u32 max_added_vertices = num_vertices + 5; if (!IsFlushed()) { const bool buffer_overflow = GetBatchVertexSpace() < max_added_vertices; diff --git a/src/core/gpu_hw.h b/src/core/gpu_hw.h index b64e46b31..a1e68d6dd 100644 --- a/src/core/gpu_hw.h +++ b/src/core/gpu_hw.h @@ -27,7 +27,8 @@ public: GPU_HW(); virtual ~GPU_HW(); - virtual bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers) override; + virtual bool Initialize(HostDisplay* host_display, System* system, DMA* dma, + InterruptController* interrupt_controller, Timers* timers) override; virtual void Reset() override; virtual void UpdateSettings() override; diff --git a/src/core/gpu_hw_d3d11.cpp b/src/core/gpu_hw_d3d11.cpp new file mode 100644 index 000000000..053d87a37 --- /dev/null +++ b/src/core/gpu_hw_d3d11.cpp @@ -0,0 +1,731 @@ +#include "gpu_hw_d3d11.h" +#include "YBaseLib/Assert.h" +#include "YBaseLib/Log.h" +#include "YBaseLib/String.h" +#include "common/d3d11/shader_compiler.h" +#include "gpu_hw_shadergen.h" +#include "host_display.h" +#include "host_interface.h" +#include "imgui.h" +#include "system.h" +Log_SetChannel(GPU_HW_D3D11); + +GPU_HW_D3D11::GPU_HW_D3D11() = default; + +GPU_HW_D3D11::~GPU_HW_D3D11() +{ + m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, 1.0f); +} + +bool GPU_HW_D3D11::Initialize(HostDisplay* host_display, System* system, DMA* dma, + InterruptController* interrupt_controller, Timers* timers) +{ + SetCapabilities(); + + if (!GPU_HW::Initialize(host_display, system, dma, interrupt_controller, timers)) + return false; + + if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::D3D11) + { + Log_ErrorPrintf("Host render API is incompatible"); + return false; + } + + m_device = static_cast(host_display->GetHostRenderDevice()); + m_context = static_cast(host_display->GetHostRenderContext()); + if (!m_device || !m_context) + return false; + + if (!CreateFramebuffer()) + { + Log_ErrorPrintf("Failed to create framebuffer"); + return false; + } + + if (!CreateVertexBuffer()) + { + Log_ErrorPrintf("Failed to create vertex buffer"); + return false; + } + + if (!CreateUniformBuffer()) + { + Log_ErrorPrintf("Failed to create uniform buffer"); + return false; + } + + if (!CreateTextureBuffer()) + { + Log_ErrorPrintf("Failed to create texture buffer"); + return false; + } + + if (!CreateStateObjects()) + { + Log_ErrorPrintf("Failed to create state objects"); + return false; + } + + if (!CreateBatchInputLayout()) + { + Log_ErrorPrintf("Failed to create batch input layout"); + return false; + } + + if (!CompileShaders()) + { + Log_ErrorPrintf("Failed to compile shaders"); + return false; + } + + RestoreGraphicsAPIState(); + return true; +} + +void GPU_HW_D3D11::Reset() +{ + GPU_HW::Reset(); + + ClearFramebuffer(); +} + +void GPU_HW_D3D11::ResetGraphicsAPIState() +{ + GPU_HW::ResetGraphicsAPIState(); +} + +void GPU_HW_D3D11::RestoreGraphicsAPIState() +{ + const UINT stride = sizeof(BatchVertex); + const UINT offset = 0; + m_context->IASetVertexBuffers(0, 1, m_vertex_stream_buffer.GetD3DBufferArray(), &stride, &offset); + m_context->IASetInputLayout(m_batch_input_layout.Get()); + m_context->PSSetShaderResources(0, 1, m_vram_read_texture.GetD3DSRVArray()); + m_context->OMSetDepthStencilState(m_depth_disabled_state.Get(), 0); + m_context->OMSetRenderTargets(1, m_vram_texture.GetD3DRTVArray(), nullptr); + m_context->RSSetState(m_cull_none_rasterizer_state.Get()); + SetViewport(0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight()); + m_drawing_area_changed = true; + m_batch_ubo_dirty = true; +} + +void GPU_HW_D3D11::UpdateSettings() +{ + GPU_HW::UpdateSettings(); + + CreateFramebuffer(); + CompileShaders(); + UpdateDisplay(); +} + +void GPU_HW_D3D11::DrawRendererStatsWindow() +{ + GPU_HW::DrawRendererStatsWindow(); + + ImGui::SetNextWindowSize(ImVec2(300.0f, 150.0f), ImGuiCond_FirstUseEver); + + const bool is_null_frame = m_stats.num_batches == 0; + if (!is_null_frame) + { + m_last_stats = m_stats; + m_stats = {}; + } + + if (ImGui::Begin("GPU Renderer Statistics", &m_show_renderer_statistics)) + { + ImGui::Columns(2); + ImGui::SetColumnWidth(0, 200.0f); + + ImGui::TextUnformatted("GPU Active In This Frame: "); + ImGui::NextColumn(); + ImGui::Text("%s", is_null_frame ? "Yes" : "No"); + ImGui::NextColumn(); + + ImGui::TextUnformatted("VRAM Reads: "); + ImGui::NextColumn(); + ImGui::Text("%u", m_last_stats.num_vram_reads); + ImGui::NextColumn(); + + ImGui::TextUnformatted("VRAM Writes: "); + ImGui::NextColumn(); + ImGui::Text("%u", m_last_stats.num_vram_writes); + ImGui::NextColumn(); + + ImGui::TextUnformatted("VRAM Read Texture Updates:"); + ImGui::NextColumn(); + ImGui::Text("%u", m_last_stats.num_vram_read_texture_updates); + ImGui::NextColumn(); + + ImGui::TextUnformatted("Batches Drawn:"); + ImGui::NextColumn(); + ImGui::Text("%u", m_last_stats.num_batches); + ImGui::NextColumn(); + + ImGui::TextUnformatted("Vertices Drawn: "); + ImGui::NextColumn(); + ImGui::Text("%u", m_last_stats.num_vertices); + ImGui::NextColumn(); + } + + ImGui::End(); +} + +void GPU_HW_D3D11::InvalidateVRAMReadCache() +{ + m_vram_read_texture_dirty = true; +} + +void GPU_HW_D3D11::MapBatchVertexPointer(u32 required_vertices) +{ + Assert(!m_batch_start_vertex_ptr); + + const D3D11::StreamBuffer::MappingResult res = + m_vertex_stream_buffer.Map(m_context.Get(), sizeof(BatchVertex), required_vertices * sizeof(BatchVertex)); + + m_batch_start_vertex_ptr = static_cast(res.pointer); + m_batch_current_vertex_ptr = m_batch_start_vertex_ptr; + m_batch_end_vertex_ptr = m_batch_start_vertex_ptr + res.space_aligned; + m_batch_base_vertex = res.index_aligned; +} + +void GPU_HW_D3D11::SetCapabilities() +{ + const u32 max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; + Log_InfoPrintf("Max texture size: %dx%d", max_texture_size, max_texture_size); + const u32 max_texture_scale = max_texture_size / VRAM_WIDTH; + + m_max_resolution_scale = max_texture_scale; + Log_InfoPrintf("Maximum resolution scale is %u", m_max_resolution_scale); +} + +bool GPU_HW_D3D11::CreateFramebuffer() +{ + // save old vram texture/fbo, in case we're changing scale + auto old_vram_texture = std::move(m_vram_texture); + DestroyFramebuffer(); + + // scale vram size to internal resolution + const u32 texture_width = VRAM_WIDTH * m_resolution_scale; + const u32 texture_height = VRAM_HEIGHT * m_resolution_scale; + const DXGI_FORMAT texture_format = DXGI_FORMAT_R8G8B8A8_UNORM; + + if (!m_vram_texture.Create(m_device.Get(), texture_width, texture_height, texture_format, true, true) || + !m_vram_read_texture.Create(m_device.Get(), texture_width, texture_height, texture_format, true, false) || + !m_display_texture.Create(m_device.Get(), texture_width, texture_height, texture_format, true, true)) + { + return false; + } + + // do we need to restore the framebuffer after a size change? + if (old_vram_texture) + { + const bool linear_filter = old_vram_texture.GetWidth() > m_vram_texture.GetWidth(); + Log_DevPrintf("Scaling %ux%u VRAM texture to %ux%u using %s filter", old_vram_texture.GetWidth(), + old_vram_texture.GetHeight(), m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), + linear_filter ? "linear" : "nearest"); + + BlitTexture(m_vram_texture.GetD3DRTV(), 0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), + old_vram_texture.GetD3DSRV(), 0, 0, old_vram_texture.GetWidth(), old_vram_texture.GetHeight(), + old_vram_texture.GetWidth(), old_vram_texture.GetHeight(), linear_filter); + } + + if (m_resolution_scale > 1 && + !m_vram_downsample_texture.Create(m_device.Get(), texture_width, texture_height, texture_format, true, true)) + { + return false; + } + + m_context->OMSetRenderTargets(1, m_vram_texture.GetD3DRTVArray(), nullptr); + m_vram_read_texture_dirty = true; + return true; +} + +void GPU_HW_D3D11::ClearFramebuffer() +{ + static constexpr std::array color = {}; + m_context->ClearRenderTargetView(m_vram_texture.GetD3DRTV(), color.data()); + m_vram_read_texture_dirty = true; +} + +void GPU_HW_D3D11::DestroyFramebuffer() +{ + m_vram_read_texture.Destroy(); + m_vram_texture.Destroy(); + m_vram_downsample_texture.Destroy(); + m_display_texture.Destroy(); +} + +bool GPU_HW_D3D11::CreateVertexBuffer() +{ + return m_vertex_stream_buffer.Create(m_device.Get(), D3D11_BIND_VERTEX_BUFFER, VERTEX_BUFFER_SIZE); +} + +bool GPU_HW_D3D11::CreateUniformBuffer() +{ + return m_uniform_stream_buffer.Create(m_device.Get(), D3D11_BIND_CONSTANT_BUFFER, UNIFORM_BUFFER_SIZE); +} + +bool GPU_HW_D3D11::CreateTextureBuffer() +{ + if (!m_texture_stream_buffer.Create(m_device.Get(), D3D11_BIND_SHADER_RESOURCE, VRAM_UPDATE_TEXTURE_BUFFER_SIZE)) + return false; + + const CD3D11_SHADER_RESOURCE_VIEW_DESC srv_desc(D3D11_SRV_DIMENSION_BUFFER, DXGI_FORMAT_R16_UINT, 0, + VRAM_UPDATE_TEXTURE_BUFFER_SIZE / sizeof(u16)); + const HRESULT hr = m_device->CreateShaderResourceView(m_texture_stream_buffer.GetD3DBuffer(), &srv_desc, + m_texture_stream_buffer_srv_r16ui.ReleaseAndGetAddressOf()); + if (FAILED(hr)) + { + Log_ErrorPrintf("Creation of texture buffer SRV failed: 0x%08X", hr); + return false; + } + + return true; +} + +bool GPU_HW_D3D11::CreateBatchInputLayout() +{ + static constexpr std::array attributes = { + {{"ATTR", 0, DXGI_FORMAT_R32G32_SINT, 0, offsetof(BatchVertex, x), D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"ATTR", 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(BatchVertex, color), D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"ATTR", 2, DXGI_FORMAT_R32_SINT, 0, offsetof(BatchVertex, texcoord), D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"ATTR", 3, DXGI_FORMAT_R32_SINT, 0, offsetof(BatchVertex, texpage), D3D11_INPUT_PER_VERTEX_DATA, 0}}}; + + // we need a vertex shader... + GPU_HW_ShaderGen shadergen(GPU_HW_ShaderGen::API::D3D11, m_resolution_scale, m_true_color); + ComPtr vs_bytecode = D3D11::ShaderCompiler::CompileShader( + D3D11::ShaderCompiler::Type::Vertex, m_device->GetFeatureLevel(), shadergen.GenerateBatchVertexShader(true), false); + if (!vs_bytecode) + return false; + + const HRESULT hr = m_device->CreateInputLayout(attributes.data(), static_cast(attributes.size()), + vs_bytecode->GetBufferPointer(), vs_bytecode->GetBufferSize(), + m_batch_input_layout.GetAddressOf()); + if (FAILED(hr)) + { + Log_ErrorPrintf("CreateInputLayout failed: 0x%08X", hr); + return false; + } + + return true; +} + +bool GPU_HW_D3D11::CreateStateObjects() +{ + HRESULT hr; + + CD3D11_RASTERIZER_DESC rs_desc = CD3D11_RASTERIZER_DESC(CD3D11_DEFAULT()); + rs_desc.CullMode = D3D11_CULL_NONE; + rs_desc.ScissorEnable = TRUE; + hr = m_device->CreateRasterizerState(&rs_desc, m_cull_none_rasterizer_state.GetAddressOf()); + if (FAILED(hr)) + return false; + + CD3D11_DEPTH_STENCIL_DESC ds_desc = CD3D11_DEPTH_STENCIL_DESC(CD3D11_DEFAULT()); + ds_desc.DepthEnable = FALSE; + ds_desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; + hr = m_device->CreateDepthStencilState(&ds_desc, m_depth_disabled_state.GetAddressOf()); + if (FAILED(hr)) + return false; + + CD3D11_BLEND_DESC bl_desc = CD3D11_BLEND_DESC(CD3D11_DEFAULT()); + hr = m_device->CreateBlendState(&bl_desc, m_blend_disabled_state.GetAddressOf()); + if (FAILED(hr)) + return false; + + CD3D11_SAMPLER_DESC sampler_desc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT()); + sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + hr = m_device->CreateSamplerState(&sampler_desc, m_point_sampler_state.GetAddressOf()); + if (FAILED(hr)) + return false; + + sampler_desc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; + hr = m_device->CreateSamplerState(&sampler_desc, m_linear_sampler_state.GetAddressOf()); + if (FAILED(hr)) + return false; + + m_batch_blend_states[static_cast(TransparencyMode::Disabled)] = m_blend_disabled_state; + + for (u8 transparency_mode = 0; transparency_mode < 4; transparency_mode++) + { + bl_desc.RenderTarget[0].BlendEnable = TRUE; + bl_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + bl_desc.RenderTarget[0].DestBlend = D3D11_BLEND_SRC_ALPHA; + bl_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + bl_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + bl_desc.RenderTarget[0].BlendOp = + (transparency_mode == static_cast(TransparencyMode::BackgroundMinusForeground)) ? + D3D11_BLEND_OP_REV_SUBTRACT : + D3D11_BLEND_OP_ADD; + bl_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + + hr = m_device->CreateBlendState(&bl_desc, m_batch_blend_states[transparency_mode].GetAddressOf()); + if (FAILED(hr)) + return false; + } + + return true; +} + +bool GPU_HW_D3D11::CompileShaders() +{ + const bool debug = false; + GPU_HW_ShaderGen shadergen(GPU_HW_ShaderGen::API::D3D11, m_resolution_scale, m_true_color); + + m_screen_quad_vertex_shader = D3D11::ShaderCompiler::CompileAndCreateVertexShader( + m_device.Get(), shadergen.GenerateScreenQuadVertexShader(), debug); + if (!m_screen_quad_vertex_shader) + return false; + + for (u8 textured = 0; textured < 2; textured++) + { + const std::string vs = shadergen.GenerateBatchVertexShader(ConvertToBoolUnchecked(textured)); + m_batch_vertex_shaders[textured] = D3D11::ShaderCompiler::CompileAndCreateVertexShader(m_device.Get(), vs, debug); + if (!m_batch_vertex_shaders[textured]) + return false; + } + + for (u8 render_mode = 0; render_mode < 4; render_mode++) + { + for (u8 texture_mode = 0; texture_mode < 9; texture_mode++) + { + for (u8 dithering = 0; dithering < 2; dithering++) + { + const std::string ps = shadergen.GenerateBatchFragmentShader(static_cast(render_mode), + static_cast(texture_mode), + ConvertToBoolUnchecked(dithering)); + + m_batch_pixel_shaders[render_mode][texture_mode][dithering] = + D3D11::ShaderCompiler::CompileAndCreatePixelShader(m_device.Get(), ps, debug); + if (!m_batch_pixel_shaders[render_mode][texture_mode][dithering]) + return false; + } + } + } + + m_copy_pixel_shader = + D3D11::ShaderCompiler::CompileAndCreatePixelShader(m_device.Get(), shadergen.GenerateCopyFragmentShader(), debug); + if (!m_copy_pixel_shader) + return false; + + m_fill_pixel_shader = + D3D11::ShaderCompiler::CompileAndCreatePixelShader(m_device.Get(), shadergen.GenerateFillFragmentShader(), debug); + if (!m_fill_pixel_shader) + return false; + + m_vram_write_pixel_shader = D3D11::ShaderCompiler::CompileAndCreatePixelShader( + m_device.Get(), shadergen.GenerateVRAMWriteFragmentShader(), debug); + if (!m_vram_write_pixel_shader) + return false; + + for (u8 depth_24bit = 0; depth_24bit < 2; depth_24bit++) + { + for (u8 interlaced = 0; interlaced < 2; interlaced++) + { + const std::string ps = shadergen.GenerateDisplayFragmentShader(ConvertToBoolUnchecked(depth_24bit), + ConvertToBoolUnchecked(interlaced)); + m_display_pixel_shaders[depth_24bit][interlaced] = + D3D11::ShaderCompiler::CompileAndCreatePixelShader(m_device.Get(), ps, debug); + if (!m_display_pixel_shaders[depth_24bit][interlaced]) + return false; + } + } + + return true; +} + +void GPU_HW_D3D11::UploadUniformBlock(const void* data, u32 data_size) +{ + const auto res = m_uniform_stream_buffer.Map(m_context.Get(), m_uniform_stream_buffer.GetSize(), data_size); + std::memcpy(res.pointer, data, data_size); + m_uniform_stream_buffer.Unmap(m_context.Get(), data_size); + + m_context->VSSetConstantBuffers(0, 1, m_uniform_stream_buffer.GetD3DBufferArray()); + m_context->PSSetConstantBuffers(0, 1, m_uniform_stream_buffer.GetD3DBufferArray()); + + m_stats.num_uniform_buffer_updates++; +} + +void GPU_HW_D3D11::SetViewport(u32 x, u32 y, u32 width, u32 height) +{ + const CD3D11_VIEWPORT vp(static_cast(x), static_cast(y), static_cast(width), + static_cast(height)); + m_context->RSSetViewports(1, &vp); +} + +void GPU_HW_D3D11::SetScissor(u32 x, u32 y, u32 width, u32 height) +{ + const CD3D11_RECT rc(x, y, x + width, y + height); + m_context->RSSetScissorRects(1, &rc); +} + +void GPU_HW_D3D11::SetViewportAndScissor(u32 x, u32 y, u32 width, u32 height) +{ + SetViewport(x, y, width, height); + SetScissor(x, y, width, height); +} + +void GPU_HW_D3D11::BlitTexture(ID3D11RenderTargetView* dst, u32 dst_x, u32 dst_y, u32 dst_width, u32 dst_height, + ID3D11ShaderResourceView* src, u32 src_x, u32 src_y, u32 src_width, u32 src_height, + u32 src_texture_width, u32 src_texture_height, bool linear_filter) +{ + const float uniforms[4] = {static_cast(src_x) / static_cast(src_texture_width), + static_cast(src_y) / static_cast(src_texture_height), + static_cast(src_width) / static_cast(src_texture_width), + static_cast(src_height) / static_cast(src_texture_height)}; + + m_context->OMSetRenderTargets(1, &dst, nullptr); + SetViewport(dst_x, dst_y, dst_width, dst_height); + SetScissor(dst_x, dst_y, dst_width, dst_height); + DrawUtilityShader(m_copy_pixel_shader.Get(), uniforms, sizeof(uniforms)); +} + +void GPU_HW_D3D11::DrawUtilityShader(ID3D11PixelShader* shader, const void* uniforms, u32 uniforms_size) +{ + if (uniforms) + { + UploadUniformBlock(uniforms, uniforms_size); + m_batch_ubo_dirty = true; + } + + m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + m_context->VSSetShader(m_screen_quad_vertex_shader.Get(), nullptr, 0); + m_context->PSSetShader(shader, nullptr, 0); + m_context->OMSetBlendState(m_blend_disabled_state.Get(), nullptr, 0xFFFFFFFFu); + + m_context->Draw(3, 0); +} + +void GPU_HW_D3D11::SetDrawState(BatchRenderMode render_mode) +{ + const bool textured = (m_batch.texture_mode != TextureMode::Disabled); + + static constexpr std::array d3d_primitives = { + {D3D11_PRIMITIVE_TOPOLOGY_LINELIST, D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, + D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP}}; + m_context->IASetPrimitiveTopology(d3d_primitives[static_cast(m_batch.primitive)]); + + m_context->VSSetShader(m_batch_vertex_shaders[BoolToUInt8(textured)].Get(), nullptr, 0); + + m_context->PSSetShader(m_batch_pixel_shaders[static_cast(render_mode)][static_cast(m_batch.texture_mode)] + [BoolToUInt8(m_batch.dithering)] + .Get(), + nullptr, 0); + + const TransparencyMode transparency_mode = + (render_mode == BatchRenderMode::OnlyOpaque) ? TransparencyMode::Disabled : m_batch.transparency_mode; + m_context->OMSetBlendState(m_batch_blend_states[static_cast(transparency_mode)].Get(), nullptr, 0xFFFFFFFFu); + + if (m_drawing_area_changed) + { + m_drawing_area_changed = false; + + int left, top, right, bottom; + CalcScissorRect(&left, &top, &right, &bottom); + + CD3D11_RECT rc(left, top, right, bottom); + m_context->RSSetScissorRects(1, &rc); + } + + if (m_batch_ubo_dirty) + { + UploadUniformBlock(&m_batch_ubo_data, sizeof(m_batch_ubo_data)); + m_batch_ubo_dirty = false; + } +} + +void GPU_HW_D3D11::UpdateDrawingArea() +{ + m_drawing_area_changed = true; +} + +void GPU_HW_D3D11::UpdateDisplay() +{ + GPU_HW::UpdateDisplay(); + + if (m_system->GetSettings().debugging.show_vram) + { + m_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), 0, 0, m_vram_texture.GetWidth(), + m_vram_texture.GetHeight(), m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), + 1.0f); + } + else + { + const u32 vram_offset_x = m_crtc_state.regs.X; + const u32 vram_offset_y = m_crtc_state.regs.Y; + const u32 scaled_vram_offset_x = vram_offset_x * m_resolution_scale; + const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale; + const u32 display_width = std::min(m_crtc_state.display_width, VRAM_WIDTH - vram_offset_x); + const u32 display_height = std::min(m_crtc_state.display_height << BoolToUInt8(m_GPUSTAT.vertical_interlace), + VRAM_HEIGHT - vram_offset_y); + const u32 scaled_display_width = display_width * m_resolution_scale; + const u32 scaled_display_height = display_height * m_resolution_scale; + + if (m_GPUSTAT.display_disable) + { + m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, m_crtc_state.display_aspect_ratio); + } + else if (!m_GPUSTAT.display_area_color_depth_24 && !m_GPUSTAT.vertical_interlace) + { + const CD3D11_BOX src_box(scaled_vram_offset_x, scaled_vram_offset_y, 0, + scaled_vram_offset_x + scaled_display_width, + scaled_vram_offset_y + scaled_display_height, 1); + m_context->CopySubresourceRegion(m_display_texture.GetD3DTexture(), 0, 0, 0, 0, m_vram_texture.GetD3DTexture(), 0, + &src_box); + + m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), 0, 0, scaled_display_width, + scaled_display_height, m_display_texture.GetWidth(), + m_display_texture.GetHeight(), m_crtc_state.display_aspect_ratio); + } + else + { + const u32 field_offset = BoolToUInt8(m_GPUSTAT.vertical_interlace && !m_GPUSTAT.drawing_even_line); + const u32 scaled_field_offset = field_offset * m_resolution_scale; + + ID3D11PixelShader* display_pixel_shader = + m_display_pixel_shaders[BoolToUInt8(m_GPUSTAT.display_area_color_depth_24)] + [BoolToUInt8(m_GPUSTAT.vertical_interlace)] + .Get(); + + // Because of how the reinterpret shader works, we need to use the downscaled version. + if (m_GPUSTAT.display_area_color_depth_24 && m_resolution_scale > 1) + { + const u32 copy_width = std::min((display_width * 4) / 3, VRAM_WIDTH - vram_offset_x); + const u32 scaled_copy_width = copy_width * m_resolution_scale; + BlitTexture(m_vram_downsample_texture.GetD3DRTV(), vram_offset_x, vram_offset_y, copy_width, display_height, + m_vram_texture.GetD3DSRV(), scaled_vram_offset_x, scaled_vram_offset_y, scaled_copy_width, + scaled_display_height, m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), false); + + m_context->OMSetRenderTargets(1, m_display_texture.GetD3DRTVArray(), nullptr); + m_context->PSSetShaderResources(0, 1, m_vram_downsample_texture.GetD3DSRVArray()); + + const u32 uniforms[4] = {vram_offset_x, vram_offset_y, field_offset}; + SetViewportAndScissor(0, scaled_field_offset, display_width, display_height); + DrawUtilityShader(display_pixel_shader, uniforms, sizeof(uniforms)); + UploadUniformBlock(uniforms, sizeof(uniforms)); + + m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), 0, 0, display_width, display_height, + m_display_texture.GetWidth(), m_display_texture.GetHeight(), + m_crtc_state.display_aspect_ratio); + } + else + { + m_context->OMSetRenderTargets(1, m_display_texture.GetD3DRTVArray(), nullptr); + m_context->PSSetShaderResources(0, 1, m_vram_texture.GetD3DSRVArray()); + + const u32 uniforms[4] = {scaled_vram_offset_x, scaled_vram_offset_y, scaled_field_offset}; + SetViewportAndScissor(0, scaled_field_offset, scaled_display_width, scaled_display_height); + DrawUtilityShader(display_pixel_shader, uniforms, sizeof(uniforms)); + UploadUniformBlock(uniforms, sizeof(uniforms)); + + m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), 0, 0, scaled_display_width, + scaled_display_height, m_display_texture.GetWidth(), + m_display_texture.GetHeight(), m_crtc_state.display_aspect_ratio); + } + + RestoreGraphicsAPIState(); + } + } +} + +void GPU_HW_D3D11::ReadVRAM(u32 x, u32 y, u32 width, u32 height, void* buffer) +{ + Log_WarningPrintf("VRAM readback not implemented"); + m_stats.num_vram_reads++; +} + +void GPU_HW_D3D11::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) +{ + // drop precision unless true colour is enabled + if (!m_true_color) + color = RGBA5551ToRGBA8888(RGBA8888ToRGBA5551(color)); + + float uniforms[4]; + std::tie(uniforms[0], uniforms[1], uniforms[2], uniforms[3]) = RGBA8ToFloat(color); + + SetViewportAndScissor(x * m_resolution_scale, y * m_resolution_scale, width * m_resolution_scale, + height * m_resolution_scale); + DrawUtilityShader(m_fill_pixel_shader.Get(), uniforms, sizeof(uniforms)); + + RestoreGraphicsAPIState(); + InvalidateVRAMReadCache(); +} + +void GPU_HW_D3D11::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data) +{ + const u32 num_pixels = width * height; + const auto map_result = m_texture_stream_buffer.Map(m_context.Get(), sizeof(u16), num_pixels * sizeof(u16)); + std::memcpy(map_result.pointer, data, num_pixels * sizeof(u16)); + m_texture_stream_buffer.Unmap(m_context.Get(), num_pixels * sizeof(u16)); + + const u32 uniforms[5] = {x, y, width, height, map_result.index_aligned}; + m_context->PSSetShaderResources(0, 1, m_texture_stream_buffer_srv_r16ui.GetAddressOf()); + + // the viewport should already be set to the full vram, so just adjust the scissor + SetScissor(x * m_resolution_scale, y * m_resolution_scale, width * m_resolution_scale, height * m_resolution_scale); + + DrawUtilityShader(m_vram_write_pixel_shader.Get(), uniforms, sizeof(uniforms)); + + RestoreGraphicsAPIState(); + InvalidateVRAMReadCache(); + m_stats.num_vram_writes++; +} + +void GPU_HW_D3D11::CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height) +{ + src_x *= m_resolution_scale; + src_y *= m_resolution_scale; + dst_x *= m_resolution_scale; + dst_y *= m_resolution_scale; + width *= m_resolution_scale; + height *= m_resolution_scale; + + const CD3D11_BOX src_box(src_x, src_y, 0, src_x + width, src_y + height, 1); + m_context->CopySubresourceRegion(m_vram_texture, 0, dst_x, dst_y, 0, m_vram_texture, 0, &src_box); + InvalidateVRAMReadCache(); +} + +void GPU_HW_D3D11::UpdateVRAMReadTexture() +{ + m_stats.num_vram_read_texture_updates++; + m_vram_read_texture_dirty = false; + + const CD3D11_BOX src_box(0, 0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 1); + m_context->CopySubresourceRegion(m_vram_read_texture, 0, 0, 0, 0, m_vram_texture, 0, &src_box); +} + +void GPU_HW_D3D11::FlushRender() +{ + const u32 vertex_count = GetBatchVertexCount(); + if (vertex_count == 0) + return; + + if (m_vram_read_texture_dirty) + UpdateVRAMReadTexture(); + + m_stats.num_batches++; + m_stats.num_vertices += vertex_count; + + m_vertex_stream_buffer.Unmap(m_context.Get(), vertex_count * sizeof(BatchVertex)); + m_batch_start_vertex_ptr = nullptr; + m_batch_end_vertex_ptr = nullptr; + m_batch_current_vertex_ptr = nullptr; + + if (m_batch.NeedsTwoPassRendering()) + { + SetDrawState(BatchRenderMode::OnlyTransparent); + m_context->Draw(vertex_count, m_batch_base_vertex); + SetDrawState(BatchRenderMode::OnlyOpaque); + m_context->Draw(vertex_count, m_batch_base_vertex); + } + else + { + SetDrawState(m_batch.GetRenderMode()); + m_context->Draw(vertex_count, m_batch_base_vertex); + } +} + +std::unique_ptr GPU::CreateHardwareD3D11Renderer() +{ + return std::make_unique(); +} diff --git a/src/core/gpu_hw_d3d11.h b/src/core/gpu_hw_d3d11.h new file mode 100644 index 000000000..a4d082b72 --- /dev/null +++ b/src/core/gpu_hw_d3d11.h @@ -0,0 +1,122 @@ +#pragma once +#include "common/d3d11/staging_texture.h" +#include "common/d3d11/stream_buffer.h" +#include "common/d3d11/texture.h" +#include "gpu_hw.h" +#include +#include +#include +#include +#include + +class GPU_HW_D3D11 : public GPU_HW +{ +public: + template + using ComPtr = Microsoft::WRL::ComPtr; + + GPU_HW_D3D11(); + ~GPU_HW_D3D11() override; + + bool Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller, + Timers* timers) override; + void Reset() override; + + void ResetGraphicsAPIState() override; + void RestoreGraphicsAPIState() override; + void UpdateSettings() override; + + void DrawRendererStatsWindow() override; + +protected: + void UpdateDisplay() override; + void UpdateDrawingArea() override; + void ReadVRAM(u32 x, u32 y, u32 width, u32 height, void* buffer) override; + void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) override; + void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data) override; + void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height) override; + void FlushRender() override; + void InvalidateVRAMReadCache() override; + void MapBatchVertexPointer(u32 required_vertices) override; + +private: + struct GLStats + { + u32 num_batches; + u32 num_vertices; + u32 num_vram_reads; + u32 num_vram_writes; + u32 num_vram_read_texture_updates; + u32 num_uniform_buffer_updates; + }; + + void SetCapabilities(); + bool CreateFramebuffer(); + void ClearFramebuffer(); + void DestroyFramebuffer(); + void UpdateVRAMReadTexture(); + + bool CreateVertexBuffer(); + bool CreateUniformBuffer(); + bool CreateTextureBuffer(); + bool CreateBatchInputLayout(); + bool CreateStateObjects(); + + bool CompileShaders(); + void SetDrawState(BatchRenderMode render_mode); + void UploadUniformBlock(const void* data, u32 data_size); + void SetViewport(u32 x, u32 y, u32 width, u32 height); + void SetScissor(u32 x, u32 y, u32 width, u32 height); + void SetViewportAndScissor(u32 x, u32 y, u32 width, u32 height); + + /// Blits from src to dst, downscaling or upscaling in the process. + void BlitTexture(ID3D11RenderTargetView* dst, u32 dst_x, u32 dst_y, u32 dst_width, u32 dst_height, + ID3D11ShaderResourceView* src, u32 src_x, u32 src_y, u32 src_width, u32 src_height, + u32 src_texture_width, u32 src_texture_height, bool linear_filter); + + void DrawUtilityShader(ID3D11PixelShader* shader, const void* uniforms, u32 uniforms_size); + + ComPtr m_device; + ComPtr m_context; + + // downsample texture - used for readbacks at >1xIR. + D3D11::Texture m_vram_texture; + D3D11::Texture m_vram_read_texture; + D3D11::Texture m_vram_downsample_texture; + D3D11::Texture m_display_texture; + + D3D11::StreamBuffer m_vertex_stream_buffer; + + D3D11::StreamBuffer m_uniform_stream_buffer; + + D3D11::StreamBuffer m_texture_stream_buffer; + ComPtr m_texture_stream_buffer_srv_r16ui; + + ComPtr m_cull_none_rasterizer_state; + + ComPtr m_depth_disabled_state; + + ComPtr m_blend_disabled_state; + + ComPtr m_point_sampler_state; + ComPtr m_linear_sampler_state; + + std::array, 5> m_batch_blend_states; // [transparency_mode] + ComPtr m_batch_input_layout; + std::array, 2> m_batch_vertex_shaders; // [textured] + std::array, 2>, 9>, 4> + m_batch_pixel_shaders; // [render_mode][texture_mode][dithering] + + ComPtr m_screen_quad_vertex_shader; + ComPtr m_copy_pixel_shader; + ComPtr m_fill_pixel_shader; + ComPtr m_vram_write_pixel_shader; + std::array, 2>, 2> m_display_pixel_shaders; // [depth_24][interlaced] + + GLStats m_stats = {}; + GLStats m_last_stats = {}; + + bool m_vram_read_texture_dirty = true; + bool m_drawing_area_changed = true; + bool m_show_renderer_statistics = false; +}; diff --git a/src/core/gpu_hw_opengl.cpp b/src/core/gpu_hw_opengl.cpp index b8a462950..bd8e5393c 100644 --- a/src/core/gpu_hw_opengl.cpp +++ b/src/core/gpu_hw_opengl.cpp @@ -3,7 +3,7 @@ #include "YBaseLib/Log.h" #include "YBaseLib/String.h" #include "gpu_hw_shadergen.h" -#include "host_interface.h" +#include "host_display.h" #include "imgui.h" #include "system.h" Log_SetChannel(GPU_HW_OpenGL); @@ -12,16 +12,24 @@ GPU_HW_OpenGL::GPU_HW_OpenGL() : GPU_HW() {} GPU_HW_OpenGL::~GPU_HW_OpenGL() { + m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, 1.0f); DestroyFramebuffer(); } -bool GPU_HW_OpenGL::Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers) +bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display, System* system, DMA* dma, + InterruptController* interrupt_controller, Timers* timers) { SetCapabilities(); - if (!GPU_HW::Initialize(system, dma, interrupt_controller, timers)) + if (!GPU_HW::Initialize(host_display, system, dma, interrupt_controller, timers)) return false; + if (m_host_display->GetRenderAPI() != HostDisplay::RenderAPI::OpenGL) + { + Log_ErrorPrintf("Host render API type is incompatible"); + return false; + } + CreateFramebuffer(); CreateVertexBuffer(); CreateUniformBuffer(); @@ -29,7 +37,9 @@ bool GPU_HW_OpenGL::Initialize(System* system, DMA* dma, InterruptController* in if (!CompilePrograms()) return false; - m_system->GetHostInterface()->SetDisplayTexture(m_display_texture.get(), 0, 0, VRAM_WIDTH, VRAM_HEIGHT, 1.0f); + m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_vram_texture->GetGLId())), 0, 0, + m_display_texture->GetWidth(), m_display_texture->GetHeight(), + m_display_texture->GetWidth(), m_display_texture->GetHeight(), 1.0f); RestoreGraphicsAPIState(); return true; } @@ -250,7 +260,7 @@ void GPU_HW_OpenGL::CreateVertexBuffer() glVertexAttribIPointer(0, 2, GL_INT, sizeof(BatchVertex), reinterpret_cast(offsetof(BatchVertex, x))); glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(BatchVertex), reinterpret_cast(offsetof(BatchVertex, color))); - glVertexAttribIPointer(2, 2, GL_INT, sizeof(BatchVertex), reinterpret_cast(offsetof(BatchVertex, texcoord))); + glVertexAttribIPointer(2, 1, GL_INT, sizeof(BatchVertex), reinterpret_cast(offsetof(BatchVertex, texcoord))); glVertexAttribIPointer(3, 1, GL_INT, sizeof(BatchVertex), reinterpret_cast(offsetof(BatchVertex, texpage))); glBindVertexArray(0); @@ -429,8 +439,9 @@ void GPU_HW_OpenGL::UpdateDisplay() if (m_system->GetSettings().debugging.show_vram) { - m_system->GetHostInterface()->SetDisplayTexture(m_vram_texture.get(), 0, 0, m_vram_texture->GetWidth(), - m_vram_texture->GetHeight(), 1.0f); + m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_vram_texture->GetGLId())), 0, 0, + m_vram_texture->GetWidth(), m_vram_texture->GetHeight(), + m_vram_texture->GetWidth(), m_vram_texture->GetHeight(), 1.0f); } else { @@ -448,7 +459,7 @@ void GPU_HW_OpenGL::UpdateDisplay() if (m_GPUSTAT.display_disable) { - m_system->GetHostInterface()->SetDisplayTexture(nullptr, 0, 0, 0, 0, m_crtc_state.display_aspect_ratio); + m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, m_crtc_state.display_aspect_ratio); } else if (!m_GPUSTAT.display_area_color_depth_24 && !m_GPUSTAT.vertical_interlace) { @@ -457,8 +468,9 @@ void GPU_HW_OpenGL::UpdateDisplay() scaled_flipped_vram_offset_y, 0, m_display_texture->GetGLId(), GL_TEXTURE_2D, 0, 0, 0, 0, scaled_display_width, scaled_display_height, 1); - m_system->GetHostInterface()->SetDisplayTexture(m_display_texture.get(), 0, 0, scaled_display_width, - scaled_display_height, m_crtc_state.display_aspect_ratio); + m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_vram_texture->GetGLId())), 0, + 0, scaled_display_width, scaled_display_height, m_display_texture->GetWidth(), + m_display_texture->GetHeight(), m_crtc_state.display_aspect_ratio); } else { @@ -495,8 +507,9 @@ void GPU_HW_OpenGL::UpdateDisplay() glDrawArrays(GL_TRIANGLES, 0, 3); - m_system->GetHostInterface()->SetDisplayTexture(m_display_texture.get(), 0, 0, display_width, display_height, - m_crtc_state.display_aspect_ratio); + m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_vram_texture->GetGLId())), 0, + 0, display_width, display_height, m_display_texture->GetWidth(), + m_display_texture->GetHeight(), m_crtc_state.display_aspect_ratio); } else { @@ -511,8 +524,9 @@ void GPU_HW_OpenGL::UpdateDisplay() glDrawArrays(GL_TRIANGLES, 0, 3); - m_system->GetHostInterface()->SetDisplayTexture(m_display_texture.get(), 0, 0, scaled_display_width, - scaled_display_height, m_crtc_state.display_aspect_ratio); + m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_vram_texture->GetGLId())), 0, + 0, scaled_display_width, scaled_display_height, m_display_texture->GetWidth(), + m_display_texture->GetHeight(), m_crtc_state.display_aspect_ratio); } // restore state @@ -679,7 +693,7 @@ void GPU_HW_OpenGL::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* m_vram_write_program.Bind(); glBindTexture(GL_TEXTURE_BUFFER, m_texture_buffer_r16ui_texture); - const u32 uniforms[4] = {x, flipped_y, width, height}; + const u32 uniforms[5] = {x, flipped_y, width, height, map_result.index_aligned}; UploadUniformBlock(uniforms, sizeof(uniforms)); m_batch_ubo_dirty = true; diff --git a/src/core/gpu_hw_opengl.h b/src/core/gpu_hw_opengl.h index aa151d8ef..d0856cfcd 100644 --- a/src/core/gpu_hw_opengl.h +++ b/src/core/gpu_hw_opengl.h @@ -14,7 +14,8 @@ public: GPU_HW_OpenGL(); ~GPU_HW_OpenGL() override; - bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers) override; + bool Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller, + Timers* timers) override; void Reset() override; void ResetGraphicsAPIState() override; diff --git a/src/core/gpu_hw_shadergen.cpp b/src/core/gpu_hw_shadergen.cpp index 0aee040c3..85543441d 100644 --- a/src/core/gpu_hw_shadergen.cpp +++ b/src/core/gpu_hw_shadergen.cpp @@ -1,7 +1,7 @@ #include "gpu_hw_shadergen.h" GPU_HW_ShaderGen::GPU_HW_ShaderGen(API backend, u32 resolution_scale, bool true_color) - : m_backend(backend), m_resolution_scale(resolution_scale), m_true_color(true_color), m_glsl(backend != API::Direct3D) + : m_backend(backend), m_resolution_scale(resolution_scale), m_true_color(true_color), m_glsl(backend != API::D3D11) { } @@ -22,9 +22,9 @@ void GPU_HW_ShaderGen::WriteHeader(std::stringstream& ss) ss << "#version 330 core\n\n"; ss << "#define API_OPENGL 1\n"; } - else if (m_backend == API::Direct3D) + else if (m_backend == API::D3D11) { - ss << "#define API_DIRECT3D 1\n"; + ss << "#define API_D3D11 1\n"; } if (m_glsl) @@ -52,7 +52,7 @@ void GPU_HW_ShaderGen::WriteHeader(std::stringstream& ss) ss << "#define CONSTANT static const\n"; ss << "#define SAMPLE_TEXTURE(name, coords) name.Sample(name##_ss, coords)\n"; ss << "#define LOAD_TEXTURE(name, coords, mip) name.Load(int3(coords, mip))\n"; - ss << "#define LOAD_TEXTURE_BUFFER(name, index) name.Load(name, index)\n"; + ss << "#define LOAD_TEXTURE_BUFFER(name, index) name.Load(index)\n"; } ss << "\n"; @@ -152,7 +152,8 @@ void GPU_HW_ShaderGen::DeclareTextureBuffer(std::stringstream& ss, const char* n void GPU_HW_ShaderGen::DeclareVertexEntryPoint(std::stringstream& ss, const std::initializer_list& attributes, u32 num_color_outputs, u32 num_texcoord_outputs, - const std::initializer_list& additional_outputs) + const std::initializer_list& additional_outputs, + bool declare_vertex_id) { if (m_glsl) { @@ -169,6 +170,10 @@ void GPU_HW_ShaderGen::DeclareVertexEntryPoint(std::stringstream& ss, ss << output << ";\n"; ss << "#define v_pos gl_Position\n\n"; + if (declare_vertex_id) + ss << "#define v_id uint(gl_VertexID)\n"; + + ss << "\n"; ss << "void main()\n"; } else @@ -182,6 +187,9 @@ void GPU_HW_ShaderGen::DeclareVertexEntryPoint(std::stringstream& ss, attribute_counter++; } + if (declare_vertex_id) + ss << " in uint v_id : SV_VertexID,\n"; + for (u32 i = 0; i < num_color_outputs; i++) ss << " out float4 v_col" << i << " : COLOR" << i << ",\n"; @@ -498,12 +506,14 @@ std::string GPU_HW_ShaderGen::GenerateScreenQuadVertexShader() { std::stringstream ss; WriteHeader(ss); - DeclareVertexEntryPoint(ss, {}, 0, 1, {}); + DeclareVertexEntryPoint(ss, {}, 0, 1, {}, true); ss << R"( { - v_tex0 = float2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2)); - gl_Position = float4(v_tex0 * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f); - gl_Position.y = -gl_Position.y; + v_tex0 = float2(float((v_id << 1) & 2u), float(v_id & 2u)); + v_pos = float4(v_tex0 * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f); + #if API_OPENGL + v_pos.y = -gl_Position.y; + #endif } )"; @@ -517,7 +527,8 @@ std::string GPU_HW_ShaderGen::GenerateFillFragmentShader() DeclareUniformBuffer(ss, {"float4 u_fill_color"}); DeclareFragmentEntryPoint(ss, 0, 1, {}, false, false); - ss << R"({ + ss << R"( +{ o_col0 = u_fill_color; } )"; @@ -525,6 +536,24 @@ std::string GPU_HW_ShaderGen::GenerateFillFragmentShader() return ss.str(); } +std::string GPU_HW_ShaderGen::GenerateCopyFragmentShader() +{ + std::stringstream ss; + WriteHeader(ss); + DeclareUniformBuffer(ss, {"float4 u_src_rect"}); + DeclareTexture(ss, "samp0", 0); + DeclareFragmentEntryPoint(ss, 0, 1, {}, false, false); + + ss << R"( +{ + float2 coords = u_src_rect.xy + v_tex0 * u_src_rect.zw; + o_col0 = SAMPLE_TEXTURE(samp0, coords); +} +)"; + + return ss.str(); +} + std::string GPU_HW_ShaderGen::GenerateDisplayFragmentShader(bool depth_24bit, bool interlaced) { std::stringstream ss; @@ -603,7 +632,7 @@ std::string GPU_HW_ShaderGen::GenerateVRAMWriteFragmentShader() std::stringstream ss; WriteHeader(ss); WriteCommonFunctions(ss); - DeclareUniformBuffer(ss, {"int2 u_base_coords", "int2 u_size"}); + DeclareUniformBuffer(ss, {"int2 u_base_coords", "int2 u_size", "int u_buffer_base_offset"}); DeclareTextureBuffer(ss, "samp0", 0, true, true); DeclareFragmentEntryPoint(ss, 0, 1, {}, true, false); @@ -611,9 +640,13 @@ std::string GPU_HW_ShaderGen::GenerateVRAMWriteFragmentShader() { int2 coords = int2(v_pos.xy) / int2(RESOLUTION_SCALE, RESOLUTION_SCALE); int2 offset = coords - u_base_coords; - offset.y = u_size.y - offset.y - 1; - int buffer_offset = offset.y * u_size.x + offset.x; + #if API_OPENGL + // Lower-left origin flip for OpenGL + offset.y = u_size.y - offset.y - 1; + #endif + + int buffer_offset = u_buffer_base_offset + (offset.y * u_size.x) + offset.x; uint value = LOAD_TEXTURE_BUFFER(samp0, buffer_offset).r; o_col0 = RGBA5551ToRGBA8(value); diff --git a/src/core/gpu_hw_shadergen.h b/src/core/gpu_hw_shadergen.h index 28e347dd0..547ec1259 100644 --- a/src/core/gpu_hw_shadergen.h +++ b/src/core/gpu_hw_shadergen.h @@ -9,7 +9,7 @@ public: enum class API { OpenGL, - Direct3D + D3D11 }; public: @@ -23,6 +23,7 @@ public: bool dithering); std::string GenerateScreenQuadVertexShader(); std::string GenerateFillFragmentShader(); + std::string GenerateCopyFragmentShader(); std::string GenerateDisplayFragmentShader(bool depth_24bit, bool interlaced); std::string GenerateVRAMWriteFragmentShader(); @@ -38,7 +39,8 @@ private: void DeclareTextureBuffer(std::stringstream& ss, const char* name, u32 index, bool is_int, bool is_unsigned); void DeclareVertexEntryPoint(std::stringstream& ss, const std::initializer_list& attributes, u32 num_color_outputs, u32 num_texcoord_outputs, - const std::initializer_list& additional_outputs); + const std::initializer_list& additional_outputs, + bool declare_vertex_id = false); void DeclareFragmentEntryPoint(std::stringstream& ss, u32 num_color_inputs, u32 num_texcoord_inputs, const std::initializer_list& additional_inputs, bool declare_fragcoord = false, bool dual_color_output = false); diff --git a/src/core/gpu_sw.cpp b/src/core/gpu_sw.cpp index 9a54c6441..5c04542b3 100644 --- a/src/core/gpu_sw.cpp +++ b/src/core/gpu_sw.cpp @@ -2,7 +2,7 @@ #include "YBaseLib/Log.h" #include "YBaseLib/Timer.h" #include "common/gl/texture.h" -#include "host_interface.h" +#include "host_display.h" #include "system.h" #include Log_SetChannel(GPU_SW); @@ -12,14 +12,21 @@ GPU_SW::GPU_SW() m_vram.fill(0); } -GPU_SW::~GPU_SW() = default; - -bool GPU_SW::Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers) +GPU_SW::~GPU_SW() { - if (!GPU::Initialize(system, dma, interrupt_controller, timers)) + m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, 1.0f); +} + +bool GPU_SW::Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller, + Timers* timers) +{ + if (!GPU::Initialize(host_display, system, dma, interrupt_controller, timers)) + return false; + + m_display_texture = host_display->CreateTexture(VRAM_WIDTH, VRAM_HEIGHT, nullptr, 0, true); + if (!m_display_texture) return false; - m_display_texture = std::make_unique(VRAM_WIDTH, VRAM_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE); return true; } @@ -71,9 +78,6 @@ void GPU_SW::CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 void GPU_SW::CopyOut15Bit(const u16* src_ptr, u32 src_stride, u32* dst_ptr, u32 dst_stride, u32 width, u32 height) { - // OpenGL is beeg silly for lower-left origin - dst_ptr = (dst_ptr + ((height - 1) * dst_stride)); - for (u32 row = 0; row < height; row++) { const u16* src_row_ptr = src_ptr; @@ -82,15 +86,12 @@ void GPU_SW::CopyOut15Bit(const u16* src_ptr, u32 src_stride, u32* dst_ptr, u32 *(dst_row_ptr++) = RGBA5551ToRGBA8888(*(src_row_ptr++)); src_ptr += src_stride; - dst_ptr -= dst_stride; + dst_ptr += dst_stride; } } void GPU_SW::CopyOut24Bit(const u16* src_ptr, u32 src_stride, u32* dst_ptr, u32 dst_stride, u32 width, u32 height) { - // OpenGL is beeg silly for lower-left origin - dst_ptr = (dst_ptr + ((height - 1) * dst_stride)); - for (u32 row = 0; row < height; row++) { const u8* src_row_ptr = reinterpret_cast(src_ptr); @@ -106,7 +107,7 @@ void GPU_SW::CopyOut24Bit(const u16* src_ptr, u32 src_stride, u32* dst_ptr, u32 } src_ptr += src_stride; - dst_ptr -= dst_stride; + dst_ptr += dst_stride; } } @@ -130,7 +131,7 @@ void GPU_SW::UpdateDisplay() if (m_GPUSTAT.display_disable) { - m_system->GetHostInterface()->SetDisplayTexture(nullptr, 0, 0, 0, 0, display_aspect_ratio); + m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, display_aspect_ratio); return; } else if (m_GPUSTAT.display_area_color_depth_24) @@ -153,11 +154,10 @@ void GPU_SW::UpdateDisplay() display_height); } - m_display_texture->Bind(); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, display_width, display_height, GL_RGBA, GL_UNSIGNED_BYTE, - m_display_texture_buffer.data()); - m_system->GetHostInterface()->SetDisplayTexture(m_display_texture.get(), 0, 0, display_width, display_height, - display_aspect_ratio); + m_host_display->UpdateTexture(m_display_texture.get(), 0, 0, display_width, display_height, + m_display_texture_buffer.data(), display_width * sizeof(u32)); + m_host_display->SetDisplayTexture(m_display_texture->GetHandle(), 0, 0, display_width, display_height, VRAM_WIDTH, + VRAM_HEIGHT, display_aspect_ratio); } void GPU_SW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32* command_ptr) diff --git a/src/core/gpu_sw.h b/src/core/gpu_sw.h index 917ec3fbc..2bb24ba82 100644 --- a/src/core/gpu_sw.h +++ b/src/core/gpu_sw.h @@ -1,13 +1,10 @@ #pragma once -#include "common/gl/texture.h" #include "gpu.h" #include #include #include -namespace GL { -class Texture; -} +class HostDisplayTexture; class GPU_SW final : public GPU { @@ -15,7 +12,8 @@ public: GPU_SW(); ~GPU_SW() override; - bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers) override; + bool Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller, + Timers* timers) override; void Reset() override; u16 GetPixel(u32 x, u32 y) const { return m_vram[VRAM_WIDTH * y + x]; } @@ -58,13 +56,14 @@ protected: static bool IsClockwiseWinding(const SWVertex* v0, const SWVertex* v1, const SWVertex* v2); - void ShadePixel(RenderCommand rc, u32 x, u32 y, u8 color_r, u8 color_g, u8 color_b, u8 texcoord_x, u8 texcoord_y, bool dithering); + void ShadePixel(RenderCommand rc, u32 x, u32 y, u8 color_r, u8 color_g, u8 color_b, u8 texcoord_x, u8 texcoord_y, + bool dithering); void DrawTriangle(RenderCommand rc, const SWVertex* v0, const SWVertex* v1, const SWVertex* v2); void DrawRectangle(RenderCommand rc, s32 origin_x, s32 origin_y, u32 width, u32 height, u8 r, u8 g, u8 b, u8 origin_texcoord_x, u8 origin_texcoord_y); - std::unique_ptr m_display_texture; std::vector m_display_texture_buffer; + std::unique_ptr m_display_texture; std::array m_vertex_buffer; std::array m_vram; diff --git a/src/core/host_display.h b/src/core/host_display.h new file mode 100644 index 000000000..60c8a5854 --- /dev/null +++ b/src/core/host_display.h @@ -0,0 +1,71 @@ +#pragma once +#include "types.h" +#include +#include + +// An abstracted RGBA8 texture. +class HostDisplayTexture +{ +public: + virtual ~HostDisplayTexture() {} + + virtual void* GetHandle() const = 0; + virtual u32 GetWidth() const = 0; + virtual u32 GetHeight() const = 0; +}; + +// Interface to the frontend's renderer. +class HostDisplay +{ +public: + enum class RenderAPI + { + None, + D3D11, + OpenGL + }; + + virtual RenderAPI GetRenderAPI() const = 0; + virtual void* GetHostRenderDevice() const = 0; + virtual void* GetHostRenderContext() const = 0; + + /// Creates an abstracted RGBA8 texture. If dynamic, the texture can be updated with UpdateTexture() below. + virtual std::unique_ptr CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, + bool dynamic = false) = 0; + virtual void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, + u32 data_stride) = 0; + + virtual void SetDisplayTexture(void* texture_handle, u32 offset_x, u32 offset_y, u32 width, u32 height, + u32 texture_width, u32 texture_height, float aspect_ratio) = 0; + virtual void SetDisplayLinearFiltering(bool enabled) = 0; + + virtual void Render() = 0; + + virtual void SetVSync(bool enabled) = 0; + + virtual std::tuple GetWindowSize() const = 0; + virtual void WindowResized() = 0; + + // Helper function for computing the draw rectangle in a larger window. + static std::tuple CalculateDrawRect(int window_width, int window_height, float display_ratio) + { + const float window_ratio = float(window_width) / float(window_height); + int left, top, width, height; + if (window_ratio >= display_ratio) + { + width = static_cast(float(window_height) * display_ratio); + height = static_cast(window_height); + left = (window_width - width) / 2; + top = 0; + } + else + { + width = static_cast(window_width); + height = static_cast(float(window_width) / display_ratio); + left = 0; + top = (window_height - height) / 2; + } + + return std::tie(left, top, width, height); + } +}; diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp index f72b38c8b..986bed235 100644 --- a/src/core/host_interface.cpp +++ b/src/core/host_interface.cpp @@ -5,16 +5,17 @@ #include "system.h" Log_SetChannel(HostInterface); -HostInterface::HostInterface() = default; +HostInterface::HostInterface() +{ + m_settings.gpu_renderer = Settings::GPURenderer::HardwareD3D11; + m_settings.memory_card_a_filename = "memory_card_a.mcd"; +} HostInterface::~HostInterface() = default; bool HostInterface::InitializeSystem(const char* filename, const char* exp1_filename) { - Settings settings; - settings.memory_card_a_filename = "memory_card_a.mcd"; - - m_system = std::make_unique(this, settings); + m_system = std::make_unique(this, m_settings); if (!m_system->Initialize()) { m_system.reset(); @@ -50,6 +51,7 @@ bool HostInterface::InitializeSystem(const char* filename, const char* exp1_file m_system->SetExpansionROM(exp1_filename); // Resume execution. + m_settings = m_system->GetSettings(); return true; } diff --git a/src/core/host_interface.h b/src/core/host_interface.h index 25de1b55a..5eb0f8df6 100644 --- a/src/core/host_interface.h +++ b/src/core/host_interface.h @@ -1,12 +1,10 @@ #pragma once #include "types.h" +#include "settings.h" #include class AudioStream; - -namespace GL { -class Texture; -} +class HostDisplay; class System; @@ -20,7 +18,8 @@ public: bool InitializeSystem(const char* filename, const char* exp1_filename); - virtual void SetDisplayTexture(GL::Texture* texture, u32 offset_x, u32 offset_y, u32 width, u32 height, float aspect_ratio) = 0; + virtual HostDisplay* GetDisplay() const = 0; + virtual void ReportMessage(const char* message) = 0; // Adds OSD messages, duration is in seconds. @@ -32,4 +31,6 @@ public: protected: std::unique_ptr m_audio_stream; std::unique_ptr m_system; + + Settings m_settings; }; diff --git a/src/core/settings.h b/src/core/settings.h index 879c36949..7d2c7297b 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -5,6 +5,7 @@ struct Settings { enum class GPURenderer { + HardwareD3D11, HardwareOpenGL, Software }; diff --git a/src/core/system.cpp b/src/core/system.cpp index 9808931a1..2e8e3eee3 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -7,6 +7,7 @@ #include "cpu_core.h" #include "dma.h" #include "gpu.h" +#include "host_interface.h" #include "interrupt_controller.h" #include "mdec.h" #include "memory_card.h" @@ -112,20 +113,28 @@ bool System::CreateGPU() m_gpu = GPU::CreateHardwareOpenGLRenderer(); break; + case Settings::GPURenderer::HardwareD3D11: + m_gpu = GPU::CreateHardwareD3D11Renderer(); + break; + case Settings::GPURenderer::Software: default: m_gpu = GPU::CreateSoftwareRenderer(); break; } - if (!m_gpu || !m_gpu->Initialize(this, m_dma.get(), m_interrupt_controller.get(), m_timers.get())) + if (!m_gpu || !m_gpu->Initialize(m_host_interface->GetDisplay(), this, m_dma.get(), m_interrupt_controller.get(), + m_timers.get())) { Log_ErrorPrintf("Failed to initialize GPU, falling back to software"); m_gpu.reset(); m_settings.gpu_renderer = Settings::GPURenderer::Software; m_gpu = GPU::CreateSoftwareRenderer(); - if (!m_gpu->Initialize(this, m_dma.get(), m_interrupt_controller.get(), m_timers.get())) + if (!m_gpu->Initialize(m_host_interface->GetDisplay(), this, m_dma.get(), m_interrupt_controller.get(), + m_timers.get())) + { return false; + } } m_bus->SetGPU(m_gpu.get()); diff --git a/src/duckstation/CMakeLists.txt b/src/duckstation/CMakeLists.txt index 6efb34bb2..89b94f131 100644 --- a/src/duckstation/CMakeLists.txt +++ b/src/duckstation/CMakeLists.txt @@ -1,12 +1,21 @@ -set(SRCS +add_executable(duckstation icon.cpp main.cpp + opengl_host_display.cpp + opengl_host_display.h sdl_audio_stream.cpp sdl_audio_stream.h - sdl_interface.cpp - sdl_interface.h + sdl_host_interface.cpp + sdl_host_interface.h ) -add_executable(duckstation ${SRCS}) target_include_directories(duckstation PRIVATE "${SDL2_INCLUDE_DIRS}") -target_link_libraries(duckstation core imgui nativefiledialog "${SDL2_LIBRARIES}") +target_link_libraries(duckstation PRIVATE YBaseLib core common imgui nativefiledialog glad "${SDL2_LIBRARIES}") + +if(WIN32) + target_sources(duckstation PRIVATE + d3d11_host_display.cpp + d3d11_host_display.h + ) + target_link_libraries(duckstation PRIVATE d3d11.lib) +endif() diff --git a/src/duckstation/d3d11_host_display.cpp b/src/duckstation/d3d11_host_display.cpp new file mode 100644 index 000000000..0de26d160 --- /dev/null +++ b/src/duckstation/d3d11_host_display.cpp @@ -0,0 +1,394 @@ +#include "d3d11_host_display.h" +#include "YBaseLib/Log.h" +#include "common/d3d11/shader_compiler.h" +#include +#include +#include +#include +#include +Log_SetChannel(D3D11HostDisplay); + +class D3D11HostDisplayTexture : public HostDisplayTexture +{ +public: + template + using ComPtr = Microsoft::WRL::ComPtr; + + D3D11HostDisplayTexture(ComPtr texture, ComPtr srv, u32 width, u32 height, + bool dynamic) + : m_texture(std::move(texture)), m_srv(std::move(srv)), m_width(width), m_height(height), m_dynamic(dynamic) + { + } + ~D3D11HostDisplayTexture() override = default; + + void* GetHandle() const override { return m_srv.Get(); } + u32 GetWidth() const override { return m_width; } + u32 GetHeight() const override { return m_height; } + + ID3D11Texture2D* GetD3DTexture() const { return m_texture.Get(); } + ID3D11ShaderResourceView* GetD3DSRV() const { return m_srv.Get(); } + bool IsDynamic() const { return m_dynamic; } + + static std::unique_ptr Create(ID3D11Device* device, u32 width, u32 height, const void* data, + u32 data_stride, bool dynamic) + { + const CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, width, height, 1, 1, D3D11_BIND_SHADER_RESOURCE, + dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT, + dynamic ? D3D11_CPU_ACCESS_WRITE : 0, 1, 0, 0); + const D3D11_SUBRESOURCE_DATA srd{data, data_stride, data_stride * height}; + ComPtr texture; + HRESULT hr = device->CreateTexture2D(&desc, data ? &srd : nullptr, texture.GetAddressOf()); + if (FAILED(hr)) + return {}; + + const CD3D11_SHADER_RESOURCE_VIEW_DESC srv_desc(D3D11_SRV_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 1, 0, + 1); + ComPtr srv; + hr = device->CreateShaderResourceView(texture.Get(), &srv_desc, srv.GetAddressOf()); + if (FAILED(hr)) + return {}; + + return std::make_unique(std::move(texture), std::move(srv), width, height, dynamic); + } + +private: + ComPtr m_texture; + ComPtr m_srv; + u32 m_width; + u32 m_height; + bool m_dynamic; +}; + +D3D11HostDisplay::D3D11HostDisplay(SDL_Window* window) : m_window(window) +{ + SDL_GetWindowSize(window, &m_window_width, &m_window_height); +} + +D3D11HostDisplay::~D3D11HostDisplay() +{ + ImGui_ImplDX11_Shutdown(); + ImGui_ImplSDL2_Shutdown(); + + if (m_window) + SDL_DestroyWindow(m_window); +} + +HostDisplay::RenderAPI D3D11HostDisplay::GetRenderAPI() const +{ + return HostDisplay::RenderAPI::D3D11; +} + +void* D3D11HostDisplay::GetHostRenderDevice() const +{ + return m_device.Get(); +} + +void* D3D11HostDisplay::GetHostRenderContext() const +{ + return m_context.Get(); +} + +std::unique_ptr D3D11HostDisplay::CreateTexture(u32 width, u32 height, const void* data, + u32 data_stride, bool dynamic) +{ + return D3D11HostDisplayTexture::Create(m_device.Get(), width, height, data, data_stride, dynamic); +} + +void D3D11HostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, + u32 data_stride) +{ + D3D11HostDisplayTexture* d3d11_texture = static_cast(texture); + if (!d3d11_texture->IsDynamic()) + { + const CD3D11_BOX dst_box(x, y, 0, x + width, y + height, 1); + m_context->UpdateSubresource(d3d11_texture->GetD3DTexture(), 0, &dst_box, data, data_stride, data_stride * height); + } + else + { + D3D11_MAPPED_SUBRESOURCE sr; + HRESULT hr = m_context->Map(d3d11_texture->GetD3DTexture(), 0, D3D11_MAP_WRITE_DISCARD, 0, &sr); + if (FAILED(hr)) + Panic("Failed to map dynamic host display texture"); + + char* dst_ptr = static_cast(sr.pData) + (y * sr.RowPitch) + (x * sizeof(u32)); + const char* src_ptr = static_cast(data); + if (sr.RowPitch == data_stride) + { + std::memcpy(dst_ptr, src_ptr, data_stride * height); + } + else + { + for (u32 row = 0; row < height; row++) + { + std::memcpy(dst_ptr, src_ptr, width * sizeof(u32)); + src_ptr += data_stride; + dst_ptr += sr.RowPitch; + } + } + + m_context->Unmap(d3d11_texture->GetD3DTexture(), 0); + } +} + +void D3D11HostDisplay::SetDisplayTexture(void* texture, u32 offset_x, u32 offset_y, u32 width, u32 height, + u32 texture_width, u32 texture_height, float aspect_ratio) +{ + m_display_srv = static_cast(texture); + m_display_offset_x = offset_x; + m_display_offset_y = offset_y; + m_display_width = width; + m_display_height = height; + m_display_texture_width = texture_width; + m_display_texture_height = texture_height; + m_display_aspect_ratio = aspect_ratio; + m_display_texture_changed = true; +} + +void D3D11HostDisplay::SetDisplayLinearFiltering(bool enabled) +{ + m_display_linear_filtering = enabled; +} + +void D3D11HostDisplay::SetVSync(bool enabled) +{ + m_vsync = enabled; +} + +std::tuple D3D11HostDisplay::GetWindowSize() const +{ + return std::make_tuple(static_cast(m_window_width), static_cast(m_window_height)); +} + +void D3D11HostDisplay::WindowResized() +{ + SDL_GetWindowSize(m_window, &m_window_width, &m_window_height); + + m_swap_chain_rtv.Reset(); + + HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0); + if (FAILED(hr)) + Log_ErrorPrintf("ResizeBuffers() failed: 0x%08X", hr); + + if (!CreateSwapChainRTV()) + Panic("Failed to recreate swap chain RTV after resize"); +} + +bool D3D11HostDisplay::CreateD3DDevice() +{ + const bool debug = false; + + SDL_SysWMinfo syswm = {}; + if (!SDL_GetWindowWMInfo(m_window, &syswm)) + { + Log_ErrorPrintf("SDL_GetWindowWMInfo failed"); + return false; + } + + UINT create_flags = 0; + if (debug) + create_flags |= D3D11_CREATE_DEVICE_DEBUG; + + DXGI_SWAP_CHAIN_DESC swap_chain_desc = {}; + swap_chain_desc.BufferDesc.Width = m_window_width; + swap_chain_desc.BufferDesc.Height = m_window_height; + swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swap_chain_desc.SampleDesc.Count = 1; + swap_chain_desc.BufferCount = 3; + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.OutputWindow = syswm.info.win.window; + swap_chain_desc.Windowed = TRUE; + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + + HRESULT hr = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, create_flags, nullptr, 0, + D3D11_SDK_VERSION, &swap_chain_desc, m_swap_chain.GetAddressOf(), + m_device.GetAddressOf(), nullptr, m_context.GetAddressOf()); + if (FAILED(hr)) + { + Log_ErrorPrintf("D3D11CreateDeviceAndSwapChain failed: 0x%08X", hr); + return false; + } + +#if 0 + ComPtr info; + hr = m_device.As(&info); + if (SUCCEEDED(hr)) + { + info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, TRUE); + info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, TRUE); + } +#endif + + return true; +} + +bool D3D11HostDisplay::CreateSwapChainRTV() +{ + ComPtr backbuffer; + HRESULT hr = m_swap_chain->GetBuffer(0, IID_PPV_ARGS(backbuffer.GetAddressOf())); + if (FAILED(hr)) + { + Log_ErrorPrintf("GetBuffer for RTV failed: 0x%08X", hr); + return false; + } + + D3D11_TEXTURE2D_DESC backbuffer_desc; + backbuffer->GetDesc(&backbuffer_desc); + + CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D, backbuffer_desc.Format, 0, 0, + backbuffer_desc.ArraySize); + hr = m_device->CreateRenderTargetView(backbuffer.Get(), &rtv_desc, m_swap_chain_rtv.GetAddressOf()); + if (FAILED(hr)) + { + Log_ErrorPrintf("CreateRenderTargetView for swap chain failed: 0x%08X", hr); + return false; + } + + return true; +} + +bool D3D11HostDisplay::CreateD3DResources() +{ + static constexpr char fullscreen_quad_vertex_shader[] = R"( +void main(in uint vertex_id : SV_VertexID, + out float2 v_tex0 : TEXCOORD0, + out float4 o_pos : SV_Position) +{ + v_tex0 = float2(float((vertex_id << 1) & 2u), float(vertex_id & 2u)); + o_pos = float4(v_tex0 * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f); +} +)"; + + static constexpr char display_pixel_shader[] = R"( +cbuffer UBOBlock : register(b0) +{ + float4 u_src_rect; +}; + +Texture2D samp0 : register(t0); +SamplerState samp0_ss : register(s0); + +void main(in float2 v_tex0 : TEXCOORD0, + out float4 o_col0 : SV_Target) +{ + float2 coords = u_src_rect.xy + v_tex0 * u_src_rect.zw; + o_col0 = samp0.Sample(samp0_ss, coords); +} +)"; + + HRESULT hr; + + m_display_vertex_shader = + D3D11::ShaderCompiler::CompileAndCreateVertexShader(m_device.Get(), fullscreen_quad_vertex_shader, false); + m_display_pixel_shader = + D3D11::ShaderCompiler::CompileAndCreatePixelShader(m_device.Get(), display_pixel_shader, false); + if (!m_display_vertex_shader || !m_display_pixel_shader) + return false; + + if (!m_display_uniform_buffer.Create(m_device.Get(), D3D11_BIND_CONSTANT_BUFFER, DISPLAY_UNIFORM_BUFFER_SIZE)) + return false; + + CD3D11_RASTERIZER_DESC rasterizer_desc = CD3D11_RASTERIZER_DESC(CD3D11_DEFAULT()); + rasterizer_desc.CullMode = D3D11_CULL_NONE; + hr = m_device->CreateRasterizerState(&rasterizer_desc, m_display_rasterizer_state.GetAddressOf()); + if (FAILED(hr)) + return false; + + CD3D11_DEPTH_STENCIL_DESC depth_stencil_desc = CD3D11_DEPTH_STENCIL_DESC(CD3D11_DEFAULT()); + depth_stencil_desc.DepthEnable = FALSE; + depth_stencil_desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; + hr = m_device->CreateDepthStencilState(&depth_stencil_desc, m_display_depth_stencil_state.GetAddressOf()); + if (FAILED(hr)) + return false; + + CD3D11_BLEND_DESC blend_desc = CD3D11_BLEND_DESC(CD3D11_DEFAULT()); + hr = m_device->CreateBlendState(&blend_desc, m_display_blend_state.GetAddressOf()); + if (FAILED(hr)) + return false; + + CD3D11_SAMPLER_DESC sampler_desc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT()); + sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + hr = m_device->CreateSamplerState(&sampler_desc, m_point_sampler.GetAddressOf()); + if (FAILED(hr)) + return false; + + sampler_desc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; + hr = m_device->CreateSamplerState(&sampler_desc, m_linear_sampler.GetAddressOf()); + if (FAILED(hr)) + return false; + + return true; +} + +bool D3D11HostDisplay::CreateImGuiContext() +{ + if (!ImGui_ImplSDL2_InitForD3D(m_window) || !ImGui_ImplDX11_Init(m_device.Get(), m_context.Get())) + return false; + + ImGui_ImplDX11_NewFrame(); + ImGui_ImplSDL2_NewFrame(m_window); + return true; +} + +std::unique_ptr D3D11HostDisplay::Create(SDL_Window* window) +{ + std::unique_ptr display = std::make_unique(window); + if (!display->CreateD3DDevice() || !display->CreateSwapChainRTV() || !display->CreateD3DResources() || + !display->CreateImGuiContext()) + { + return nullptr; + } + + return display; +} + +void D3D11HostDisplay::Render() +{ + static constexpr std::array clear_color = {}; + m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), clear_color.data()); + m_context->OMSetRenderTargets(1, m_swap_chain_rtv.GetAddressOf(), nullptr); + + RenderDisplay(); + + ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + + m_swap_chain->Present(BoolToUInt32(m_vsync), 0); + + ImGui_ImplSDL2_NewFrame(m_window); + ImGui_ImplDX11_NewFrame(); +} + +void D3D11HostDisplay::RenderDisplay() +{ + if (!m_display_srv) + return; + + // - 20 for main menu padding + auto [vp_left, vp_top, vp_width, vp_height] = + CalculateDrawRect(m_window_width, std::max(m_window_height - 20, 1), m_display_aspect_ratio); + vp_top += 20; + + m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + m_context->VSSetShader(m_display_vertex_shader.Get(), nullptr, 0); + m_context->PSSetShader(m_display_pixel_shader.Get(), nullptr, 0); + m_context->PSSetShaderResources(0, 1, &m_display_srv); + m_context->PSSetSamplers( + 0, 1, m_display_linear_filtering ? m_linear_sampler.GetAddressOf() : m_point_sampler.GetAddressOf()); + + const float uniforms[4] = {static_cast(m_display_offset_x) / static_cast(m_display_texture_width), + static_cast(m_display_offset_y) / static_cast(m_display_texture_height), + static_cast(m_display_width) / static_cast(m_display_texture_width), + static_cast(m_display_height) / static_cast(m_display_texture_height)}; + const auto map = m_display_uniform_buffer.Map(m_context.Get(), sizeof(uniforms), sizeof(uniforms)); + std::memcpy(map.pointer, uniforms, sizeof(uniforms)); + m_display_uniform_buffer.Unmap(m_context.Get(), sizeof(uniforms)); + m_context->PSSetConstantBuffers(0, 1, m_display_uniform_buffer.GetD3DBufferArray()); + + const CD3D11_VIEWPORT vp(static_cast(vp_left), static_cast(vp_top), static_cast(vp_width), + static_cast(vp_height)); + m_context->RSSetViewports(1, &vp); + m_context->RSSetState(m_display_rasterizer_state.Get()); + m_context->OMSetDepthStencilState(m_display_depth_stencil_state.Get(), 0); + m_context->OMSetBlendState(m_display_blend_state.Get(), nullptr, 0xFFFFFFFFu); + + m_context->Draw(3, 0); +} diff --git a/src/duckstation/d3d11_host_display.h b/src/duckstation/d3d11_host_display.h new file mode 100644 index 000000000..7fd4fdb30 --- /dev/null +++ b/src/duckstation/d3d11_host_display.h @@ -0,0 +1,84 @@ +#pragma once +#include "YBaseLib/Windows/WindowsHeaders.h" +#include "common/d3d11/stream_buffer.h" +#include "common/d3d11/texture.h" +#include "core/host_display.h" +#include +#include +#include +#include + +class D3D11HostDisplay final : public HostDisplay +{ +public: + template + using ComPtr = Microsoft::WRL::ComPtr; + + D3D11HostDisplay(SDL_Window* window); + ~D3D11HostDisplay(); + + static std::unique_ptr Create(SDL_Window* window); + + RenderAPI GetRenderAPI() const override; + void* GetHostRenderDevice() const override; + void* GetHostRenderContext() const override; + + std::unique_ptr CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, + bool dynamic) override; + void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, + u32 data_stride) override; + + void SetDisplayTexture(void* texture, u32 offset_x, u32 offset_y, u32 width, u32 height, u32 texture_width, + u32 texture_height, float aspect_ratio) override; + void SetDisplayLinearFiltering(bool enabled) override; + + void SetVSync(bool enabled) override; + + std::tuple GetWindowSize() const override; + void WindowResized() override; + +private: + static constexpr u32 DISPLAY_UNIFORM_BUFFER_SIZE = 16; + + bool CreateD3DDevice(); + bool CreateD3DResources(); + bool CreateSwapChainRTV(); + bool CreateImGuiContext(); + + void Render(); + void RenderDisplay(); + + SDL_Window* m_window = nullptr; + SDL_GLContext m_gl_context = nullptr; + int m_window_width = 0; + int m_window_height = 0; + + ComPtr m_device; + ComPtr m_context; + ComPtr m_swap_chain; + ComPtr m_swap_chain_rtv; + + ComPtr m_display_rasterizer_state; + ComPtr m_display_depth_stencil_state; + ComPtr m_display_blend_state; + ComPtr m_display_vertex_shader; + ComPtr m_display_pixel_shader; + ComPtr m_point_sampler; + ComPtr m_linear_sampler; + + D3D11::Texture m_display_pixels_texture; + D3D11::StreamBuffer m_display_uniform_buffer; + + ID3D11ShaderResourceView* m_display_srv = nullptr; + u32 m_display_offset_x = 0; + u32 m_display_offset_y = 0; + u32 m_display_width = 0; + u32 m_display_height = 0; + u32 m_display_texture_width = 0; + u32 m_display_texture_height = 0; + float m_display_aspect_ratio = 1.0f; + + bool m_display_texture_changed = false; + bool m_display_linear_filtering = false; + bool m_vsync = false; +}; diff --git a/src/duckstation/duckstation.vcxproj b/src/duckstation/duckstation.vcxproj index 8ed283c7b..a8c06e028 100644 --- a/src/duckstation/duckstation.vcxproj +++ b/src/duckstation/duckstation.vcxproj @@ -52,15 +52,19 @@ + + - + + + - + {DAA8F93D-9C17-4DE2-BD0B-57891E0FF0D9} @@ -213,7 +217,7 @@ Console true - SDL2.lib;%(AdditionalDependencies) + SDL2.lib;d3d11.lib;%(AdditionalDependencies) $(SolutionDir)dep\msvc\lib32-debug;%(AdditionalLibraryDirectories) @@ -235,7 +239,7 @@ Console true - SDL2.lib;%(AdditionalDependencies) + SDL2.lib;d3d11.lib;%(AdditionalDependencies) $(SolutionDir)dep\msvc\lib64-debug;%(AdditionalLibraryDirectories) @@ -260,7 +264,7 @@ Console true - SDL2.lib;%(AdditionalDependencies) + SDL2.lib;d3d11.lib;%(AdditionalDependencies) $(SolutionDir)dep\msvc\lib32-debug;%(AdditionalLibraryDirectories) @@ -285,7 +289,7 @@ Console true - SDL2.lib;%(AdditionalDependencies) + SDL2.lib;d3d11.lib;%(AdditionalDependencies) $(SolutionDir)dep\msvc\lib64-debug;%(AdditionalLibraryDirectories) @@ -308,7 +312,7 @@ true true true - SDL2.lib;%(AdditionalDependencies) + SDL2.lib;d3d11.lib;%(AdditionalDependencies) $(SolutionDir)dep\msvc\lib32;%(AdditionalLibraryDirectories) Default @@ -333,7 +337,7 @@ true true true - SDL2.lib;%(AdditionalDependencies) + SDL2.lib;d3d11.lib;%(AdditionalDependencies) $(SolutionDir)dep\msvc\lib32;%(AdditionalLibraryDirectories) UseLinkTimeCodeGeneration @@ -357,7 +361,7 @@ true true true - SDL2.lib;%(AdditionalDependencies) + SDL2.lib;d3d11.lib;%(AdditionalDependencies) $(SolutionDir)dep\msvc\lib64;%(AdditionalLibraryDirectories) Default @@ -382,7 +386,7 @@ true true true - SDL2.lib;%(AdditionalDependencies) + SDL2.lib;d3d11.lib;%(AdditionalDependencies) $(SolutionDir)dep\msvc\lib64;%(AdditionalLibraryDirectories) UseLinkTimeCodeGeneration diff --git a/src/duckstation/duckstation.vcxproj.filters b/src/duckstation/duckstation.vcxproj.filters index 9efb6a869..a11978e13 100644 --- a/src/duckstation/duckstation.vcxproj.filters +++ b/src/duckstation/duckstation.vcxproj.filters @@ -2,13 +2,17 @@ - + + + - + + + \ No newline at end of file diff --git a/src/duckstation/main.cpp b/src/duckstation/main.cpp index fea0d37b2..5ea805e82 100644 --- a/src/duckstation/main.cpp +++ b/src/duckstation/main.cpp @@ -2,7 +2,7 @@ #include "YBaseLib/Log.h" #include "YBaseLib/StringConverter.h" #include "core/system.h" -#include "sdl_interface.h" +#include "sdl_host_interface.h" #include #include @@ -40,7 +40,7 @@ static int Run(int argc, char* argv[]) #define CHECK_ARG_PARAM(str) (!std::strcmp(argv[i], str) && ((i + 1) < argc)) if (CHECK_ARG_PARAM("-state")) - state_filename = SDLInterface::GetSaveStateFilename(std::strtoul(argv[++i], nullptr, 10)); + state_filename = SDLHostInterface::GetSaveStateFilename(std::strtoul(argv[++i], nullptr, 10)); else if (CHECK_ARG_PARAM("-exp1")) exp1_filename = argv[++i]; else @@ -51,8 +51,8 @@ static int Run(int argc, char* argv[]) } // create display and host interface - std::unique_ptr host_interface = - SDLInterface::Create(filename, exp1_filename, state_filename.IsEmpty() ? nullptr : state_filename.GetCharArray()); + std::unique_ptr host_interface = + SDLHostInterface::Create(filename, exp1_filename, state_filename.IsEmpty() ? nullptr : state_filename.GetCharArray()); if (!host_interface) { Panic("Failed to create host interface"); diff --git a/src/duckstation/opengl_host_display.cpp b/src/duckstation/opengl_host_display.cpp new file mode 100644 index 000000000..2b08d342d --- /dev/null +++ b/src/duckstation/opengl_host_display.cpp @@ -0,0 +1,318 @@ +#include "opengl_host_display.h" +#include "YBaseLib/Log.h" +#include "icon.h" +#include +#include +#include +Log_SetChannel(OpenGLHostDisplay); + +class OpenGLHostDisplayTexture : public HostDisplayTexture +{ +public: + OpenGLHostDisplayTexture(GLuint id, u32 width, u32 height) : m_id(id), m_width(width), m_height(height) {} + ~OpenGLHostDisplayTexture() override { glDeleteTextures(1, &m_id); } + + void* GetHandle() const override { return reinterpret_cast(static_cast(m_id)); } + u32 GetWidth() const override { return m_width; } + u32 GetHeight() const override { return m_height; } + + GLuint GetGLID() const { return m_id; } + + static std::unique_ptr Create(u32 width, u32 height, const void* initial_data, + u32 initial_data_stride) + { + GLuint id; + glGenTextures(1, &id); + + GLint old_texture_binding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture_binding); + + // TODO: Set pack width + Assert(initial_data_stride == (width * sizeof(u32))); + + glBindTexture(GL_TEXTURE_2D, id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, initial_data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + + glBindTexture(GL_TEXTURE_2D, id); + return std::make_unique(id, width, height); + } + +private: + GLuint m_id; + u32 m_width; + u32 m_height; +}; + +OpenGLHostDisplay::OpenGLHostDisplay(SDL_Window* window) : m_window(window) +{ + SDL_GetWindowSize(window, &m_window_width, &m_window_height); +} + +OpenGLHostDisplay::~OpenGLHostDisplay() +{ + if (m_gl_context) + { + if (m_display_vao != 0) + glDeleteVertexArrays(1, &m_display_vao); + if (m_display_linear_sampler != 0) + glDeleteSamplers(1, &m_display_linear_sampler); + if (m_display_nearest_sampler != 0) + glDeleteSamplers(1, &m_display_nearest_sampler); + + m_display_program.Destroy(); + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplSDL2_Shutdown(); + SDL_GL_MakeCurrent(nullptr, nullptr); + SDL_GL_DeleteContext(m_gl_context); + } + + if (m_window) + SDL_DestroyWindow(m_window); +} + +HostDisplay::RenderAPI OpenGLHostDisplay::GetRenderAPI() const +{ + return HostDisplay::RenderAPI::OpenGL; +} + +void* OpenGLHostDisplay::GetHostRenderDevice() const +{ + return nullptr; +} + +void* OpenGLHostDisplay::GetHostRenderContext() const +{ + return m_gl_context; +} + +std::unique_ptr OpenGLHostDisplay::CreateTexture(u32 width, u32 height, const void* data, + u32 data_stride, bool dynamic) +{ + return OpenGLHostDisplayTexture::Create(width, height, data, data_stride); +} + +void OpenGLHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, + const void* data, u32 data_stride) +{ + OpenGLHostDisplayTexture* tex = static_cast(texture); + Assert(data_stride == (width * sizeof(u32))); + + GLint old_texture_binding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture_binding); + + glBindTexture(GL_TEXTURE_2D, tex->GetGLID()); + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); + + glBindTexture(GL_TEXTURE_2D, old_texture_binding); +} + +void OpenGLHostDisplay::SetDisplayTexture(void* texture, u32 offset_x, u32 offset_y, u32 width, u32 height, + u32 texture_width, u32 texture_height, float aspect_ratio) +{ + m_display_texture_id = static_cast(reinterpret_cast(texture)); + m_display_offset_x = offset_x; + m_display_offset_y = offset_y; + m_display_width = width; + m_display_height = height; + m_display_texture_width = texture_width; + m_display_texture_height = texture_height; + m_display_aspect_ratio = aspect_ratio; + m_display_texture_changed = true; +} + +void OpenGLHostDisplay::SetDisplayLinearFiltering(bool enabled) +{ + m_display_linear_filtering = enabled; +} + +void OpenGLHostDisplay::SetVSync(bool enabled) +{ + // Window framebuffer has to be bound to call SetSwapInterval. + GLint current_fbo = 0; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + SDL_GL_SetSwapInterval(enabled ? 1 : 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo); +} + +std::tuple OpenGLHostDisplay::GetWindowSize() const +{ + return std::make_tuple(static_cast(m_window_width), static_cast(m_window_height)); +} + +void OpenGLHostDisplay::WindowResized() +{ + SDL_GetWindowSize(m_window, &m_window_width, &m_window_height); +} + +static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, + const GLchar* message, const void* userParam) +{ + switch (severity) + { + case GL_DEBUG_SEVERITY_HIGH_KHR: + Log_ErrorPrintf(message); + break; + case GL_DEBUG_SEVERITY_MEDIUM_KHR: + Log_WarningPrint(message); + break; + case GL_DEBUG_SEVERITY_LOW_KHR: + Log_InfoPrintf(message); + break; + case GL_DEBUG_SEVERITY_NOTIFICATION: + // Log_DebugPrint(message); + break; + } +} + +bool OpenGLHostDisplay::CreateGLContext() +{ + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + m_gl_context = SDL_GL_CreateContext(m_window); + if (!m_gl_context || SDL_GL_MakeCurrent(m_window, m_gl_context) != 0 || !gladLoadGL()) + { + Panic("Failed to create GL context"); + return false; + } + +#if 1 + if (GLAD_GL_KHR_debug) + { + glad_glDebugMessageCallbackKHR(GLDebugCallback, nullptr); + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + } +#endif + + SDL_GL_SetSwapInterval(0); + return true; +} + +bool OpenGLHostDisplay::CreateImGuiContext() +{ + if (!ImGui_ImplSDL2_InitForOpenGL(m_window, m_gl_context) || !ImGui_ImplOpenGL3_Init()) + return false; + + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL2_NewFrame(m_window); + return true; +} + +bool OpenGLHostDisplay::CreateGLResources() +{ + static constexpr char fullscreen_quad_vertex_shader[] = R"( +#version 330 core + +out vec2 v_tex0; + +void main() +{ + v_tex0 = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2)); + gl_Position = vec4(v_tex0 * vec2(2.0f, -2.0f) + vec2(-1.0f, 1.0f), 0.0f, 1.0f); + gl_Position.y = -gl_Position.y; +} +)"; + + static constexpr char display_fragment_shader[] = R"( +#version 330 core + +uniform sampler2D samp0; +uniform vec4 u_src_rect; + +in vec2 v_tex0; +out vec4 o_col0; + +void main() +{ + vec2 coords = u_src_rect.xy + v_tex0 * u_src_rect.zw; + o_col0 = texture(samp0, coords); +} +)"; + + if (!m_display_program.Compile(fullscreen_quad_vertex_shader, display_fragment_shader)) + return false; + + m_display_program.BindFragData(0, "o_col0"); + if (!m_display_program.Link()) + return false; + + m_display_program.Bind(); + m_display_program.RegisterUniform("u_src_rect"); + m_display_program.RegisterUniform("samp0"); + m_display_program.Uniform1i(1, 0); + + glGenVertexArrays(1, &m_display_vao); + + m_app_icon_texture = + std::make_unique(APP_ICON_WIDTH, APP_ICON_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, APP_ICON_DATA, true); + + // samplers + glGenSamplers(1, &m_display_nearest_sampler); + glSamplerParameteri(m_display_nearest_sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glSamplerParameteri(m_display_nearest_sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glGenSamplers(1, &m_display_linear_sampler); + glSamplerParameteri(m_display_linear_sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glSamplerParameteri(m_display_linear_sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + return true; +} + +std::unique_ptr OpenGLHostDisplay::Create(SDL_Window* window) +{ + std::unique_ptr display = std::make_unique(window); + if (!display->CreateGLContext() || !display->CreateImGuiContext() || !display->CreateGLResources()) + return nullptr; + + return display; +} + +void OpenGLHostDisplay::Render() +{ + glDisable(GL_SCISSOR_TEST); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + RenderDisplay(); + + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + SDL_GL_SwapWindow(m_window); + + ImGui_ImplSDL2_NewFrame(m_window); + ImGui_ImplOpenGL3_NewFrame(); + + GL::Program::ResetLastProgram(); +} + +void OpenGLHostDisplay::RenderDisplay() +{ + if (!m_display_texture_id) + return; + + // - 20 for main menu padding + const auto [vp_left, vp_top, vp_width, vp_height] = + CalculateDrawRect(m_window_width, std::max(m_window_height - 20, 1), m_display_aspect_ratio); + + glViewport(vp_left, m_window_height - (20 + vp_top) - vp_height, vp_width, vp_height); + glDisable(GL_BLEND); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDepthMask(GL_FALSE); + m_display_program.Bind(); + m_display_program.Uniform4f(0, static_cast(m_display_offset_x) / static_cast(m_display_texture_width), + static_cast(m_display_offset_y) / static_cast(m_display_texture_height), + static_cast(m_display_width) / static_cast(m_display_texture_width), + static_cast(m_display_height) / static_cast(m_display_texture_height)); + glBindTexture(GL_TEXTURE_2D, m_display_texture_id); + glBindSampler(0, m_display_linear_filtering ? m_display_linear_sampler : m_display_nearest_sampler); + glBindVertexArray(m_display_vao); + glDrawArrays(GL_TRIANGLES, 0, 3); + glBindSampler(0, 0); +} diff --git a/src/duckstation/opengl_host_display.h b/src/duckstation/opengl_host_display.h new file mode 100644 index 000000000..b1fc121d6 --- /dev/null +++ b/src/duckstation/opengl_host_display.h @@ -0,0 +1,64 @@ +#pragma once +#include "common/gl/program.h" +#include "common/gl/texture.h" +#include "core/host_display.h" +#include +#include + +class OpenGLHostDisplay final : public HostDisplay +{ +public: + OpenGLHostDisplay(SDL_Window* window); + ~OpenGLHostDisplay(); + + static std::unique_ptr Create(SDL_Window* window); + + RenderAPI GetRenderAPI() const override; + void* GetHostRenderDevice() const override; + void* GetHostRenderContext() const override; + + std::unique_ptr CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, + bool dynamic) override; + void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, + u32 data_stride) override; + + void SetDisplayTexture(void* texture, u32 offset_x, u32 offset_y, u32 width, u32 height, u32 texture_width, + u32 texture_height, float aspect_ratio) override; + void SetDisplayLinearFiltering(bool enabled) override; + + void SetVSync(bool enabled) override; + + std::tuple GetWindowSize() const override; + void WindowResized() override; + +private: + bool CreateGLContext(); + bool CreateImGuiContext(); + bool CreateGLResources(); + + void Render(); + void RenderDisplay(); + + SDL_Window* m_window = nullptr; + SDL_GLContext m_gl_context = nullptr; + int m_window_width = 0; + int m_window_height = 0; + + std::unique_ptr m_app_icon_texture = nullptr; + + GL::Program m_display_program; + GLuint m_display_vao = 0; + GLuint m_display_texture_id = 0; + u32 m_display_offset_x = 0; + u32 m_display_offset_y = 0; + u32 m_display_width = 0; + u32 m_display_height = 0; + u32 m_display_texture_width = 0; + u32 m_display_texture_height = 0; + float m_display_aspect_ratio = 1.0f; + GLuint m_display_nearest_sampler = 0; + GLuint m_display_linear_sampler = 0; + + bool m_display_texture_changed = false; + bool m_display_linear_filtering = false; +}; diff --git a/src/duckstation/sdl_interface.cpp b/src/duckstation/sdl_host_interface.cpp similarity index 75% rename from src/duckstation/sdl_interface.cpp rename to src/duckstation/sdl_host_interface.cpp index aaa8fa121..39457955b 100644 --- a/src/duckstation/sdl_interface.cpp +++ b/src/duckstation/sdl_host_interface.cpp @@ -1,4 +1,4 @@ -#include "sdl_interface.h" +#include "sdl_host_interface.h" #include "YBaseLib/ByteStream.h" #include "YBaseLib/Error.h" #include "YBaseLib/Log.h" @@ -6,62 +6,54 @@ #include "core/digital_controller.h" #include "core/dma.h" #include "core/gpu.h" +#include "core/host_display.h" #include "core/mdec.h" #include "core/memory_card.h" #include "core/spu.h" #include "core/system.h" #include "core/timers.h" +#ifdef Y_PLATFORM_WINDOWS +#include "d3d11_host_display.h" +#endif #include "icon.h" +#include "opengl_host_display.h" #include "sdl_audio_stream.h" #include -#include #include -#include #include #include -Log_SetChannel(SDLInterface); +Log_SetChannel(SDLHostInterface); -static constexpr std::array, 2> s_gpu_renderer_names = { - {{Settings::GPURenderer::HardwareOpenGL, "Hardware (OpenGL)"}, {Settings::GPURenderer::Software, "Software"}}}; +static constexpr std::array, 3> s_gpu_renderer_names = { + {{Settings::GPURenderer::HardwareD3D11, "Hardware (Direct3D 11)"}, + {Settings::GPURenderer::HardwareOpenGL, "Hardware (OpenGL)"}, + {Settings::GPURenderer::Software, "Software"}}}; -SDLInterface::SDLInterface() = default; +SDLHostInterface::SDLHostInterface() = default; -SDLInterface::~SDLInterface() +SDLHostInterface::~SDLHostInterface() { CloseGameControllers(); - - if (m_gl_context) - { - if (m_display_vao != 0) - glDeleteVertexArrays(1, &m_display_vao); - - m_display_program.Destroy(); - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplSDL2_Shutdown(); - ImGui::DestroyContext(); - SDL_GL_MakeCurrent(nullptr, nullptr); - SDL_GL_DeleteContext(m_gl_context); - } + m_display.reset(); + ImGui::DestroyContext(); if (m_window) SDL_DestroyWindow(m_window); } -bool SDLInterface::CreateSDLWindow() +bool SDLHostInterface::CreateSDLWindow() { constexpr u32 DEFAULT_WINDOW_WIDTH = 900; constexpr u32 DEFAULT_WINDOW_HEIGHT = 700; // Create window. - constexpr u32 window_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL; + const u32 window_flags = + SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | (UseOpenGLRenderer() ? SDL_WINDOW_OPENGL : 0); m_window = SDL_CreateWindow("DuckStation", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT, window_flags); if (!m_window) - { - Panic("Failed to create window"); return false; - } // Set window icon. SDL_Surface* icon_surface = @@ -74,133 +66,38 @@ bool SDLInterface::CreateSDLWindow() SDL_FreeSurface(icon_surface); } - SDL_GetWindowSize(m_window, &m_window_width, &m_window_height); return true; } -static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, - const GLchar* message, const void* userParam) +bool SDLHostInterface::CreateDisplay() { - switch (severity) - { - case GL_DEBUG_SEVERITY_HIGH_KHR: - Log_InfoPrint(message); - break; - case GL_DEBUG_SEVERITY_MEDIUM_KHR: - Log_WarningPrint(message); - break; - case GL_DEBUG_SEVERITY_LOW_KHR: - Log_InfoPrintf(message); - break; - case GL_DEBUG_SEVERITY_NOTIFICATION: - // Log_DebugPrint(message); - break; - } -} - -bool SDLInterface::CreateGLContext() -{ - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - m_gl_context = SDL_GL_CreateContext(m_window); - if (!m_gl_context || SDL_GL_MakeCurrent(m_window, m_gl_context) != 0 || !gladLoadGL()) - { - Panic("Failed to create GL context"); - return false; - } - -#if 0 - if (GLAD_GL_KHR_debug) - { - glad_glDebugMessageCallbackKHR(GLDebugCallback, nullptr); - glEnable(GL_DEBUG_OUTPUT); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - } +#ifdef Y_PLATFORM_WINDOWS + m_display = UseOpenGLRenderer() ? OpenGLHostDisplay::Create(m_window) : D3D11HostDisplay::Create(m_window); +#else + m_display = OpenGLHostDisplay::Create(m_window); #endif - SDL_GL_SetSwapInterval(0); + if (!m_display) + return false; + + m_app_icon_texture = + m_display->CreateTexture(APP_ICON_WIDTH, APP_ICON_HEIGHT, APP_ICON_DATA, APP_ICON_WIDTH * sizeof(u32)); + if (!m_app_icon_texture) + return false; + return true; } -bool SDLInterface::CreateImGuiContext() +bool SDLHostInterface::CreateImGuiContext() { ImGui::CreateContext(); ImGui::GetIO().IniFilename = nullptr; ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad; ImGui::GetIO().BackendFlags |= ImGuiBackendFlags_HasGamepad; - - if (!ImGui_ImplSDL2_InitForOpenGL(m_window, m_gl_context) || !ImGui_ImplOpenGL3_Init()) - return false; - - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplSDL2_NewFrame(m_window); - ImGui::NewFrame(); return true; } -bool SDLInterface::CreateGLResources() -{ - static constexpr char fullscreen_quad_vertex_shader[] = R"( -#version 330 core - -out vec2 v_tex0; - -void main() -{ - v_tex0 = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2)); - gl_Position = vec4(v_tex0 * vec2(2.0f, -2.0f) + vec2(-1.0f, 1.0f), 0.0f, 1.0f); - gl_Position.y = -gl_Position.y; -} -)"; - - static constexpr char display_fragment_shader[] = R"( -#version 330 core - -uniform sampler2D samp0; -uniform vec4 u_src_rect; - -in vec2 v_tex0; -out vec4 o_col0; - -void main() -{ - vec2 coords = u_src_rect.xy + v_tex0 * u_src_rect.zw; - o_col0 = texture(samp0, coords); -} -)"; - - if (!m_display_program.Compile(fullscreen_quad_vertex_shader, display_fragment_shader)) - return false; - - m_display_program.BindFragData(0, "o_col0"); - if (!m_display_program.Link()) - return false; - - m_display_program.Bind(); - m_display_program.RegisterUniform("u_src_rect"); - m_display_program.RegisterUniform("samp0"); - m_display_program.Uniform1i(1, 0); - - glGenVertexArrays(1, &m_display_vao); - - m_app_icon_texture = - std::make_unique(APP_ICON_WIDTH, APP_ICON_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, APP_ICON_DATA, true); - - // samplers - glGenSamplers(1, &m_display_nearest_sampler); - glSamplerParameteri(m_display_nearest_sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glSamplerParameteri(m_display_nearest_sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glGenSamplers(1, &m_display_linear_sampler); - glSamplerParameteri(m_display_linear_sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(m_display_linear_sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - return true; -} - -bool SDLInterface::CreateAudioStream() +bool SDLHostInterface::CreateAudioStream() { m_audio_stream = std::make_unique(); if (!m_audio_stream->Reconfigure(44100, 2)) @@ -212,7 +109,7 @@ bool SDLInterface::CreateAudioStream() return true; } -void SDLInterface::UpdateAudioVisualSync() +void SDLHostInterface::UpdateAudioVisualSync() { const bool speed_limiter_enabled = m_speed_limiter_enabled && !m_speed_limiter_temp_disabled; const bool audio_sync_enabled = speed_limiter_enabled; @@ -221,16 +118,10 @@ void SDLInterface::UpdateAudioVisualSync() (speed_limiter_enabled && vsync_enabled) ? " and video" : ""); m_audio_stream->SetSync(false); - - // Window framebuffer has to be bound to call SetSwapInterval. - GLint current_fbo = 0; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - SDL_GL_SetSwapInterval(vsync_enabled ? 1 : 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo); + m_display->SetVSync(vsync_enabled); } -void SDLInterface::OpenGameControllers() +void SDLHostInterface::OpenGameControllers() { for (int i = 0; i < SDL_NumJoysticks(); i++) { @@ -247,14 +138,14 @@ void SDLInterface::OpenGameControllers() } } -void SDLInterface::CloseGameControllers() +void SDLHostInterface::CloseGameControllers() { for (auto& it : m_sdl_controllers) SDL_GameControllerClose(it.second); m_sdl_controllers.clear(); } -bool SDLInterface::InitializeSystem(const char* filename, const char* exp1_filename) +bool SDLHostInterface::InitializeSystem(const char* filename, const char* exp1_filename) { if (!HostInterface::InitializeSystem(filename, exp1_filename)) { @@ -268,13 +159,13 @@ bool SDLInterface::InitializeSystem(const char* filename, const char* exp1_filen return true; } -void SDLInterface::ConnectDevices() +void SDLHostInterface::ConnectDevices() { m_controller = DigitalController::Create(); m_system->SetController(0, m_controller); } -void SDLInterface::ResetPerformanceCounters() +void SDLHostInterface::ResetPerformanceCounters() { if (m_system) { @@ -291,24 +182,22 @@ void SDLInterface::ResetPerformanceCounters() m_fps_timer.Reset(); } -void SDLInterface::ShutdownSystem() +void SDLHostInterface::ShutdownSystem() { m_system.reset(); m_paused = false; - m_display_texture = nullptr; UpdateAudioVisualSync(); } -std::unique_ptr SDLInterface::Create(const char* filename /* = nullptr */, - const char* exp1_filename /* = nullptr */, - const char* save_state_filename /* = nullptr */) +std::unique_ptr SDLHostInterface::Create(const char* filename /* = nullptr */, + const char* exp1_filename /* = nullptr */, + const char* save_state_filename /* = nullptr */) { - std::unique_ptr intf = std::make_unique(); - if (!intf->CreateSDLWindow() || !intf->CreateGLContext() || !intf->CreateImGuiContext() || - !intf->CreateGLResources() || !intf->CreateAudioStream()) - { + std::unique_ptr intf = std::make_unique(); + if (!intf->CreateSDLWindow() || !intf->CreateImGuiContext() || !intf->CreateDisplay() || !intf->CreateAudioStream()) return nullptr; - } + + ImGui::NewFrame(); intf->OpenGameControllers(); @@ -323,20 +212,26 @@ std::unique_ptr SDLInterface::Create(const char* filename /* = nul } intf->UpdateAudioVisualSync(); + return intf; } -TinyString SDLInterface::GetSaveStateFilename(u32 index) +TinyString SDLHostInterface::GetSaveStateFilename(u32 index) { return TinyString::FromFormat("savestate_%u.bin", index); } -void SDLInterface::ReportMessage(const char* message) +HostDisplay* SDLHostInterface::GetDisplay() const +{ + return m_display.get(); +} + +void SDLHostInterface::ReportMessage(const char* message) { AddOSDMessage(message, 3.0f); } -bool SDLInterface::IsWindowFullscreen() const +bool SDLHostInterface::IsWindowFullscreen() const { return ((SDL_GetWindowFlags(m_window) & SDL_WINDOW_FULLSCREEN) != 0); } @@ -491,7 +386,7 @@ static void HandleSDLControllerButtonEventForController(const SDL_Event* ev, Dig } } -void SDLInterface::HandleSDLEvent(const SDL_Event* event) +void SDLHostInterface::HandleSDLEvent(const SDL_Event* event) { ImGui_ImplSDL2_ProcessEvent(event); @@ -500,10 +395,7 @@ void SDLInterface::HandleSDLEvent(const SDL_Event* event) case SDL_WINDOWEVENT: { if (event->window.event == SDL_WINDOWEVENT_RESIZED) - { - m_window_width = event->window.data1; - m_window_height = event->window.data2; - } + m_display->WindowResized(); } break; @@ -569,7 +461,7 @@ void SDLInterface::HandleSDLEvent(const SDL_Event* event) } } -void SDLInterface::HandleSDLKeyEvent(const SDL_Event* event) +void SDLHostInterface::HandleSDLKeyEvent(const SDL_Event* event) { const bool repeat = event->key.repeat != 0; if (!repeat && m_controller && HandleSDLKeyEventForController(event, m_controller.get())) @@ -652,92 +544,12 @@ void SDLInterface::HandleSDLKeyEvent(const SDL_Event* event) } } -void SDLInterface::ClearImGuiFocus() +void SDLHostInterface::ClearImGuiFocus() { ImGui::SetWindowFocus(nullptr); } -void SDLInterface::Render() -{ - DrawImGui(); - - if (m_system) - m_system->GetGPU()->ResetGraphicsAPIState(); - - glDisable(GL_SCISSOR_TEST); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - - RenderDisplay(); - - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - - SDL_GL_SwapWindow(m_window); - - ImGui_ImplSDL2_NewFrame(m_window); - ImGui_ImplOpenGL3_NewFrame(); - - ImGui::NewFrame(); - - GL::Program::ResetLastProgram(); - - if (m_system) - m_system->GetGPU()->RestoreGraphicsAPIState(); -} - -static std::tuple CalculateDrawRect(int window_width, int window_height, float display_ratio) -{ - const float window_ratio = float(window_width) / float(window_height); - int left, top, width, height; - if (window_ratio >= display_ratio) - { - width = static_cast(float(window_height) * display_ratio); - height = static_cast(window_height); - left = (window_width - width) / 2; - top = 0; - } - else - { - width = static_cast(window_width); - height = static_cast(float(window_width) / display_ratio); - left = 0; - top = (window_height - height) / 2; - } - - return std::tie(left, top, width, height); -} - -void SDLInterface::RenderDisplay() -{ - if (!m_display_texture) - return; - - // - 20 for main menu padding - const auto [vp_left, vp_top, vp_width, vp_height] = - CalculateDrawRect(m_window_width, std::max(m_window_height - 20, 1), m_display_aspect_ratio); - const bool linear_filter = m_system ? m_system->GetSettings().display_linear_filtering : false; - - glViewport(vp_left, m_window_height - (20 + vp_top) - vp_height, vp_width, vp_height); - glDisable(GL_BLEND); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glDisable(GL_SCISSOR_TEST); - glDepthMask(GL_FALSE); - m_display_program.Bind(); - m_display_program.Uniform4f( - 0, static_cast(m_display_texture_offset_x) / static_cast(m_display_texture->GetWidth()), - static_cast(m_display_texture_offset_y) / static_cast(m_display_texture->GetHeight()), - static_cast(m_display_texture_width) / static_cast(m_display_texture->GetWidth()), - static_cast(m_display_texture_height) / static_cast(m_display_texture->GetHeight())); - m_display_texture->Bind(); - glBindSampler(0, linear_filter ? m_display_linear_sampler : m_display_nearest_sampler); - glBindVertexArray(m_display_vao); - glDrawArrays(GL_TRIANGLES, 0, 3); - glBindSampler(0, 0); -} - -void SDLInterface::DrawImGui() +void SDLHostInterface::DrawImGui() { DrawMainMenuBar(); @@ -754,7 +566,7 @@ void SDLInterface::DrawImGui() ImGui::Render(); } -void SDLInterface::DrawMainMenuBar() +void SDLHostInterface::DrawMainMenuBar() { if (!ImGui::BeginMainMenuBar()) return; @@ -936,7 +748,7 @@ void SDLInterface::DrawMainMenuBar() ImGui::EndMainMenuBar(); } -void SDLInterface::DrawPoweredOffWindow() +void SDLHostInterface::DrawPoweredOffWindow() { constexpr int WINDOW_WIDTH = 400; constexpr int WINDOW_HEIGHT = 650; @@ -956,8 +768,7 @@ void SDLInterface::DrawPoweredOffWindow() } ImGui::SetCursorPosX((WINDOW_WIDTH - APP_ICON_WIDTH) / 2); - ImGui::Image(reinterpret_cast(static_cast(m_app_icon_texture->GetGLId())), - ImVec2(APP_ICON_WIDTH, APP_ICON_HEIGHT)); + ImGui::Image(m_app_icon_texture->GetHandle(), ImVec2(APP_ICON_WIDTH, APP_ICON_HEIGHT)); ImGui::SetCursorPosY(APP_ICON_HEIGHT + 32); static const ImVec2 button_size(static_cast(BUTTON_WIDTH), static_cast(BUTTON_HEIGHT)); @@ -1014,7 +825,7 @@ void SDLInterface::DrawPoweredOffWindow() ImGui::End(); } -void SDLInterface::DrawAboutWindow() +void SDLHostInterface::DrawAboutWindow() { ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x * 0.5f, ImGui::GetIO().DisplaySize.y * 0.5f), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); @@ -1044,7 +855,7 @@ void SDLInterface::DrawAboutWindow() ImGui::End(); } -void SDLInterface::DrawDebugMenu() +void SDLHostInterface::DrawDebugMenu() { if (!ImGui::BeginMenu("Debug", m_system != nullptr)) return; @@ -1076,7 +887,7 @@ void SDLInterface::DrawDebugMenu() ImGui::EndMenu(); } -void SDLInterface::DrawDebugWindows() +void SDLHostInterface::DrawDebugWindows() { const Settings::DebugSettings& debug_settings = m_system->GetSettings().debugging; @@ -1094,7 +905,7 @@ void SDLInterface::DrawDebugWindows() m_system->GetMDEC()->DrawDebugStateWindow(); } -void SDLInterface::AddOSDMessage(const char* message, float duration /*= 2.0f*/) +void SDLHostInterface::AddOSDMessage(const char* message, float duration /*= 2.0f*/) { OSDMessage msg; msg.text = message; @@ -1104,19 +915,7 @@ void SDLInterface::AddOSDMessage(const char* message, float duration /*= 2.0f*/) m_osd_messages.push_back(std::move(msg)); } -void SDLInterface::SetDisplayTexture(GL::Texture* texture, u32 offset_x, u32 offset_y, u32 width, u32 height, - float aspect_ratio) -{ - m_display_texture = texture; - m_display_texture_offset_x = offset_x; - m_display_texture_offset_y = offset_y; - m_display_texture_width = width; - m_display_texture_height = height; - m_display_aspect_ratio = aspect_ratio; - m_display_texture_changed = true; -} - -void SDLInterface::DrawOSDMessages() +void SDLHostInterface::DrawOSDMessages() { constexpr ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | @@ -1157,21 +956,21 @@ void SDLInterface::DrawOSDMessages() } } -void SDLInterface::DoReset() +void SDLHostInterface::DoReset() { m_system->Reset(); ResetPerformanceCounters(); AddOSDMessage("System reset."); } -void SDLInterface::DoPowerOff() +void SDLHostInterface::DoPowerOff() { Assert(m_system); ShutdownSystem(); AddOSDMessage("System powered off."); } -void SDLInterface::DoResume() +void SDLHostInterface::DoResume() { Assert(!m_system); if (!InitializeSystem()) @@ -1189,7 +988,7 @@ void SDLInterface::DoResume() ClearImGuiFocus(); } -void SDLInterface::DoStartDisc() +void SDLHostInterface::DoStartDisc() { Assert(!m_system); @@ -1205,7 +1004,7 @@ void SDLInterface::DoStartDisc() ClearImGuiFocus(); } -void SDLInterface::DoStartBIOS() +void SDLHostInterface::DoStartBIOS() { Assert(!m_system); @@ -1217,7 +1016,7 @@ void SDLInterface::DoStartBIOS() ClearImGuiFocus(); } -void SDLInterface::DoChangeDisc() +void SDLHostInterface::DoChangeDisc() { Assert(m_system); @@ -1234,7 +1033,7 @@ void SDLInterface::DoChangeDisc() ClearImGuiFocus(); } -void SDLInterface::DoLoadState(u32 index) +void SDLHostInterface::DoLoadState(u32 index) { if (!HasSystem() && !InitializeSystem(nullptr, nullptr)) return; @@ -1244,14 +1043,14 @@ void SDLInterface::DoLoadState(u32 index) ClearImGuiFocus(); } -void SDLInterface::DoSaveState(u32 index) +void SDLHostInterface::DoSaveState(u32 index) { Assert(m_system); SaveState(GetSaveStateFilename(index)); ClearImGuiFocus(); } -void SDLInterface::DoTogglePause() +void SDLHostInterface::DoTogglePause() { if (!m_system) return; @@ -1261,7 +1060,7 @@ void SDLInterface::DoTogglePause() m_fps_timer.Reset(); } -void SDLInterface::DoFrameStep() +void SDLHostInterface::DoFrameStep() { if (!m_system) return; @@ -1270,7 +1069,7 @@ void SDLInterface::DoFrameStep() m_paused = false; } -void SDLInterface::DoToggleSoftwareRendering() +void SDLHostInterface::DoToggleSoftwareRendering() { if (!m_system) return; @@ -1283,14 +1082,16 @@ void SDLInterface::DoToggleSoftwareRendering() } else { - settings.gpu_renderer = Settings::GPURenderer::HardwareOpenGL; + settings.gpu_renderer = m_display->GetRenderAPI() == HostDisplay::RenderAPI::D3D11 ? + Settings::GPURenderer::HardwareD3D11 : + Settings::GPURenderer::HardwareOpenGL; AddOSDMessage("Switched to hardware GPU renderer."); } m_system->RecreateGPU(); } -void SDLInterface::DoModifyInternalResolution(s32 increment) +void SDLHostInterface::DoModifyInternalResolution(s32 increment) { if (!m_system) return; @@ -1310,7 +1111,7 @@ void SDLInterface::DoModifyInternalResolution(s32 increment) GPU::VRAM_HEIGHT * settings.gpu_resolution_scale)); } -void SDLInterface::Run() +void SDLHostInterface::Run() { m_audio_stream->PauseOutput(false); @@ -1335,7 +1136,21 @@ void SDLInterface::Run() } } - Render(); + // rendering + { + DrawImGui(); + + if (m_system) + m_system->GetGPU()->ResetGraphicsAPIState(); + + ImGui::Render(); + m_display->Render(); + + ImGui::NewFrame(); + + if (m_system) + m_system->GetGPU()->RestoreGraphicsAPIState(); + } if (m_system) { diff --git a/src/duckstation/sdl_interface.h b/src/duckstation/sdl_host_interface.h similarity index 72% rename from src/duckstation/sdl_interface.h rename to src/duckstation/sdl_host_interface.h index 7187c920a..acab84d16 100644 --- a/src/duckstation/sdl_interface.h +++ b/src/duckstation/sdl_host_interface.h @@ -4,6 +4,7 @@ #include "common/gl/program.h" #include "common/gl/texture.h" #include "core/host_interface.h" +#include "core/host_display.h" #include #include #include @@ -16,19 +17,18 @@ class DigitalController; class MemoryCard; class AudioStream; -class SDLInterface : public HostInterface +class SDLHostInterface : public HostInterface { public: - SDLInterface(); - ~SDLInterface(); + SDLHostInterface(); + ~SDLHostInterface(); - static std::unique_ptr Create(const char* filename = nullptr, const char* exp1_filename = nullptr, + static std::unique_ptr Create(const char* filename = nullptr, const char* exp1_filename = nullptr, const char* save_state_filename = nullptr); static TinyString GetSaveStateFilename(u32 index); - void SetDisplayTexture(GL::Texture* texture, u32 offset_x, u32 offset_y, u32 width, u32 height, - float aspect_ratio) override; + HostDisplay* GetDisplay() const override; void ReportMessage(const char* message) override; @@ -50,10 +50,15 @@ private: bool HasSystem() const { return static_cast(m_system); } +#ifdef Y_PLATFORM_WINDOWS + bool UseOpenGLRenderer() const { return m_settings.gpu_renderer == Settings::GPURenderer::HardwareOpenGL; } +#else + bool UseOpenGLRenderer() const { return true; } +#endif + bool CreateSDLWindow(); - bool CreateGLContext(); + bool CreateDisplay(); bool CreateImGuiContext(); - bool CreateGLResources(); bool CreateAudioStream(); void UpdateAudioVisualSync(); @@ -85,8 +90,6 @@ private: void HandleSDLKeyEvent(const SDL_Event* event); void ClearImGuiFocus(); - void Render(); - void RenderDisplay(); void DrawMainMenuBar(); void DrawPoweredOffWindow(); void DrawAboutWindow(); @@ -95,23 +98,8 @@ private: void DrawDebugWindows(); SDL_Window* m_window = nullptr; - SDL_GLContext m_gl_context = nullptr; - int m_window_width = 0; - int m_window_height = 0; - - std::unique_ptr m_app_icon_texture = nullptr; - - GL::Program m_display_program; - GLuint m_display_vao = 0; - GL::Texture* m_display_texture = nullptr; - u32 m_display_texture_offset_x = 0; - u32 m_display_texture_offset_y = 0; - u32 m_display_texture_width = 0; - u32 m_display_texture_height = 0; - float m_display_aspect_ratio = 1.0f; - bool m_display_texture_changed = false; - GLuint m_display_nearest_sampler = false; - GLuint m_display_linear_sampler = false; + std::unique_ptr m_display; + std::unique_ptr m_app_icon_texture; std::deque m_osd_messages; std::mutex m_osd_messages_lock;