mirror of
				https://github.com/RetroDECK/Duckstation.git
				synced 2025-04-10 19:15:14 +00:00 
			
		
		
		
	GPU/HW: Add D3D11 renderer and refactor host interface/display
This commit is contained in:
		
							parent
							
								
									f244da86a2
								
							
						
					
					
						commit
						9de9cf3be2
					
				|  | @ -55,5 +55,11 @@ add_library(core | |||
| 
 | ||||
| target_include_directories(core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..") | ||||
| target_include_directories(core 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()); | ||||
|  |  | |||
|  | @ -1,12 +1,21 @@ | |||
| set(SRCS | ||||
| add_executable(duckstation | ||||
|   icon.cpp | ||||
|   main.cpp | ||||
|   opengl_host_display.cpp | ||||
|   opengl_host_display.h | ||||
|   sdl_audio_stream.cpp | ||||
|   sdl_audio_stream.h | ||||
|   sdl_interface.cpp | ||||
|   sdl_interface.h | ||||
|   sdl_host_interface.cpp | ||||
|   sdl_host_interface.h | ||||
| ) | ||||
| 
 | ||||
| add_executable(duckstation ${SRCS}) | ||||
| target_include_directories(duckstation PRIVATE "${SDL2_INCLUDE_DIRS}") | ||||
| target_link_libraries(duckstation core imgui nativefiledialog "${SDL2_LIBRARIES}") | ||||
| target_link_libraries(duckstation PRIVATE YBaseLib core common imgui nativefiledialog glad "${SDL2_LIBRARIES}") | ||||
| 
 | ||||
| if(WIN32) | ||||
|   target_sources(duckstation PRIVATE | ||||
|     d3d11_host_display.cpp | ||||
|     d3d11_host_display.h | ||||
|   ) | ||||
|   target_link_libraries(duckstation PRIVATE d3d11.lib) | ||||
| endif() | ||||
|  |  | |||
							
								
								
									
										394
									
								
								src/duckstation/d3d11_host_display.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										394
									
								
								src/duckstation/d3d11_host_display.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,394 @@ | |||
| #include "d3d11_host_display.h" | ||||
| #include "YBaseLib/Log.h" | ||||
| #include "common/d3d11/shader_compiler.h" | ||||
| #include <SDL_syswm.h> | ||||
| #include <array> | ||||
| #include <imgui.h> | ||||
| #include <imgui_impl_dx11.h> | ||||
| #include <imgui_impl_sdl.h> | ||||
| Log_SetChannel(D3D11HostDisplay); | ||||
| 
 | ||||
| class D3D11HostDisplayTexture : public HostDisplayTexture | ||||
| { | ||||
| public: | ||||
|   template<typename T> | ||||
|   using ComPtr = Microsoft::WRL::ComPtr<T>; | ||||
| 
 | ||||
|   D3D11HostDisplayTexture(ComPtr<ID3D11Texture2D> texture, ComPtr<ID3D11ShaderResourceView> srv, u32 width, u32 height, | ||||
|                           bool dynamic) | ||||
|     : m_texture(std::move(texture)), m_srv(std::move(srv)), m_width(width), m_height(height), m_dynamic(dynamic) | ||||
|   { | ||||
|   } | ||||
|   ~D3D11HostDisplayTexture() override = default; | ||||
| 
 | ||||
|   void* GetHandle() const override { return m_srv.Get(); } | ||||
|   u32 GetWidth() const override { return m_width; } | ||||
|   u32 GetHeight() const override { return m_height; } | ||||
| 
 | ||||
|   ID3D11Texture2D* GetD3DTexture() const { return m_texture.Get(); } | ||||
|   ID3D11ShaderResourceView* GetD3DSRV() const { return m_srv.Get(); } | ||||
|   bool IsDynamic() const { return m_dynamic; } | ||||
| 
 | ||||
|   static std::unique_ptr<D3D11HostDisplayTexture> Create(ID3D11Device* device, u32 width, u32 height, const void* data, | ||||
|                                                          u32 data_stride, bool dynamic) | ||||
|   { | ||||
|     const CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, width, height, 1, 1, D3D11_BIND_SHADER_RESOURCE, | ||||
|                                      dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT, | ||||
|                                      dynamic ? D3D11_CPU_ACCESS_WRITE : 0, 1, 0, 0); | ||||
|     const D3D11_SUBRESOURCE_DATA srd{data, data_stride, data_stride * height}; | ||||
|     ComPtr<ID3D11Texture2D> texture; | ||||
|     HRESULT hr = device->CreateTexture2D(&desc, data ? &srd : nullptr, texture.GetAddressOf()); | ||||
|     if (FAILED(hr)) | ||||
|       return {}; | ||||
| 
 | ||||
|     const CD3D11_SHADER_RESOURCE_VIEW_DESC srv_desc(D3D11_SRV_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 1, 0, | ||||
|                                                     1); | ||||
|     ComPtr<ID3D11ShaderResourceView> srv; | ||||
|     hr = device->CreateShaderResourceView(texture.Get(), &srv_desc, srv.GetAddressOf()); | ||||
|     if (FAILED(hr)) | ||||
|       return {}; | ||||
| 
 | ||||
|     return std::make_unique<D3D11HostDisplayTexture>(std::move(texture), std::move(srv), width, height, dynamic); | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   ComPtr<ID3D11Texture2D> m_texture; | ||||
|   ComPtr<ID3D11ShaderResourceView> m_srv; | ||||
|   u32 m_width; | ||||
|   u32 m_height; | ||||
|   bool m_dynamic; | ||||
| }; | ||||
| 
 | ||||
| D3D11HostDisplay::D3D11HostDisplay(SDL_Window* window) : m_window(window) | ||||
| { | ||||
|   SDL_GetWindowSize(window, &m_window_width, &m_window_height); | ||||
| } | ||||
| 
 | ||||
| D3D11HostDisplay::~D3D11HostDisplay() | ||||
| { | ||||
|   ImGui_ImplDX11_Shutdown(); | ||||
|   ImGui_ImplSDL2_Shutdown(); | ||||
| 
 | ||||
|   if (m_window) | ||||
|     SDL_DestroyWindow(m_window); | ||||
| } | ||||
| 
 | ||||
| HostDisplay::RenderAPI D3D11HostDisplay::GetRenderAPI() const | ||||
| { | ||||
|   return HostDisplay::RenderAPI::D3D11; | ||||
| } | ||||
| 
 | ||||
| void* D3D11HostDisplay::GetHostRenderDevice() const | ||||
| { | ||||
|   return m_device.Get(); | ||||
| } | ||||
| 
 | ||||
| void* D3D11HostDisplay::GetHostRenderContext() const | ||||
| { | ||||
|   return m_context.Get(); | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<HostDisplayTexture> D3D11HostDisplay::CreateTexture(u32 width, u32 height, const void* data, | ||||
|                                                                     u32 data_stride, bool dynamic) | ||||
| { | ||||
|   return D3D11HostDisplayTexture::Create(m_device.Get(), width, height, data, data_stride, dynamic); | ||||
| } | ||||
| 
 | ||||
| void D3D11HostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, | ||||
|                                      u32 data_stride) | ||||
| { | ||||
|   D3D11HostDisplayTexture* d3d11_texture = static_cast<D3D11HostDisplayTexture*>(texture); | ||||
|   if (!d3d11_texture->IsDynamic()) | ||||
|   { | ||||
|     const CD3D11_BOX dst_box(x, y, 0, x + width, y + height, 1); | ||||
|     m_context->UpdateSubresource(d3d11_texture->GetD3DTexture(), 0, &dst_box, data, data_stride, data_stride * height); | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     D3D11_MAPPED_SUBRESOURCE sr; | ||||
|     HRESULT hr = m_context->Map(d3d11_texture->GetD3DTexture(), 0, D3D11_MAP_WRITE_DISCARD, 0, &sr); | ||||
|     if (FAILED(hr)) | ||||
|       Panic("Failed to map dynamic host display texture"); | ||||
| 
 | ||||
|     char* dst_ptr = static_cast<char*>(sr.pData) + (y * sr.RowPitch) + (x * sizeof(u32)); | ||||
|     const char* src_ptr = static_cast<const char*>(data); | ||||
|     if (sr.RowPitch == data_stride) | ||||
|     { | ||||
|       std::memcpy(dst_ptr, src_ptr, data_stride * height); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       for (u32 row = 0; row < height; row++) | ||||
|       { | ||||
|         std::memcpy(dst_ptr, src_ptr, width * sizeof(u32)); | ||||
|         src_ptr += data_stride; | ||||
|         dst_ptr += sr.RowPitch; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     m_context->Unmap(d3d11_texture->GetD3DTexture(), 0); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void D3D11HostDisplay::SetDisplayTexture(void* texture, u32 offset_x, u32 offset_y, u32 width, u32 height, | ||||
|                                          u32 texture_width, u32 texture_height, float aspect_ratio) | ||||
| { | ||||
|   m_display_srv = static_cast<ID3D11ShaderResourceView*>(texture); | ||||
|   m_display_offset_x = offset_x; | ||||
|   m_display_offset_y = offset_y; | ||||
|   m_display_width = width; | ||||
|   m_display_height = height; | ||||
|   m_display_texture_width = texture_width; | ||||
|   m_display_texture_height = texture_height; | ||||
|   m_display_aspect_ratio = aspect_ratio; | ||||
|   m_display_texture_changed = true; | ||||
| } | ||||
| 
 | ||||
| void D3D11HostDisplay::SetDisplayLinearFiltering(bool enabled) | ||||
| { | ||||
|   m_display_linear_filtering = enabled; | ||||
| } | ||||
| 
 | ||||
| void D3D11HostDisplay::SetVSync(bool enabled) | ||||
| { | ||||
|   m_vsync = enabled; | ||||
| } | ||||
| 
 | ||||
| std::tuple<u32, u32> D3D11HostDisplay::GetWindowSize() const | ||||
| { | ||||
|   return std::make_tuple(static_cast<u32>(m_window_width), static_cast<u32>(m_window_height)); | ||||
| } | ||||
| 
 | ||||
| void D3D11HostDisplay::WindowResized() | ||||
| { | ||||
|   SDL_GetWindowSize(m_window, &m_window_width, &m_window_height); | ||||
| 
 | ||||
|   m_swap_chain_rtv.Reset(); | ||||
| 
 | ||||
|   HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0); | ||||
|   if (FAILED(hr)) | ||||
|     Log_ErrorPrintf("ResizeBuffers() failed: 0x%08X", hr); | ||||
| 
 | ||||
|   if (!CreateSwapChainRTV()) | ||||
|     Panic("Failed to recreate swap chain RTV after resize"); | ||||
| } | ||||
| 
 | ||||
| bool D3D11HostDisplay::CreateD3DDevice() | ||||
| { | ||||
|   const bool debug = false; | ||||
| 
 | ||||
|   SDL_SysWMinfo syswm = {}; | ||||
|   if (!SDL_GetWindowWMInfo(m_window, &syswm)) | ||||
|   { | ||||
|     Log_ErrorPrintf("SDL_GetWindowWMInfo failed"); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   UINT create_flags = 0; | ||||
|   if (debug) | ||||
|     create_flags |= D3D11_CREATE_DEVICE_DEBUG; | ||||
| 
 | ||||
|   DXGI_SWAP_CHAIN_DESC swap_chain_desc = {}; | ||||
|   swap_chain_desc.BufferDesc.Width = m_window_width; | ||||
|   swap_chain_desc.BufferDesc.Height = m_window_height; | ||||
|   swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; | ||||
|   swap_chain_desc.SampleDesc.Count = 1; | ||||
|   swap_chain_desc.BufferCount = 3; | ||||
|   swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; | ||||
|   swap_chain_desc.OutputWindow = syswm.info.win.window; | ||||
|   swap_chain_desc.Windowed = TRUE; | ||||
|   swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; | ||||
| 
 | ||||
|   HRESULT hr = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, create_flags, nullptr, 0, | ||||
|                                              D3D11_SDK_VERSION, &swap_chain_desc, m_swap_chain.GetAddressOf(), | ||||
|                                              m_device.GetAddressOf(), nullptr, m_context.GetAddressOf()); | ||||
|   if (FAILED(hr)) | ||||
|   { | ||||
|     Log_ErrorPrintf("D3D11CreateDeviceAndSwapChain failed: 0x%08X", hr); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
| #if 0 | ||||
|   ComPtr<ID3D11InfoQueue> info; | ||||
|   hr = m_device.As(&info); | ||||
|   if (SUCCEEDED(hr)) | ||||
|   { | ||||
|     info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, TRUE); | ||||
|     info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, TRUE); | ||||
|   } | ||||
| #endif | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| bool D3D11HostDisplay::CreateSwapChainRTV() | ||||
| { | ||||
|   ComPtr<ID3D11Texture2D> backbuffer; | ||||
|   HRESULT hr = m_swap_chain->GetBuffer(0, IID_PPV_ARGS(backbuffer.GetAddressOf())); | ||||
|   if (FAILED(hr)) | ||||
|   { | ||||
|     Log_ErrorPrintf("GetBuffer for RTV failed: 0x%08X", hr); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   D3D11_TEXTURE2D_DESC backbuffer_desc; | ||||
|   backbuffer->GetDesc(&backbuffer_desc); | ||||
| 
 | ||||
|   CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D, backbuffer_desc.Format, 0, 0, | ||||
|                                           backbuffer_desc.ArraySize); | ||||
|   hr = m_device->CreateRenderTargetView(backbuffer.Get(), &rtv_desc, m_swap_chain_rtv.GetAddressOf()); | ||||
|   if (FAILED(hr)) | ||||
|   { | ||||
|     Log_ErrorPrintf("CreateRenderTargetView for swap chain failed: 0x%08X", hr); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| bool D3D11HostDisplay::CreateD3DResources() | ||||
| { | ||||
|   static constexpr char fullscreen_quad_vertex_shader[] = R"( | ||||
| void main(in uint vertex_id : SV_VertexID, | ||||
|           out float2 v_tex0 : TEXCOORD0, | ||||
|           out float4 o_pos : SV_Position) | ||||
| { | ||||
|   v_tex0 = float2(float((vertex_id << 1) & 2u), float(vertex_id & 2u)); | ||||
|   o_pos = float4(v_tex0 * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f); | ||||
| } | ||||
| )"; | ||||
| 
 | ||||
|   static constexpr char display_pixel_shader[] = R"( | ||||
| cbuffer UBOBlock : register(b0) | ||||
| { | ||||
|   float4 u_src_rect; | ||||
| }; | ||||
| 
 | ||||
| Texture2D samp0 : register(t0); | ||||
| SamplerState samp0_ss : register(s0); | ||||
| 
 | ||||
| void main(in float2 v_tex0 : TEXCOORD0, | ||||
|           out float4 o_col0 : SV_Target) | ||||
| { | ||||
|   float2 coords = u_src_rect.xy + v_tex0 * u_src_rect.zw; | ||||
|   o_col0 = samp0.Sample(samp0_ss, coords); | ||||
| } | ||||
| )"; | ||||
| 
 | ||||
|   HRESULT hr; | ||||
| 
 | ||||
|   m_display_vertex_shader = | ||||
|     D3D11::ShaderCompiler::CompileAndCreateVertexShader(m_device.Get(), fullscreen_quad_vertex_shader, false); | ||||
|   m_display_pixel_shader = | ||||
|     D3D11::ShaderCompiler::CompileAndCreatePixelShader(m_device.Get(), display_pixel_shader, false); | ||||
|   if (!m_display_vertex_shader || !m_display_pixel_shader) | ||||
|     return false; | ||||
| 
 | ||||
|   if (!m_display_uniform_buffer.Create(m_device.Get(), D3D11_BIND_CONSTANT_BUFFER, DISPLAY_UNIFORM_BUFFER_SIZE)) | ||||
|     return false; | ||||
| 
 | ||||
|   CD3D11_RASTERIZER_DESC rasterizer_desc = CD3D11_RASTERIZER_DESC(CD3D11_DEFAULT()); | ||||
|   rasterizer_desc.CullMode = D3D11_CULL_NONE; | ||||
|   hr = m_device->CreateRasterizerState(&rasterizer_desc, m_display_rasterizer_state.GetAddressOf()); | ||||
|   if (FAILED(hr)) | ||||
|     return false; | ||||
| 
 | ||||
|   CD3D11_DEPTH_STENCIL_DESC depth_stencil_desc = CD3D11_DEPTH_STENCIL_DESC(CD3D11_DEFAULT()); | ||||
|   depth_stencil_desc.DepthEnable = FALSE; | ||||
|   depth_stencil_desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; | ||||
|   hr = m_device->CreateDepthStencilState(&depth_stencil_desc, m_display_depth_stencil_state.GetAddressOf()); | ||||
|   if (FAILED(hr)) | ||||
|     return false; | ||||
| 
 | ||||
|   CD3D11_BLEND_DESC blend_desc = CD3D11_BLEND_DESC(CD3D11_DEFAULT()); | ||||
|   hr = m_device->CreateBlendState(&blend_desc, m_display_blend_state.GetAddressOf()); | ||||
|   if (FAILED(hr)) | ||||
|     return false; | ||||
| 
 | ||||
|   CD3D11_SAMPLER_DESC sampler_desc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT()); | ||||
|   sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; | ||||
|   hr = m_device->CreateSamplerState(&sampler_desc, m_point_sampler.GetAddressOf()); | ||||
|   if (FAILED(hr)) | ||||
|     return false; | ||||
| 
 | ||||
|   sampler_desc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; | ||||
|   hr = m_device->CreateSamplerState(&sampler_desc, m_linear_sampler.GetAddressOf()); | ||||
|   if (FAILED(hr)) | ||||
|     return false; | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| bool D3D11HostDisplay::CreateImGuiContext() | ||||
| { | ||||
|   if (!ImGui_ImplSDL2_InitForD3D(m_window) || !ImGui_ImplDX11_Init(m_device.Get(), m_context.Get())) | ||||
|     return false; | ||||
| 
 | ||||
|   ImGui_ImplDX11_NewFrame(); | ||||
|   ImGui_ImplSDL2_NewFrame(m_window); | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<HostDisplay> D3D11HostDisplay::Create(SDL_Window* window) | ||||
| { | ||||
|   std::unique_ptr<D3D11HostDisplay> display = std::make_unique<D3D11HostDisplay>(window); | ||||
|   if (!display->CreateD3DDevice() || !display->CreateSwapChainRTV() || !display->CreateD3DResources() || | ||||
|       !display->CreateImGuiContext()) | ||||
|   { | ||||
|     return nullptr; | ||||
|   } | ||||
| 
 | ||||
|   return display; | ||||
| } | ||||
| 
 | ||||
| void D3D11HostDisplay::Render() | ||||
| { | ||||
|   static constexpr std::array<float, 4> clear_color = {}; | ||||
|   m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), clear_color.data()); | ||||
|   m_context->OMSetRenderTargets(1, m_swap_chain_rtv.GetAddressOf(), nullptr); | ||||
| 
 | ||||
|   RenderDisplay(); | ||||
| 
 | ||||
|   ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); | ||||
| 
 | ||||
|   m_swap_chain->Present(BoolToUInt32(m_vsync), 0); | ||||
| 
 | ||||
|   ImGui_ImplSDL2_NewFrame(m_window); | ||||
|   ImGui_ImplDX11_NewFrame(); | ||||
| } | ||||
| 
 | ||||
| void D3D11HostDisplay::RenderDisplay() | ||||
| { | ||||
|   if (!m_display_srv) | ||||
|     return; | ||||
| 
 | ||||
|   // - 20 for main menu padding
 | ||||
|   auto [vp_left, vp_top, vp_width, vp_height] = | ||||
|     CalculateDrawRect(m_window_width, std::max(m_window_height - 20, 1), m_display_aspect_ratio); | ||||
|   vp_top += 20; | ||||
| 
 | ||||
|   m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); | ||||
|   m_context->VSSetShader(m_display_vertex_shader.Get(), nullptr, 0); | ||||
|   m_context->PSSetShader(m_display_pixel_shader.Get(), nullptr, 0); | ||||
|   m_context->PSSetShaderResources(0, 1, &m_display_srv); | ||||
|   m_context->PSSetSamplers( | ||||
|     0, 1, m_display_linear_filtering ? m_linear_sampler.GetAddressOf() : m_point_sampler.GetAddressOf()); | ||||
| 
 | ||||
|   const float uniforms[4] = {static_cast<float>(m_display_offset_x) / static_cast<float>(m_display_texture_width), | ||||
|                              static_cast<float>(m_display_offset_y) / static_cast<float>(m_display_texture_height), | ||||
|                              static_cast<float>(m_display_width) / static_cast<float>(m_display_texture_width), | ||||
|                              static_cast<float>(m_display_height) / static_cast<float>(m_display_texture_height)}; | ||||
|   const auto map = m_display_uniform_buffer.Map(m_context.Get(), sizeof(uniforms), sizeof(uniforms)); | ||||
|   std::memcpy(map.pointer, uniforms, sizeof(uniforms)); | ||||
|   m_display_uniform_buffer.Unmap(m_context.Get(), sizeof(uniforms)); | ||||
|   m_context->PSSetConstantBuffers(0, 1, m_display_uniform_buffer.GetD3DBufferArray()); | ||||
| 
 | ||||
|   const CD3D11_VIEWPORT vp(static_cast<float>(vp_left), static_cast<float>(vp_top), static_cast<float>(vp_width), | ||||
|                            static_cast<float>(vp_height)); | ||||
|   m_context->RSSetViewports(1, &vp); | ||||
|   m_context->RSSetState(m_display_rasterizer_state.Get()); | ||||
|   m_context->OMSetDepthStencilState(m_display_depth_stencil_state.Get(), 0); | ||||
|   m_context->OMSetBlendState(m_display_blend_state.Get(), nullptr, 0xFFFFFFFFu); | ||||
| 
 | ||||
|   m_context->Draw(3, 0); | ||||
| } | ||||
							
								
								
									
										84
									
								
								src/duckstation/d3d11_host_display.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/duckstation/d3d11_host_display.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,84 @@ | |||
| #pragma once | ||||
| #include "YBaseLib/Windows/WindowsHeaders.h" | ||||
| #include "common/d3d11/stream_buffer.h" | ||||
| #include "common/d3d11/texture.h" | ||||
| #include "core/host_display.h" | ||||
| #include <SDL.h> | ||||
| #include <d3d11.h> | ||||
| #include <memory> | ||||
| #include <wrl/client.h> | ||||
| 
 | ||||
| class D3D11HostDisplay final : public HostDisplay | ||||
| { | ||||
| public: | ||||
|   template<typename T> | ||||
|   using ComPtr = Microsoft::WRL::ComPtr<T>; | ||||
| 
 | ||||
|   D3D11HostDisplay(SDL_Window* window); | ||||
|   ~D3D11HostDisplay(); | ||||
| 
 | ||||
|   static std::unique_ptr<HostDisplay> Create(SDL_Window* window); | ||||
| 
 | ||||
|   RenderAPI GetRenderAPI() const override; | ||||
|   void* GetHostRenderDevice() const override; | ||||
|   void* GetHostRenderContext() const override; | ||||
| 
 | ||||
|   std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, | ||||
|                                                     bool dynamic) override; | ||||
|   void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, | ||||
|                      u32 data_stride) override; | ||||
| 
 | ||||
|   void SetDisplayTexture(void* texture, u32 offset_x, u32 offset_y, u32 width, u32 height, u32 texture_width, | ||||
|                          u32 texture_height, float aspect_ratio) override; | ||||
|   void SetDisplayLinearFiltering(bool enabled) override; | ||||
| 
 | ||||
|   void SetVSync(bool enabled) override; | ||||
| 
 | ||||
|   std::tuple<u32, u32> GetWindowSize() const override; | ||||
|   void WindowResized() override; | ||||
| 
 | ||||
| private: | ||||
|   static constexpr u32 DISPLAY_UNIFORM_BUFFER_SIZE = 16; | ||||
| 
 | ||||
|   bool CreateD3DDevice(); | ||||
|   bool CreateD3DResources(); | ||||
|   bool CreateSwapChainRTV(); | ||||
|   bool CreateImGuiContext(); | ||||
| 
 | ||||
|   void Render(); | ||||
|   void RenderDisplay(); | ||||
| 
 | ||||
|   SDL_Window* m_window = nullptr; | ||||
|   SDL_GLContext m_gl_context = nullptr; | ||||
|   int m_window_width = 0; | ||||
|   int m_window_height = 0; | ||||
| 
 | ||||
|   ComPtr<ID3D11Device> m_device; | ||||
|   ComPtr<ID3D11DeviceContext> m_context; | ||||
|   ComPtr<IDXGISwapChain> m_swap_chain; | ||||
|   ComPtr<ID3D11RenderTargetView> m_swap_chain_rtv; | ||||
| 
 | ||||
|   ComPtr<ID3D11RasterizerState> m_display_rasterizer_state; | ||||
|   ComPtr<ID3D11DepthStencilState> m_display_depth_stencil_state; | ||||
|   ComPtr<ID3D11BlendState> m_display_blend_state; | ||||
|   ComPtr<ID3D11VertexShader> m_display_vertex_shader; | ||||
|   ComPtr<ID3D11PixelShader> m_display_pixel_shader; | ||||
|   ComPtr<ID3D11SamplerState> m_point_sampler; | ||||
|   ComPtr<ID3D11SamplerState> m_linear_sampler; | ||||
| 
 | ||||
|   D3D11::Texture m_display_pixels_texture; | ||||
|   D3D11::StreamBuffer m_display_uniform_buffer; | ||||
| 
 | ||||
|   ID3D11ShaderResourceView* m_display_srv = nullptr; | ||||
|   u32 m_display_offset_x = 0; | ||||
|   u32 m_display_offset_y = 0; | ||||
|   u32 m_display_width = 0; | ||||
|   u32 m_display_height = 0; | ||||
|   u32 m_display_texture_width = 0; | ||||
|   u32 m_display_texture_height = 0; | ||||
|   float m_display_aspect_ratio = 1.0f; | ||||
| 
 | ||||
|   bool m_display_texture_changed = false; | ||||
|   bool m_display_linear_filtering = false; | ||||
|   bool m_vsync = false; | ||||
| }; | ||||
|  | @ -52,15 +52,19 @@ | |||
|     </ProjectReference> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClCompile Include="d3d11_host_display.cpp" /> | ||||
|     <ClCompile Include="opengl_host_display.cpp" /> | ||||
|     <ClCompile Include="icon.cpp" /> | ||||
|     <ClCompile Include="sdl_audio_stream.cpp" /> | ||||
|     <ClCompile Include="sdl_interface.cpp" /> | ||||
|     <ClCompile Include="sdl_host_interface.cpp" /> | ||||
|     <ClCompile Include="main.cpp" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClInclude Include="d3d11_host_display.h" /> | ||||
|     <ClInclude Include="opengl_host_display.h" /> | ||||
|     <ClInclude Include="icon.h" /> | ||||
|     <ClInclude Include="sdl_audio_stream.h" /> | ||||
|     <ClInclude Include="sdl_interface.h" /> | ||||
|     <ClInclude Include="sdl_host_interface.h" /> | ||||
|   </ItemGroup> | ||||
|   <PropertyGroup Label="Globals"> | ||||
|     <ProjectGuid>{DAA8F93D-9C17-4DE2-BD0B-57891E0FF0D9}</ProjectGuid> | ||||
|  | @ -213,7 +217,7 @@ | |||
|     <Link> | ||||
|       <SubSystem>Console</SubSystem> | ||||
|       <GenerateDebugInformation>true</GenerateDebugInformation> | ||||
|       <AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalDependencies>SDL2.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib32-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | ||||
|     </Link> | ||||
|   </ItemDefinitionGroup> | ||||
|  | @ -235,7 +239,7 @@ | |||
|     <Link> | ||||
|       <SubSystem>Console</SubSystem> | ||||
|       <GenerateDebugInformation>true</GenerateDebugInformation> | ||||
|       <AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalDependencies>SDL2.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib64-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | ||||
|     </Link> | ||||
|   </ItemDefinitionGroup> | ||||
|  | @ -260,7 +264,7 @@ | |||
|     <Link> | ||||
|       <SubSystem>Console</SubSystem> | ||||
|       <GenerateDebugInformation>true</GenerateDebugInformation> | ||||
|       <AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalDependencies>SDL2.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib32-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | ||||
|     </Link> | ||||
|   </ItemDefinitionGroup> | ||||
|  | @ -285,7 +289,7 @@ | |||
|     <Link> | ||||
|       <SubSystem>Console</SubSystem> | ||||
|       <GenerateDebugInformation>true</GenerateDebugInformation> | ||||
|       <AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalDependencies>SDL2.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib64-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | ||||
|     </Link> | ||||
|   </ItemDefinitionGroup> | ||||
|  | @ -308,7 +312,7 @@ | |||
|       <GenerateDebugInformation>true</GenerateDebugInformation> | ||||
|       <EnableCOMDATFolding>true</EnableCOMDATFolding> | ||||
|       <OptimizeReferences>true</OptimizeReferences> | ||||
|       <AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalDependencies>SDL2.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | ||||
|       <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration> | ||||
|     </Link> | ||||
|  | @ -333,7 +337,7 @@ | |||
|       <GenerateDebugInformation>true</GenerateDebugInformation> | ||||
|       <EnableCOMDATFolding>true</EnableCOMDATFolding> | ||||
|       <OptimizeReferences>true</OptimizeReferences> | ||||
|       <AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalDependencies>SDL2.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | ||||
|       <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration> | ||||
|     </Link> | ||||
|  | @ -357,7 +361,7 @@ | |||
|       <GenerateDebugInformation>true</GenerateDebugInformation> | ||||
|       <EnableCOMDATFolding>true</EnableCOMDATFolding> | ||||
|       <OptimizeReferences>true</OptimizeReferences> | ||||
|       <AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalDependencies>SDL2.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | ||||
|       <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration> | ||||
|     </Link> | ||||
|  | @ -382,7 +386,7 @@ | |||
|       <GenerateDebugInformation>true</GenerateDebugInformation> | ||||
|       <EnableCOMDATFolding>true</EnableCOMDATFolding> | ||||
|       <OptimizeReferences>true</OptimizeReferences> | ||||
|       <AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalDependencies>SDL2.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | ||||
|       <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration> | ||||
|     </Link> | ||||
|  |  | |||
|  | @ -2,13 +2,17 @@ | |||
| <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||||
|   <ItemGroup> | ||||
|     <ClCompile Include="main.cpp" /> | ||||
|     <ClCompile Include="sdl_interface.cpp" /> | ||||
|     <ClCompile Include="sdl_audio_stream.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> | ||||
|     <ClInclude Include="sdl_interface.h" /> | ||||
|     <ClInclude Include="icon.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> | ||||
| </Project> | ||||
|  | @ -2,7 +2,7 @@ | |||
| #include "YBaseLib/Log.h" | ||||
| #include "YBaseLib/StringConverter.h" | ||||
| #include "core/system.h" | ||||
| #include "sdl_interface.h" | ||||
| #include "sdl_host_interface.h" | ||||
| #include <SDL.h> | ||||
| #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)) | ||||
| 
 | ||||
|     if (CHECK_ARG_PARAM("-state")) | ||||
|       state_filename = SDLInterface::GetSaveStateFilename(std::strtoul(argv[++i], nullptr, 10)); | ||||
|       state_filename = SDLHostInterface::GetSaveStateFilename(std::strtoul(argv[++i], nullptr, 10)); | ||||
|     else if (CHECK_ARG_PARAM("-exp1")) | ||||
|       exp1_filename = argv[++i]; | ||||
|     else | ||||
|  | @ -51,8 +51,8 @@ static int Run(int argc, char* argv[]) | |||
|   } | ||||
| 
 | ||||
|   // create display and host interface
 | ||||
|   std::unique_ptr<SDLInterface> host_interface = | ||||
|     SDLInterface::Create(filename, exp1_filename, state_filename.IsEmpty() ? nullptr : state_filename.GetCharArray()); | ||||
|   std::unique_ptr<SDLHostInterface> host_interface = | ||||
|     SDLHostInterface::Create(filename, exp1_filename, state_filename.IsEmpty() ? nullptr : state_filename.GetCharArray()); | ||||
|   if (!host_interface) | ||||
|   { | ||||
|     Panic("Failed to create host interface"); | ||||
|  |  | |||
							
								
								
									
										318
									
								
								src/duckstation/opengl_host_display.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								src/duckstation/opengl_host_display.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,318 @@ | |||
| #include "opengl_host_display.h" | ||||
| #include "YBaseLib/Log.h" | ||||
| #include "icon.h" | ||||
| #include <imgui.h> | ||||
| #include <imgui_impl_opengl3.h> | ||||
| #include <imgui_impl_sdl.h> | ||||
| Log_SetChannel(OpenGLHostDisplay); | ||||
| 
 | ||||
| class OpenGLHostDisplayTexture : public HostDisplayTexture | ||||
| { | ||||
| public: | ||||
|   OpenGLHostDisplayTexture(GLuint id, u32 width, u32 height) : m_id(id), m_width(width), m_height(height) {} | ||||
|   ~OpenGLHostDisplayTexture() override { glDeleteTextures(1, &m_id); } | ||||
| 
 | ||||
|   void* GetHandle() const override { return reinterpret_cast<void*>(static_cast<uintptr_t>(m_id)); } | ||||
|   u32 GetWidth() const override { return m_width; } | ||||
|   u32 GetHeight() const override { return m_height; } | ||||
| 
 | ||||
|   GLuint GetGLID() const { return m_id; } | ||||
| 
 | ||||
|   static std::unique_ptr<OpenGLHostDisplayTexture> Create(u32 width, u32 height, const void* initial_data, | ||||
|                                                           u32 initial_data_stride) | ||||
|   { | ||||
|     GLuint id; | ||||
|     glGenTextures(1, &id); | ||||
| 
 | ||||
|     GLint old_texture_binding = 0; | ||||
|     glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture_binding); | ||||
| 
 | ||||
|     // TODO: Set pack width
 | ||||
|     Assert(initial_data_stride == (width * sizeof(u32))); | ||||
| 
 | ||||
|     glBindTexture(GL_TEXTURE_2D, id); | ||||
|     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, initial_data); | ||||
|     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); | ||||
| 
 | ||||
|     glBindTexture(GL_TEXTURE_2D, id); | ||||
|     return std::make_unique<OpenGLHostDisplayTexture>(id, width, height); | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   GLuint m_id; | ||||
|   u32 m_width; | ||||
|   u32 m_height; | ||||
| }; | ||||
| 
 | ||||
| OpenGLHostDisplay::OpenGLHostDisplay(SDL_Window* window) : m_window(window) | ||||
| { | ||||
|   SDL_GetWindowSize(window, &m_window_width, &m_window_height); | ||||
| } | ||||
| 
 | ||||
| OpenGLHostDisplay::~OpenGLHostDisplay() | ||||
| { | ||||
|   if (m_gl_context) | ||||
|   { | ||||
|     if (m_display_vao != 0) | ||||
|       glDeleteVertexArrays(1, &m_display_vao); | ||||
|     if (m_display_linear_sampler != 0) | ||||
|       glDeleteSamplers(1, &m_display_linear_sampler); | ||||
|     if (m_display_nearest_sampler != 0) | ||||
|       glDeleteSamplers(1, &m_display_nearest_sampler); | ||||
| 
 | ||||
|     m_display_program.Destroy(); | ||||
|     ImGui_ImplOpenGL3_Shutdown(); | ||||
|     ImGui_ImplSDL2_Shutdown(); | ||||
|     SDL_GL_MakeCurrent(nullptr, nullptr); | ||||
|     SDL_GL_DeleteContext(m_gl_context); | ||||
|   } | ||||
| 
 | ||||
|   if (m_window) | ||||
|     SDL_DestroyWindow(m_window); | ||||
| } | ||||
| 
 | ||||
| HostDisplay::RenderAPI OpenGLHostDisplay::GetRenderAPI() const | ||||
| { | ||||
|   return HostDisplay::RenderAPI::OpenGL; | ||||
| } | ||||
| 
 | ||||
| void* OpenGLHostDisplay::GetHostRenderDevice() const | ||||
| { | ||||
|   return nullptr; | ||||
| } | ||||
| 
 | ||||
| void* OpenGLHostDisplay::GetHostRenderContext() const | ||||
| { | ||||
|   return m_gl_context; | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<HostDisplayTexture> OpenGLHostDisplay::CreateTexture(u32 width, u32 height, const void* data, | ||||
|                                                                      u32 data_stride, bool dynamic) | ||||
| { | ||||
|   return OpenGLHostDisplayTexture::Create(width, height, data, data_stride); | ||||
| } | ||||
| 
 | ||||
| void OpenGLHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, | ||||
|                                       const void* data, u32 data_stride) | ||||
| { | ||||
|   OpenGLHostDisplayTexture* tex = static_cast<OpenGLHostDisplayTexture*>(texture); | ||||
|   Assert(data_stride == (width * sizeof(u32))); | ||||
| 
 | ||||
|   GLint old_texture_binding = 0; | ||||
|   glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture_binding); | ||||
| 
 | ||||
|   glBindTexture(GL_TEXTURE_2D, tex->GetGLID()); | ||||
|   glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); | ||||
| 
 | ||||
|   glBindTexture(GL_TEXTURE_2D, old_texture_binding); | ||||
| } | ||||
| 
 | ||||
| void OpenGLHostDisplay::SetDisplayTexture(void* texture, u32 offset_x, u32 offset_y, u32 width, u32 height, | ||||
|                                           u32 texture_width, u32 texture_height, float aspect_ratio) | ||||
| { | ||||
|   m_display_texture_id = static_cast<GLuint>(reinterpret_cast<uintptr_t>(texture)); | ||||
|   m_display_offset_x = offset_x; | ||||
|   m_display_offset_y = offset_y; | ||||
|   m_display_width = width; | ||||
|   m_display_height = height; | ||||
|   m_display_texture_width = texture_width; | ||||
|   m_display_texture_height = texture_height; | ||||
|   m_display_aspect_ratio = aspect_ratio; | ||||
|   m_display_texture_changed = true; | ||||
| } | ||||
| 
 | ||||
| void OpenGLHostDisplay::SetDisplayLinearFiltering(bool enabled) | ||||
| { | ||||
|   m_display_linear_filtering = enabled; | ||||
| } | ||||
| 
 | ||||
| void OpenGLHostDisplay::SetVSync(bool enabled) | ||||
| { | ||||
|   // Window framebuffer has to be bound to call SetSwapInterval.
 | ||||
|   GLint current_fbo = 0; | ||||
|   glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo); | ||||
|   glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | ||||
|   SDL_GL_SetSwapInterval(enabled ? 1 : 0); | ||||
|   glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo); | ||||
| } | ||||
| 
 | ||||
| std::tuple<u32, u32> OpenGLHostDisplay::GetWindowSize() const | ||||
| { | ||||
|   return std::make_tuple(static_cast<u32>(m_window_width), static_cast<u32>(m_window_height)); | ||||
| } | ||||
| 
 | ||||
| void OpenGLHostDisplay::WindowResized() | ||||
| { | ||||
|   SDL_GetWindowSize(m_window, &m_window_width, &m_window_height); | ||||
| } | ||||
| 
 | ||||
| static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, | ||||
|                                      const GLchar* message, const void* userParam) | ||||
| { | ||||
|   switch (severity) | ||||
|   { | ||||
|     case GL_DEBUG_SEVERITY_HIGH_KHR: | ||||
|       Log_ErrorPrintf(message); | ||||
|       break; | ||||
|     case GL_DEBUG_SEVERITY_MEDIUM_KHR: | ||||
|       Log_WarningPrint(message); | ||||
|       break; | ||||
|     case GL_DEBUG_SEVERITY_LOW_KHR: | ||||
|       Log_InfoPrintf(message); | ||||
|       break; | ||||
|     case GL_DEBUG_SEVERITY_NOTIFICATION: | ||||
|       // Log_DebugPrint(message);
 | ||||
|       break; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| bool OpenGLHostDisplay::CreateGLContext() | ||||
| { | ||||
|   SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); | ||||
|   SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); | ||||
|   SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); | ||||
|   SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); | ||||
|   SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | ||||
|   m_gl_context = SDL_GL_CreateContext(m_window); | ||||
|   if (!m_gl_context || SDL_GL_MakeCurrent(m_window, m_gl_context) != 0 || !gladLoadGL()) | ||||
|   { | ||||
|     Panic("Failed to create GL context"); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
| #if 1 | ||||
|   if (GLAD_GL_KHR_debug) | ||||
|   { | ||||
|     glad_glDebugMessageCallbackKHR(GLDebugCallback, nullptr); | ||||
|     glEnable(GL_DEBUG_OUTPUT); | ||||
|     glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); | ||||
|   } | ||||
| #endif | ||||
| 
 | ||||
|   SDL_GL_SetSwapInterval(0); | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| bool OpenGLHostDisplay::CreateImGuiContext() | ||||
| { | ||||
|   if (!ImGui_ImplSDL2_InitForOpenGL(m_window, m_gl_context) || !ImGui_ImplOpenGL3_Init()) | ||||
|     return false; | ||||
| 
 | ||||
|   ImGui_ImplOpenGL3_NewFrame(); | ||||
|   ImGui_ImplSDL2_NewFrame(m_window); | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| bool OpenGLHostDisplay::CreateGLResources() | ||||
| { | ||||
|   static constexpr char fullscreen_quad_vertex_shader[] = R"( | ||||
| #version 330 core | ||||
| 
 | ||||
| out vec2 v_tex0; | ||||
| 
 | ||||
| void main() | ||||
| { | ||||
|   v_tex0 = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2)); | ||||
|   gl_Position = vec4(v_tex0 * vec2(2.0f, -2.0f) + vec2(-1.0f, 1.0f), 0.0f, 1.0f); | ||||
|   gl_Position.y = -gl_Position.y; | ||||
| } | ||||
| )"; | ||||
| 
 | ||||
|   static constexpr char display_fragment_shader[] = R"( | ||||
| #version 330 core | ||||
| 
 | ||||
| uniform sampler2D samp0; | ||||
| uniform vec4 u_src_rect; | ||||
| 
 | ||||
| in vec2 v_tex0; | ||||
| out vec4 o_col0; | ||||
| 
 | ||||
| void main() | ||||
| { | ||||
|   vec2 coords = u_src_rect.xy + v_tex0 * u_src_rect.zw; | ||||
|   o_col0 = texture(samp0, coords); | ||||
| } | ||||
| )"; | ||||
| 
 | ||||
|   if (!m_display_program.Compile(fullscreen_quad_vertex_shader, display_fragment_shader)) | ||||
|     return false; | ||||
| 
 | ||||
|   m_display_program.BindFragData(0, "o_col0"); | ||||
|   if (!m_display_program.Link()) | ||||
|     return false; | ||||
| 
 | ||||
|   m_display_program.Bind(); | ||||
|   m_display_program.RegisterUniform("u_src_rect"); | ||||
|   m_display_program.RegisterUniform("samp0"); | ||||
|   m_display_program.Uniform1i(1, 0); | ||||
| 
 | ||||
|   glGenVertexArrays(1, &m_display_vao); | ||||
| 
 | ||||
|   m_app_icon_texture = | ||||
|     std::make_unique<GL::Texture>(APP_ICON_WIDTH, APP_ICON_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, APP_ICON_DATA, true); | ||||
| 
 | ||||
|   // samplers
 | ||||
|   glGenSamplers(1, &m_display_nearest_sampler); | ||||
|   glSamplerParameteri(m_display_nearest_sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||||
|   glSamplerParameteri(m_display_nearest_sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||||
|   glGenSamplers(1, &m_display_linear_sampler); | ||||
|   glSamplerParameteri(m_display_linear_sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||||
|   glSamplerParameteri(m_display_linear_sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<HostDisplay> OpenGLHostDisplay::Create(SDL_Window* window) | ||||
| { | ||||
|   std::unique_ptr<OpenGLHostDisplay> display = std::make_unique<OpenGLHostDisplay>(window); | ||||
|   if (!display->CreateGLContext() || !display->CreateImGuiContext() || !display->CreateGLResources()) | ||||
|     return nullptr; | ||||
| 
 | ||||
|   return display; | ||||
| } | ||||
| 
 | ||||
| void OpenGLHostDisplay::Render() | ||||
| { | ||||
|   glDisable(GL_SCISSOR_TEST); | ||||
|   glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | ||||
|   glClearColor(0.0f, 0.0f, 0.0f, 0.0f); | ||||
|   glClear(GL_COLOR_BUFFER_BIT); | ||||
| 
 | ||||
|   RenderDisplay(); | ||||
| 
 | ||||
|   ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); | ||||
| 
 | ||||
|   SDL_GL_SwapWindow(m_window); | ||||
| 
 | ||||
|   ImGui_ImplSDL2_NewFrame(m_window); | ||||
|   ImGui_ImplOpenGL3_NewFrame(); | ||||
| 
 | ||||
|   GL::Program::ResetLastProgram(); | ||||
| } | ||||
| 
 | ||||
| void OpenGLHostDisplay::RenderDisplay() | ||||
| { | ||||
|   if (!m_display_texture_id) | ||||
|     return; | ||||
| 
 | ||||
|   // - 20 for main menu padding
 | ||||
|   const auto [vp_left, vp_top, vp_width, vp_height] = | ||||
|     CalculateDrawRect(m_window_width, std::max(m_window_height - 20, 1), m_display_aspect_ratio); | ||||
| 
 | ||||
|   glViewport(vp_left, m_window_height - (20 + vp_top) - vp_height, vp_width, vp_height); | ||||
|   glDisable(GL_BLEND); | ||||
|   glDisable(GL_CULL_FACE); | ||||
|   glDisable(GL_DEPTH_TEST); | ||||
|   glDisable(GL_SCISSOR_TEST); | ||||
|   glDepthMask(GL_FALSE); | ||||
|   m_display_program.Bind(); | ||||
|   m_display_program.Uniform4f(0, static_cast<float>(m_display_offset_x) / static_cast<float>(m_display_texture_width), | ||||
|                               static_cast<float>(m_display_offset_y) / static_cast<float>(m_display_texture_height), | ||||
|                               static_cast<float>(m_display_width) / static_cast<float>(m_display_texture_width), | ||||
|                               static_cast<float>(m_display_height) / static_cast<float>(m_display_texture_height)); | ||||
|   glBindTexture(GL_TEXTURE_2D, m_display_texture_id); | ||||
|   glBindSampler(0, m_display_linear_filtering ? m_display_linear_sampler : m_display_nearest_sampler); | ||||
|   glBindVertexArray(m_display_vao); | ||||
|   glDrawArrays(GL_TRIANGLES, 0, 3); | ||||
|   glBindSampler(0, 0); | ||||
| } | ||||
							
								
								
									
										64
									
								
								src/duckstation/opengl_host_display.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/duckstation/opengl_host_display.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | |||
| #pragma once | ||||
| #include "common/gl/program.h" | ||||
| #include "common/gl/texture.h" | ||||
| #include "core/host_display.h" | ||||
| #include <SDL.h> | ||||
| #include <memory> | ||||
| 
 | ||||
| class OpenGLHostDisplay final : public HostDisplay | ||||
| { | ||||
| public: | ||||
|   OpenGLHostDisplay(SDL_Window* window); | ||||
|   ~OpenGLHostDisplay(); | ||||
| 
 | ||||
|   static std::unique_ptr<HostDisplay> Create(SDL_Window* window); | ||||
| 
 | ||||
|   RenderAPI GetRenderAPI() const override; | ||||
|   void* GetHostRenderDevice() const override; | ||||
|   void* GetHostRenderContext() const override; | ||||
| 
 | ||||
|   std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, | ||||
|                                                     bool dynamic) override; | ||||
|   void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, | ||||
|                      u32 data_stride) override; | ||||
| 
 | ||||
|   void SetDisplayTexture(void* texture, u32 offset_x, u32 offset_y, u32 width, u32 height, u32 texture_width, | ||||
|                          u32 texture_height, float aspect_ratio) override; | ||||
|   void SetDisplayLinearFiltering(bool enabled) override; | ||||
| 
 | ||||
|   void SetVSync(bool enabled) override; | ||||
| 
 | ||||
|   std::tuple<u32, u32> GetWindowSize() const override; | ||||
|   void WindowResized() override; | ||||
| 
 | ||||
| private: | ||||
|   bool CreateGLContext(); | ||||
|   bool CreateImGuiContext(); | ||||
|   bool CreateGLResources(); | ||||
| 
 | ||||
|   void Render(); | ||||
|   void RenderDisplay(); | ||||
| 
 | ||||
|   SDL_Window* m_window = nullptr; | ||||
|   SDL_GLContext m_gl_context = nullptr; | ||||
|   int m_window_width = 0; | ||||
|   int m_window_height = 0; | ||||
| 
 | ||||
|   std::unique_ptr<GL::Texture> m_app_icon_texture = nullptr; | ||||
| 
 | ||||
|   GL::Program m_display_program; | ||||
|   GLuint m_display_vao = 0; | ||||
|   GLuint m_display_texture_id = 0; | ||||
|   u32 m_display_offset_x = 0; | ||||
|   u32 m_display_offset_y = 0; | ||||
|   u32 m_display_width = 0; | ||||
|   u32 m_display_height = 0; | ||||
|   u32 m_display_texture_width = 0; | ||||
|   u32 m_display_texture_height = 0; | ||||
|   float m_display_aspect_ratio = 1.0f; | ||||
|   GLuint m_display_nearest_sampler = 0; | ||||
|   GLuint m_display_linear_sampler = 0; | ||||
| 
 | ||||
|   bool m_display_texture_changed = false; | ||||
|   bool m_display_linear_filtering = false; | ||||
| }; | ||||
|  | @ -1,4 +1,4 @@ | |||
| #include "sdl_interface.h" | ||||
| #include "sdl_host_interface.h" | ||||
| #include "YBaseLib/ByteStream.h" | ||||
| #include "YBaseLib/Error.h" | ||||
| #include "YBaseLib/Log.h" | ||||
|  | @ -6,62 +6,54 @@ | |||
| #include "core/digital_controller.h" | ||||
| #include "core/dma.h" | ||||
| #include "core/gpu.h" | ||||
| #include "core/host_display.h" | ||||
| #include "core/mdec.h" | ||||
| #include "core/memory_card.h" | ||||
| #include "core/spu.h" | ||||
| #include "core/system.h" | ||||
| #include "core/timers.h" | ||||
| #ifdef Y_PLATFORM_WINDOWS | ||||
| #include "d3d11_host_display.h" | ||||
| #endif | ||||
| #include "icon.h" | ||||
| #include "opengl_host_display.h" | ||||
| #include "sdl_audio_stream.h" | ||||
| #include <cinttypes> | ||||
| #include <glad.h> | ||||
| #include <imgui.h> | ||||
| #include <imgui_impl_opengl3.h> | ||||
| #include <imgui_impl_sdl.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 = { | ||||
|   {{Settings::GPURenderer::HardwareOpenGL, "Hardware (OpenGL)"}, {Settings::GPURenderer::Software, "Software"}}}; | ||||
| static constexpr std::array<std::pair<Settings::GPURenderer, const char*>, 3> s_gpu_renderer_names = { | ||||
|   {{Settings::GPURenderer::HardwareD3D11, "Hardware (Direct3D 11)"}, | ||||
|    {Settings::GPURenderer::HardwareOpenGL, "Hardware (OpenGL)"}, | ||||
|    {Settings::GPURenderer::Software, "Software"}}}; | ||||
| 
 | ||||
| SDLInterface::SDLInterface() = default; | ||||
| SDLHostInterface::SDLHostInterface() = default; | ||||
| 
 | ||||
| SDLInterface::~SDLInterface() | ||||
| SDLHostInterface::~SDLHostInterface() | ||||
| { | ||||
|   CloseGameControllers(); | ||||
| 
 | ||||
|   if (m_gl_context) | ||||
|   { | ||||
|     if (m_display_vao != 0) | ||||
|       glDeleteVertexArrays(1, &m_display_vao); | ||||
| 
 | ||||
|     m_display_program.Destroy(); | ||||
|     ImGui_ImplOpenGL3_Shutdown(); | ||||
|     ImGui_ImplSDL2_Shutdown(); | ||||
|     ImGui::DestroyContext(); | ||||
|     SDL_GL_MakeCurrent(nullptr, nullptr); | ||||
|     SDL_GL_DeleteContext(m_gl_context); | ||||
|   } | ||||
|   m_display.reset(); | ||||
|   ImGui::DestroyContext(); | ||||
| 
 | ||||
|   if (m_window) | ||||
|     SDL_DestroyWindow(m_window); | ||||
| } | ||||
| 
 | ||||
| bool SDLInterface::CreateSDLWindow() | ||||
| bool SDLHostInterface::CreateSDLWindow() | ||||
| { | ||||
|   constexpr u32 DEFAULT_WINDOW_WIDTH = 900; | ||||
|   constexpr u32 DEFAULT_WINDOW_HEIGHT = 700; | ||||
| 
 | ||||
|   // Create window.
 | ||||
|   constexpr u32 window_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL; | ||||
|   const u32 window_flags = | ||||
|     SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | (UseOpenGLRenderer() ? SDL_WINDOW_OPENGL : 0); | ||||
| 
 | ||||
|   m_window = SDL_CreateWindow("DuckStation", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, DEFAULT_WINDOW_WIDTH, | ||||
|                               DEFAULT_WINDOW_HEIGHT, window_flags); | ||||
|   if (!m_window) | ||||
|   { | ||||
|     Panic("Failed to create window"); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   // Set window icon.
 | ||||
|   SDL_Surface* icon_surface = | ||||
|  | @ -74,133 +66,38 @@ bool SDLInterface::CreateSDLWindow() | |||
|     SDL_FreeSurface(icon_surface); | ||||
|   } | ||||
| 
 | ||||
|   SDL_GetWindowSize(m_window, &m_window_width, &m_window_height); | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, | ||||
|                                      const GLchar* message, const void* userParam) | ||||
| bool SDLHostInterface::CreateDisplay() | ||||
| { | ||||
|   switch (severity) | ||||
|   { | ||||
|     case GL_DEBUG_SEVERITY_HIGH_KHR: | ||||
|       Log_InfoPrint(message); | ||||
|       break; | ||||
|     case GL_DEBUG_SEVERITY_MEDIUM_KHR: | ||||
|       Log_WarningPrint(message); | ||||
|       break; | ||||
|     case GL_DEBUG_SEVERITY_LOW_KHR: | ||||
|       Log_InfoPrintf(message); | ||||
|       break; | ||||
|     case GL_DEBUG_SEVERITY_NOTIFICATION: | ||||
|       // Log_DebugPrint(message);
 | ||||
|       break; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| bool SDLInterface::CreateGLContext() | ||||
| { | ||||
|   SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); | ||||
|   SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); | ||||
|   SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); | ||||
|   SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); | ||||
|   SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | ||||
|   m_gl_context = SDL_GL_CreateContext(m_window); | ||||
|   if (!m_gl_context || SDL_GL_MakeCurrent(m_window, m_gl_context) != 0 || !gladLoadGL()) | ||||
|   { | ||||
|     Panic("Failed to create GL context"); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
| #if 0 | ||||
|   if (GLAD_GL_KHR_debug) | ||||
|   { | ||||
|     glad_glDebugMessageCallbackKHR(GLDebugCallback, nullptr); | ||||
|     glEnable(GL_DEBUG_OUTPUT); | ||||
|     glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); | ||||
|   } | ||||
| #ifdef Y_PLATFORM_WINDOWS | ||||
|   m_display = UseOpenGLRenderer() ? OpenGLHostDisplay::Create(m_window) : D3D11HostDisplay::Create(m_window); | ||||
| #else | ||||
|   m_display = OpenGLHostDisplay::Create(m_window); | ||||
| #endif | ||||
| 
 | ||||
|   SDL_GL_SetSwapInterval(0); | ||||
|   if (!m_display) | ||||
|     return false; | ||||
| 
 | ||||
|   m_app_icon_texture = | ||||
|     m_display->CreateTexture(APP_ICON_WIDTH, APP_ICON_HEIGHT, APP_ICON_DATA, APP_ICON_WIDTH * sizeof(u32)); | ||||
|   if (!m_app_icon_texture) | ||||
|     return false; | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| bool SDLInterface::CreateImGuiContext() | ||||
| bool SDLHostInterface::CreateImGuiContext() | ||||
| { | ||||
|   ImGui::CreateContext(); | ||||
|   ImGui::GetIO().IniFilename = nullptr; | ||||
|   ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad; | ||||
|   ImGui::GetIO().BackendFlags |= ImGuiBackendFlags_HasGamepad; | ||||
| 
 | ||||
|   if (!ImGui_ImplSDL2_InitForOpenGL(m_window, m_gl_context) || !ImGui_ImplOpenGL3_Init()) | ||||
|     return false; | ||||
| 
 | ||||
|   ImGui_ImplOpenGL3_NewFrame(); | ||||
|   ImGui_ImplSDL2_NewFrame(m_window); | ||||
|   ImGui::NewFrame(); | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| bool SDLInterface::CreateGLResources() | ||||
| { | ||||
|   static constexpr char fullscreen_quad_vertex_shader[] = R"( | ||||
| #version 330 core | ||||
| 
 | ||||
| out vec2 v_tex0; | ||||
| 
 | ||||
| void main() | ||||
| { | ||||
|   v_tex0 = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2)); | ||||
|   gl_Position = vec4(v_tex0 * vec2(2.0f, -2.0f) + vec2(-1.0f, 1.0f), 0.0f, 1.0f); | ||||
|   gl_Position.y = -gl_Position.y; | ||||
| } | ||||
| )"; | ||||
| 
 | ||||
|   static constexpr char display_fragment_shader[] = R"( | ||||
| #version 330 core | ||||
| 
 | ||||
| uniform sampler2D samp0; | ||||
| uniform vec4 u_src_rect; | ||||
| 
 | ||||
| in vec2 v_tex0; | ||||
| out vec4 o_col0; | ||||
| 
 | ||||
| void main() | ||||
| { | ||||
|   vec2 coords = u_src_rect.xy + v_tex0 * u_src_rect.zw; | ||||
|   o_col0 = texture(samp0, coords); | ||||
| } | ||||
| )"; | ||||
| 
 | ||||
|   if (!m_display_program.Compile(fullscreen_quad_vertex_shader, display_fragment_shader)) | ||||
|     return false; | ||||
| 
 | ||||
|   m_display_program.BindFragData(0, "o_col0"); | ||||
|   if (!m_display_program.Link()) | ||||
|     return false; | ||||
| 
 | ||||
|   m_display_program.Bind(); | ||||
|   m_display_program.RegisterUniform("u_src_rect"); | ||||
|   m_display_program.RegisterUniform("samp0"); | ||||
|   m_display_program.Uniform1i(1, 0); | ||||
| 
 | ||||
|   glGenVertexArrays(1, &m_display_vao); | ||||
| 
 | ||||
|   m_app_icon_texture = | ||||
|     std::make_unique<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() | ||||
| bool SDLHostInterface::CreateAudioStream() | ||||
| { | ||||
|   m_audio_stream = std::make_unique<SDLAudioStream>(); | ||||
|   if (!m_audio_stream->Reconfigure(44100, 2)) | ||||
|  | @ -212,7 +109,7 @@ bool SDLInterface::CreateAudioStream() | |||
|   return true; | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::UpdateAudioVisualSync() | ||||
| void SDLHostInterface::UpdateAudioVisualSync() | ||||
| { | ||||
|   const bool speed_limiter_enabled = m_speed_limiter_enabled && !m_speed_limiter_temp_disabled; | ||||
|   const bool audio_sync_enabled = speed_limiter_enabled; | ||||
|  | @ -221,16 +118,10 @@ void SDLInterface::UpdateAudioVisualSync() | |||
|                  (speed_limiter_enabled && vsync_enabled) ? " and video" : ""); | ||||
| 
 | ||||
|   m_audio_stream->SetSync(false); | ||||
| 
 | ||||
|   // Window framebuffer has to be bound to call SetSwapInterval.
 | ||||
|   GLint current_fbo = 0; | ||||
|   glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo); | ||||
|   glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | ||||
|   SDL_GL_SetSwapInterval(vsync_enabled ? 1 : 0); | ||||
|   glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo); | ||||
|   m_display->SetVSync(vsync_enabled); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::OpenGameControllers() | ||||
| void SDLHostInterface::OpenGameControllers() | ||||
| { | ||||
|   for (int i = 0; i < SDL_NumJoysticks(); i++) | ||||
|   { | ||||
|  | @ -247,14 +138,14 @@ void SDLInterface::OpenGameControllers() | |||
|   } | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::CloseGameControllers() | ||||
| void SDLHostInterface::CloseGameControllers() | ||||
| { | ||||
|   for (auto& it : m_sdl_controllers) | ||||
|     SDL_GameControllerClose(it.second); | ||||
|   m_sdl_controllers.clear(); | ||||
| } | ||||
| 
 | ||||
| bool SDLInterface::InitializeSystem(const char* filename, const char* exp1_filename) | ||||
| bool SDLHostInterface::InitializeSystem(const char* filename, const char* exp1_filename) | ||||
| { | ||||
|   if (!HostInterface::InitializeSystem(filename, exp1_filename)) | ||||
|   { | ||||
|  | @ -268,13 +159,13 @@ bool SDLInterface::InitializeSystem(const char* filename, const char* exp1_filen | |||
|   return true; | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::ConnectDevices() | ||||
| void SDLHostInterface::ConnectDevices() | ||||
| { | ||||
|   m_controller = DigitalController::Create(); | ||||
|   m_system->SetController(0, m_controller); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::ResetPerformanceCounters() | ||||
| void SDLHostInterface::ResetPerformanceCounters() | ||||
| { | ||||
|   if (m_system) | ||||
|   { | ||||
|  | @ -291,24 +182,22 @@ void SDLInterface::ResetPerformanceCounters() | |||
|   m_fps_timer.Reset(); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::ShutdownSystem() | ||||
| void SDLHostInterface::ShutdownSystem() | ||||
| { | ||||
|   m_system.reset(); | ||||
|   m_paused = false; | ||||
|   m_display_texture = nullptr; | ||||
|   UpdateAudioVisualSync(); | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<SDLInterface> SDLInterface::Create(const char* filename /* = nullptr */, | ||||
|                                                    const char* exp1_filename /* = nullptr */, | ||||
|                                                    const char* save_state_filename /* = nullptr */) | ||||
| std::unique_ptr<SDLHostInterface> SDLHostInterface::Create(const char* filename /* = nullptr */, | ||||
|                                                            const char* exp1_filename /* = nullptr */, | ||||
|                                                            const char* save_state_filename /* = nullptr */) | ||||
| { | ||||
|   std::unique_ptr<SDLInterface> intf = std::make_unique<SDLInterface>(); | ||||
|   if (!intf->CreateSDLWindow() || !intf->CreateGLContext() || !intf->CreateImGuiContext() || | ||||
|       !intf->CreateGLResources() || !intf->CreateAudioStream()) | ||||
|   { | ||||
|   std::unique_ptr<SDLHostInterface> intf = std::make_unique<SDLHostInterface>(); | ||||
|   if (!intf->CreateSDLWindow() || !intf->CreateImGuiContext() || !intf->CreateDisplay() || !intf->CreateAudioStream()) | ||||
|     return nullptr; | ||||
|   } | ||||
| 
 | ||||
|   ImGui::NewFrame(); | ||||
| 
 | ||||
|   intf->OpenGameControllers(); | ||||
| 
 | ||||
|  | @ -323,20 +212,26 @@ std::unique_ptr<SDLInterface> SDLInterface::Create(const char* filename /* = nul | |||
|   } | ||||
| 
 | ||||
|   intf->UpdateAudioVisualSync(); | ||||
| 
 | ||||
|   return intf; | ||||
| } | ||||
| 
 | ||||
| TinyString SDLInterface::GetSaveStateFilename(u32 index) | ||||
| TinyString SDLHostInterface::GetSaveStateFilename(u32 index) | ||||
| { | ||||
|   return TinyString::FromFormat("savestate_%u.bin", index); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::ReportMessage(const char* message) | ||||
| HostDisplay* SDLHostInterface::GetDisplay() const | ||||
| { | ||||
|   return m_display.get(); | ||||
| } | ||||
| 
 | ||||
| void SDLHostInterface::ReportMessage(const char* message) | ||||
| { | ||||
|   AddOSDMessage(message, 3.0f); | ||||
| } | ||||
| 
 | ||||
| bool SDLInterface::IsWindowFullscreen() const | ||||
| bool SDLHostInterface::IsWindowFullscreen() const | ||||
| { | ||||
|   return ((SDL_GetWindowFlags(m_window) & SDL_WINDOW_FULLSCREEN) != 0); | ||||
| } | ||||
|  | @ -491,7 +386,7 @@ static void HandleSDLControllerButtonEventForController(const SDL_Event* ev, Dig | |||
|   } | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::HandleSDLEvent(const SDL_Event* event) | ||||
| void SDLHostInterface::HandleSDLEvent(const SDL_Event* event) | ||||
| { | ||||
|   ImGui_ImplSDL2_ProcessEvent(event); | ||||
| 
 | ||||
|  | @ -500,10 +395,7 @@ void SDLInterface::HandleSDLEvent(const SDL_Event* event) | |||
|     case SDL_WINDOWEVENT: | ||||
|     { | ||||
|       if (event->window.event == SDL_WINDOWEVENT_RESIZED) | ||||
|       { | ||||
|         m_window_width = event->window.data1; | ||||
|         m_window_height = event->window.data2; | ||||
|       } | ||||
|         m_display->WindowResized(); | ||||
|     } | ||||
|     break; | ||||
| 
 | ||||
|  | @ -569,7 +461,7 @@ void SDLInterface::HandleSDLEvent(const SDL_Event* event) | |||
|   } | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::HandleSDLKeyEvent(const SDL_Event* event) | ||||
| void SDLHostInterface::HandleSDLKeyEvent(const SDL_Event* event) | ||||
| { | ||||
|   const bool repeat = event->key.repeat != 0; | ||||
|   if (!repeat && m_controller && HandleSDLKeyEventForController(event, m_controller.get())) | ||||
|  | @ -652,92 +544,12 @@ void SDLInterface::HandleSDLKeyEvent(const SDL_Event* event) | |||
|   } | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::ClearImGuiFocus() | ||||
| void SDLHostInterface::ClearImGuiFocus() | ||||
| { | ||||
|   ImGui::SetWindowFocus(nullptr); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::Render() | ||||
| { | ||||
|   DrawImGui(); | ||||
| 
 | ||||
|   if (m_system) | ||||
|     m_system->GetGPU()->ResetGraphicsAPIState(); | ||||
| 
 | ||||
|   glDisable(GL_SCISSOR_TEST); | ||||
|   glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | ||||
|   glClearColor(0.0f, 0.0f, 0.0f, 0.0f); | ||||
|   glClear(GL_COLOR_BUFFER_BIT); | ||||
| 
 | ||||
|   RenderDisplay(); | ||||
| 
 | ||||
|   ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); | ||||
| 
 | ||||
|   SDL_GL_SwapWindow(m_window); | ||||
| 
 | ||||
|   ImGui_ImplSDL2_NewFrame(m_window); | ||||
|   ImGui_ImplOpenGL3_NewFrame(); | ||||
| 
 | ||||
|   ImGui::NewFrame(); | ||||
| 
 | ||||
|   GL::Program::ResetLastProgram(); | ||||
| 
 | ||||
|   if (m_system) | ||||
|     m_system->GetGPU()->RestoreGraphicsAPIState(); | ||||
| } | ||||
| 
 | ||||
| static std::tuple<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() | ||||
| void SDLHostInterface::DrawImGui() | ||||
| { | ||||
|   DrawMainMenuBar(); | ||||
| 
 | ||||
|  | @ -754,7 +566,7 @@ void SDLInterface::DrawImGui() | |||
|   ImGui::Render(); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DrawMainMenuBar() | ||||
| void SDLHostInterface::DrawMainMenuBar() | ||||
| { | ||||
|   if (!ImGui::BeginMainMenuBar()) | ||||
|     return; | ||||
|  | @ -936,7 +748,7 @@ void SDLInterface::DrawMainMenuBar() | |||
|   ImGui::EndMainMenuBar(); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DrawPoweredOffWindow() | ||||
| void SDLHostInterface::DrawPoweredOffWindow() | ||||
| { | ||||
|   constexpr int WINDOW_WIDTH = 400; | ||||
|   constexpr int WINDOW_HEIGHT = 650; | ||||
|  | @ -956,8 +768,7 @@ void SDLInterface::DrawPoweredOffWindow() | |||
|   } | ||||
| 
 | ||||
|   ImGui::SetCursorPosX((WINDOW_WIDTH - APP_ICON_WIDTH) / 2); | ||||
|   ImGui::Image(reinterpret_cast<ImTextureID>(static_cast<std::uintptr_t>(m_app_icon_texture->GetGLId())), | ||||
|                ImVec2(APP_ICON_WIDTH, APP_ICON_HEIGHT)); | ||||
|   ImGui::Image(m_app_icon_texture->GetHandle(), ImVec2(APP_ICON_WIDTH, APP_ICON_HEIGHT)); | ||||
|   ImGui::SetCursorPosY(APP_ICON_HEIGHT + 32); | ||||
| 
 | ||||
|   static const ImVec2 button_size(static_cast<float>(BUTTON_WIDTH), static_cast<float>(BUTTON_HEIGHT)); | ||||
|  | @ -1014,7 +825,7 @@ void SDLInterface::DrawPoweredOffWindow() | |||
|   ImGui::End(); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DrawAboutWindow() | ||||
| void SDLHostInterface::DrawAboutWindow() | ||||
| { | ||||
|   ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x * 0.5f, ImGui::GetIO().DisplaySize.y * 0.5f), | ||||
|                           ImGuiCond_Always, ImVec2(0.5f, 0.5f)); | ||||
|  | @ -1044,7 +855,7 @@ void SDLInterface::DrawAboutWindow() | |||
|   ImGui::End(); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DrawDebugMenu() | ||||
| void SDLHostInterface::DrawDebugMenu() | ||||
| { | ||||
|   if (!ImGui::BeginMenu("Debug", m_system != nullptr)) | ||||
|     return; | ||||
|  | @ -1076,7 +887,7 @@ void SDLInterface::DrawDebugMenu() | |||
|   ImGui::EndMenu(); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DrawDebugWindows() | ||||
| void SDLHostInterface::DrawDebugWindows() | ||||
| { | ||||
|   const Settings::DebugSettings& debug_settings = m_system->GetSettings().debugging; | ||||
| 
 | ||||
|  | @ -1094,7 +905,7 @@ void SDLInterface::DrawDebugWindows() | |||
|     m_system->GetMDEC()->DrawDebugStateWindow(); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::AddOSDMessage(const char* message, float duration /*= 2.0f*/) | ||||
| void SDLHostInterface::AddOSDMessage(const char* message, float duration /*= 2.0f*/) | ||||
| { | ||||
|   OSDMessage msg; | ||||
|   msg.text = message; | ||||
|  | @ -1104,19 +915,7 @@ void SDLInterface::AddOSDMessage(const char* message, float duration /*= 2.0f*/) | |||
|   m_osd_messages.push_back(std::move(msg)); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::SetDisplayTexture(GL::Texture* texture, u32 offset_x, u32 offset_y, u32 width, u32 height, | ||||
|                                      float aspect_ratio) | ||||
| { | ||||
|   m_display_texture = texture; | ||||
|   m_display_texture_offset_x = offset_x; | ||||
|   m_display_texture_offset_y = offset_y; | ||||
|   m_display_texture_width = width; | ||||
|   m_display_texture_height = height; | ||||
|   m_display_aspect_ratio = aspect_ratio; | ||||
|   m_display_texture_changed = true; | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DrawOSDMessages() | ||||
| void SDLHostInterface::DrawOSDMessages() | ||||
| { | ||||
|   constexpr ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | | ||||
|                                             ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | | ||||
|  | @ -1157,21 +956,21 @@ void SDLInterface::DrawOSDMessages() | |||
|   } | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DoReset() | ||||
| void SDLHostInterface::DoReset() | ||||
| { | ||||
|   m_system->Reset(); | ||||
|   ResetPerformanceCounters(); | ||||
|   AddOSDMessage("System reset."); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DoPowerOff() | ||||
| void SDLHostInterface::DoPowerOff() | ||||
| { | ||||
|   Assert(m_system); | ||||
|   ShutdownSystem(); | ||||
|   AddOSDMessage("System powered off."); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DoResume() | ||||
| void SDLHostInterface::DoResume() | ||||
| { | ||||
|   Assert(!m_system); | ||||
|   if (!InitializeSystem()) | ||||
|  | @ -1189,7 +988,7 @@ void SDLInterface::DoResume() | |||
|   ClearImGuiFocus(); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DoStartDisc() | ||||
| void SDLHostInterface::DoStartDisc() | ||||
| { | ||||
|   Assert(!m_system); | ||||
| 
 | ||||
|  | @ -1205,7 +1004,7 @@ void SDLInterface::DoStartDisc() | |||
|   ClearImGuiFocus(); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DoStartBIOS() | ||||
| void SDLHostInterface::DoStartBIOS() | ||||
| { | ||||
|   Assert(!m_system); | ||||
| 
 | ||||
|  | @ -1217,7 +1016,7 @@ void SDLInterface::DoStartBIOS() | |||
|   ClearImGuiFocus(); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DoChangeDisc() | ||||
| void SDLHostInterface::DoChangeDisc() | ||||
| { | ||||
|   Assert(m_system); | ||||
| 
 | ||||
|  | @ -1234,7 +1033,7 @@ void SDLInterface::DoChangeDisc() | |||
|   ClearImGuiFocus(); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DoLoadState(u32 index) | ||||
| void SDLHostInterface::DoLoadState(u32 index) | ||||
| { | ||||
|   if (!HasSystem() && !InitializeSystem(nullptr, nullptr)) | ||||
|     return; | ||||
|  | @ -1244,14 +1043,14 @@ void SDLInterface::DoLoadState(u32 index) | |||
|   ClearImGuiFocus(); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DoSaveState(u32 index) | ||||
| void SDLHostInterface::DoSaveState(u32 index) | ||||
| { | ||||
|   Assert(m_system); | ||||
|   SaveState(GetSaveStateFilename(index)); | ||||
|   ClearImGuiFocus(); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DoTogglePause() | ||||
| void SDLHostInterface::DoTogglePause() | ||||
| { | ||||
|   if (!m_system) | ||||
|     return; | ||||
|  | @ -1261,7 +1060,7 @@ void SDLInterface::DoTogglePause() | |||
|     m_fps_timer.Reset(); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DoFrameStep() | ||||
| void SDLHostInterface::DoFrameStep() | ||||
| { | ||||
|   if (!m_system) | ||||
|     return; | ||||
|  | @ -1270,7 +1069,7 @@ void SDLInterface::DoFrameStep() | |||
|   m_paused = false; | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DoToggleSoftwareRendering() | ||||
| void SDLHostInterface::DoToggleSoftwareRendering() | ||||
| { | ||||
|   if (!m_system) | ||||
|     return; | ||||
|  | @ -1283,14 +1082,16 @@ void SDLInterface::DoToggleSoftwareRendering() | |||
|   } | ||||
|   else | ||||
|   { | ||||
|     settings.gpu_renderer = Settings::GPURenderer::HardwareOpenGL; | ||||
|     settings.gpu_renderer = m_display->GetRenderAPI() == HostDisplay::RenderAPI::D3D11 ? | ||||
|                               Settings::GPURenderer::HardwareD3D11 : | ||||
|                               Settings::GPURenderer::HardwareOpenGL; | ||||
|     AddOSDMessage("Switched to hardware GPU renderer."); | ||||
|   } | ||||
| 
 | ||||
|   m_system->RecreateGPU(); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::DoModifyInternalResolution(s32 increment) | ||||
| void SDLHostInterface::DoModifyInternalResolution(s32 increment) | ||||
| { | ||||
|   if (!m_system) | ||||
|     return; | ||||
|  | @ -1310,7 +1111,7 @@ void SDLInterface::DoModifyInternalResolution(s32 increment) | |||
|                                        GPU::VRAM_HEIGHT * settings.gpu_resolution_scale)); | ||||
| } | ||||
| 
 | ||||
| void SDLInterface::Run() | ||||
| void SDLHostInterface::Run() | ||||
| { | ||||
|   m_audio_stream->PauseOutput(false); | ||||
| 
 | ||||
|  | @ -1335,7 +1136,21 @@ void SDLInterface::Run() | |||
|       } | ||||
|     } | ||||
| 
 | ||||
|     Render(); | ||||
|     // rendering
 | ||||
|     { | ||||
|       DrawImGui(); | ||||
| 
 | ||||
|       if (m_system) | ||||
|         m_system->GetGPU()->ResetGraphicsAPIState(); | ||||
| 
 | ||||
|       ImGui::Render(); | ||||
|       m_display->Render(); | ||||
| 
 | ||||
|       ImGui::NewFrame(); | ||||
| 
 | ||||
|       if (m_system) | ||||
|         m_system->GetGPU()->RestoreGraphicsAPIState(); | ||||
|     } | ||||
| 
 | ||||
|     if (m_system) | ||||
|     { | ||||
|  | @ -4,6 +4,7 @@ | |||
| #include "common/gl/program.h" | ||||
| #include "common/gl/texture.h" | ||||
| #include "core/host_interface.h" | ||||
| #include "core/host_display.h" | ||||
| #include <SDL.h> | ||||
| #include <array> | ||||
| #include <deque> | ||||
|  | @ -16,19 +17,18 @@ class DigitalController; | |||
| class MemoryCard; | ||||
| class AudioStream; | ||||
| 
 | ||||
| class SDLInterface : public HostInterface | ||||
| class SDLHostInterface : public HostInterface | ||||
| { | ||||
| public: | ||||
|   SDLInterface(); | ||||
|   ~SDLInterface(); | ||||
|   SDLHostInterface(); | ||||
|   ~SDLHostInterface(); | ||||
| 
 | ||||
|   static std::unique_ptr<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); | ||||
| 
 | ||||
|   static TinyString GetSaveStateFilename(u32 index); | ||||
| 
 | ||||
|   void SetDisplayTexture(GL::Texture* texture, u32 offset_x, u32 offset_y, u32 width, u32 height, | ||||
|                          float aspect_ratio) override; | ||||
|   HostDisplay* GetDisplay() const override; | ||||
| 
 | ||||
|   void ReportMessage(const char* message) override; | ||||
| 
 | ||||
|  | @ -50,10 +50,15 @@ private: | |||
| 
 | ||||
|   bool HasSystem() const { return static_cast<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 CreateGLContext(); | ||||
|   bool CreateDisplay(); | ||||
|   bool CreateImGuiContext(); | ||||
|   bool CreateGLResources(); | ||||
|   bool CreateAudioStream(); | ||||
|   void UpdateAudioVisualSync(); | ||||
| 
 | ||||
|  | @ -85,8 +90,6 @@ private: | |||
|   void HandleSDLKeyEvent(const SDL_Event* event); | ||||
|   void ClearImGuiFocus(); | ||||
| 
 | ||||
|   void Render(); | ||||
|   void RenderDisplay(); | ||||
|   void DrawMainMenuBar(); | ||||
|   void DrawPoweredOffWindow(); | ||||
|   void DrawAboutWindow(); | ||||
|  | @ -95,23 +98,8 @@ private: | |||
|   void DrawDebugWindows(); | ||||
| 
 | ||||
|   SDL_Window* m_window = nullptr; | ||||
|   SDL_GLContext m_gl_context = nullptr; | ||||
|   int m_window_width = 0; | ||||
|   int m_window_height = 0; | ||||
| 
 | ||||
|   std::unique_ptr<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::unique_ptr<HostDisplay> m_display; | ||||
|   std::unique_ptr<HostDisplayTexture> m_app_icon_texture; | ||||
| 
 | ||||
|   std::deque<OSDMessage> m_osd_messages; | ||||
|   std::mutex m_osd_messages_lock; | ||||
		Loading…
	
		Reference in a new issue
	
	 Connor McLaughlin
						Connor McLaughlin