GPU/HW: Add D3D11 renderer and refactor host interface/display
This commit is contained in:
@ -55,5 +55,11 @@ add_library(core
|
||||
|
||||
target_include_directories(core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
target_include_directories(core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
target_link_libraries(core Threads::Threads YBaseLib common imgui stb)
|
||||
target_link_libraries(core PRIVATE Threads::Threads YBaseLib common imgui glad stb)
|
||||
|
||||
if(WIN32)
|
||||
target_sources(core PRIVATE
|
||||
gpu_hw_d3d11.cpp
|
||||
gpu_hw_d3d11.h
|
||||
)
|
||||
endif()
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
<ClCompile Include="cpu_disasm.cpp" />
|
||||
<ClCompile Include="digital_controller.cpp" />
|
||||
<ClCompile Include="gpu_commands.cpp" />
|
||||
<ClCompile Include="gpu_hw_d3d11.cpp" />
|
||||
<ClCompile Include="gpu_hw_shadergen.cpp" />
|
||||
<ClCompile Include="gpu_sw.cpp" />
|
||||
<ClCompile Include="gte.cpp" />
|
||||
@ -65,6 +66,7 @@
|
||||
<ClInclude Include="cpu_core.h" />
|
||||
<ClInclude Include="cpu_disasm.h" />
|
||||
<ClInclude Include="digital_controller.h" />
|
||||
<ClInclude Include="gpu_hw_d3d11.h" />
|
||||
<ClInclude Include="gpu_hw_shadergen.h" />
|
||||
<ClInclude Include="gpu_sw.h" />
|
||||
<ClInclude Include="gte.h" />
|
||||
@ -74,6 +76,7 @@
|
||||
<ClInclude Include="gpu_hw.h" />
|
||||
<ClInclude Include="gpu_hw_opengl.h" />
|
||||
<ClInclude Include="gte_types.h" />
|
||||
<ClInclude Include="host_display.h" />
|
||||
<ClInclude Include="host_interface.h" />
|
||||
<ClInclude Include="interrupt_controller.h" />
|
||||
<ClInclude Include="mdec.h" />
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
<ClCompile Include="gpu_commands.cpp" />
|
||||
<ClCompile Include="gpu_sw.cpp" />
|
||||
<ClCompile Include="gpu_hw_shadergen.cpp" />
|
||||
<ClCompile Include="gpu_hw_d3d11.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="types.h" />
|
||||
@ -52,6 +53,8 @@
|
||||
<ClInclude Include="settings.h" />
|
||||
<ClInclude Include="gpu_sw.h" />
|
||||
<ClInclude Include="gpu_hw_shadergen.h" />
|
||||
<ClInclude Include="gpu_hw_d3d11.h" />
|
||||
<ClInclude Include="host_display.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="cpu_core.inl" />
|
||||
|
||||
@ -17,8 +17,10 @@ GPU::GPU() = default;
|
||||
|
||||
GPU::~GPU() = default;
|
||||
|
||||
bool GPU::Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers)
|
||||
bool GPU::Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller,
|
||||
Timers* timers)
|
||||
{
|
||||
m_host_display = host_display;
|
||||
m_system = system;
|
||||
m_dma = dma;
|
||||
m_interrupt_controller = interrupt_controller;
|
||||
@ -26,10 +28,7 @@ bool GPU::Initialize(System* system, DMA* dma, InterruptController* interrupt_co
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU::UpdateSettings()
|
||||
{
|
||||
|
||||
}
|
||||
void GPU::UpdateSettings() {}
|
||||
|
||||
void GPU::Reset()
|
||||
{
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
|
||||
class StateWrapper;
|
||||
|
||||
class HostDisplay;
|
||||
|
||||
class System;
|
||||
class DMA;
|
||||
class InterruptController;
|
||||
@ -90,7 +92,8 @@ public:
|
||||
GPU();
|
||||
virtual ~GPU();
|
||||
|
||||
virtual bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers);
|
||||
virtual bool Initialize(HostDisplay* host_display, System* system, DMA* dma,
|
||||
InterruptController* interrupt_controller, Timers* timers);
|
||||
virtual void Reset();
|
||||
virtual bool DoState(StateWrapper& sw);
|
||||
|
||||
@ -116,6 +119,9 @@ public:
|
||||
// Ticks for hblank/vblank.
|
||||
void Execute(TickCount ticks);
|
||||
|
||||
// gpu_hw_d3d11.cpp
|
||||
static std::unique_ptr<GPU> CreateHardwareD3D11Renderer();
|
||||
|
||||
// gpu_hw_opengl.cpp
|
||||
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 FlushRender();
|
||||
|
||||
HostDisplay* m_host_display = nullptr;
|
||||
System* m_system = nullptr;
|
||||
DMA* m_dma = nullptr;
|
||||
InterruptController* m_interrupt_controller = nullptr;
|
||||
|
||||
@ -19,9 +19,10 @@ void GPU_HW::Reset()
|
||||
m_batch_ubo_dirty = true;
|
||||
}
|
||||
|
||||
bool GPU_HW::Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers)
|
||||
bool GPU_HW::Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller,
|
||||
Timers* timers)
|
||||
{
|
||||
if (!GPU::Initialize(system, dma, interrupt_controller, timers))
|
||||
if (!GPU::Initialize(host_display, system, dma, interrupt_controller, timers))
|
||||
return false;
|
||||
|
||||
m_resolution_scale = std::clamp<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;
|
||||
const BatchPrimitive rc_primitive = GetPrimitiveForCommand(rc);
|
||||
const bool dithering_enable = (!m_true_color && rc.IsDitheringEnabled()) ? m_GPUSTAT.dither_enable : false;
|
||||
const u32 max_added_vertices = num_vertices + 2;
|
||||
const u32 max_added_vertices = num_vertices + 5;
|
||||
if (!IsFlushed())
|
||||
{
|
||||
const bool buffer_overflow = GetBatchVertexSpace() < max_added_vertices;
|
||||
|
||||
@ -27,7 +27,8 @@ public:
|
||||
GPU_HW();
|
||||
virtual ~GPU_HW();
|
||||
|
||||
virtual bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers) override;
|
||||
virtual bool Initialize(HostDisplay* host_display, System* system, DMA* dma,
|
||||
InterruptController* interrupt_controller, Timers* timers) override;
|
||||
virtual void Reset() override;
|
||||
virtual void UpdateSettings() override;
|
||||
|
||||
|
||||
731
src/core/gpu_hw_d3d11.cpp
Normal file
731
src/core/gpu_hw_d3d11.cpp
Normal file
@ -0,0 +1,731 @@
|
||||
#include "gpu_hw_d3d11.h"
|
||||
#include "YBaseLib/Assert.h"
|
||||
#include "YBaseLib/Log.h"
|
||||
#include "YBaseLib/String.h"
|
||||
#include "common/d3d11/shader_compiler.h"
|
||||
#include "gpu_hw_shadergen.h"
|
||||
#include "host_display.h"
|
||||
#include "host_interface.h"
|
||||
#include "imgui.h"
|
||||
#include "system.h"
|
||||
Log_SetChannel(GPU_HW_D3D11);
|
||||
|
||||
GPU_HW_D3D11::GPU_HW_D3D11() = default;
|
||||
|
||||
GPU_HW_D3D11::~GPU_HW_D3D11()
|
||||
{
|
||||
m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, 1.0f);
|
||||
}
|
||||
|
||||
bool GPU_HW_D3D11::Initialize(HostDisplay* host_display, System* system, DMA* dma,
|
||||
InterruptController* interrupt_controller, Timers* timers)
|
||||
{
|
||||
SetCapabilities();
|
||||
|
||||
if (!GPU_HW::Initialize(host_display, system, dma, interrupt_controller, timers))
|
||||
return false;
|
||||
|
||||
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::D3D11)
|
||||
{
|
||||
Log_ErrorPrintf("Host render API is incompatible");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_device = static_cast<ID3D11Device*>(host_display->GetHostRenderDevice());
|
||||
m_context = static_cast<ID3D11DeviceContext*>(host_display->GetHostRenderContext());
|
||||
if (!m_device || !m_context)
|
||||
return false;
|
||||
|
||||
if (!CreateFramebuffer())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create framebuffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateVertexBuffer())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create vertex buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateUniformBuffer())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create uniform buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateTextureBuffer())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create texture buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateStateObjects())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create state objects");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateBatchInputLayout())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create batch input layout");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CompileShaders())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to compile shaders");
|
||||
return false;
|
||||
}
|
||||
|
||||
RestoreGraphicsAPIState();
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::Reset()
|
||||
{
|
||||
GPU_HW::Reset();
|
||||
|
||||
ClearFramebuffer();
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::ResetGraphicsAPIState()
|
||||
{
|
||||
GPU_HW::ResetGraphicsAPIState();
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::RestoreGraphicsAPIState()
|
||||
{
|
||||
const UINT stride = sizeof(BatchVertex);
|
||||
const UINT offset = 0;
|
||||
m_context->IASetVertexBuffers(0, 1, m_vertex_stream_buffer.GetD3DBufferArray(), &stride, &offset);
|
||||
m_context->IASetInputLayout(m_batch_input_layout.Get());
|
||||
m_context->PSSetShaderResources(0, 1, m_vram_read_texture.GetD3DSRVArray());
|
||||
m_context->OMSetDepthStencilState(m_depth_disabled_state.Get(), 0);
|
||||
m_context->OMSetRenderTargets(1, m_vram_texture.GetD3DRTVArray(), nullptr);
|
||||
m_context->RSSetState(m_cull_none_rasterizer_state.Get());
|
||||
SetViewport(0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight());
|
||||
m_drawing_area_changed = true;
|
||||
m_batch_ubo_dirty = true;
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::UpdateSettings()
|
||||
{
|
||||
GPU_HW::UpdateSettings();
|
||||
|
||||
CreateFramebuffer();
|
||||
CompileShaders();
|
||||
UpdateDisplay();
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::DrawRendererStatsWindow()
|
||||
{
|
||||
GPU_HW::DrawRendererStatsWindow();
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(300.0f, 150.0f), ImGuiCond_FirstUseEver);
|
||||
|
||||
const bool is_null_frame = m_stats.num_batches == 0;
|
||||
if (!is_null_frame)
|
||||
{
|
||||
m_last_stats = m_stats;
|
||||
m_stats = {};
|
||||
}
|
||||
|
||||
if (ImGui::Begin("GPU Renderer Statistics", &m_show_renderer_statistics))
|
||||
{
|
||||
ImGui::Columns(2);
|
||||
ImGui::SetColumnWidth(0, 200.0f);
|
||||
|
||||
ImGui::TextUnformatted("GPU Active In This Frame: ");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%s", is_null_frame ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::TextUnformatted("VRAM Reads: ");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%u", m_last_stats.num_vram_reads);
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::TextUnformatted("VRAM Writes: ");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%u", m_last_stats.num_vram_writes);
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::TextUnformatted("VRAM Read Texture Updates:");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%u", m_last_stats.num_vram_read_texture_updates);
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::TextUnformatted("Batches Drawn:");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%u", m_last_stats.num_batches);
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::TextUnformatted("Vertices Drawn: ");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%u", m_last_stats.num_vertices);
|
||||
ImGui::NextColumn();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::InvalidateVRAMReadCache()
|
||||
{
|
||||
m_vram_read_texture_dirty = true;
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::MapBatchVertexPointer(u32 required_vertices)
|
||||
{
|
||||
Assert(!m_batch_start_vertex_ptr);
|
||||
|
||||
const D3D11::StreamBuffer::MappingResult res =
|
||||
m_vertex_stream_buffer.Map(m_context.Get(), sizeof(BatchVertex), required_vertices * sizeof(BatchVertex));
|
||||
|
||||
m_batch_start_vertex_ptr = static_cast<BatchVertex*>(res.pointer);
|
||||
m_batch_current_vertex_ptr = m_batch_start_vertex_ptr;
|
||||
m_batch_end_vertex_ptr = m_batch_start_vertex_ptr + res.space_aligned;
|
||||
m_batch_base_vertex = res.index_aligned;
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::SetCapabilities()
|
||||
{
|
||||
const u32 max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
||||
Log_InfoPrintf("Max texture size: %dx%d", max_texture_size, max_texture_size);
|
||||
const u32 max_texture_scale = max_texture_size / VRAM_WIDTH;
|
||||
|
||||
m_max_resolution_scale = max_texture_scale;
|
||||
Log_InfoPrintf("Maximum resolution scale is %u", m_max_resolution_scale);
|
||||
}
|
||||
|
||||
bool GPU_HW_D3D11::CreateFramebuffer()
|
||||
{
|
||||
// save old vram texture/fbo, in case we're changing scale
|
||||
auto old_vram_texture = std::move(m_vram_texture);
|
||||
DestroyFramebuffer();
|
||||
|
||||
// scale vram size to internal resolution
|
||||
const u32 texture_width = VRAM_WIDTH * m_resolution_scale;
|
||||
const u32 texture_height = VRAM_HEIGHT * m_resolution_scale;
|
||||
const DXGI_FORMAT texture_format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
if (!m_vram_texture.Create(m_device.Get(), texture_width, texture_height, texture_format, true, true) ||
|
||||
!m_vram_read_texture.Create(m_device.Get(), texture_width, texture_height, texture_format, true, false) ||
|
||||
!m_display_texture.Create(m_device.Get(), texture_width, texture_height, texture_format, true, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// do we need to restore the framebuffer after a size change?
|
||||
if (old_vram_texture)
|
||||
{
|
||||
const bool linear_filter = old_vram_texture.GetWidth() > m_vram_texture.GetWidth();
|
||||
Log_DevPrintf("Scaling %ux%u VRAM texture to %ux%u using %s filter", old_vram_texture.GetWidth(),
|
||||
old_vram_texture.GetHeight(), m_vram_texture.GetWidth(), m_vram_texture.GetHeight(),
|
||||
linear_filter ? "linear" : "nearest");
|
||||
|
||||
BlitTexture(m_vram_texture.GetD3DRTV(), 0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight(),
|
||||
old_vram_texture.GetD3DSRV(), 0, 0, old_vram_texture.GetWidth(), old_vram_texture.GetHeight(),
|
||||
old_vram_texture.GetWidth(), old_vram_texture.GetHeight(), linear_filter);
|
||||
}
|
||||
|
||||
if (m_resolution_scale > 1 &&
|
||||
!m_vram_downsample_texture.Create(m_device.Get(), texture_width, texture_height, texture_format, true, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_context->OMSetRenderTargets(1, m_vram_texture.GetD3DRTVArray(), nullptr);
|
||||
m_vram_read_texture_dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::ClearFramebuffer()
|
||||
{
|
||||
static constexpr std::array<float, 4> color = {};
|
||||
m_context->ClearRenderTargetView(m_vram_texture.GetD3DRTV(), color.data());
|
||||
m_vram_read_texture_dirty = true;
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::DestroyFramebuffer()
|
||||
{
|
||||
m_vram_read_texture.Destroy();
|
||||
m_vram_texture.Destroy();
|
||||
m_vram_downsample_texture.Destroy();
|
||||
m_display_texture.Destroy();
|
||||
}
|
||||
|
||||
bool GPU_HW_D3D11::CreateVertexBuffer()
|
||||
{
|
||||
return m_vertex_stream_buffer.Create(m_device.Get(), D3D11_BIND_VERTEX_BUFFER, VERTEX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
bool GPU_HW_D3D11::CreateUniformBuffer()
|
||||
{
|
||||
return m_uniform_stream_buffer.Create(m_device.Get(), D3D11_BIND_CONSTANT_BUFFER, UNIFORM_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
bool GPU_HW_D3D11::CreateTextureBuffer()
|
||||
{
|
||||
if (!m_texture_stream_buffer.Create(m_device.Get(), D3D11_BIND_SHADER_RESOURCE, VRAM_UPDATE_TEXTURE_BUFFER_SIZE))
|
||||
return false;
|
||||
|
||||
const CD3D11_SHADER_RESOURCE_VIEW_DESC srv_desc(D3D11_SRV_DIMENSION_BUFFER, DXGI_FORMAT_R16_UINT, 0,
|
||||
VRAM_UPDATE_TEXTURE_BUFFER_SIZE / sizeof(u16));
|
||||
const HRESULT hr = m_device->CreateShaderResourceView(m_texture_stream_buffer.GetD3DBuffer(), &srv_desc,
|
||||
m_texture_stream_buffer_srv_r16ui.ReleaseAndGetAddressOf());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Log_ErrorPrintf("Creation of texture buffer SRV failed: 0x%08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU_HW_D3D11::CreateBatchInputLayout()
|
||||
{
|
||||
static constexpr std::array<D3D11_INPUT_ELEMENT_DESC, 4> attributes = {
|
||||
{{"ATTR", 0, DXGI_FORMAT_R32G32_SINT, 0, offsetof(BatchVertex, x), D3D11_INPUT_PER_VERTEX_DATA, 0},
|
||||
{"ATTR", 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(BatchVertex, color), D3D11_INPUT_PER_VERTEX_DATA, 0},
|
||||
{"ATTR", 2, DXGI_FORMAT_R32_SINT, 0, offsetof(BatchVertex, texcoord), D3D11_INPUT_PER_VERTEX_DATA, 0},
|
||||
{"ATTR", 3, DXGI_FORMAT_R32_SINT, 0, offsetof(BatchVertex, texpage), D3D11_INPUT_PER_VERTEX_DATA, 0}}};
|
||||
|
||||
// we need a vertex shader...
|
||||
GPU_HW_ShaderGen shadergen(GPU_HW_ShaderGen::API::D3D11, m_resolution_scale, m_true_color);
|
||||
ComPtr<ID3DBlob> vs_bytecode = D3D11::ShaderCompiler::CompileShader(
|
||||
D3D11::ShaderCompiler::Type::Vertex, m_device->GetFeatureLevel(), shadergen.GenerateBatchVertexShader(true), false);
|
||||
if (!vs_bytecode)
|
||||
return false;
|
||||
|
||||
const HRESULT hr = m_device->CreateInputLayout(attributes.data(), static_cast<UINT>(attributes.size()),
|
||||
vs_bytecode->GetBufferPointer(), vs_bytecode->GetBufferSize(),
|
||||
m_batch_input_layout.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Log_ErrorPrintf("CreateInputLayout failed: 0x%08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU_HW_D3D11::CreateStateObjects()
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
CD3D11_RASTERIZER_DESC rs_desc = CD3D11_RASTERIZER_DESC(CD3D11_DEFAULT());
|
||||
rs_desc.CullMode = D3D11_CULL_NONE;
|
||||
rs_desc.ScissorEnable = TRUE;
|
||||
hr = m_device->CreateRasterizerState(&rs_desc, m_cull_none_rasterizer_state.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
CD3D11_DEPTH_STENCIL_DESC ds_desc = CD3D11_DEPTH_STENCIL_DESC(CD3D11_DEFAULT());
|
||||
ds_desc.DepthEnable = FALSE;
|
||||
ds_desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
|
||||
hr = m_device->CreateDepthStencilState(&ds_desc, m_depth_disabled_state.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
CD3D11_BLEND_DESC bl_desc = CD3D11_BLEND_DESC(CD3D11_DEFAULT());
|
||||
hr = m_device->CreateBlendState(&bl_desc, m_blend_disabled_state.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
CD3D11_SAMPLER_DESC sampler_desc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT());
|
||||
sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
|
||||
hr = m_device->CreateSamplerState(&sampler_desc, m_point_sampler_state.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
sampler_desc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
|
||||
hr = m_device->CreateSamplerState(&sampler_desc, m_linear_sampler_state.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
m_batch_blend_states[static_cast<u8>(TransparencyMode::Disabled)] = m_blend_disabled_state;
|
||||
|
||||
for (u8 transparency_mode = 0; transparency_mode < 4; transparency_mode++)
|
||||
{
|
||||
bl_desc.RenderTarget[0].BlendEnable = TRUE;
|
||||
bl_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
|
||||
bl_desc.RenderTarget[0].DestBlend = D3D11_BLEND_SRC_ALPHA;
|
||||
bl_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
|
||||
bl_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
|
||||
bl_desc.RenderTarget[0].BlendOp =
|
||||
(transparency_mode == static_cast<u8>(TransparencyMode::BackgroundMinusForeground)) ?
|
||||
D3D11_BLEND_OP_REV_SUBTRACT :
|
||||
D3D11_BLEND_OP_ADD;
|
||||
bl_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
|
||||
|
||||
hr = m_device->CreateBlendState(&bl_desc, m_batch_blend_states[transparency_mode].GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU_HW_D3D11::CompileShaders()
|
||||
{
|
||||
const bool debug = false;
|
||||
GPU_HW_ShaderGen shadergen(GPU_HW_ShaderGen::API::D3D11, m_resolution_scale, m_true_color);
|
||||
|
||||
m_screen_quad_vertex_shader = D3D11::ShaderCompiler::CompileAndCreateVertexShader(
|
||||
m_device.Get(), shadergen.GenerateScreenQuadVertexShader(), debug);
|
||||
if (!m_screen_quad_vertex_shader)
|
||||
return false;
|
||||
|
||||
for (u8 textured = 0; textured < 2; textured++)
|
||||
{
|
||||
const std::string vs = shadergen.GenerateBatchVertexShader(ConvertToBoolUnchecked(textured));
|
||||
m_batch_vertex_shaders[textured] = D3D11::ShaderCompiler::CompileAndCreateVertexShader(m_device.Get(), vs, debug);
|
||||
if (!m_batch_vertex_shaders[textured])
|
||||
return false;
|
||||
}
|
||||
|
||||
for (u8 render_mode = 0; render_mode < 4; render_mode++)
|
||||
{
|
||||
for (u8 texture_mode = 0; texture_mode < 9; texture_mode++)
|
||||
{
|
||||
for (u8 dithering = 0; dithering < 2; dithering++)
|
||||
{
|
||||
const std::string ps = shadergen.GenerateBatchFragmentShader(static_cast<BatchRenderMode>(render_mode),
|
||||
static_cast<TextureMode>(texture_mode),
|
||||
ConvertToBoolUnchecked(dithering));
|
||||
|
||||
m_batch_pixel_shaders[render_mode][texture_mode][dithering] =
|
||||
D3D11::ShaderCompiler::CompileAndCreatePixelShader(m_device.Get(), ps, debug);
|
||||
if (!m_batch_pixel_shaders[render_mode][texture_mode][dithering])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_copy_pixel_shader =
|
||||
D3D11::ShaderCompiler::CompileAndCreatePixelShader(m_device.Get(), shadergen.GenerateCopyFragmentShader(), debug);
|
||||
if (!m_copy_pixel_shader)
|
||||
return false;
|
||||
|
||||
m_fill_pixel_shader =
|
||||
D3D11::ShaderCompiler::CompileAndCreatePixelShader(m_device.Get(), shadergen.GenerateFillFragmentShader(), debug);
|
||||
if (!m_fill_pixel_shader)
|
||||
return false;
|
||||
|
||||
m_vram_write_pixel_shader = D3D11::ShaderCompiler::CompileAndCreatePixelShader(
|
||||
m_device.Get(), shadergen.GenerateVRAMWriteFragmentShader(), debug);
|
||||
if (!m_vram_write_pixel_shader)
|
||||
return false;
|
||||
|
||||
for (u8 depth_24bit = 0; depth_24bit < 2; depth_24bit++)
|
||||
{
|
||||
for (u8 interlaced = 0; interlaced < 2; interlaced++)
|
||||
{
|
||||
const std::string ps = shadergen.GenerateDisplayFragmentShader(ConvertToBoolUnchecked(depth_24bit),
|
||||
ConvertToBoolUnchecked(interlaced));
|
||||
m_display_pixel_shaders[depth_24bit][interlaced] =
|
||||
D3D11::ShaderCompiler::CompileAndCreatePixelShader(m_device.Get(), ps, debug);
|
||||
if (!m_display_pixel_shaders[depth_24bit][interlaced])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::UploadUniformBlock(const void* data, u32 data_size)
|
||||
{
|
||||
const auto res = m_uniform_stream_buffer.Map(m_context.Get(), m_uniform_stream_buffer.GetSize(), data_size);
|
||||
std::memcpy(res.pointer, data, data_size);
|
||||
m_uniform_stream_buffer.Unmap(m_context.Get(), data_size);
|
||||
|
||||
m_context->VSSetConstantBuffers(0, 1, m_uniform_stream_buffer.GetD3DBufferArray());
|
||||
m_context->PSSetConstantBuffers(0, 1, m_uniform_stream_buffer.GetD3DBufferArray());
|
||||
|
||||
m_stats.num_uniform_buffer_updates++;
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::SetViewport(u32 x, u32 y, u32 width, u32 height)
|
||||
{
|
||||
const CD3D11_VIEWPORT vp(static_cast<float>(x), static_cast<float>(y), static_cast<float>(width),
|
||||
static_cast<float>(height));
|
||||
m_context->RSSetViewports(1, &vp);
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::SetScissor(u32 x, u32 y, u32 width, u32 height)
|
||||
{
|
||||
const CD3D11_RECT rc(x, y, x + width, y + height);
|
||||
m_context->RSSetScissorRects(1, &rc);
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::SetViewportAndScissor(u32 x, u32 y, u32 width, u32 height)
|
||||
{
|
||||
SetViewport(x, y, width, height);
|
||||
SetScissor(x, y, width, height);
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::BlitTexture(ID3D11RenderTargetView* dst, u32 dst_x, u32 dst_y, u32 dst_width, u32 dst_height,
|
||||
ID3D11ShaderResourceView* src, u32 src_x, u32 src_y, u32 src_width, u32 src_height,
|
||||
u32 src_texture_width, u32 src_texture_height, bool linear_filter)
|
||||
{
|
||||
const float uniforms[4] = {static_cast<float>(src_x) / static_cast<float>(src_texture_width),
|
||||
static_cast<float>(src_y) / static_cast<float>(src_texture_height),
|
||||
static_cast<float>(src_width) / static_cast<float>(src_texture_width),
|
||||
static_cast<float>(src_height) / static_cast<float>(src_texture_height)};
|
||||
|
||||
m_context->OMSetRenderTargets(1, &dst, nullptr);
|
||||
SetViewport(dst_x, dst_y, dst_width, dst_height);
|
||||
SetScissor(dst_x, dst_y, dst_width, dst_height);
|
||||
DrawUtilityShader(m_copy_pixel_shader.Get(), uniforms, sizeof(uniforms));
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::DrawUtilityShader(ID3D11PixelShader* shader, const void* uniforms, u32 uniforms_size)
|
||||
{
|
||||
if (uniforms)
|
||||
{
|
||||
UploadUniformBlock(uniforms, uniforms_size);
|
||||
m_batch_ubo_dirty = true;
|
||||
}
|
||||
|
||||
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
m_context->VSSetShader(m_screen_quad_vertex_shader.Get(), nullptr, 0);
|
||||
m_context->PSSetShader(shader, nullptr, 0);
|
||||
m_context->OMSetBlendState(m_blend_disabled_state.Get(), nullptr, 0xFFFFFFFFu);
|
||||
|
||||
m_context->Draw(3, 0);
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::SetDrawState(BatchRenderMode render_mode)
|
||||
{
|
||||
const bool textured = (m_batch.texture_mode != TextureMode::Disabled);
|
||||
|
||||
static constexpr std::array<D3D11_PRIMITIVE_TOPOLOGY, 4> d3d_primitives = {
|
||||
{D3D11_PRIMITIVE_TOPOLOGY_LINELIST, D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
|
||||
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP}};
|
||||
m_context->IASetPrimitiveTopology(d3d_primitives[static_cast<u8>(m_batch.primitive)]);
|
||||
|
||||
m_context->VSSetShader(m_batch_vertex_shaders[BoolToUInt8(textured)].Get(), nullptr, 0);
|
||||
|
||||
m_context->PSSetShader(m_batch_pixel_shaders[static_cast<u8>(render_mode)][static_cast<u8>(m_batch.texture_mode)]
|
||||
[BoolToUInt8(m_batch.dithering)]
|
||||
.Get(),
|
||||
nullptr, 0);
|
||||
|
||||
const TransparencyMode transparency_mode =
|
||||
(render_mode == BatchRenderMode::OnlyOpaque) ? TransparencyMode::Disabled : m_batch.transparency_mode;
|
||||
m_context->OMSetBlendState(m_batch_blend_states[static_cast<u8>(transparency_mode)].Get(), nullptr, 0xFFFFFFFFu);
|
||||
|
||||
if (m_drawing_area_changed)
|
||||
{
|
||||
m_drawing_area_changed = false;
|
||||
|
||||
int left, top, right, bottom;
|
||||
CalcScissorRect(&left, &top, &right, &bottom);
|
||||
|
||||
CD3D11_RECT rc(left, top, right, bottom);
|
||||
m_context->RSSetScissorRects(1, &rc);
|
||||
}
|
||||
|
||||
if (m_batch_ubo_dirty)
|
||||
{
|
||||
UploadUniformBlock(&m_batch_ubo_data, sizeof(m_batch_ubo_data));
|
||||
m_batch_ubo_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::UpdateDrawingArea()
|
||||
{
|
||||
m_drawing_area_changed = true;
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::UpdateDisplay()
|
||||
{
|
||||
GPU_HW::UpdateDisplay();
|
||||
|
||||
if (m_system->GetSettings().debugging.show_vram)
|
||||
{
|
||||
m_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), 0, 0, m_vram_texture.GetWidth(),
|
||||
m_vram_texture.GetHeight(), m_vram_texture.GetWidth(), m_vram_texture.GetHeight(),
|
||||
1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
const u32 vram_offset_x = m_crtc_state.regs.X;
|
||||
const u32 vram_offset_y = m_crtc_state.regs.Y;
|
||||
const u32 scaled_vram_offset_x = vram_offset_x * m_resolution_scale;
|
||||
const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale;
|
||||
const u32 display_width = std::min<u32>(m_crtc_state.display_width, VRAM_WIDTH - vram_offset_x);
|
||||
const u32 display_height = std::min<u32>(m_crtc_state.display_height << BoolToUInt8(m_GPUSTAT.vertical_interlace),
|
||||
VRAM_HEIGHT - vram_offset_y);
|
||||
const u32 scaled_display_width = display_width * m_resolution_scale;
|
||||
const u32 scaled_display_height = display_height * m_resolution_scale;
|
||||
|
||||
if (m_GPUSTAT.display_disable)
|
||||
{
|
||||
m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, m_crtc_state.display_aspect_ratio);
|
||||
}
|
||||
else if (!m_GPUSTAT.display_area_color_depth_24 && !m_GPUSTAT.vertical_interlace)
|
||||
{
|
||||
const CD3D11_BOX src_box(scaled_vram_offset_x, scaled_vram_offset_y, 0,
|
||||
scaled_vram_offset_x + scaled_display_width,
|
||||
scaled_vram_offset_y + scaled_display_height, 1);
|
||||
m_context->CopySubresourceRegion(m_display_texture.GetD3DTexture(), 0, 0, 0, 0, m_vram_texture.GetD3DTexture(), 0,
|
||||
&src_box);
|
||||
|
||||
m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), 0, 0, scaled_display_width,
|
||||
scaled_display_height, m_display_texture.GetWidth(),
|
||||
m_display_texture.GetHeight(), m_crtc_state.display_aspect_ratio);
|
||||
}
|
||||
else
|
||||
{
|
||||
const u32 field_offset = BoolToUInt8(m_GPUSTAT.vertical_interlace && !m_GPUSTAT.drawing_even_line);
|
||||
const u32 scaled_field_offset = field_offset * m_resolution_scale;
|
||||
|
||||
ID3D11PixelShader* display_pixel_shader =
|
||||
m_display_pixel_shaders[BoolToUInt8(m_GPUSTAT.display_area_color_depth_24)]
|
||||
[BoolToUInt8(m_GPUSTAT.vertical_interlace)]
|
||||
.Get();
|
||||
|
||||
// Because of how the reinterpret shader works, we need to use the downscaled version.
|
||||
if (m_GPUSTAT.display_area_color_depth_24 && m_resolution_scale > 1)
|
||||
{
|
||||
const u32 copy_width = std::min<u32>((display_width * 4) / 3, VRAM_WIDTH - vram_offset_x);
|
||||
const u32 scaled_copy_width = copy_width * m_resolution_scale;
|
||||
BlitTexture(m_vram_downsample_texture.GetD3DRTV(), vram_offset_x, vram_offset_y, copy_width, display_height,
|
||||
m_vram_texture.GetD3DSRV(), scaled_vram_offset_x, scaled_vram_offset_y, scaled_copy_width,
|
||||
scaled_display_height, m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), false);
|
||||
|
||||
m_context->OMSetRenderTargets(1, m_display_texture.GetD3DRTVArray(), nullptr);
|
||||
m_context->PSSetShaderResources(0, 1, m_vram_downsample_texture.GetD3DSRVArray());
|
||||
|
||||
const u32 uniforms[4] = {vram_offset_x, vram_offset_y, field_offset};
|
||||
SetViewportAndScissor(0, scaled_field_offset, display_width, display_height);
|
||||
DrawUtilityShader(display_pixel_shader, uniforms, sizeof(uniforms));
|
||||
UploadUniformBlock(uniforms, sizeof(uniforms));
|
||||
|
||||
m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), 0, 0, display_width, display_height,
|
||||
m_display_texture.GetWidth(), m_display_texture.GetHeight(),
|
||||
m_crtc_state.display_aspect_ratio);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_context->OMSetRenderTargets(1, m_display_texture.GetD3DRTVArray(), nullptr);
|
||||
m_context->PSSetShaderResources(0, 1, m_vram_texture.GetD3DSRVArray());
|
||||
|
||||
const u32 uniforms[4] = {scaled_vram_offset_x, scaled_vram_offset_y, scaled_field_offset};
|
||||
SetViewportAndScissor(0, scaled_field_offset, scaled_display_width, scaled_display_height);
|
||||
DrawUtilityShader(display_pixel_shader, uniforms, sizeof(uniforms));
|
||||
UploadUniformBlock(uniforms, sizeof(uniforms));
|
||||
|
||||
m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), 0, 0, scaled_display_width,
|
||||
scaled_display_height, m_display_texture.GetWidth(),
|
||||
m_display_texture.GetHeight(), m_crtc_state.display_aspect_ratio);
|
||||
}
|
||||
|
||||
RestoreGraphicsAPIState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::ReadVRAM(u32 x, u32 y, u32 width, u32 height, void* buffer)
|
||||
{
|
||||
Log_WarningPrintf("VRAM readback not implemented");
|
||||
m_stats.num_vram_reads++;
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color)
|
||||
{
|
||||
// drop precision unless true colour is enabled
|
||||
if (!m_true_color)
|
||||
color = RGBA5551ToRGBA8888(RGBA8888ToRGBA5551(color));
|
||||
|
||||
float uniforms[4];
|
||||
std::tie(uniforms[0], uniforms[1], uniforms[2], uniforms[3]) = RGBA8ToFloat(color);
|
||||
|
||||
SetViewportAndScissor(x * m_resolution_scale, y * m_resolution_scale, width * m_resolution_scale,
|
||||
height * m_resolution_scale);
|
||||
DrawUtilityShader(m_fill_pixel_shader.Get(), uniforms, sizeof(uniforms));
|
||||
|
||||
RestoreGraphicsAPIState();
|
||||
InvalidateVRAMReadCache();
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data)
|
||||
{
|
||||
const u32 num_pixels = width * height;
|
||||
const auto map_result = m_texture_stream_buffer.Map(m_context.Get(), sizeof(u16), num_pixels * sizeof(u16));
|
||||
std::memcpy(map_result.pointer, data, num_pixels * sizeof(u16));
|
||||
m_texture_stream_buffer.Unmap(m_context.Get(), num_pixels * sizeof(u16));
|
||||
|
||||
const u32 uniforms[5] = {x, y, width, height, map_result.index_aligned};
|
||||
m_context->PSSetShaderResources(0, 1, m_texture_stream_buffer_srv_r16ui.GetAddressOf());
|
||||
|
||||
// the viewport should already be set to the full vram, so just adjust the scissor
|
||||
SetScissor(x * m_resolution_scale, y * m_resolution_scale, width * m_resolution_scale, height * m_resolution_scale);
|
||||
|
||||
DrawUtilityShader(m_vram_write_pixel_shader.Get(), uniforms, sizeof(uniforms));
|
||||
|
||||
RestoreGraphicsAPIState();
|
||||
InvalidateVRAMReadCache();
|
||||
m_stats.num_vram_writes++;
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height)
|
||||
{
|
||||
src_x *= m_resolution_scale;
|
||||
src_y *= m_resolution_scale;
|
||||
dst_x *= m_resolution_scale;
|
||||
dst_y *= m_resolution_scale;
|
||||
width *= m_resolution_scale;
|
||||
height *= m_resolution_scale;
|
||||
|
||||
const CD3D11_BOX src_box(src_x, src_y, 0, src_x + width, src_y + height, 1);
|
||||
m_context->CopySubresourceRegion(m_vram_texture, 0, dst_x, dst_y, 0, m_vram_texture, 0, &src_box);
|
||||
InvalidateVRAMReadCache();
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::UpdateVRAMReadTexture()
|
||||
{
|
||||
m_stats.num_vram_read_texture_updates++;
|
||||
m_vram_read_texture_dirty = false;
|
||||
|
||||
const CD3D11_BOX src_box(0, 0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 1);
|
||||
m_context->CopySubresourceRegion(m_vram_read_texture, 0, 0, 0, 0, m_vram_texture, 0, &src_box);
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::FlushRender()
|
||||
{
|
||||
const u32 vertex_count = GetBatchVertexCount();
|
||||
if (vertex_count == 0)
|
||||
return;
|
||||
|
||||
if (m_vram_read_texture_dirty)
|
||||
UpdateVRAMReadTexture();
|
||||
|
||||
m_stats.num_batches++;
|
||||
m_stats.num_vertices += vertex_count;
|
||||
|
||||
m_vertex_stream_buffer.Unmap(m_context.Get(), vertex_count * sizeof(BatchVertex));
|
||||
m_batch_start_vertex_ptr = nullptr;
|
||||
m_batch_end_vertex_ptr = nullptr;
|
||||
m_batch_current_vertex_ptr = nullptr;
|
||||
|
||||
if (m_batch.NeedsTwoPassRendering())
|
||||
{
|
||||
SetDrawState(BatchRenderMode::OnlyTransparent);
|
||||
m_context->Draw(vertex_count, m_batch_base_vertex);
|
||||
SetDrawState(BatchRenderMode::OnlyOpaque);
|
||||
m_context->Draw(vertex_count, m_batch_base_vertex);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetDrawState(m_batch.GetRenderMode());
|
||||
m_context->Draw(vertex_count, m_batch_base_vertex);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<GPU> GPU::CreateHardwareD3D11Renderer()
|
||||
{
|
||||
return std::make_unique<GPU_HW_D3D11>();
|
||||
}
|
||||
122
src/core/gpu_hw_d3d11.h
Normal file
122
src/core/gpu_hw_d3d11.h
Normal file
@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
#include "common/d3d11/staging_texture.h"
|
||||
#include "common/d3d11/stream_buffer.h"
|
||||
#include "common/d3d11/texture.h"
|
||||
#include "gpu_hw.h"
|
||||
#include <array>
|
||||
#include <d3d11.h>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <wrl/client.h>
|
||||
|
||||
class GPU_HW_D3D11 : public GPU_HW
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
using ComPtr = Microsoft::WRL::ComPtr<T>;
|
||||
|
||||
GPU_HW_D3D11();
|
||||
~GPU_HW_D3D11() override;
|
||||
|
||||
bool Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller,
|
||||
Timers* timers) override;
|
||||
void Reset() override;
|
||||
|
||||
void ResetGraphicsAPIState() override;
|
||||
void RestoreGraphicsAPIState() override;
|
||||
void UpdateSettings() override;
|
||||
|
||||
void DrawRendererStatsWindow() override;
|
||||
|
||||
protected:
|
||||
void UpdateDisplay() override;
|
||||
void UpdateDrawingArea() override;
|
||||
void ReadVRAM(u32 x, u32 y, u32 width, u32 height, void* buffer) override;
|
||||
void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) override;
|
||||
void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data) override;
|
||||
void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height) override;
|
||||
void FlushRender() override;
|
||||
void InvalidateVRAMReadCache() override;
|
||||
void MapBatchVertexPointer(u32 required_vertices) override;
|
||||
|
||||
private:
|
||||
struct GLStats
|
||||
{
|
||||
u32 num_batches;
|
||||
u32 num_vertices;
|
||||
u32 num_vram_reads;
|
||||
u32 num_vram_writes;
|
||||
u32 num_vram_read_texture_updates;
|
||||
u32 num_uniform_buffer_updates;
|
||||
};
|
||||
|
||||
void SetCapabilities();
|
||||
bool CreateFramebuffer();
|
||||
void ClearFramebuffer();
|
||||
void DestroyFramebuffer();
|
||||
void UpdateVRAMReadTexture();
|
||||
|
||||
bool CreateVertexBuffer();
|
||||
bool CreateUniformBuffer();
|
||||
bool CreateTextureBuffer();
|
||||
bool CreateBatchInputLayout();
|
||||
bool CreateStateObjects();
|
||||
|
||||
bool CompileShaders();
|
||||
void SetDrawState(BatchRenderMode render_mode);
|
||||
void UploadUniformBlock(const void* data, u32 data_size);
|
||||
void SetViewport(u32 x, u32 y, u32 width, u32 height);
|
||||
void SetScissor(u32 x, u32 y, u32 width, u32 height);
|
||||
void SetViewportAndScissor(u32 x, u32 y, u32 width, u32 height);
|
||||
|
||||
/// Blits from src to dst, downscaling or upscaling in the process.
|
||||
void BlitTexture(ID3D11RenderTargetView* dst, u32 dst_x, u32 dst_y, u32 dst_width, u32 dst_height,
|
||||
ID3D11ShaderResourceView* src, u32 src_x, u32 src_y, u32 src_width, u32 src_height,
|
||||
u32 src_texture_width, u32 src_texture_height, bool linear_filter);
|
||||
|
||||
void DrawUtilityShader(ID3D11PixelShader* shader, const void* uniforms, u32 uniforms_size);
|
||||
|
||||
ComPtr<ID3D11Device> m_device;
|
||||
ComPtr<ID3D11DeviceContext> m_context;
|
||||
|
||||
// downsample texture - used for readbacks at >1xIR.
|
||||
D3D11::Texture m_vram_texture;
|
||||
D3D11::Texture m_vram_read_texture;
|
||||
D3D11::Texture m_vram_downsample_texture;
|
||||
D3D11::Texture m_display_texture;
|
||||
|
||||
D3D11::StreamBuffer m_vertex_stream_buffer;
|
||||
|
||||
D3D11::StreamBuffer m_uniform_stream_buffer;
|
||||
|
||||
D3D11::StreamBuffer m_texture_stream_buffer;
|
||||
ComPtr<ID3D11ShaderResourceView> m_texture_stream_buffer_srv_r16ui;
|
||||
|
||||
ComPtr<ID3D11RasterizerState> m_cull_none_rasterizer_state;
|
||||
|
||||
ComPtr<ID3D11DepthStencilState> m_depth_disabled_state;
|
||||
|
||||
ComPtr<ID3D11BlendState> m_blend_disabled_state;
|
||||
|
||||
ComPtr<ID3D11SamplerState> m_point_sampler_state;
|
||||
ComPtr<ID3D11SamplerState> m_linear_sampler_state;
|
||||
|
||||
std::array<ComPtr<ID3D11BlendState>, 5> m_batch_blend_states; // [transparency_mode]
|
||||
ComPtr<ID3D11InputLayout> m_batch_input_layout;
|
||||
std::array<ComPtr<ID3D11VertexShader>, 2> m_batch_vertex_shaders; // [textured]
|
||||
std::array<std::array<std::array<ComPtr<ID3D11PixelShader>, 2>, 9>, 4>
|
||||
m_batch_pixel_shaders; // [render_mode][texture_mode][dithering]
|
||||
|
||||
ComPtr<ID3D11VertexShader> m_screen_quad_vertex_shader;
|
||||
ComPtr<ID3D11PixelShader> m_copy_pixel_shader;
|
||||
ComPtr<ID3D11PixelShader> m_fill_pixel_shader;
|
||||
ComPtr<ID3D11PixelShader> m_vram_write_pixel_shader;
|
||||
std::array<std::array<ComPtr<ID3D11PixelShader>, 2>, 2> m_display_pixel_shaders; // [depth_24][interlaced]
|
||||
|
||||
GLStats m_stats = {};
|
||||
GLStats m_last_stats = {};
|
||||
|
||||
bool m_vram_read_texture_dirty = true;
|
||||
bool m_drawing_area_changed = true;
|
||||
bool m_show_renderer_statistics = false;
|
||||
};
|
||||
@ -3,7 +3,7 @@
|
||||
#include "YBaseLib/Log.h"
|
||||
#include "YBaseLib/String.h"
|
||||
#include "gpu_hw_shadergen.h"
|
||||
#include "host_interface.h"
|
||||
#include "host_display.h"
|
||||
#include "imgui.h"
|
||||
#include "system.h"
|
||||
Log_SetChannel(GPU_HW_OpenGL);
|
||||
@ -12,16 +12,24 @@ GPU_HW_OpenGL::GPU_HW_OpenGL() : GPU_HW() {}
|
||||
|
||||
GPU_HW_OpenGL::~GPU_HW_OpenGL()
|
||||
{
|
||||
m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, 1.0f);
|
||||
DestroyFramebuffer();
|
||||
}
|
||||
|
||||
bool GPU_HW_OpenGL::Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers)
|
||||
bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display, System* system, DMA* dma,
|
||||
InterruptController* interrupt_controller, Timers* timers)
|
||||
{
|
||||
SetCapabilities();
|
||||
|
||||
if (!GPU_HW::Initialize(system, dma, interrupt_controller, timers))
|
||||
if (!GPU_HW::Initialize(host_display, system, dma, interrupt_controller, timers))
|
||||
return false;
|
||||
|
||||
if (m_host_display->GetRenderAPI() != HostDisplay::RenderAPI::OpenGL)
|
||||
{
|
||||
Log_ErrorPrintf("Host render API type is incompatible");
|
||||
return false;
|
||||
}
|
||||
|
||||
CreateFramebuffer();
|
||||
CreateVertexBuffer();
|
||||
CreateUniformBuffer();
|
||||
@ -29,7 +37,9 @@ bool GPU_HW_OpenGL::Initialize(System* system, DMA* dma, InterruptController* in
|
||||
if (!CompilePrograms())
|
||||
return false;
|
||||
|
||||
m_system->GetHostInterface()->SetDisplayTexture(m_display_texture.get(), 0, 0, VRAM_WIDTH, VRAM_HEIGHT, 1.0f);
|
||||
m_host_display->SetDisplayTexture(reinterpret_cast<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();
|
||||
return true;
|
||||
}
|
||||
@ -250,7 +260,7 @@ void GPU_HW_OpenGL::CreateVertexBuffer()
|
||||
glVertexAttribIPointer(0, 2, GL_INT, sizeof(BatchVertex), reinterpret_cast<void*>(offsetof(BatchVertex, x)));
|
||||
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(BatchVertex),
|
||||
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)));
|
||||
glBindVertexArray(0);
|
||||
|
||||
@ -429,8 +439,9 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
||||
|
||||
if (m_system->GetSettings().debugging.show_vram)
|
||||
{
|
||||
m_system->GetHostInterface()->SetDisplayTexture(m_vram_texture.get(), 0, 0, m_vram_texture->GetWidth(),
|
||||
m_vram_texture->GetHeight(), 1.0f);
|
||||
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture->GetGLId())), 0, 0,
|
||||
m_vram_texture->GetWidth(), m_vram_texture->GetHeight(),
|
||||
m_vram_texture->GetWidth(), m_vram_texture->GetHeight(), 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -448,7 +459,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
||||
|
||||
if (m_GPUSTAT.display_disable)
|
||||
{
|
||||
m_system->GetHostInterface()->SetDisplayTexture(nullptr, 0, 0, 0, 0, m_crtc_state.display_aspect_ratio);
|
||||
m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, m_crtc_state.display_aspect_ratio);
|
||||
}
|
||||
else if (!m_GPUSTAT.display_area_color_depth_24 && !m_GPUSTAT.vertical_interlace)
|
||||
{
|
||||
@ -457,8 +468,9 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
||||
scaled_flipped_vram_offset_y, 0, m_display_texture->GetGLId(), GL_TEXTURE_2D, 0, 0, 0, 0,
|
||||
scaled_display_width, scaled_display_height, 1);
|
||||
|
||||
m_system->GetHostInterface()->SetDisplayTexture(m_display_texture.get(), 0, 0, scaled_display_width,
|
||||
scaled_display_height, m_crtc_state.display_aspect_ratio);
|
||||
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture->GetGLId())), 0,
|
||||
0, scaled_display_width, scaled_display_height, m_display_texture->GetWidth(),
|
||||
m_display_texture->GetHeight(), m_crtc_state.display_aspect_ratio);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -495,8 +507,9 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
m_system->GetHostInterface()->SetDisplayTexture(m_display_texture.get(), 0, 0, display_width, display_height,
|
||||
m_crtc_state.display_aspect_ratio);
|
||||
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture->GetGLId())), 0,
|
||||
0, display_width, display_height, m_display_texture->GetWidth(),
|
||||
m_display_texture->GetHeight(), m_crtc_state.display_aspect_ratio);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -511,8 +524,9 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
m_system->GetHostInterface()->SetDisplayTexture(m_display_texture.get(), 0, 0, scaled_display_width,
|
||||
scaled_display_height, m_crtc_state.display_aspect_ratio);
|
||||
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture->GetGLId())), 0,
|
||||
0, scaled_display_width, scaled_display_height, m_display_texture->GetWidth(),
|
||||
m_display_texture->GetHeight(), m_crtc_state.display_aspect_ratio);
|
||||
}
|
||||
|
||||
// restore state
|
||||
@ -679,7 +693,7 @@ void GPU_HW_OpenGL::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void*
|
||||
m_vram_write_program.Bind();
|
||||
glBindTexture(GL_TEXTURE_BUFFER, m_texture_buffer_r16ui_texture);
|
||||
|
||||
const u32 uniforms[4] = {x, flipped_y, width, height};
|
||||
const u32 uniforms[5] = {x, flipped_y, width, height, map_result.index_aligned};
|
||||
UploadUniformBlock(uniforms, sizeof(uniforms));
|
||||
m_batch_ubo_dirty = true;
|
||||
|
||||
|
||||
@ -14,7 +14,8 @@ public:
|
||||
GPU_HW_OpenGL();
|
||||
~GPU_HW_OpenGL() override;
|
||||
|
||||
bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers) override;
|
||||
bool Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller,
|
||||
Timers* timers) override;
|
||||
void Reset() override;
|
||||
|
||||
void ResetGraphicsAPIState() override;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#include "gpu_hw_shadergen.h"
|
||||
|
||||
GPU_HW_ShaderGen::GPU_HW_ShaderGen(API backend, u32 resolution_scale, bool true_color)
|
||||
: m_backend(backend), m_resolution_scale(resolution_scale), m_true_color(true_color), m_glsl(backend != API::Direct3D)
|
||||
: m_backend(backend), m_resolution_scale(resolution_scale), m_true_color(true_color), m_glsl(backend != API::D3D11)
|
||||
{
|
||||
}
|
||||
|
||||
@ -22,9 +22,9 @@ void GPU_HW_ShaderGen::WriteHeader(std::stringstream& ss)
|
||||
ss << "#version 330 core\n\n";
|
||||
ss << "#define API_OPENGL 1\n";
|
||||
}
|
||||
else if (m_backend == API::Direct3D)
|
||||
else if (m_backend == API::D3D11)
|
||||
{
|
||||
ss << "#define API_DIRECT3D 1\n";
|
||||
ss << "#define API_D3D11 1\n";
|
||||
}
|
||||
|
||||
if (m_glsl)
|
||||
@ -52,7 +52,7 @@ void GPU_HW_ShaderGen::WriteHeader(std::stringstream& ss)
|
||||
ss << "#define CONSTANT static const\n";
|
||||
ss << "#define SAMPLE_TEXTURE(name, coords) name.Sample(name##_ss, coords)\n";
|
||||
ss << "#define LOAD_TEXTURE(name, coords, mip) name.Load(int3(coords, mip))\n";
|
||||
ss << "#define LOAD_TEXTURE_BUFFER(name, index) name.Load(name, index)\n";
|
||||
ss << "#define LOAD_TEXTURE_BUFFER(name, index) name.Load(index)\n";
|
||||
}
|
||||
|
||||
ss << "\n";
|
||||
@ -152,7 +152,8 @@ void GPU_HW_ShaderGen::DeclareTextureBuffer(std::stringstream& ss, const char* n
|
||||
void GPU_HW_ShaderGen::DeclareVertexEntryPoint(std::stringstream& ss,
|
||||
const std::initializer_list<const char*>& attributes,
|
||||
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)
|
||||
{
|
||||
@ -169,6 +170,10 @@ void GPU_HW_ShaderGen::DeclareVertexEntryPoint(std::stringstream& ss,
|
||||
ss << output << ";\n";
|
||||
|
||||
ss << "#define v_pos gl_Position\n\n";
|
||||
if (declare_vertex_id)
|
||||
ss << "#define v_id uint(gl_VertexID)\n";
|
||||
|
||||
ss << "\n";
|
||||
ss << "void main()\n";
|
||||
}
|
||||
else
|
||||
@ -182,6 +187,9 @@ void GPU_HW_ShaderGen::DeclareVertexEntryPoint(std::stringstream& ss,
|
||||
attribute_counter++;
|
||||
}
|
||||
|
||||
if (declare_vertex_id)
|
||||
ss << " in uint v_id : SV_VertexID,\n";
|
||||
|
||||
for (u32 i = 0; i < num_color_outputs; i++)
|
||||
ss << " out float4 v_col" << i << " : COLOR" << i << ",\n";
|
||||
|
||||
@ -498,12 +506,14 @@ std::string GPU_HW_ShaderGen::GenerateScreenQuadVertexShader()
|
||||
{
|
||||
std::stringstream ss;
|
||||
WriteHeader(ss);
|
||||
DeclareVertexEntryPoint(ss, {}, 0, 1, {});
|
||||
DeclareVertexEntryPoint(ss, {}, 0, 1, {}, true);
|
||||
ss << R"(
|
||||
{
|
||||
v_tex0 = float2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
|
||||
gl_Position = float4(v_tex0 * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);
|
||||
gl_Position.y = -gl_Position.y;
|
||||
v_tex0 = float2(float((v_id << 1) & 2u), float(v_id & 2u));
|
||||
v_pos = float4(v_tex0 * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);
|
||||
#if API_OPENGL
|
||||
v_pos.y = -gl_Position.y;
|
||||
#endif
|
||||
}
|
||||
)";
|
||||
|
||||
@ -517,7 +527,8 @@ std::string GPU_HW_ShaderGen::GenerateFillFragmentShader()
|
||||
DeclareUniformBuffer(ss, {"float4 u_fill_color"});
|
||||
DeclareFragmentEntryPoint(ss, 0, 1, {}, false, false);
|
||||
|
||||
ss << R"({
|
||||
ss << R"(
|
||||
{
|
||||
o_col0 = u_fill_color;
|
||||
}
|
||||
)";
|
||||
@ -525,6 +536,24 @@ std::string GPU_HW_ShaderGen::GenerateFillFragmentShader()
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string GPU_HW_ShaderGen::GenerateCopyFragmentShader()
|
||||
{
|
||||
std::stringstream ss;
|
||||
WriteHeader(ss);
|
||||
DeclareUniformBuffer(ss, {"float4 u_src_rect"});
|
||||
DeclareTexture(ss, "samp0", 0);
|
||||
DeclareFragmentEntryPoint(ss, 0, 1, {}, false, false);
|
||||
|
||||
ss << R"(
|
||||
{
|
||||
float2 coords = u_src_rect.xy + v_tex0 * u_src_rect.zw;
|
||||
o_col0 = SAMPLE_TEXTURE(samp0, coords);
|
||||
}
|
||||
)";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string GPU_HW_ShaderGen::GenerateDisplayFragmentShader(bool depth_24bit, bool interlaced)
|
||||
{
|
||||
std::stringstream ss;
|
||||
@ -603,7 +632,7 @@ std::string GPU_HW_ShaderGen::GenerateVRAMWriteFragmentShader()
|
||||
std::stringstream ss;
|
||||
WriteHeader(ss);
|
||||
WriteCommonFunctions(ss);
|
||||
DeclareUniformBuffer(ss, {"int2 u_base_coords", "int2 u_size"});
|
||||
DeclareUniformBuffer(ss, {"int2 u_base_coords", "int2 u_size", "int u_buffer_base_offset"});
|
||||
|
||||
DeclareTextureBuffer(ss, "samp0", 0, true, true);
|
||||
DeclareFragmentEntryPoint(ss, 0, 1, {}, true, false);
|
||||
@ -611,9 +640,13 @@ std::string GPU_HW_ShaderGen::GenerateVRAMWriteFragmentShader()
|
||||
{
|
||||
int2 coords = int2(v_pos.xy) / int2(RESOLUTION_SCALE, RESOLUTION_SCALE);
|
||||
int2 offset = coords - u_base_coords;
|
||||
offset.y = u_size.y - offset.y - 1;
|
||||
|
||||
int buffer_offset = offset.y * u_size.x + offset.x;
|
||||
#if API_OPENGL
|
||||
// Lower-left origin flip for OpenGL
|
||||
offset.y = u_size.y - offset.y - 1;
|
||||
#endif
|
||||
|
||||
int buffer_offset = u_buffer_base_offset + (offset.y * u_size.x) + offset.x;
|
||||
uint value = LOAD_TEXTURE_BUFFER(samp0, buffer_offset).r;
|
||||
|
||||
o_col0 = RGBA5551ToRGBA8(value);
|
||||
|
||||
@ -9,7 +9,7 @@ public:
|
||||
enum class API
|
||||
{
|
||||
OpenGL,
|
||||
Direct3D
|
||||
D3D11
|
||||
};
|
||||
|
||||
public:
|
||||
@ -23,6 +23,7 @@ public:
|
||||
bool dithering);
|
||||
std::string GenerateScreenQuadVertexShader();
|
||||
std::string GenerateFillFragmentShader();
|
||||
std::string GenerateCopyFragmentShader();
|
||||
std::string GenerateDisplayFragmentShader(bool depth_24bit, bool interlaced);
|
||||
std::string GenerateVRAMWriteFragmentShader();
|
||||
|
||||
@ -38,7 +39,8 @@ private:
|
||||
void DeclareTextureBuffer(std::stringstream& ss, const char* name, u32 index, bool is_int, bool is_unsigned);
|
||||
void DeclareVertexEntryPoint(std::stringstream& ss, const std::initializer_list<const char*>& attributes,
|
||||
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,
|
||||
const std::initializer_list<const char*>& additional_inputs,
|
||||
bool declare_fragcoord = false, bool dual_color_output = false);
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#include "YBaseLib/Log.h"
|
||||
#include "YBaseLib/Timer.h"
|
||||
#include "common/gl/texture.h"
|
||||
#include "host_interface.h"
|
||||
#include "host_display.h"
|
||||
#include "system.h"
|
||||
#include <algorithm>
|
||||
Log_SetChannel(GPU_SW);
|
||||
@ -12,14 +12,21 @@ GPU_SW::GPU_SW()
|
||||
m_vram.fill(0);
|
||||
}
|
||||
|
||||
GPU_SW::~GPU_SW() = default;
|
||||
|
||||
bool GPU_SW::Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers)
|
||||
GPU_SW::~GPU_SW()
|
||||
{
|
||||
if (!GPU::Initialize(system, dma, interrupt_controller, timers))
|
||||
m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, 1.0f);
|
||||
}
|
||||
|
||||
bool GPU_SW::Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller,
|
||||
Timers* timers)
|
||||
{
|
||||
if (!GPU::Initialize(host_display, system, dma, interrupt_controller, timers))
|
||||
return false;
|
||||
|
||||
m_display_texture = host_display->CreateTexture(VRAM_WIDTH, VRAM_HEIGHT, nullptr, 0, true);
|
||||
if (!m_display_texture)
|
||||
return false;
|
||||
|
||||
m_display_texture = std::make_unique<GL::Texture>(VRAM_WIDTH, VRAM_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -71,9 +78,6 @@ void GPU_SW::CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32
|
||||
|
||||
void GPU_SW::CopyOut15Bit(const u16* src_ptr, u32 src_stride, u32* dst_ptr, u32 dst_stride, u32 width, u32 height)
|
||||
{
|
||||
// OpenGL is beeg silly for lower-left origin
|
||||
dst_ptr = (dst_ptr + ((height - 1) * dst_stride));
|
||||
|
||||
for (u32 row = 0; row < height; row++)
|
||||
{
|
||||
const u16* src_row_ptr = src_ptr;
|
||||
@ -82,15 +86,12 @@ void GPU_SW::CopyOut15Bit(const u16* src_ptr, u32 src_stride, u32* dst_ptr, u32
|
||||
*(dst_row_ptr++) = RGBA5551ToRGBA8888(*(src_row_ptr++));
|
||||
|
||||
src_ptr += src_stride;
|
||||
dst_ptr -= dst_stride;
|
||||
dst_ptr += dst_stride;
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_SW::CopyOut24Bit(const u16* src_ptr, u32 src_stride, u32* dst_ptr, u32 dst_stride, u32 width, u32 height)
|
||||
{
|
||||
// OpenGL is beeg silly for lower-left origin
|
||||
dst_ptr = (dst_ptr + ((height - 1) * dst_stride));
|
||||
|
||||
for (u32 row = 0; row < height; row++)
|
||||
{
|
||||
const u8* src_row_ptr = reinterpret_cast<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;
|
||||
dst_ptr -= dst_stride;
|
||||
dst_ptr += dst_stride;
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,7 +131,7 @@ void GPU_SW::UpdateDisplay()
|
||||
|
||||
if (m_GPUSTAT.display_disable)
|
||||
{
|
||||
m_system->GetHostInterface()->SetDisplayTexture(nullptr, 0, 0, 0, 0, display_aspect_ratio);
|
||||
m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, display_aspect_ratio);
|
||||
return;
|
||||
}
|
||||
else if (m_GPUSTAT.display_area_color_depth_24)
|
||||
@ -153,11 +154,10 @@ void GPU_SW::UpdateDisplay()
|
||||
display_height);
|
||||
}
|
||||
|
||||
m_display_texture->Bind();
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, display_width, display_height, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
m_display_texture_buffer.data());
|
||||
m_system->GetHostInterface()->SetDisplayTexture(m_display_texture.get(), 0, 0, display_width, display_height,
|
||||
display_aspect_ratio);
|
||||
m_host_display->UpdateTexture(m_display_texture.get(), 0, 0, display_width, display_height,
|
||||
m_display_texture_buffer.data(), display_width * sizeof(u32));
|
||||
m_host_display->SetDisplayTexture(m_display_texture->GetHandle(), 0, 0, display_width, display_height, VRAM_WIDTH,
|
||||
VRAM_HEIGHT, display_aspect_ratio);
|
||||
}
|
||||
|
||||
void GPU_SW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32* command_ptr)
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
#pragma once
|
||||
#include "common/gl/texture.h"
|
||||
#include "gpu.h"
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace GL {
|
||||
class Texture;
|
||||
}
|
||||
class HostDisplayTexture;
|
||||
|
||||
class GPU_SW final : public GPU
|
||||
{
|
||||
@ -15,7 +12,8 @@ public:
|
||||
GPU_SW();
|
||||
~GPU_SW() override;
|
||||
|
||||
bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers) override;
|
||||
bool Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller,
|
||||
Timers* timers) override;
|
||||
void Reset() override;
|
||||
|
||||
u16 GetPixel(u32 x, u32 y) const { return m_vram[VRAM_WIDTH * y + x]; }
|
||||
@ -58,13 +56,14 @@ protected:
|
||||
|
||||
static bool IsClockwiseWinding(const SWVertex* v0, const SWVertex* v1, const SWVertex* v2);
|
||||
|
||||
void ShadePixel(RenderCommand rc, u32 x, u32 y, u8 color_r, u8 color_g, u8 color_b, u8 texcoord_x, u8 texcoord_y, bool dithering);
|
||||
void ShadePixel(RenderCommand rc, u32 x, u32 y, u8 color_r, u8 color_g, u8 color_b, u8 texcoord_x, u8 texcoord_y,
|
||||
bool dithering);
|
||||
void DrawTriangle(RenderCommand rc, const SWVertex* v0, const SWVertex* v1, const SWVertex* v2);
|
||||
void DrawRectangle(RenderCommand rc, s32 origin_x, s32 origin_y, u32 width, u32 height, u8 r, u8 g, u8 b,
|
||||
u8 origin_texcoord_x, u8 origin_texcoord_y);
|
||||
|
||||
std::unique_ptr<GL::Texture> m_display_texture;
|
||||
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<u16, VRAM_WIDTH * VRAM_HEIGHT> m_vram;
|
||||
|
||||
71
src/core/host_display.h
Normal file
71
src/core/host_display.h
Normal file
@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
// An abstracted RGBA8 texture.
|
||||
class HostDisplayTexture
|
||||
{
|
||||
public:
|
||||
virtual ~HostDisplayTexture() {}
|
||||
|
||||
virtual void* GetHandle() const = 0;
|
||||
virtual u32 GetWidth() const = 0;
|
||||
virtual u32 GetHeight() const = 0;
|
||||
};
|
||||
|
||||
// Interface to the frontend's renderer.
|
||||
class HostDisplay
|
||||
{
|
||||
public:
|
||||
enum class RenderAPI
|
||||
{
|
||||
None,
|
||||
D3D11,
|
||||
OpenGL
|
||||
};
|
||||
|
||||
virtual RenderAPI GetRenderAPI() const = 0;
|
||||
virtual void* GetHostRenderDevice() const = 0;
|
||||
virtual void* GetHostRenderContext() const = 0;
|
||||
|
||||
/// Creates an abstracted RGBA8 texture. If dynamic, the texture can be updated with UpdateTexture() below.
|
||||
virtual std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride,
|
||||
bool dynamic = false) = 0;
|
||||
virtual void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data,
|
||||
u32 data_stride) = 0;
|
||||
|
||||
virtual void SetDisplayTexture(void* texture_handle, u32 offset_x, u32 offset_y, u32 width, u32 height,
|
||||
u32 texture_width, u32 texture_height, float aspect_ratio) = 0;
|
||||
virtual void SetDisplayLinearFiltering(bool enabled) = 0;
|
||||
|
||||
virtual void Render() = 0;
|
||||
|
||||
virtual void SetVSync(bool enabled) = 0;
|
||||
|
||||
virtual std::tuple<u32, u32> GetWindowSize() const = 0;
|
||||
virtual void WindowResized() = 0;
|
||||
|
||||
// Helper function for computing the draw rectangle in a larger window.
|
||||
static std::tuple<int, int, int, int> CalculateDrawRect(int window_width, int window_height, float display_ratio)
|
||||
{
|
||||
const float window_ratio = float(window_width) / float(window_height);
|
||||
int left, top, width, height;
|
||||
if (window_ratio >= display_ratio)
|
||||
{
|
||||
width = static_cast<int>(float(window_height) * display_ratio);
|
||||
height = static_cast<int>(window_height);
|
||||
left = (window_width - width) / 2;
|
||||
top = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
width = static_cast<int>(window_width);
|
||||
height = static_cast<int>(float(window_width) / display_ratio);
|
||||
left = 0;
|
||||
top = (window_height - height) / 2;
|
||||
}
|
||||
|
||||
return std::tie(left, top, width, height);
|
||||
}
|
||||
};
|
||||
@ -5,16 +5,17 @@
|
||||
#include "system.h"
|
||||
Log_SetChannel(HostInterface);
|
||||
|
||||
HostInterface::HostInterface() = default;
|
||||
HostInterface::HostInterface()
|
||||
{
|
||||
m_settings.gpu_renderer = Settings::GPURenderer::HardwareD3D11;
|
||||
m_settings.memory_card_a_filename = "memory_card_a.mcd";
|
||||
}
|
||||
|
||||
HostInterface::~HostInterface() = default;
|
||||
|
||||
bool HostInterface::InitializeSystem(const char* filename, const char* exp1_filename)
|
||||
{
|
||||
Settings settings;
|
||||
settings.memory_card_a_filename = "memory_card_a.mcd";
|
||||
|
||||
m_system = std::make_unique<System>(this, settings);
|
||||
m_system = std::make_unique<System>(this, m_settings);
|
||||
if (!m_system->Initialize())
|
||||
{
|
||||
m_system.reset();
|
||||
@ -50,6 +51,7 @@ bool HostInterface::InitializeSystem(const char* filename, const char* exp1_file
|
||||
m_system->SetExpansionROM(exp1_filename);
|
||||
|
||||
// Resume execution.
|
||||
m_settings = m_system->GetSettings();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#include "settings.h"
|
||||
#include <memory>
|
||||
|
||||
class AudioStream;
|
||||
|
||||
namespace GL {
|
||||
class Texture;
|
||||
}
|
||||
class HostDisplay;
|
||||
|
||||
class System;
|
||||
|
||||
@ -20,7 +18,8 @@ public:
|
||||
|
||||
bool InitializeSystem(const char* filename, const char* exp1_filename);
|
||||
|
||||
virtual void SetDisplayTexture(GL::Texture* texture, u32 offset_x, u32 offset_y, u32 width, u32 height, float aspect_ratio) = 0;
|
||||
virtual HostDisplay* GetDisplay() const = 0;
|
||||
|
||||
virtual void ReportMessage(const char* message) = 0;
|
||||
|
||||
// Adds OSD messages, duration is in seconds.
|
||||
@ -32,4 +31,6 @@ public:
|
||||
protected:
|
||||
std::unique_ptr<AudioStream> m_audio_stream;
|
||||
std::unique_ptr<System> m_system;
|
||||
|
||||
Settings m_settings;
|
||||
};
|
||||
|
||||
@ -5,6 +5,7 @@ struct Settings
|
||||
{
|
||||
enum class GPURenderer
|
||||
{
|
||||
HardwareD3D11,
|
||||
HardwareOpenGL,
|
||||
Software
|
||||
};
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include "cpu_core.h"
|
||||
#include "dma.h"
|
||||
#include "gpu.h"
|
||||
#include "host_interface.h"
|
||||
#include "interrupt_controller.h"
|
||||
#include "mdec.h"
|
||||
#include "memory_card.h"
|
||||
@ -112,20 +113,28 @@ bool System::CreateGPU()
|
||||
m_gpu = GPU::CreateHardwareOpenGLRenderer();
|
||||
break;
|
||||
|
||||
case Settings::GPURenderer::HardwareD3D11:
|
||||
m_gpu = GPU::CreateHardwareD3D11Renderer();
|
||||
break;
|
||||
|
||||
case Settings::GPURenderer::Software:
|
||||
default:
|
||||
m_gpu = GPU::CreateSoftwareRenderer();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_gpu || !m_gpu->Initialize(this, m_dma.get(), m_interrupt_controller.get(), m_timers.get()))
|
||||
if (!m_gpu || !m_gpu->Initialize(m_host_interface->GetDisplay(), this, m_dma.get(), m_interrupt_controller.get(),
|
||||
m_timers.get()))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to initialize GPU, falling back to software");
|
||||
m_gpu.reset();
|
||||
m_settings.gpu_renderer = Settings::GPURenderer::Software;
|
||||
m_gpu = GPU::CreateSoftwareRenderer();
|
||||
if (!m_gpu->Initialize(this, m_dma.get(), m_interrupt_controller.get(), m_timers.get()))
|
||||
if (!m_gpu->Initialize(m_host_interface->GetDisplay(), this, m_dma.get(), m_interrupt_controller.get(),
|
||||
m_timers.get()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_bus->SetGPU(m_gpu.get());
|
||||
|
||||
Reference in New Issue
Block a user