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;