GPU/HW: Add D3D11 renderer and refactor host interface/display

This commit is contained in:
Connor McLaughlin 2019-11-04 00:39:48 +10:00
parent f244da86a2
commit 9de9cf3be2
30 changed files with 2101 additions and 415 deletions

View file

@ -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()

View file

@ -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" />

View file

@ -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" />

View file

@ -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()
{ {

View file

@ -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;

View file

@ -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;

View file

@ -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
View 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
View 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;
};

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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)

View file

@ -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
View 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);
}
};

View file

@ -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;
} }

View file

@ -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;
}; };

View file

@ -5,6 +5,7 @@ struct Settings
{ {
enum class GPURenderer enum class GPURenderer
{ {
HardwareD3D11,
HardwareOpenGL, HardwareOpenGL,
Software Software
}; };

View file

@ -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());

View file

@ -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()

View 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);
}

View 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;
};

View file

@ -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>

View file

@ -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>

View file

@ -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");

View 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, &current_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);
}

View 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;
};

View file

@ -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, &current_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)
{ {

View file

@ -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;