mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 15:45:42 +00:00
GPU/HW: Add D3D11 renderer and refactor host interface/display
This commit is contained in:
parent
f244da86a2
commit
9de9cf3be2
|
@ -55,5 +55,11 @@ add_library(core
|
||||||
|
|
||||||
target_include_directories(core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
target_include_directories(core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||||
target_include_directories(core PUBLIC "${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()
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
<ClCompile Include="cpu_disasm.cpp" />
|
<ClCompile Include="cpu_disasm.cpp" />
|
||||||
<ClCompile Include="digital_controller.cpp" />
|
<ClCompile Include="digital_controller.cpp" />
|
||||||
<ClCompile Include="gpu_commands.cpp" />
|
<ClCompile Include="gpu_commands.cpp" />
|
||||||
|
<ClCompile Include="gpu_hw_d3d11.cpp" />
|
||||||
<ClCompile Include="gpu_hw_shadergen.cpp" />
|
<ClCompile Include="gpu_hw_shadergen.cpp" />
|
||||||
<ClCompile Include="gpu_sw.cpp" />
|
<ClCompile Include="gpu_sw.cpp" />
|
||||||
<ClCompile Include="gte.cpp" />
|
<ClCompile Include="gte.cpp" />
|
||||||
|
@ -65,6 +66,7 @@
|
||||||
<ClInclude Include="cpu_core.h" />
|
<ClInclude Include="cpu_core.h" />
|
||||||
<ClInclude Include="cpu_disasm.h" />
|
<ClInclude Include="cpu_disasm.h" />
|
||||||
<ClInclude Include="digital_controller.h" />
|
<ClInclude Include="digital_controller.h" />
|
||||||
|
<ClInclude Include="gpu_hw_d3d11.h" />
|
||||||
<ClInclude Include="gpu_hw_shadergen.h" />
|
<ClInclude Include="gpu_hw_shadergen.h" />
|
||||||
<ClInclude Include="gpu_sw.h" />
|
<ClInclude Include="gpu_sw.h" />
|
||||||
<ClInclude Include="gte.h" />
|
<ClInclude Include="gte.h" />
|
||||||
|
@ -74,6 +76,7 @@
|
||||||
<ClInclude Include="gpu_hw.h" />
|
<ClInclude Include="gpu_hw.h" />
|
||||||
<ClInclude Include="gpu_hw_opengl.h" />
|
<ClInclude Include="gpu_hw_opengl.h" />
|
||||||
<ClInclude Include="gte_types.h" />
|
<ClInclude Include="gte_types.h" />
|
||||||
|
<ClInclude Include="host_display.h" />
|
||||||
<ClInclude Include="host_interface.h" />
|
<ClInclude Include="host_interface.h" />
|
||||||
<ClInclude Include="interrupt_controller.h" />
|
<ClInclude Include="interrupt_controller.h" />
|
||||||
<ClInclude Include="mdec.h" />
|
<ClInclude Include="mdec.h" />
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
<ClCompile Include="gpu_commands.cpp" />
|
<ClCompile Include="gpu_commands.cpp" />
|
||||||
<ClCompile Include="gpu_sw.cpp" />
|
<ClCompile Include="gpu_sw.cpp" />
|
||||||
<ClCompile Include="gpu_hw_shadergen.cpp" />
|
<ClCompile Include="gpu_hw_shadergen.cpp" />
|
||||||
|
<ClCompile Include="gpu_hw_d3d11.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="types.h" />
|
<ClInclude Include="types.h" />
|
||||||
|
@ -52,6 +53,8 @@
|
||||||
<ClInclude Include="settings.h" />
|
<ClInclude Include="settings.h" />
|
||||||
<ClInclude Include="gpu_sw.h" />
|
<ClInclude Include="gpu_sw.h" />
|
||||||
<ClInclude Include="gpu_hw_shadergen.h" />
|
<ClInclude Include="gpu_hw_shadergen.h" />
|
||||||
|
<ClInclude Include="gpu_hw_d3d11.h" />
|
||||||
|
<ClInclude Include="host_display.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="cpu_core.inl" />
|
<None Include="cpu_core.inl" />
|
||||||
|
|
|
@ -17,8 +17,10 @@ GPU::GPU() = default;
|
||||||
|
|
||||||
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_system = system;
|
||||||
m_dma = dma;
|
m_dma = dma;
|
||||||
m_interrupt_controller = interrupt_controller;
|
m_interrupt_controller = interrupt_controller;
|
||||||
|
@ -26,10 +28,7 @@ bool GPU::Initialize(System* system, DMA* dma, InterruptController* interrupt_co
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::UpdateSettings()
|
void GPU::UpdateSettings() {}
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPU::Reset()
|
void GPU::Reset()
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
class StateWrapper;
|
class StateWrapper;
|
||||||
|
|
||||||
|
class HostDisplay;
|
||||||
|
|
||||||
class System;
|
class System;
|
||||||
class DMA;
|
class DMA;
|
||||||
class InterruptController;
|
class InterruptController;
|
||||||
|
@ -90,7 +92,8 @@ public:
|
||||||
GPU();
|
GPU();
|
||||||
virtual ~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 void Reset();
|
||||||
virtual bool DoState(StateWrapper& sw);
|
virtual bool DoState(StateWrapper& sw);
|
||||||
|
|
||||||
|
@ -116,6 +119,9 @@ public:
|
||||||
// Ticks for hblank/vblank.
|
// Ticks for hblank/vblank.
|
||||||
void Execute(TickCount ticks);
|
void Execute(TickCount ticks);
|
||||||
|
|
||||||
|
// gpu_hw_d3d11.cpp
|
||||||
|
static std::unique_ptr<GPU> CreateHardwareD3D11Renderer();
|
||||||
|
|
||||||
// gpu_hw_opengl.cpp
|
// gpu_hw_opengl.cpp
|
||||||
static std::unique_ptr<GPU> CreateHardwareOpenGLRenderer();
|
static std::unique_ptr<GPU> CreateHardwareOpenGLRenderer();
|
||||||
|
|
||||||
|
@ -292,6 +298,7 @@ protected:
|
||||||
virtual void DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32* command_ptr);
|
virtual void DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32* command_ptr);
|
||||||
virtual void FlushRender();
|
virtual void FlushRender();
|
||||||
|
|
||||||
|
HostDisplay* m_host_display = nullptr;
|
||||||
System* m_system = nullptr;
|
System* m_system = nullptr;
|
||||||
DMA* m_dma = nullptr;
|
DMA* m_dma = nullptr;
|
||||||
InterruptController* m_interrupt_controller = nullptr;
|
InterruptController* m_interrupt_controller = nullptr;
|
||||||
|
|
|
@ -19,9 +19,10 @@ void GPU_HW::Reset()
|
||||||
m_batch_ubo_dirty = true;
|
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;
|
return false;
|
||||||
|
|
||||||
m_resolution_scale = std::clamp<u32>(m_system->GetSettings().gpu_resolution_scale, 1, m_max_resolution_scale);
|
m_resolution_scale = std::clamp<u32>(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;
|
rc.transparency_enable ? m_render_state.transparency_mode : TransparencyMode::Disabled;
|
||||||
const BatchPrimitive rc_primitive = GetPrimitiveForCommand(rc);
|
const BatchPrimitive rc_primitive = GetPrimitiveForCommand(rc);
|
||||||
const bool dithering_enable = (!m_true_color && rc.IsDitheringEnabled()) ? m_GPUSTAT.dither_enable : false;
|
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())
|
if (!IsFlushed())
|
||||||
{
|
{
|
||||||
const bool buffer_overflow = GetBatchVertexSpace() < max_added_vertices;
|
const bool buffer_overflow = GetBatchVertexSpace() < max_added_vertices;
|
||||||
|
|
|
@ -27,7 +27,8 @@ public:
|
||||||
GPU_HW();
|
GPU_HW();
|
||||||
virtual ~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 Reset() override;
|
||||||
virtual void UpdateSettings() override;
|
virtual void UpdateSettings() override;
|
||||||
|
|
||||||
|
|
731
src/core/gpu_hw_d3d11.cpp
Normal file
731
src/core/gpu_hw_d3d11.cpp
Normal file
|
@ -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<ID3D11Device*>(host_display->GetHostRenderDevice());
|
||||||
|
m_context = static_cast<ID3D11DeviceContext*>(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<BatchVertex*>(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<float, 4> 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<D3D11_INPUT_ELEMENT_DESC, 4> 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<ID3DBlob> 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<UINT>(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<u8>(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<u8>(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<BatchRenderMode>(render_mode),
|
||||||
|
static_cast<TextureMode>(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<float>(x), static_cast<float>(y), static_cast<float>(width),
|
||||||
|
static_cast<float>(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<float>(src_x) / static_cast<float>(src_texture_width),
|
||||||
|
static_cast<float>(src_y) / static_cast<float>(src_texture_height),
|
||||||
|
static_cast<float>(src_width) / static_cast<float>(src_texture_width),
|
||||||
|
static_cast<float>(src_height) / static_cast<float>(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<D3D11_PRIMITIVE_TOPOLOGY, 4> 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<u8>(m_batch.primitive)]);
|
||||||
|
|
||||||
|
m_context->VSSetShader(m_batch_vertex_shaders[BoolToUInt8(textured)].Get(), nullptr, 0);
|
||||||
|
|
||||||
|
m_context->PSSetShader(m_batch_pixel_shaders[static_cast<u8>(render_mode)][static_cast<u8>(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<u8>(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<u32>(m_crtc_state.display_width, VRAM_WIDTH - vram_offset_x);
|
||||||
|
const u32 display_height = std::min<u32>(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<u32>((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> GPU::CreateHardwareD3D11Renderer()
|
||||||
|
{
|
||||||
|
return std::make_unique<GPU_HW_D3D11>();
|
||||||
|
}
|
122
src/core/gpu_hw_d3d11.h
Normal file
122
src/core/gpu_hw_d3d11.h
Normal file
|
@ -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 <array>
|
||||||
|
#include <d3d11.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <tuple>
|
||||||
|
#include <wrl/client.h>
|
||||||
|
|
||||||
|
class GPU_HW_D3D11 : public GPU_HW
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template<typename T>
|
||||||
|
using ComPtr = Microsoft::WRL::ComPtr<T>;
|
||||||
|
|
||||||
|
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<ID3D11Device> m_device;
|
||||||
|
ComPtr<ID3D11DeviceContext> 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<ID3D11ShaderResourceView> m_texture_stream_buffer_srv_r16ui;
|
||||||
|
|
||||||
|
ComPtr<ID3D11RasterizerState> m_cull_none_rasterizer_state;
|
||||||
|
|
||||||
|
ComPtr<ID3D11DepthStencilState> m_depth_disabled_state;
|
||||||
|
|
||||||
|
ComPtr<ID3D11BlendState> m_blend_disabled_state;
|
||||||
|
|
||||||
|
ComPtr<ID3D11SamplerState> m_point_sampler_state;
|
||||||
|
ComPtr<ID3D11SamplerState> m_linear_sampler_state;
|
||||||
|
|
||||||
|
std::array<ComPtr<ID3D11BlendState>, 5> m_batch_blend_states; // [transparency_mode]
|
||||||
|
ComPtr<ID3D11InputLayout> m_batch_input_layout;
|
||||||
|
std::array<ComPtr<ID3D11VertexShader>, 2> m_batch_vertex_shaders; // [textured]
|
||||||
|
std::array<std::array<std::array<ComPtr<ID3D11PixelShader>, 2>, 9>, 4>
|
||||||
|
m_batch_pixel_shaders; // [render_mode][texture_mode][dithering]
|
||||||
|
|
||||||
|
ComPtr<ID3D11VertexShader> m_screen_quad_vertex_shader;
|
||||||
|
ComPtr<ID3D11PixelShader> m_copy_pixel_shader;
|
||||||
|
ComPtr<ID3D11PixelShader> m_fill_pixel_shader;
|
||||||
|
ComPtr<ID3D11PixelShader> m_vram_write_pixel_shader;
|
||||||
|
std::array<std::array<ComPtr<ID3D11PixelShader>, 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;
|
||||||
|
};
|
|
@ -3,7 +3,7 @@
|
||||||
#include "YBaseLib/Log.h"
|
#include "YBaseLib/Log.h"
|
||||||
#include "YBaseLib/String.h"
|
#include "YBaseLib/String.h"
|
||||||
#include "gpu_hw_shadergen.h"
|
#include "gpu_hw_shadergen.h"
|
||||||
#include "host_interface.h"
|
#include "host_display.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
Log_SetChannel(GPU_HW_OpenGL);
|
Log_SetChannel(GPU_HW_OpenGL);
|
||||||
|
@ -12,16 +12,24 @@ GPU_HW_OpenGL::GPU_HW_OpenGL() : GPU_HW() {}
|
||||||
|
|
||||||
GPU_HW_OpenGL::~GPU_HW_OpenGL()
|
GPU_HW_OpenGL::~GPU_HW_OpenGL()
|
||||||
{
|
{
|
||||||
|
m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, 1.0f);
|
||||||
DestroyFramebuffer();
|
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();
|
SetCapabilities();
|
||||||
|
|
||||||
if (!GPU_HW::Initialize(system, dma, interrupt_controller, timers))
|
if (!GPU_HW::Initialize(host_display, system, dma, interrupt_controller, timers))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (m_host_display->GetRenderAPI() != HostDisplay::RenderAPI::OpenGL)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Host render API type is incompatible");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
CreateFramebuffer();
|
CreateFramebuffer();
|
||||||
CreateVertexBuffer();
|
CreateVertexBuffer();
|
||||||
CreateUniformBuffer();
|
CreateUniformBuffer();
|
||||||
|
@ -29,7 +37,9 @@ bool GPU_HW_OpenGL::Initialize(System* system, DMA* dma, InterruptController* in
|
||||||
if (!CompilePrograms())
|
if (!CompilePrograms())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_system->GetHostInterface()->SetDisplayTexture(m_display_texture.get(), 0, 0, VRAM_WIDTH, VRAM_HEIGHT, 1.0f);
|
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(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();
|
RestoreGraphicsAPIState();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -250,7 +260,7 @@ void GPU_HW_OpenGL::CreateVertexBuffer()
|
||||||
glVertexAttribIPointer(0, 2, GL_INT, sizeof(BatchVertex), reinterpret_cast<void*>(offsetof(BatchVertex, x)));
|
glVertexAttribIPointer(0, 2, GL_INT, sizeof(BatchVertex), reinterpret_cast<void*>(offsetof(BatchVertex, x)));
|
||||||
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(BatchVertex),
|
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(BatchVertex),
|
||||||
reinterpret_cast<void*>(offsetof(BatchVertex, color)));
|
reinterpret_cast<void*>(offsetof(BatchVertex, color)));
|
||||||
glVertexAttribIPointer(2, 2, GL_INT, sizeof(BatchVertex), reinterpret_cast<void*>(offsetof(BatchVertex, texcoord)));
|
glVertexAttribIPointer(2, 1, GL_INT, sizeof(BatchVertex), reinterpret_cast<void*>(offsetof(BatchVertex, texcoord)));
|
||||||
glVertexAttribIPointer(3, 1, GL_INT, sizeof(BatchVertex), reinterpret_cast<void*>(offsetof(BatchVertex, texpage)));
|
glVertexAttribIPointer(3, 1, GL_INT, sizeof(BatchVertex), reinterpret_cast<void*>(offsetof(BatchVertex, texpage)));
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
@ -429,8 +439,9 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
||||||
|
|
||||||
if (m_system->GetSettings().debugging.show_vram)
|
if (m_system->GetSettings().debugging.show_vram)
|
||||||
{
|
{
|
||||||
m_system->GetHostInterface()->SetDisplayTexture(m_vram_texture.get(), 0, 0, m_vram_texture->GetWidth(),
|
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture->GetGLId())), 0, 0,
|
||||||
m_vram_texture->GetHeight(), 1.0f);
|
m_vram_texture->GetWidth(), m_vram_texture->GetHeight(),
|
||||||
|
m_vram_texture->GetWidth(), m_vram_texture->GetHeight(), 1.0f);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -448,7 +459,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
||||||
|
|
||||||
if (m_GPUSTAT.display_disable)
|
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)
|
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_flipped_vram_offset_y, 0, m_display_texture->GetGLId(), GL_TEXTURE_2D, 0, 0, 0, 0,
|
||||||
scaled_display_width, scaled_display_height, 1);
|
scaled_display_width, scaled_display_height, 1);
|
||||||
|
|
||||||
m_system->GetHostInterface()->SetDisplayTexture(m_display_texture.get(), 0, 0, scaled_display_width,
|
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture->GetGLId())), 0,
|
||||||
scaled_display_height, m_crtc_state.display_aspect_ratio);
|
0, scaled_display_width, scaled_display_height, m_display_texture->GetWidth(),
|
||||||
|
m_display_texture->GetHeight(), m_crtc_state.display_aspect_ratio);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -495,8 +507,9 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
||||||
|
|
||||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||||
|
|
||||||
m_system->GetHostInterface()->SetDisplayTexture(m_display_texture.get(), 0, 0, display_width, display_height,
|
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture->GetGLId())), 0,
|
||||||
m_crtc_state.display_aspect_ratio);
|
0, display_width, display_height, m_display_texture->GetWidth(),
|
||||||
|
m_display_texture->GetHeight(), m_crtc_state.display_aspect_ratio);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -511,8 +524,9 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
||||||
|
|
||||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||||
|
|
||||||
m_system->GetHostInterface()->SetDisplayTexture(m_display_texture.get(), 0, 0, scaled_display_width,
|
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture->GetGLId())), 0,
|
||||||
scaled_display_height, m_crtc_state.display_aspect_ratio);
|
0, scaled_display_width, scaled_display_height, m_display_texture->GetWidth(),
|
||||||
|
m_display_texture->GetHeight(), m_crtc_state.display_aspect_ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore state
|
// 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();
|
m_vram_write_program.Bind();
|
||||||
glBindTexture(GL_TEXTURE_BUFFER, m_texture_buffer_r16ui_texture);
|
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));
|
UploadUniformBlock(uniforms, sizeof(uniforms));
|
||||||
m_batch_ubo_dirty = true;
|
m_batch_ubo_dirty = true;
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,8 @@ public:
|
||||||
GPU_HW_OpenGL();
|
GPU_HW_OpenGL();
|
||||||
~GPU_HW_OpenGL() override;
|
~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 Reset() override;
|
||||||
|
|
||||||
void ResetGraphicsAPIState() override;
|
void ResetGraphicsAPIState() override;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include "gpu_hw_shadergen.h"
|
#include "gpu_hw_shadergen.h"
|
||||||
|
|
||||||
GPU_HW_ShaderGen::GPU_HW_ShaderGen(API backend, u32 resolution_scale, bool true_color)
|
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 << "#version 330 core\n\n";
|
||||||
ss << "#define API_OPENGL 1\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)
|
if (m_glsl)
|
||||||
|
@ -52,7 +52,7 @@ void GPU_HW_ShaderGen::WriteHeader(std::stringstream& ss)
|
||||||
ss << "#define CONSTANT static const\n";
|
ss << "#define CONSTANT static const\n";
|
||||||
ss << "#define SAMPLE_TEXTURE(name, coords) name.Sample(name##_ss, coords)\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(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";
|
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,
|
void GPU_HW_ShaderGen::DeclareVertexEntryPoint(std::stringstream& ss,
|
||||||
const std::initializer_list<const char*>& attributes,
|
const std::initializer_list<const char*>& attributes,
|
||||||
u32 num_color_outputs, u32 num_texcoord_outputs,
|
u32 num_color_outputs, u32 num_texcoord_outputs,
|
||||||
const std::initializer_list<const char*>& additional_outputs)
|
const std::initializer_list<const char*>& additional_outputs,
|
||||||
|
bool declare_vertex_id)
|
||||||
{
|
{
|
||||||
if (m_glsl)
|
if (m_glsl)
|
||||||
{
|
{
|
||||||
|
@ -169,6 +170,10 @@ void GPU_HW_ShaderGen::DeclareVertexEntryPoint(std::stringstream& ss,
|
||||||
ss << output << ";\n";
|
ss << output << ";\n";
|
||||||
|
|
||||||
ss << "#define v_pos gl_Position\n\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";
|
ss << "void main()\n";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -182,6 +187,9 @@ void GPU_HW_ShaderGen::DeclareVertexEntryPoint(std::stringstream& ss,
|
||||||
attribute_counter++;
|
attribute_counter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (declare_vertex_id)
|
||||||
|
ss << " in uint v_id : SV_VertexID,\n";
|
||||||
|
|
||||||
for (u32 i = 0; i < num_color_outputs; i++)
|
for (u32 i = 0; i < num_color_outputs; i++)
|
||||||
ss << " out float4 v_col" << i << " : COLOR" << i << ",\n";
|
ss << " out float4 v_col" << i << " : COLOR" << i << ",\n";
|
||||||
|
|
||||||
|
@ -498,12 +506,14 @@ std::string GPU_HW_ShaderGen::GenerateScreenQuadVertexShader()
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
WriteHeader(ss);
|
WriteHeader(ss);
|
||||||
DeclareVertexEntryPoint(ss, {}, 0, 1, {});
|
DeclareVertexEntryPoint(ss, {}, 0, 1, {}, true);
|
||||||
ss << R"(
|
ss << R"(
|
||||||
{
|
{
|
||||||
v_tex0 = float2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
|
v_tex0 = float2(float((v_id << 1) & 2u), float(v_id & 2u));
|
||||||
gl_Position = float4(v_tex0 * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);
|
v_pos = float4(v_tex0 * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);
|
||||||
gl_Position.y = -gl_Position.y;
|
#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"});
|
DeclareUniformBuffer(ss, {"float4 u_fill_color"});
|
||||||
DeclareFragmentEntryPoint(ss, 0, 1, {}, false, false);
|
DeclareFragmentEntryPoint(ss, 0, 1, {}, false, false);
|
||||||
|
|
||||||
ss << R"({
|
ss << R"(
|
||||||
|
{
|
||||||
o_col0 = u_fill_color;
|
o_col0 = u_fill_color;
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
@ -525,6 +536,24 @@ std::string GPU_HW_ShaderGen::GenerateFillFragmentShader()
|
||||||
return ss.str();
|
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::string GPU_HW_ShaderGen::GenerateDisplayFragmentShader(bool depth_24bit, bool interlaced)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
@ -603,7 +632,7 @@ std::string GPU_HW_ShaderGen::GenerateVRAMWriteFragmentShader()
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
WriteHeader(ss);
|
WriteHeader(ss);
|
||||||
WriteCommonFunctions(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);
|
DeclareTextureBuffer(ss, "samp0", 0, true, true);
|
||||||
DeclareFragmentEntryPoint(ss, 0, 1, {}, true, false);
|
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 coords = int2(v_pos.xy) / int2(RESOLUTION_SCALE, RESOLUTION_SCALE);
|
||||||
int2 offset = coords - u_base_coords;
|
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;
|
uint value = LOAD_TEXTURE_BUFFER(samp0, buffer_offset).r;
|
||||||
|
|
||||||
o_col0 = RGBA5551ToRGBA8(value);
|
o_col0 = RGBA5551ToRGBA8(value);
|
||||||
|
|
|
@ -9,7 +9,7 @@ public:
|
||||||
enum class API
|
enum class API
|
||||||
{
|
{
|
||||||
OpenGL,
|
OpenGL,
|
||||||
Direct3D
|
D3D11
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -23,6 +23,7 @@ public:
|
||||||
bool dithering);
|
bool dithering);
|
||||||
std::string GenerateScreenQuadVertexShader();
|
std::string GenerateScreenQuadVertexShader();
|
||||||
std::string GenerateFillFragmentShader();
|
std::string GenerateFillFragmentShader();
|
||||||
|
std::string GenerateCopyFragmentShader();
|
||||||
std::string GenerateDisplayFragmentShader(bool depth_24bit, bool interlaced);
|
std::string GenerateDisplayFragmentShader(bool depth_24bit, bool interlaced);
|
||||||
std::string GenerateVRAMWriteFragmentShader();
|
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 DeclareTextureBuffer(std::stringstream& ss, const char* name, u32 index, bool is_int, bool is_unsigned);
|
||||||
void DeclareVertexEntryPoint(std::stringstream& ss, const std::initializer_list<const char*>& attributes,
|
void DeclareVertexEntryPoint(std::stringstream& ss, const std::initializer_list<const char*>& attributes,
|
||||||
u32 num_color_outputs, u32 num_texcoord_outputs,
|
u32 num_color_outputs, u32 num_texcoord_outputs,
|
||||||
const std::initializer_list<const char*>& additional_outputs);
|
const std::initializer_list<const char*>& additional_outputs,
|
||||||
|
bool declare_vertex_id = false);
|
||||||
void DeclareFragmentEntryPoint(std::stringstream& ss, u32 num_color_inputs, u32 num_texcoord_inputs,
|
void DeclareFragmentEntryPoint(std::stringstream& ss, u32 num_color_inputs, u32 num_texcoord_inputs,
|
||||||
const std::initializer_list<const char*>& additional_inputs,
|
const std::initializer_list<const char*>& additional_inputs,
|
||||||
bool declare_fragcoord = false, bool dual_color_output = false);
|
bool declare_fragcoord = false, bool dual_color_output = false);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#include "YBaseLib/Log.h"
|
#include "YBaseLib/Log.h"
|
||||||
#include "YBaseLib/Timer.h"
|
#include "YBaseLib/Timer.h"
|
||||||
#include "common/gl/texture.h"
|
#include "common/gl/texture.h"
|
||||||
#include "host_interface.h"
|
#include "host_display.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
Log_SetChannel(GPU_SW);
|
Log_SetChannel(GPU_SW);
|
||||||
|
@ -12,14 +12,21 @@ GPU_SW::GPU_SW()
|
||||||
m_vram.fill(0);
|
m_vram.fill(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
GPU_SW::~GPU_SW() = default;
|
GPU_SW::~GPU_SW()
|
||||||
|
|
||||||
bool GPU_SW::Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers)
|
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
|
|
||||||
m_display_texture = std::make_unique<GL::Texture>(VRAM_WIDTH, VRAM_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE);
|
|
||||||
return true;
|
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)
|
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++)
|
for (u32 row = 0; row < height; row++)
|
||||||
{
|
{
|
||||||
const u16* src_row_ptr = src_ptr;
|
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++));
|
*(dst_row_ptr++) = RGBA5551ToRGBA8888(*(src_row_ptr++));
|
||||||
|
|
||||||
src_ptr += src_stride;
|
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)
|
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++)
|
for (u32 row = 0; row < height; row++)
|
||||||
{
|
{
|
||||||
const u8* src_row_ptr = reinterpret_cast<const u8*>(src_ptr);
|
const u8* src_row_ptr = reinterpret_cast<const u8*>(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;
|
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)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
else if (m_GPUSTAT.display_area_color_depth_24)
|
else if (m_GPUSTAT.display_area_color_depth_24)
|
||||||
|
@ -153,11 +154,10 @@ void GPU_SW::UpdateDisplay()
|
||||||
display_height);
|
display_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_display_texture->Bind();
|
m_host_display->UpdateTexture(m_display_texture.get(), 0, 0, display_width, display_height,
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, display_width, display_height, GL_RGBA, GL_UNSIGNED_BYTE,
|
m_display_texture_buffer.data(), display_width * sizeof(u32));
|
||||||
m_display_texture_buffer.data());
|
m_host_display->SetDisplayTexture(m_display_texture->GetHandle(), 0, 0, display_width, display_height, VRAM_WIDTH,
|
||||||
m_system->GetHostInterface()->SetDisplayTexture(m_display_texture.get(), 0, 0, display_width, display_height,
|
VRAM_HEIGHT, display_aspect_ratio);
|
||||||
display_aspect_ratio);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU_SW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32* command_ptr)
|
void GPU_SW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32* command_ptr)
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "common/gl/texture.h"
|
|
||||||
#include "gpu.h"
|
#include "gpu.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace GL {
|
class HostDisplayTexture;
|
||||||
class Texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
class GPU_SW final : public GPU
|
class GPU_SW final : public GPU
|
||||||
{
|
{
|
||||||
|
@ -15,7 +12,8 @@ public:
|
||||||
GPU_SW();
|
GPU_SW();
|
||||||
~GPU_SW() override;
|
~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;
|
void Reset() override;
|
||||||
|
|
||||||
u16 GetPixel(u32 x, u32 y) const { return m_vram[VRAM_WIDTH * y + x]; }
|
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);
|
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 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,
|
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);
|
u8 origin_texcoord_x, u8 origin_texcoord_y);
|
||||||
|
|
||||||
std::unique_ptr<GL::Texture> m_display_texture;
|
|
||||||
std::vector<u32> m_display_texture_buffer;
|
std::vector<u32> m_display_texture_buffer;
|
||||||
|
std::unique_ptr<HostDisplayTexture> m_display_texture;
|
||||||
|
|
||||||
std::array<SWVertex, MAX_VERTICES_PER_POLYGON> m_vertex_buffer;
|
std::array<SWVertex, MAX_VERTICES_PER_POLYGON> m_vertex_buffer;
|
||||||
std::array<u16, VRAM_WIDTH * VRAM_HEIGHT> m_vram;
|
std::array<u16, VRAM_WIDTH * VRAM_HEIGHT> m_vram;
|
||||||
|
|
71
src/core/host_display.h
Normal file
71
src/core/host_display.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
#pragma once
|
||||||
|
#include "types.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
// 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<HostDisplayTexture> 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<u32, u32> GetWindowSize() const = 0;
|
||||||
|
virtual void WindowResized() = 0;
|
||||||
|
|
||||||
|
// Helper function for computing the draw rectangle in a larger window.
|
||||||
|
static std::tuple<int, int, int, int> 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<int>(float(window_height) * display_ratio);
|
||||||
|
height = static_cast<int>(window_height);
|
||||||
|
left = (window_width - width) / 2;
|
||||||
|
top = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
width = static_cast<int>(window_width);
|
||||||
|
height = static_cast<int>(float(window_width) / display_ratio);
|
||||||
|
left = 0;
|
||||||
|
top = (window_height - height) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::tie(left, top, width, height);
|
||||||
|
}
|
||||||
|
};
|
|
@ -5,16 +5,17 @@
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
Log_SetChannel(HostInterface);
|
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;
|
HostInterface::~HostInterface() = default;
|
||||||
|
|
||||||
bool HostInterface::InitializeSystem(const char* filename, const char* exp1_filename)
|
bool HostInterface::InitializeSystem(const char* filename, const char* exp1_filename)
|
||||||
{
|
{
|
||||||
Settings settings;
|
m_system = std::make_unique<System>(this, m_settings);
|
||||||
settings.memory_card_a_filename = "memory_card_a.mcd";
|
|
||||||
|
|
||||||
m_system = std::make_unique<System>(this, settings);
|
|
||||||
if (!m_system->Initialize())
|
if (!m_system->Initialize())
|
||||||
{
|
{
|
||||||
m_system.reset();
|
m_system.reset();
|
||||||
|
@ -50,6 +51,7 @@ bool HostInterface::InitializeSystem(const char* filename, const char* exp1_file
|
||||||
m_system->SetExpansionROM(exp1_filename);
|
m_system->SetExpansionROM(exp1_filename);
|
||||||
|
|
||||||
// Resume execution.
|
// Resume execution.
|
||||||
|
m_settings = m_system->GetSettings();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "settings.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class AudioStream;
|
class AudioStream;
|
||||||
|
class HostDisplay;
|
||||||
namespace GL {
|
|
||||||
class Texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
class System;
|
class System;
|
||||||
|
|
||||||
|
@ -20,7 +18,8 @@ public:
|
||||||
|
|
||||||
bool InitializeSystem(const char* filename, const char* exp1_filename);
|
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;
|
virtual void ReportMessage(const char* message) = 0;
|
||||||
|
|
||||||
// Adds OSD messages, duration is in seconds.
|
// Adds OSD messages, duration is in seconds.
|
||||||
|
@ -32,4 +31,6 @@ public:
|
||||||
protected:
|
protected:
|
||||||
std::unique_ptr<AudioStream> m_audio_stream;
|
std::unique_ptr<AudioStream> m_audio_stream;
|
||||||
std::unique_ptr<System> m_system;
|
std::unique_ptr<System> m_system;
|
||||||
|
|
||||||
|
Settings m_settings;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@ struct Settings
|
||||||
{
|
{
|
||||||
enum class GPURenderer
|
enum class GPURenderer
|
||||||
{
|
{
|
||||||
|
HardwareD3D11,
|
||||||
HardwareOpenGL,
|
HardwareOpenGL,
|
||||||
Software
|
Software
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "cpu_core.h"
|
#include "cpu_core.h"
|
||||||
#include "dma.h"
|
#include "dma.h"
|
||||||
#include "gpu.h"
|
#include "gpu.h"
|
||||||
|
#include "host_interface.h"
|
||||||
#include "interrupt_controller.h"
|
#include "interrupt_controller.h"
|
||||||
#include "mdec.h"
|
#include "mdec.h"
|
||||||
#include "memory_card.h"
|
#include "memory_card.h"
|
||||||
|
@ -112,20 +113,28 @@ bool System::CreateGPU()
|
||||||
m_gpu = GPU::CreateHardwareOpenGLRenderer();
|
m_gpu = GPU::CreateHardwareOpenGLRenderer();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Settings::GPURenderer::HardwareD3D11:
|
||||||
|
m_gpu = GPU::CreateHardwareD3D11Renderer();
|
||||||
|
break;
|
||||||
|
|
||||||
case Settings::GPURenderer::Software:
|
case Settings::GPURenderer::Software:
|
||||||
default:
|
default:
|
||||||
m_gpu = GPU::CreateSoftwareRenderer();
|
m_gpu = GPU::CreateSoftwareRenderer();
|
||||||
break;
|
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");
|
Log_ErrorPrintf("Failed to initialize GPU, falling back to software");
|
||||||
m_gpu.reset();
|
m_gpu.reset();
|
||||||
m_settings.gpu_renderer = Settings::GPURenderer::Software;
|
m_settings.gpu_renderer = Settings::GPURenderer::Software;
|
||||||
m_gpu = GPU::CreateSoftwareRenderer();
|
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;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_bus->SetGPU(m_gpu.get());
|
m_bus->SetGPU(m_gpu.get());
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
set(SRCS
|
add_executable(duckstation
|
||||||
icon.cpp
|
icon.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
|
opengl_host_display.cpp
|
||||||
|
opengl_host_display.h
|
||||||
sdl_audio_stream.cpp
|
sdl_audio_stream.cpp
|
||||||
sdl_audio_stream.h
|
sdl_audio_stream.h
|
||||||
sdl_interface.cpp
|
sdl_host_interface.cpp
|
||||||
sdl_interface.h
|
sdl_host_interface.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(duckstation ${SRCS})
|
|
||||||
target_include_directories(duckstation PRIVATE "${SDL2_INCLUDE_DIRS}")
|
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()
|
||||||
|
|
394
src/duckstation/d3d11_host_display.cpp
Normal file
394
src/duckstation/d3d11_host_display.cpp
Normal file
|
@ -0,0 +1,394 @@
|
||||||
|
#include "d3d11_host_display.h"
|
||||||
|
#include "YBaseLib/Log.h"
|
||||||
|
#include "common/d3d11/shader_compiler.h"
|
||||||
|
#include <SDL_syswm.h>
|
||||||
|
#include <array>
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <imgui_impl_dx11.h>
|
||||||
|
#include <imgui_impl_sdl.h>
|
||||||
|
Log_SetChannel(D3D11HostDisplay);
|
||||||
|
|
||||||
|
class D3D11HostDisplayTexture : public HostDisplayTexture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template<typename T>
|
||||||
|
using ComPtr = Microsoft::WRL::ComPtr<T>;
|
||||||
|
|
||||||
|
D3D11HostDisplayTexture(ComPtr<ID3D11Texture2D> texture, ComPtr<ID3D11ShaderResourceView> 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<D3D11HostDisplayTexture> 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<ID3D11Texture2D> 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<ID3D11ShaderResourceView> srv;
|
||||||
|
hr = device->CreateShaderResourceView(texture.Get(), &srv_desc, srv.GetAddressOf());
|
||||||
|
if (FAILED(hr))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return std::make_unique<D3D11HostDisplayTexture>(std::move(texture), std::move(srv), width, height, dynamic);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ComPtr<ID3D11Texture2D> m_texture;
|
||||||
|
ComPtr<ID3D11ShaderResourceView> 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<HostDisplayTexture> 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<D3D11HostDisplayTexture*>(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<char*>(sr.pData) + (y * sr.RowPitch) + (x * sizeof(u32));
|
||||||
|
const char* src_ptr = static_cast<const char*>(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<ID3D11ShaderResourceView*>(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<u32, u32> D3D11HostDisplay::GetWindowSize() const
|
||||||
|
{
|
||||||
|
return std::make_tuple(static_cast<u32>(m_window_width), static_cast<u32>(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<ID3D11InfoQueue> 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<ID3D11Texture2D> 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<HostDisplay> D3D11HostDisplay::Create(SDL_Window* window)
|
||||||
|
{
|
||||||
|
std::unique_ptr<D3D11HostDisplay> display = std::make_unique<D3D11HostDisplay>(window);
|
||||||
|
if (!display->CreateD3DDevice() || !display->CreateSwapChainRTV() || !display->CreateD3DResources() ||
|
||||||
|
!display->CreateImGuiContext())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return display;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D11HostDisplay::Render()
|
||||||
|
{
|
||||||
|
static constexpr std::array<float, 4> 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<float>(m_display_offset_x) / static_cast<float>(m_display_texture_width),
|
||||||
|
static_cast<float>(m_display_offset_y) / static_cast<float>(m_display_texture_height),
|
||||||
|
static_cast<float>(m_display_width) / static_cast<float>(m_display_texture_width),
|
||||||
|
static_cast<float>(m_display_height) / static_cast<float>(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<float>(vp_left), static_cast<float>(vp_top), static_cast<float>(vp_width),
|
||||||
|
static_cast<float>(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);
|
||||||
|
}
|
84
src/duckstation/d3d11_host_display.h
Normal file
84
src/duckstation/d3d11_host_display.h
Normal file
|
@ -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 <SDL.h>
|
||||||
|
#include <d3d11.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <wrl/client.h>
|
||||||
|
|
||||||
|
class D3D11HostDisplay final : public HostDisplay
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template<typename T>
|
||||||
|
using ComPtr = Microsoft::WRL::ComPtr<T>;
|
||||||
|
|
||||||
|
D3D11HostDisplay(SDL_Window* window);
|
||||||
|
~D3D11HostDisplay();
|
||||||
|
|
||||||
|
static std::unique_ptr<HostDisplay> Create(SDL_Window* window);
|
||||||
|
|
||||||
|
RenderAPI GetRenderAPI() const override;
|
||||||
|
void* GetHostRenderDevice() const override;
|
||||||
|
void* GetHostRenderContext() const override;
|
||||||
|
|
||||||
|
std::unique_ptr<HostDisplayTexture> 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<u32, u32> 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<ID3D11Device> m_device;
|
||||||
|
ComPtr<ID3D11DeviceContext> m_context;
|
||||||
|
ComPtr<IDXGISwapChain> m_swap_chain;
|
||||||
|
ComPtr<ID3D11RenderTargetView> m_swap_chain_rtv;
|
||||||
|
|
||||||
|
ComPtr<ID3D11RasterizerState> m_display_rasterizer_state;
|
||||||
|
ComPtr<ID3D11DepthStencilState> m_display_depth_stencil_state;
|
||||||
|
ComPtr<ID3D11BlendState> m_display_blend_state;
|
||||||
|
ComPtr<ID3D11VertexShader> m_display_vertex_shader;
|
||||||
|
ComPtr<ID3D11PixelShader> m_display_pixel_shader;
|
||||||
|
ComPtr<ID3D11SamplerState> m_point_sampler;
|
||||||
|
ComPtr<ID3D11SamplerState> 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;
|
||||||
|
};
|
|
@ -52,15 +52,19 @@
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClCompile Include="d3d11_host_display.cpp" />
|
||||||
|
<ClCompile Include="opengl_host_display.cpp" />
|
||||||
<ClCompile Include="icon.cpp" />
|
<ClCompile Include="icon.cpp" />
|
||||||
<ClCompile Include="sdl_audio_stream.cpp" />
|
<ClCompile Include="sdl_audio_stream.cpp" />
|
||||||
<ClCompile Include="sdl_interface.cpp" />
|
<ClCompile Include="sdl_host_interface.cpp" />
|
||||||
<ClCompile Include="main.cpp" />
|
<ClCompile Include="main.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClInclude Include="d3d11_host_display.h" />
|
||||||
|
<ClInclude Include="opengl_host_display.h" />
|
||||||
<ClInclude Include="icon.h" />
|
<ClInclude Include="icon.h" />
|
||||||
<ClInclude Include="sdl_audio_stream.h" />
|
<ClInclude Include="sdl_audio_stream.h" />
|
||||||
<ClInclude Include="sdl_interface.h" />
|
<ClInclude Include="sdl_host_interface.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<ProjectGuid>{DAA8F93D-9C17-4DE2-BD0B-57891E0FF0D9}</ProjectGuid>
|
<ProjectGuid>{DAA8F93D-9C17-4DE2-BD0B-57891E0FF0D9}</ProjectGuid>
|
||||||
|
@ -213,7 +217,7 @@
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>SDL2.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib32-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib32-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
|
@ -235,7 +239,7 @@
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>SDL2.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib64-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib64-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
|
@ -260,7 +264,7 @@
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>SDL2.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib32-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib32-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
|
@ -285,7 +289,7 @@
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>SDL2.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib64-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib64-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
|
@ -308,7 +312,7 @@
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>SDL2.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
|
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -333,7 +337,7 @@
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>SDL2.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -357,7 +361,7 @@
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>SDL2.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
|
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -382,7 +386,7 @@
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>SDL2.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -2,13 +2,17 @@
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="main.cpp" />
|
<ClCompile Include="main.cpp" />
|
||||||
<ClCompile Include="sdl_interface.cpp" />
|
|
||||||
<ClCompile Include="sdl_audio_stream.cpp" />
|
<ClCompile Include="sdl_audio_stream.cpp" />
|
||||||
<ClCompile Include="icon.cpp" />
|
<ClCompile Include="icon.cpp" />
|
||||||
|
<ClCompile Include="opengl_host_display.cpp" />
|
||||||
|
<ClCompile Include="sdl_host_interface.cpp" />
|
||||||
|
<ClCompile Include="d3d11_host_display.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="sdl_interface.h" />
|
|
||||||
<ClInclude Include="icon.h" />
|
<ClInclude Include="icon.h" />
|
||||||
<ClInclude Include="sdl_audio_stream.h" />
|
<ClInclude Include="sdl_audio_stream.h" />
|
||||||
|
<ClInclude Include="opengl_host_display.h" />
|
||||||
|
<ClInclude Include="sdl_host_interface.h" />
|
||||||
|
<ClInclude Include="d3d11_host_display.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -2,7 +2,7 @@
|
||||||
#include "YBaseLib/Log.h"
|
#include "YBaseLib/Log.h"
|
||||||
#include "YBaseLib/StringConverter.h"
|
#include "YBaseLib/StringConverter.h"
|
||||||
#include "core/system.h"
|
#include "core/system.h"
|
||||||
#include "sdl_interface.h"
|
#include "sdl_host_interface.h"
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ static int Run(int argc, char* argv[])
|
||||||
#define CHECK_ARG_PARAM(str) (!std::strcmp(argv[i], str) && ((i + 1) < argc))
|
#define CHECK_ARG_PARAM(str) (!std::strcmp(argv[i], str) && ((i + 1) < argc))
|
||||||
|
|
||||||
if (CHECK_ARG_PARAM("-state"))
|
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"))
|
else if (CHECK_ARG_PARAM("-exp1"))
|
||||||
exp1_filename = argv[++i];
|
exp1_filename = argv[++i];
|
||||||
else
|
else
|
||||||
|
@ -51,8 +51,8 @@ static int Run(int argc, char* argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
// create display and host interface
|
// create display and host interface
|
||||||
std::unique_ptr<SDLInterface> host_interface =
|
std::unique_ptr<SDLHostInterface> host_interface =
|
||||||
SDLInterface::Create(filename, exp1_filename, state_filename.IsEmpty() ? nullptr : state_filename.GetCharArray());
|
SDLHostInterface::Create(filename, exp1_filename, state_filename.IsEmpty() ? nullptr : state_filename.GetCharArray());
|
||||||
if (!host_interface)
|
if (!host_interface)
|
||||||
{
|
{
|
||||||
Panic("Failed to create host interface");
|
Panic("Failed to create host interface");
|
||||||
|
|
318
src/duckstation/opengl_host_display.cpp
Normal file
318
src/duckstation/opengl_host_display.cpp
Normal file
|
@ -0,0 +1,318 @@
|
||||||
|
#include "opengl_host_display.h"
|
||||||
|
#include "YBaseLib/Log.h"
|
||||||
|
#include "icon.h"
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <imgui_impl_opengl3.h>
|
||||||
|
#include <imgui_impl_sdl.h>
|
||||||
|
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<void*>(static_cast<uintptr_t>(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<OpenGLHostDisplayTexture> 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<OpenGLHostDisplayTexture>(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<HostDisplayTexture> 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<OpenGLHostDisplayTexture*>(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<GLuint>(reinterpret_cast<uintptr_t>(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<u32, u32> OpenGLHostDisplay::GetWindowSize() const
|
||||||
|
{
|
||||||
|
return std::make_tuple(static_cast<u32>(m_window_width), static_cast<u32>(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<GL::Texture>(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<HostDisplay> OpenGLHostDisplay::Create(SDL_Window* window)
|
||||||
|
{
|
||||||
|
std::unique_ptr<OpenGLHostDisplay> display = std::make_unique<OpenGLHostDisplay>(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<float>(m_display_offset_x) / static_cast<float>(m_display_texture_width),
|
||||||
|
static_cast<float>(m_display_offset_y) / static_cast<float>(m_display_texture_height),
|
||||||
|
static_cast<float>(m_display_width) / static_cast<float>(m_display_texture_width),
|
||||||
|
static_cast<float>(m_display_height) / static_cast<float>(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);
|
||||||
|
}
|
64
src/duckstation/opengl_host_display.h
Normal file
64
src/duckstation/opengl_host_display.h
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
#pragma once
|
||||||
|
#include "common/gl/program.h"
|
||||||
|
#include "common/gl/texture.h"
|
||||||
|
#include "core/host_display.h"
|
||||||
|
#include <SDL.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class OpenGLHostDisplay final : public HostDisplay
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenGLHostDisplay(SDL_Window* window);
|
||||||
|
~OpenGLHostDisplay();
|
||||||
|
|
||||||
|
static std::unique_ptr<HostDisplay> Create(SDL_Window* window);
|
||||||
|
|
||||||
|
RenderAPI GetRenderAPI() const override;
|
||||||
|
void* GetHostRenderDevice() const override;
|
||||||
|
void* GetHostRenderContext() const override;
|
||||||
|
|
||||||
|
std::unique_ptr<HostDisplayTexture> 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<u32, u32> 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<GL::Texture> 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;
|
||||||
|
};
|
|
@ -1,4 +1,4 @@
|
||||||
#include "sdl_interface.h"
|
#include "sdl_host_interface.h"
|
||||||
#include "YBaseLib/ByteStream.h"
|
#include "YBaseLib/ByteStream.h"
|
||||||
#include "YBaseLib/Error.h"
|
#include "YBaseLib/Error.h"
|
||||||
#include "YBaseLib/Log.h"
|
#include "YBaseLib/Log.h"
|
||||||
|
@ -6,62 +6,54 @@
|
||||||
#include "core/digital_controller.h"
|
#include "core/digital_controller.h"
|
||||||
#include "core/dma.h"
|
#include "core/dma.h"
|
||||||
#include "core/gpu.h"
|
#include "core/gpu.h"
|
||||||
|
#include "core/host_display.h"
|
||||||
#include "core/mdec.h"
|
#include "core/mdec.h"
|
||||||
#include "core/memory_card.h"
|
#include "core/memory_card.h"
|
||||||
#include "core/spu.h"
|
#include "core/spu.h"
|
||||||
#include "core/system.h"
|
#include "core/system.h"
|
||||||
#include "core/timers.h"
|
#include "core/timers.h"
|
||||||
|
#ifdef Y_PLATFORM_WINDOWS
|
||||||
|
#include "d3d11_host_display.h"
|
||||||
|
#endif
|
||||||
#include "icon.h"
|
#include "icon.h"
|
||||||
|
#include "opengl_host_display.h"
|
||||||
#include "sdl_audio_stream.h"
|
#include "sdl_audio_stream.h"
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <glad.h>
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <imgui_impl_opengl3.h>
|
|
||||||
#include <imgui_impl_sdl.h>
|
#include <imgui_impl_sdl.h>
|
||||||
#include <nfd.h>
|
#include <nfd.h>
|
||||||
Log_SetChannel(SDLInterface);
|
Log_SetChannel(SDLHostInterface);
|
||||||
|
|
||||||
static constexpr std::array<std::pair<Settings::GPURenderer, const char*>, 2> s_gpu_renderer_names = {
|
static constexpr std::array<std::pair<Settings::GPURenderer, const char*>, 3> s_gpu_renderer_names = {
|
||||||
{{Settings::GPURenderer::HardwareOpenGL, "Hardware (OpenGL)"}, {Settings::GPURenderer::Software, "Software"}}};
|
{{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();
|
CloseGameControllers();
|
||||||
|
m_display.reset();
|
||||||
if (m_gl_context)
|
ImGui::DestroyContext();
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_window)
|
if (m_window)
|
||||||
SDL_DestroyWindow(m_window);
|
SDL_DestroyWindow(m_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLInterface::CreateSDLWindow()
|
bool SDLHostInterface::CreateSDLWindow()
|
||||||
{
|
{
|
||||||
constexpr u32 DEFAULT_WINDOW_WIDTH = 900;
|
constexpr u32 DEFAULT_WINDOW_WIDTH = 900;
|
||||||
constexpr u32 DEFAULT_WINDOW_HEIGHT = 700;
|
constexpr u32 DEFAULT_WINDOW_HEIGHT = 700;
|
||||||
|
|
||||||
// Create window.
|
// 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,
|
m_window = SDL_CreateWindow("DuckStation", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, DEFAULT_WINDOW_WIDTH,
|
||||||
DEFAULT_WINDOW_HEIGHT, window_flags);
|
DEFAULT_WINDOW_HEIGHT, window_flags);
|
||||||
if (!m_window)
|
if (!m_window)
|
||||||
{
|
|
||||||
Panic("Failed to create window");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// Set window icon.
|
// Set window icon.
|
||||||
SDL_Surface* icon_surface =
|
SDL_Surface* icon_surface =
|
||||||
|
@ -74,133 +66,38 @@ bool SDLInterface::CreateSDLWindow()
|
||||||
SDL_FreeSurface(icon_surface);
|
SDL_FreeSurface(icon_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_GetWindowSize(m_window, &m_window_width, &m_window_height);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
|
bool SDLHostInterface::CreateDisplay()
|
||||||
const GLchar* message, const void* userParam)
|
|
||||||
{
|
{
|
||||||
switch (severity)
|
#ifdef Y_PLATFORM_WINDOWS
|
||||||
{
|
m_display = UseOpenGLRenderer() ? OpenGLHostDisplay::Create(m_window) : D3D11HostDisplay::Create(m_window);
|
||||||
case GL_DEBUG_SEVERITY_HIGH_KHR:
|
#else
|
||||||
Log_InfoPrint(message);
|
m_display = OpenGLHostDisplay::Create(m_window);
|
||||||
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);
|
|
||||||
}
|
|
||||||
#endif
|
#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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLInterface::CreateImGuiContext()
|
bool SDLHostInterface::CreateImGuiContext()
|
||||||
{
|
{
|
||||||
ImGui::CreateContext();
|
ImGui::CreateContext();
|
||||||
ImGui::GetIO().IniFilename = nullptr;
|
ImGui::GetIO().IniFilename = nullptr;
|
||||||
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
|
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
|
||||||
ImGui::GetIO().BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLInterface::CreateGLResources()
|
bool SDLHostInterface::CreateAudioStream()
|
||||||
{
|
|
||||||
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<GL::Texture>(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()
|
|
||||||
{
|
{
|
||||||
m_audio_stream = std::make_unique<SDLAudioStream>();
|
m_audio_stream = std::make_unique<SDLAudioStream>();
|
||||||
if (!m_audio_stream->Reconfigure(44100, 2))
|
if (!m_audio_stream->Reconfigure(44100, 2))
|
||||||
|
@ -212,7 +109,7 @@ bool SDLInterface::CreateAudioStream()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::UpdateAudioVisualSync()
|
void SDLHostInterface::UpdateAudioVisualSync()
|
||||||
{
|
{
|
||||||
const bool speed_limiter_enabled = m_speed_limiter_enabled && !m_speed_limiter_temp_disabled;
|
const bool speed_limiter_enabled = m_speed_limiter_enabled && !m_speed_limiter_temp_disabled;
|
||||||
const bool audio_sync_enabled = speed_limiter_enabled;
|
const bool audio_sync_enabled = speed_limiter_enabled;
|
||||||
|
@ -221,16 +118,10 @@ void SDLInterface::UpdateAudioVisualSync()
|
||||||
(speed_limiter_enabled && vsync_enabled) ? " and video" : "");
|
(speed_limiter_enabled && vsync_enabled) ? " and video" : "");
|
||||||
|
|
||||||
m_audio_stream->SetSync(false);
|
m_audio_stream->SetSync(false);
|
||||||
|
m_display->SetVSync(vsync_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(vsync_enabled ? 1 : 0);
|
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::OpenGameControllers()
|
void SDLHostInterface::OpenGameControllers()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < SDL_NumJoysticks(); i++)
|
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)
|
for (auto& it : m_sdl_controllers)
|
||||||
SDL_GameControllerClose(it.second);
|
SDL_GameControllerClose(it.second);
|
||||||
m_sdl_controllers.clear();
|
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))
|
if (!HostInterface::InitializeSystem(filename, exp1_filename))
|
||||||
{
|
{
|
||||||
|
@ -268,13 +159,13 @@ bool SDLInterface::InitializeSystem(const char* filename, const char* exp1_filen
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::ConnectDevices()
|
void SDLHostInterface::ConnectDevices()
|
||||||
{
|
{
|
||||||
m_controller = DigitalController::Create();
|
m_controller = DigitalController::Create();
|
||||||
m_system->SetController(0, m_controller);
|
m_system->SetController(0, m_controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::ResetPerformanceCounters()
|
void SDLHostInterface::ResetPerformanceCounters()
|
||||||
{
|
{
|
||||||
if (m_system)
|
if (m_system)
|
||||||
{
|
{
|
||||||
|
@ -291,24 +182,22 @@ void SDLInterface::ResetPerformanceCounters()
|
||||||
m_fps_timer.Reset();
|
m_fps_timer.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::ShutdownSystem()
|
void SDLHostInterface::ShutdownSystem()
|
||||||
{
|
{
|
||||||
m_system.reset();
|
m_system.reset();
|
||||||
m_paused = false;
|
m_paused = false;
|
||||||
m_display_texture = nullptr;
|
|
||||||
UpdateAudioVisualSync();
|
UpdateAudioVisualSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<SDLInterface> SDLInterface::Create(const char* filename /* = nullptr */,
|
std::unique_ptr<SDLHostInterface> SDLHostInterface::Create(const char* filename /* = nullptr */,
|
||||||
const char* exp1_filename /* = nullptr */,
|
const char* exp1_filename /* = nullptr */,
|
||||||
const char* save_state_filename /* = nullptr */)
|
const char* save_state_filename /* = nullptr */)
|
||||||
{
|
{
|
||||||
std::unique_ptr<SDLInterface> intf = std::make_unique<SDLInterface>();
|
std::unique_ptr<SDLHostInterface> intf = std::make_unique<SDLHostInterface>();
|
||||||
if (!intf->CreateSDLWindow() || !intf->CreateGLContext() || !intf->CreateImGuiContext() ||
|
if (!intf->CreateSDLWindow() || !intf->CreateImGuiContext() || !intf->CreateDisplay() || !intf->CreateAudioStream())
|
||||||
!intf->CreateGLResources() || !intf->CreateAudioStream())
|
|
||||||
{
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
|
||||||
|
ImGui::NewFrame();
|
||||||
|
|
||||||
intf->OpenGameControllers();
|
intf->OpenGameControllers();
|
||||||
|
|
||||||
|
@ -323,20 +212,26 @@ std::unique_ptr<SDLInterface> SDLInterface::Create(const char* filename /* = nul
|
||||||
}
|
}
|
||||||
|
|
||||||
intf->UpdateAudioVisualSync();
|
intf->UpdateAudioVisualSync();
|
||||||
|
|
||||||
return intf;
|
return intf;
|
||||||
}
|
}
|
||||||
|
|
||||||
TinyString SDLInterface::GetSaveStateFilename(u32 index)
|
TinyString SDLHostInterface::GetSaveStateFilename(u32 index)
|
||||||
{
|
{
|
||||||
return TinyString::FromFormat("savestate_%u.bin", 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);
|
AddOSDMessage(message, 3.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLInterface::IsWindowFullscreen() const
|
bool SDLHostInterface::IsWindowFullscreen() const
|
||||||
{
|
{
|
||||||
return ((SDL_GetWindowFlags(m_window) & SDL_WINDOW_FULLSCREEN) != 0);
|
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);
|
ImGui_ImplSDL2_ProcessEvent(event);
|
||||||
|
|
||||||
|
@ -500,10 +395,7 @@ void SDLInterface::HandleSDLEvent(const SDL_Event* event)
|
||||||
case SDL_WINDOWEVENT:
|
case SDL_WINDOWEVENT:
|
||||||
{
|
{
|
||||||
if (event->window.event == SDL_WINDOWEVENT_RESIZED)
|
if (event->window.event == SDL_WINDOWEVENT_RESIZED)
|
||||||
{
|
m_display->WindowResized();
|
||||||
m_window_width = event->window.data1;
|
|
||||||
m_window_height = event->window.data2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
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;
|
const bool repeat = event->key.repeat != 0;
|
||||||
if (!repeat && m_controller && HandleSDLKeyEventForController(event, m_controller.get()))
|
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);
|
ImGui::SetWindowFocus(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::Render()
|
void SDLHostInterface::DrawImGui()
|
||||||
{
|
|
||||||
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<int, int, int, int> 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<int>(float(window_height) * display_ratio);
|
|
||||||
height = static_cast<int>(window_height);
|
|
||||||
left = (window_width - width) / 2;
|
|
||||||
top = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
width = static_cast<int>(window_width);
|
|
||||||
height = static_cast<int>(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<float>(m_display_texture_offset_x) / static_cast<float>(m_display_texture->GetWidth()),
|
|
||||||
static_cast<float>(m_display_texture_offset_y) / static_cast<float>(m_display_texture->GetHeight()),
|
|
||||||
static_cast<float>(m_display_texture_width) / static_cast<float>(m_display_texture->GetWidth()),
|
|
||||||
static_cast<float>(m_display_texture_height) / static_cast<float>(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()
|
|
||||||
{
|
{
|
||||||
DrawMainMenuBar();
|
DrawMainMenuBar();
|
||||||
|
|
||||||
|
@ -754,7 +566,7 @@ void SDLInterface::DrawImGui()
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DrawMainMenuBar()
|
void SDLHostInterface::DrawMainMenuBar()
|
||||||
{
|
{
|
||||||
if (!ImGui::BeginMainMenuBar())
|
if (!ImGui::BeginMainMenuBar())
|
||||||
return;
|
return;
|
||||||
|
@ -936,7 +748,7 @@ void SDLInterface::DrawMainMenuBar()
|
||||||
ImGui::EndMainMenuBar();
|
ImGui::EndMainMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DrawPoweredOffWindow()
|
void SDLHostInterface::DrawPoweredOffWindow()
|
||||||
{
|
{
|
||||||
constexpr int WINDOW_WIDTH = 400;
|
constexpr int WINDOW_WIDTH = 400;
|
||||||
constexpr int WINDOW_HEIGHT = 650;
|
constexpr int WINDOW_HEIGHT = 650;
|
||||||
|
@ -956,8 +768,7 @@ void SDLInterface::DrawPoweredOffWindow()
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SetCursorPosX((WINDOW_WIDTH - APP_ICON_WIDTH) / 2);
|
ImGui::SetCursorPosX((WINDOW_WIDTH - APP_ICON_WIDTH) / 2);
|
||||||
ImGui::Image(reinterpret_cast<ImTextureID>(static_cast<std::uintptr_t>(m_app_icon_texture->GetGLId())),
|
ImGui::Image(m_app_icon_texture->GetHandle(), ImVec2(APP_ICON_WIDTH, APP_ICON_HEIGHT));
|
||||||
ImVec2(APP_ICON_WIDTH, APP_ICON_HEIGHT));
|
|
||||||
ImGui::SetCursorPosY(APP_ICON_HEIGHT + 32);
|
ImGui::SetCursorPosY(APP_ICON_HEIGHT + 32);
|
||||||
|
|
||||||
static const ImVec2 button_size(static_cast<float>(BUTTON_WIDTH), static_cast<float>(BUTTON_HEIGHT));
|
static const ImVec2 button_size(static_cast<float>(BUTTON_WIDTH), static_cast<float>(BUTTON_HEIGHT));
|
||||||
|
@ -1014,7 +825,7 @@ void SDLInterface::DrawPoweredOffWindow()
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DrawAboutWindow()
|
void SDLHostInterface::DrawAboutWindow()
|
||||||
{
|
{
|
||||||
ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x * 0.5f, ImGui::GetIO().DisplaySize.y * 0.5f),
|
ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x * 0.5f, ImGui::GetIO().DisplaySize.y * 0.5f),
|
||||||
ImGuiCond_Always, ImVec2(0.5f, 0.5f));
|
ImGuiCond_Always, ImVec2(0.5f, 0.5f));
|
||||||
|
@ -1044,7 +855,7 @@ void SDLInterface::DrawAboutWindow()
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DrawDebugMenu()
|
void SDLHostInterface::DrawDebugMenu()
|
||||||
{
|
{
|
||||||
if (!ImGui::BeginMenu("Debug", m_system != nullptr))
|
if (!ImGui::BeginMenu("Debug", m_system != nullptr))
|
||||||
return;
|
return;
|
||||||
|
@ -1076,7 +887,7 @@ void SDLInterface::DrawDebugMenu()
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DrawDebugWindows()
|
void SDLHostInterface::DrawDebugWindows()
|
||||||
{
|
{
|
||||||
const Settings::DebugSettings& debug_settings = m_system->GetSettings().debugging;
|
const Settings::DebugSettings& debug_settings = m_system->GetSettings().debugging;
|
||||||
|
|
||||||
|
@ -1094,7 +905,7 @@ void SDLInterface::DrawDebugWindows()
|
||||||
m_system->GetMDEC()->DrawDebugStateWindow();
|
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;
|
OSDMessage msg;
|
||||||
msg.text = message;
|
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));
|
m_osd_messages.push_back(std::move(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::SetDisplayTexture(GL::Texture* texture, u32 offset_x, u32 offset_y, u32 width, u32 height,
|
void SDLHostInterface::DrawOSDMessages()
|
||||||
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()
|
|
||||||
{
|
{
|
||||||
constexpr ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs |
|
constexpr ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs |
|
||||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
|
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
|
||||||
|
@ -1157,21 +956,21 @@ void SDLInterface::DrawOSDMessages()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DoReset()
|
void SDLHostInterface::DoReset()
|
||||||
{
|
{
|
||||||
m_system->Reset();
|
m_system->Reset();
|
||||||
ResetPerformanceCounters();
|
ResetPerformanceCounters();
|
||||||
AddOSDMessage("System reset.");
|
AddOSDMessage("System reset.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DoPowerOff()
|
void SDLHostInterface::DoPowerOff()
|
||||||
{
|
{
|
||||||
Assert(m_system);
|
Assert(m_system);
|
||||||
ShutdownSystem();
|
ShutdownSystem();
|
||||||
AddOSDMessage("System powered off.");
|
AddOSDMessage("System powered off.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DoResume()
|
void SDLHostInterface::DoResume()
|
||||||
{
|
{
|
||||||
Assert(!m_system);
|
Assert(!m_system);
|
||||||
if (!InitializeSystem())
|
if (!InitializeSystem())
|
||||||
|
@ -1189,7 +988,7 @@ void SDLInterface::DoResume()
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DoStartDisc()
|
void SDLHostInterface::DoStartDisc()
|
||||||
{
|
{
|
||||||
Assert(!m_system);
|
Assert(!m_system);
|
||||||
|
|
||||||
|
@ -1205,7 +1004,7 @@ void SDLInterface::DoStartDisc()
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DoStartBIOS()
|
void SDLHostInterface::DoStartBIOS()
|
||||||
{
|
{
|
||||||
Assert(!m_system);
|
Assert(!m_system);
|
||||||
|
|
||||||
|
@ -1217,7 +1016,7 @@ void SDLInterface::DoStartBIOS()
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DoChangeDisc()
|
void SDLHostInterface::DoChangeDisc()
|
||||||
{
|
{
|
||||||
Assert(m_system);
|
Assert(m_system);
|
||||||
|
|
||||||
|
@ -1234,7 +1033,7 @@ void SDLInterface::DoChangeDisc()
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DoLoadState(u32 index)
|
void SDLHostInterface::DoLoadState(u32 index)
|
||||||
{
|
{
|
||||||
if (!HasSystem() && !InitializeSystem(nullptr, nullptr))
|
if (!HasSystem() && !InitializeSystem(nullptr, nullptr))
|
||||||
return;
|
return;
|
||||||
|
@ -1244,14 +1043,14 @@ void SDLInterface::DoLoadState(u32 index)
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DoSaveState(u32 index)
|
void SDLHostInterface::DoSaveState(u32 index)
|
||||||
{
|
{
|
||||||
Assert(m_system);
|
Assert(m_system);
|
||||||
SaveState(GetSaveStateFilename(index));
|
SaveState(GetSaveStateFilename(index));
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DoTogglePause()
|
void SDLHostInterface::DoTogglePause()
|
||||||
{
|
{
|
||||||
if (!m_system)
|
if (!m_system)
|
||||||
return;
|
return;
|
||||||
|
@ -1261,7 +1060,7 @@ void SDLInterface::DoTogglePause()
|
||||||
m_fps_timer.Reset();
|
m_fps_timer.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DoFrameStep()
|
void SDLHostInterface::DoFrameStep()
|
||||||
{
|
{
|
||||||
if (!m_system)
|
if (!m_system)
|
||||||
return;
|
return;
|
||||||
|
@ -1270,7 +1069,7 @@ void SDLInterface::DoFrameStep()
|
||||||
m_paused = false;
|
m_paused = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DoToggleSoftwareRendering()
|
void SDLHostInterface::DoToggleSoftwareRendering()
|
||||||
{
|
{
|
||||||
if (!m_system)
|
if (!m_system)
|
||||||
return;
|
return;
|
||||||
|
@ -1283,14 +1082,16 @@ void SDLInterface::DoToggleSoftwareRendering()
|
||||||
}
|
}
|
||||||
else
|
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.");
|
AddOSDMessage("Switched to hardware GPU renderer.");
|
||||||
}
|
}
|
||||||
|
|
||||||
m_system->RecreateGPU();
|
m_system->RecreateGPU();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::DoModifyInternalResolution(s32 increment)
|
void SDLHostInterface::DoModifyInternalResolution(s32 increment)
|
||||||
{
|
{
|
||||||
if (!m_system)
|
if (!m_system)
|
||||||
return;
|
return;
|
||||||
|
@ -1310,7 +1111,7 @@ void SDLInterface::DoModifyInternalResolution(s32 increment)
|
||||||
GPU::VRAM_HEIGHT * settings.gpu_resolution_scale));
|
GPU::VRAM_HEIGHT * settings.gpu_resolution_scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLInterface::Run()
|
void SDLHostInterface::Run()
|
||||||
{
|
{
|
||||||
m_audio_stream->PauseOutput(false);
|
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)
|
if (m_system)
|
||||||
{
|
{
|
|
@ -4,6 +4,7 @@
|
||||||
#include "common/gl/program.h"
|
#include "common/gl/program.h"
|
||||||
#include "common/gl/texture.h"
|
#include "common/gl/texture.h"
|
||||||
#include "core/host_interface.h"
|
#include "core/host_interface.h"
|
||||||
|
#include "core/host_display.h"
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
@ -16,19 +17,18 @@ class DigitalController;
|
||||||
class MemoryCard;
|
class MemoryCard;
|
||||||
class AudioStream;
|
class AudioStream;
|
||||||
|
|
||||||
class SDLInterface : public HostInterface
|
class SDLHostInterface : public HostInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SDLInterface();
|
SDLHostInterface();
|
||||||
~SDLInterface();
|
~SDLHostInterface();
|
||||||
|
|
||||||
static std::unique_ptr<SDLInterface> Create(const char* filename = nullptr, const char* exp1_filename = nullptr,
|
static std::unique_ptr<SDLHostInterface> Create(const char* filename = nullptr, const char* exp1_filename = nullptr,
|
||||||
const char* save_state_filename = nullptr);
|
const char* save_state_filename = nullptr);
|
||||||
|
|
||||||
static TinyString GetSaveStateFilename(u32 index);
|
static TinyString GetSaveStateFilename(u32 index);
|
||||||
|
|
||||||
void SetDisplayTexture(GL::Texture* texture, u32 offset_x, u32 offset_y, u32 width, u32 height,
|
HostDisplay* GetDisplay() const override;
|
||||||
float aspect_ratio) override;
|
|
||||||
|
|
||||||
void ReportMessage(const char* message) override;
|
void ReportMessage(const char* message) override;
|
||||||
|
|
||||||
|
@ -50,10 +50,15 @@ private:
|
||||||
|
|
||||||
bool HasSystem() const { return static_cast<bool>(m_system); }
|
bool HasSystem() const { return static_cast<bool>(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 CreateSDLWindow();
|
||||||
bool CreateGLContext();
|
bool CreateDisplay();
|
||||||
bool CreateImGuiContext();
|
bool CreateImGuiContext();
|
||||||
bool CreateGLResources();
|
|
||||||
bool CreateAudioStream();
|
bool CreateAudioStream();
|
||||||
void UpdateAudioVisualSync();
|
void UpdateAudioVisualSync();
|
||||||
|
|
||||||
|
@ -85,8 +90,6 @@ private:
|
||||||
void HandleSDLKeyEvent(const SDL_Event* event);
|
void HandleSDLKeyEvent(const SDL_Event* event);
|
||||||
void ClearImGuiFocus();
|
void ClearImGuiFocus();
|
||||||
|
|
||||||
void Render();
|
|
||||||
void RenderDisplay();
|
|
||||||
void DrawMainMenuBar();
|
void DrawMainMenuBar();
|
||||||
void DrawPoweredOffWindow();
|
void DrawPoweredOffWindow();
|
||||||
void DrawAboutWindow();
|
void DrawAboutWindow();
|
||||||
|
@ -95,23 +98,8 @@ private:
|
||||||
void DrawDebugWindows();
|
void DrawDebugWindows();
|
||||||
|
|
||||||
SDL_Window* m_window = nullptr;
|
SDL_Window* m_window = nullptr;
|
||||||
SDL_GLContext m_gl_context = nullptr;
|
std::unique_ptr<HostDisplay> m_display;
|
||||||
int m_window_width = 0;
|
std::unique_ptr<HostDisplayTexture> m_app_icon_texture;
|
||||||
int m_window_height = 0;
|
|
||||||
|
|
||||||
std::unique_ptr<GL::Texture> 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::deque<OSDMessage> m_osd_messages;
|
std::deque<OSDMessage> m_osd_messages;
|
||||||
std::mutex m_osd_messages_lock;
|
std::mutex m_osd_messages_lock;
|
Loading…
Reference in a new issue