mirror of https://github.com/PCSX2/pcsx2.git
GS: Combine HostDisplay with GSDevice
GS/DX11: Don't throw bad_alloc on surface creation fail GS: Link device and host display construction/destruction FullscreenUI: Replace HostDisplayTexture with GSTexture GS: Purge HostDisplayTexture GS: Move everything in HostDisplay to GSDevice GS: Move ImGui rendering to GSDevice GS: Get rid of reset/store API state
This commit is contained in:
parent
77f8a0f5f6
commit
398cf43782
|
@ -0,0 +1,36 @@
|
|||
cbuffer vertexBuffer : register(b0)
|
||||
{
|
||||
float4x4 ProjectionMatrix;
|
||||
};
|
||||
|
||||
struct VS_INPUT
|
||||
{
|
||||
float2 pos : POSITION;
|
||||
float4 col : COLOR0;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct PS_INPUT
|
||||
{
|
||||
float4 pos : SV_POSITION;
|
||||
float4 col : COLOR0;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
PS_INPUT vs_main(VS_INPUT input)
|
||||
{
|
||||
PS_INPUT output;
|
||||
output.pos = mul(ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));
|
||||
output.col = input.col;
|
||||
output.uv = input.uv;
|
||||
return output;
|
||||
}
|
||||
|
||||
sampler sampler0 : register(s0);
|
||||
Texture2D texture0 : register(t0);
|
||||
|
||||
float4 ps_main(PS_INPUT input) : SV_Target
|
||||
{
|
||||
float4 out_col = input.col * texture0.Sample(sampler0, input.uv);
|
||||
return out_col;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
#ifdef VERTEX_SHADER
|
||||
|
||||
layout(location = 0) in vec2 Position;
|
||||
layout(location = 1) in vec2 UV;
|
||||
layout(location = 2) in vec4 Color;
|
||||
|
||||
uniform mat4 ProjMtx;
|
||||
|
||||
out vec2 Frag_UV;
|
||||
out vec4 Frag_Color;
|
||||
|
||||
void vs_main()
|
||||
{
|
||||
Frag_UV = UV;
|
||||
Frag_Color = Color;
|
||||
gl_Position = ProjMtx * vec4(Position.xy, 0.0, 1.0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef FRAGMENT_SHADER
|
||||
|
||||
layout(binding = 0) uniform sampler2D Texture;
|
||||
|
||||
in vec2 Frag_UV;
|
||||
in vec4 Frag_Color;
|
||||
|
||||
layout(location = 0) out vec4 Out_Color;
|
||||
|
||||
void ps_main()
|
||||
{
|
||||
Out_Color = Frag_Color * texture(Texture, Frag_UV.st);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
#ifdef VERTEX_SHADER
|
||||
|
||||
layout(location = 0) in vec2 Position;
|
||||
layout(location = 1) in vec2 UV;
|
||||
layout(location = 2) in vec4 Color;
|
||||
|
||||
layout(push_constant) uniform PushConstants
|
||||
{
|
||||
vec2 uScale;
|
||||
vec2 uTranslate;
|
||||
};
|
||||
|
||||
layout(location = 0) out vec2 Frag_UV;
|
||||
layout(location = 1) out vec4 Frag_Color;
|
||||
|
||||
void vs_main()
|
||||
{
|
||||
Frag_UV = UV;
|
||||
Frag_Color = Color;
|
||||
gl_Position = vec4(Position * uScale + uTranslate, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef FRAGMENT_SHADER
|
||||
|
||||
layout(binding = 0) uniform sampler2D Texture;
|
||||
|
||||
layout(location = 0) in vec2 Frag_UV;
|
||||
layout(location = 1) in vec4 Frag_Color;
|
||||
|
||||
layout(location = 0) out vec4 Out_Color;
|
||||
|
||||
void ps_main()
|
||||
{
|
||||
Out_Color = Frag_Color * texture(Texture, Frag_UV.st);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -41,10 +41,7 @@ D3D11::ShaderCache::ShaderCache() = default;
|
|||
|
||||
D3D11::ShaderCache::~ShaderCache()
|
||||
{
|
||||
if (m_index_file)
|
||||
std::fclose(m_index_file);
|
||||
if (m_blob_file)
|
||||
std::fclose(m_blob_file);
|
||||
Close();
|
||||
}
|
||||
|
||||
bool D3D11::ShaderCache::CacheIndexKey::operator==(const CacheIndexKey& key) const
|
||||
|
@ -82,6 +79,20 @@ bool D3D11::ShaderCache::Open(std::string_view base_path, D3D_FEATURE_LEVEL feat
|
|||
return true;
|
||||
}
|
||||
|
||||
void D3D11::ShaderCache::Close()
|
||||
{
|
||||
if (m_index_file)
|
||||
{
|
||||
std::fclose(m_index_file);
|
||||
m_index_file = nullptr;
|
||||
}
|
||||
if (m_blob_file)
|
||||
{
|
||||
std::fclose(m_blob_file);
|
||||
m_blob_file = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool D3D11::ShaderCache::CreateNew(const std::string& index_filename, const std::string& blob_filename)
|
||||
{
|
||||
if (FileSystem::FileExists(index_filename.c_str()))
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace D3D11
|
|||
bool UsingDebugShaders() const { return m_debug; }
|
||||
|
||||
bool Open(std::string_view base_path, D3D_FEATURE_LEVEL feature_level, u32 version, bool debug);
|
||||
void Close();
|
||||
|
||||
wil::com_ptr_nothrow<ID3DBlob> GetShaderBlob(ShaderCompiler::Type type, const std::string_view& shader_code,
|
||||
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
|
||||
|
@ -103,7 +104,6 @@ namespace D3D11
|
|||
|
||||
bool CreateNew(const std::string& index_filename, const std::string& blob_filename);
|
||||
bool ReadExisting(const std::string& index_filename, const std::string& blob_filename);
|
||||
void Close();
|
||||
|
||||
wil::com_ptr_nothrow<ID3DBlob> CompileAndAddShaderBlob(const CacheIndexKey& key, const std::string_view& shader_code,
|
||||
const D3D_SHADER_MACRO* macros, const char* entry_point);
|
||||
|
|
|
@ -130,7 +130,10 @@ bool Context::Create(IDXGIFactory5* dxgi_factory, IDXGIAdapter1* adapter, bool e
|
|||
pxAssertRel(!g_d3d12_context, "No context exists");
|
||||
|
||||
if (!LoadD3D12Library())
|
||||
{
|
||||
Console.Error("Failed to load D3D12 library");
|
||||
return false;
|
||||
}
|
||||
|
||||
g_d3d12_context.reset(new Context());
|
||||
if (!g_d3d12_context->CreateDevice(dxgi_factory, adapter, enable_debug_layer) ||
|
||||
|
@ -188,7 +191,10 @@ bool Context::CreateDevice(IDXGIFactory5* dxgi_factory, IDXGIAdapter1* adapter,
|
|||
// Create the actual device.
|
||||
hr = s_d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("Failed to create D3D12 device: %08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
// get adapter
|
||||
const LUID luid(m_device->GetAdapterLuid());
|
||||
|
|
|
@ -45,14 +45,7 @@ ShaderCache::ShaderCache() = default;
|
|||
|
||||
ShaderCache::~ShaderCache()
|
||||
{
|
||||
if (m_pipeline_index_file)
|
||||
std::fclose(m_pipeline_index_file);
|
||||
if (m_pipeline_blob_file)
|
||||
std::fclose(m_pipeline_blob_file);
|
||||
if (m_shader_index_file)
|
||||
std::fclose(m_shader_index_file);
|
||||
if (m_shader_blob_file)
|
||||
std::fclose(m_shader_blob_file);
|
||||
Close();
|
||||
}
|
||||
|
||||
bool ShaderCache::CacheIndexKey::operator==(const CacheIndexKey& key) const
|
||||
|
@ -109,6 +102,32 @@ bool ShaderCache::Open(std::string_view base_path, D3D_FEATURE_LEVEL feature_lev
|
|||
return result;
|
||||
}
|
||||
|
||||
void ShaderCache::Close()
|
||||
{
|
||||
if (m_pipeline_index_file)
|
||||
{
|
||||
std::fclose(m_pipeline_index_file);
|
||||
m_pipeline_index_file = nullptr;
|
||||
}
|
||||
if (m_pipeline_blob_file)
|
||||
{
|
||||
std::fclose(m_pipeline_blob_file);
|
||||
m_pipeline_blob_file = nullptr;
|
||||
}
|
||||
if (m_shader_index_file)
|
||||
{
|
||||
std::fclose(m_shader_index_file);
|
||||
m_shader_index_file = nullptr;
|
||||
}
|
||||
if (m_shader_blob_file)
|
||||
{
|
||||
std::fclose(m_shader_blob_file);
|
||||
m_shader_blob_file = nullptr;
|
||||
}
|
||||
|
||||
m_base_path = {};
|
||||
}
|
||||
|
||||
void ShaderCache::InvalidatePipelineCache()
|
||||
{
|
||||
m_pipeline_index.clear();
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace D3D12
|
|||
__fi bool UsingDebugShaders() const { return m_debug; }
|
||||
|
||||
bool Open(std::string_view base_path, D3D_FEATURE_LEVEL feature_level, u32 version, bool debug);
|
||||
void Close();
|
||||
|
||||
__fi ComPtr<ID3DBlob> GetVertexShader(std::string_view shader_code,
|
||||
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main")
|
||||
|
@ -129,7 +130,6 @@ namespace D3D12
|
|||
bool ReadExisting(const std::string& index_filename, const std::string& blob_filename, std::FILE*& index_file,
|
||||
std::FILE*& blob_file, CacheIndex& index);
|
||||
void InvalidatePipelineCache();
|
||||
void Close();
|
||||
|
||||
ComPtr<ID3DBlob> CompileAndAddShaderBlob(const CacheIndexKey& key, std::string_view shader_code,
|
||||
const D3D_SHADER_MACRO* macros, const char* entry_point);
|
||||
|
|
|
@ -485,151 +485,28 @@ namespace GL
|
|||
glUniform4fv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform1ui(const char* name, u32 x) const
|
||||
void Program::UniformMatrix2fv(int index, const float* v)
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform1ui(location, x);
|
||||
glUniformMatrix2fv(location, 1, GL_FALSE, v);
|
||||
}
|
||||
|
||||
void Program::Uniform2ui(const char* name, u32 x, u32 y) const
|
||||
void Program::UniformMatrix3fv(int index, const float* v)
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform2ui(location, x, y);
|
||||
glUniformMatrix3fv(location, 1, GL_FALSE, v);
|
||||
}
|
||||
|
||||
void Program::Uniform3ui(const char* name, u32 x, u32 y, u32 z) const
|
||||
void Program::UniformMatrix4fv(int index, const float* v)
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform3ui(location, x, y, z);
|
||||
}
|
||||
|
||||
void Program::Uniform4ui(const char* name, u32 x, u32 y, u32 z, u32 w) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform4ui(location, x, y, z, w);
|
||||
}
|
||||
|
||||
void Program::Uniform1i(const char* name, s32 x) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform1i(location, x);
|
||||
}
|
||||
|
||||
void Program::Uniform2i(const char* name, s32 x, s32 y) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform2i(location, x, y);
|
||||
}
|
||||
|
||||
void Program::Uniform3i(const char* name, s32 x, s32 y, s32 z) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform3i(location, x, y, z);
|
||||
}
|
||||
|
||||
void Program::Uniform4i(const char* name, s32 x, s32 y, s32 z, s32 w) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform4i(location, x, y, z, w);
|
||||
}
|
||||
|
||||
void Program::Uniform1f(const char* name, float x) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform1f(location, x);
|
||||
}
|
||||
|
||||
void Program::Uniform2f(const char* name, float x, float y) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform2f(location, x, y);
|
||||
}
|
||||
|
||||
void Program::Uniform3f(const char* name, float x, float y, float z) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform3f(location, x, y, z);
|
||||
}
|
||||
|
||||
void Program::Uniform4f(const char* name, float x, float y, float z, float w) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform4f(location, x, y, z, w);
|
||||
}
|
||||
|
||||
void Program::Uniform2uiv(const char* name, const u32* v) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform2uiv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform3uiv(const char* name, const u32* v) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform3uiv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform4uiv(const char* name, const u32* v) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform4uiv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform2iv(const char* name, const s32* v) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform2iv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform3iv(const char* name, const s32* v) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform3iv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform4iv(const char* name, const s32* v) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform4iv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform2fv(const char* name, const float* v) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform2fv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform3fv(const char* name, const float* v) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform3fv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform4fv(const char* name, const float* v) const
|
||||
{
|
||||
const GLint location = glGetUniformLocation(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniform4fv(location, 1, v);
|
||||
glUniformMatrix4fv(location, 1, GL_FALSE, v);
|
||||
}
|
||||
|
||||
void Program::BindUniformBlock(const char* name, u32 index)
|
||||
|
|
|
@ -79,27 +79,9 @@ namespace GL
|
|||
void Uniform3fv(int index, const float* v) const;
|
||||
void Uniform4fv(int index, const float* v) const;
|
||||
|
||||
void Uniform1ui(const char* name, u32 x) const;
|
||||
void Uniform2ui(const char* name, u32 x, u32 y) const;
|
||||
void Uniform3ui(const char* name, u32 x, u32 y, u32 z) const;
|
||||
void Uniform4ui(const char* name, u32 x, u32 y, u32 z, u32 w) const;
|
||||
void Uniform1i(const char* name, s32 x) const;
|
||||
void Uniform2i(const char* name, s32 x, s32 y) const;
|
||||
void Uniform3i(const char* name, s32 x, s32 y, s32 z) const;
|
||||
void Uniform4i(const char* name, s32 x, s32 y, s32 z, s32 w) const;
|
||||
void Uniform1f(const char* name, float x) const;
|
||||
void Uniform2f(const char* name, float x, float y) const;
|
||||
void Uniform3f(const char* name, float x, float y, float z) const;
|
||||
void Uniform4f(const char* name, float x, float y, float z, float w) const;
|
||||
void Uniform2uiv(const char* name, const u32* v) const;
|
||||
void Uniform3uiv(const char* name, const u32* v) const;
|
||||
void Uniform4uiv(const char* name, const u32* v) const;
|
||||
void Uniform2iv(const char* name, const s32* v) const;
|
||||
void Uniform3iv(const char* name, const s32* v) const;
|
||||
void Uniform4iv(const char* name, const s32* v) const;
|
||||
void Uniform2fv(const char* name, const float* v) const;
|
||||
void Uniform3fv(const char* name, const float* v) const;
|
||||
void Uniform4fv(const char* name, const float* v) const;
|
||||
void UniformMatrix2fv(int index, const float* v);
|
||||
void UniformMatrix3fv(int index, const float* v);
|
||||
void UniformMatrix4fv(int index, const float* v);
|
||||
|
||||
void BindUniformBlock(const char* name, u32 index);
|
||||
|
||||
|
|
|
@ -218,9 +218,17 @@ namespace GL
|
|||
{
|
||||
m_index.clear();
|
||||
if (m_index_file)
|
||||
{
|
||||
std::fclose(m_index_file);
|
||||
m_index_file = nullptr;
|
||||
}
|
||||
if (m_blob_file)
|
||||
{
|
||||
std::fclose(m_blob_file);
|
||||
m_blob_file = nullptr;
|
||||
}
|
||||
|
||||
m_base_path = {};
|
||||
}
|
||||
|
||||
bool ShaderCache::Recreate()
|
||||
|
|
|
@ -36,6 +36,7 @@ namespace GL
|
|||
~ShaderCache();
|
||||
|
||||
bool Open(bool is_gles, std::string_view base_path, u32 version);
|
||||
void Close();
|
||||
|
||||
std::optional<Program> GetProgram(const std::string_view vertex_shader, const std::string_view geometry_shader,
|
||||
const std::string_view fragment_shader, const PreLinkCallback& callback = {});
|
||||
|
@ -94,7 +95,6 @@ namespace GL
|
|||
|
||||
bool CreateNew(const std::string& index_filename, const std::string& blob_filename);
|
||||
bool ReadExisting(const std::string& index_filename, const std::string& blob_filename);
|
||||
void Close();
|
||||
bool Recreate();
|
||||
|
||||
bool WriteToBlobFile(const CacheIndexKey& key, const std::vector<u8>& prog_data, u32 prog_format);
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
#include "pcsx2/GS.h"
|
||||
#include "pcsx2/GS/GS.h"
|
||||
#include "pcsx2/GSDumpReplayer.h"
|
||||
#include "pcsx2/HostDisplay.h"
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/INISettingsInterface.h"
|
||||
#include "pcsx2/PAD/Host/PAD.h"
|
||||
|
@ -254,36 +254,21 @@ void Host::SetRelativeMouseMode(bool enabled)
|
|||
{
|
||||
}
|
||||
|
||||
bool Host::AcquireHostDisplay(RenderAPI api, bool clear_state_on_fail)
|
||||
std::optional<WindowInfo> Host::AcquireRenderWindow(RenderAPI api)
|
||||
{
|
||||
const std::optional<WindowInfo> wi(GSRunner::GetPlatformWindowInfo());
|
||||
if (!wi.has_value())
|
||||
return false;
|
||||
|
||||
g_host_display = HostDisplay::CreateForAPI(api);
|
||||
if (!g_host_display)
|
||||
return false;
|
||||
|
||||
if (!g_host_display->CreateDevice(wi.value(), Host::GetEffectiveVSyncMode()) ||
|
||||
!g_host_display->MakeCurrent() || !g_host_display->SetupDevice() || !ImGuiManager::Initialize())
|
||||
{
|
||||
ReleaseHostDisplay(clear_state_on_fail);
|
||||
return false;
|
||||
return GSRunner::GetPlatformWindowInfo();
|
||||
}
|
||||
|
||||
Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", HostDisplay::RenderAPIToString(g_host_display->GetRenderAPI()));
|
||||
Console.Indent().WriteLn(g_host_display->GetDriverInfo());
|
||||
|
||||
return g_host_display.get();
|
||||
}
|
||||
|
||||
void Host::ReleaseHostDisplay(bool clear_state)
|
||||
std::optional<WindowInfo> Host::UpdateRenderWindow()
|
||||
{
|
||||
ImGuiManager::Shutdown(clear_state);
|
||||
g_host_display.reset();
|
||||
return GSRunner::GetPlatformWindowInfo();
|
||||
}
|
||||
|
||||
HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
|
||||
void Host::ReleaseRenderWindow()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::BeginPresentFrame()
|
||||
{
|
||||
if (s_loop_number == 0 && !s_output_prefix.empty())
|
||||
{
|
||||
|
@ -294,33 +279,6 @@ HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
|
|||
std::string dump_path(fmt::format("{}_frame{}.png", s_output_prefix, s_dump_frame_number));
|
||||
GSQueueSnapshot(dump_path);
|
||||
}
|
||||
|
||||
const HostDisplay::PresentResult result = g_host_display->BeginPresent(frame_skip);
|
||||
if (result != HostDisplay::PresentResult::OK)
|
||||
{
|
||||
// don't render imgui
|
||||
ImGuiManager::SkipFrame();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Host::EndPresentFrame()
|
||||
{
|
||||
if (GSDumpReplayer::IsReplayingDump())
|
||||
GSDumpReplayer::RenderUI();
|
||||
|
||||
ImGuiManager::RenderOSD();
|
||||
g_host_display->EndPresent();
|
||||
ImGuiManager::NewFrame();
|
||||
}
|
||||
|
||||
void Host::ResizeHostDisplay(u32 new_window_width, u32 new_window_height, float new_window_scale)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::UpdateHostDisplay()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::RequestResizeHostDisplay(s32 width, s32 height)
|
||||
|
|
|
@ -32,9 +32,9 @@
|
|||
#include "pcsx2/CDVD/CDVDdiscReader.h"
|
||||
#include "pcsx2/Frontend/GameList.h"
|
||||
#include "pcsx2/Frontend/LogSink.h"
|
||||
#include "pcsx2/GS.h"
|
||||
#include "pcsx2/GS/GS.h"
|
||||
#include "pcsx2/GSDumpReplayer.h"
|
||||
#include "pcsx2/HostDisplay.h"
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/PerformanceMetrics.h"
|
||||
#include "pcsx2/Recording/InputRecording.h"
|
||||
|
@ -415,8 +415,8 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread)
|
|||
connect(m_ui.actionStartFullscreenUI, &QAction::triggered, thread, &EmuThread::startFullscreenUI);
|
||||
connect(m_ui.actionStartFullscreenUI2, &QAction::triggered, thread, &EmuThread::startFullscreenUI);
|
||||
connect(thread, &EmuThread::messageConfirmed, this, &MainWindow::confirmMessage, Qt::BlockingQueuedConnection);
|
||||
connect(thread, &EmuThread::onCreateDisplayRequested, this, &MainWindow::createDisplay, Qt::BlockingQueuedConnection);
|
||||
connect(thread, &EmuThread::onUpdateDisplayRequested, this, &MainWindow::updateDisplay, Qt::BlockingQueuedConnection);
|
||||
connect(thread, &EmuThread::onCreateDisplayRequested, this, &MainWindow::createDisplayWindow, Qt::BlockingQueuedConnection);
|
||||
connect(thread, &EmuThread::onUpdateDisplayRequested, this, &MainWindow::updateDisplayWindow, Qt::BlockingQueuedConnection);
|
||||
connect(thread, &EmuThread::onDestroyDisplayRequested, this, &MainWindow::destroyDisplay, Qt::BlockingQueuedConnection);
|
||||
connect(thread, &EmuThread::onResizeDisplayRequested, this, &MainWindow::displayResizeRequested);
|
||||
connect(thread, &EmuThread::onRelativeMouseModeRequested, this, &MainWindow::relativeMouseModeRequested);
|
||||
|
@ -1167,7 +1167,7 @@ void MainWindow::updateEmulationActions(bool starting, bool running, bool stoppi
|
|||
void MainWindow::updateDisplayRelatedActions(bool has_surface, bool render_to_main, bool fullscreen)
|
||||
{
|
||||
// rendering to main, or switched to gamelist/grid
|
||||
m_ui.actionViewSystemDisplay->setEnabled((has_surface && render_to_main) || (!has_surface && g_host_display));
|
||||
m_ui.actionViewSystemDisplay->setEnabled((has_surface && render_to_main) || (!has_surface && GetMTGS().IsOpen()));
|
||||
m_ui.menuWindowSize->setEnabled(has_surface && !fullscreen);
|
||||
m_ui.actionFullscreen->setEnabled(has_surface);
|
||||
|
||||
|
@ -1279,10 +1279,10 @@ bool MainWindow::isShowingGameList() const
|
|||
|
||||
bool MainWindow::isRenderingFullscreen() const
|
||||
{
|
||||
if (!g_host_display || !m_display_widget)
|
||||
if (!GetMTGS().IsOpen() || !m_display_widget)
|
||||
return false;
|
||||
|
||||
return getDisplayContainer()->isFullScreen() || g_host_display->IsFullscreen();
|
||||
return getDisplayContainer()->isFullScreen();
|
||||
}
|
||||
|
||||
bool MainWindow::isRenderingToMain() const
|
||||
|
@ -2197,17 +2197,12 @@ bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr
|
|||
|
||||
#endif
|
||||
|
||||
DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main)
|
||||
std::optional<WindowInfo> MainWindow::createDisplayWindow(bool fullscreen, bool render_to_main)
|
||||
{
|
||||
DevCon.WriteLn("createDisplay(%u, %u)", static_cast<u32>(fullscreen), static_cast<u32>(render_to_main));
|
||||
DevCon.WriteLn(
|
||||
"createDisplayWindow() fullscreen=%s render_to_main=%s", fullscreen ? "true" : "false", render_to_main ? "true" : "false");
|
||||
|
||||
if (!g_host_display)
|
||||
return nullptr;
|
||||
|
||||
const std::string fullscreen_mode(Host::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", ""));
|
||||
const bool is_exclusive_fullscreen = (fullscreen && !fullscreen_mode.empty() && g_host_display->SupportsFullscreen());
|
||||
|
||||
createDisplayWidget(fullscreen, render_to_main, is_exclusive_fullscreen);
|
||||
createDisplayWidget(fullscreen, render_to_main);
|
||||
|
||||
// we need the surface visible.. this might be able to be replaced with something else
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
|
@ -2217,26 +2212,12 @@ DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main)
|
|||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("Failed to get window info from widget"));
|
||||
destroyDisplayWidget(true);
|
||||
return nullptr;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
g_emu_thread->connectDisplaySignals(m_display_widget);
|
||||
|
||||
if (!g_host_display->CreateDevice(wi.value(), Host::GetEffectiveVSyncMode()))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"),
|
||||
tr("Failed to create host display device. This may be due to your GPU not supporting the chosen renderer (%1), or because your "
|
||||
"graphics drivers need to be updated.")
|
||||
.arg(QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(EmuConfig.GS.Renderer))));
|
||||
destroyDisplayWidget(true);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
m_display_created = true;
|
||||
|
||||
if (is_exclusive_fullscreen)
|
||||
setDisplayFullscreen(fullscreen_mode);
|
||||
|
||||
updateWindowTitle();
|
||||
updateWindowState();
|
||||
|
||||
|
@ -2246,34 +2227,28 @@ DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main)
|
|||
updateDisplayWidgetCursor();
|
||||
m_display_widget->setFocus();
|
||||
|
||||
g_host_display->DoneCurrent();
|
||||
return m_display_widget;
|
||||
return wi;
|
||||
}
|
||||
|
||||
DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main, bool surfaceless)
|
||||
std::optional<WindowInfo> MainWindow::updateDisplayWindow(bool fullscreen, bool render_to_main, bool surfaceless)
|
||||
{
|
||||
DevCon.WriteLn("updateDisplay() fullscreen=%s render_to_main=%s surfaceless=%s", fullscreen ? "true" : "false",
|
||||
DevCon.WriteLn("updateDisplayWindow() fullscreen=%s render_to_main=%s surfaceless=%s", fullscreen ? "true" : "false",
|
||||
render_to_main ? "true" : "false", surfaceless ? "true" : "false");
|
||||
|
||||
QWidget* container = m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget);
|
||||
const bool is_fullscreen = isRenderingFullscreen();
|
||||
const bool is_rendering_to_main = isRenderingToMain();
|
||||
const std::string fullscreen_mode(Host::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", ""));
|
||||
const bool is_exclusive_fullscreen = (fullscreen && !fullscreen_mode.empty() && g_host_display->SupportsFullscreen());
|
||||
const bool changing_surfaceless = (!m_display_widget != surfaceless);
|
||||
if (fullscreen == is_fullscreen && is_rendering_to_main == render_to_main && !changing_surfaceless)
|
||||
return m_display_widget;
|
||||
return m_display_widget->getWindowInfo();
|
||||
|
||||
// Skip recreating the surface if we're just transitioning between fullscreen and windowed with render-to-main off.
|
||||
// .. except on Wayland, where everything tends to break if you don't recreate.
|
||||
const bool has_container = (m_display_container != nullptr);
|
||||
const bool needs_container = DisplayContainer::isNeeded(fullscreen, render_to_main);
|
||||
if (!is_rendering_to_main && !render_to_main && !is_exclusive_fullscreen && has_container == needs_container && !needs_container &&
|
||||
!changing_surfaceless)
|
||||
if (!is_rendering_to_main && !render_to_main && has_container == needs_container && !needs_container && !changing_surfaceless)
|
||||
{
|
||||
DevCon.WriteLn("Toggling to %s without recreating surface", (fullscreen ? "fullscreen" : "windowed"));
|
||||
if (g_host_display->IsFullscreen())
|
||||
g_host_display->SetFullscreen(false, 0, 0, 0.0f);
|
||||
|
||||
// since we don't destroy the display widget, we need to save it here
|
||||
if (!is_fullscreen && !is_rendering_to_main)
|
||||
|
@ -2294,45 +2269,37 @@ DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main, b
|
|||
updateWindowState();
|
||||
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
return m_display_widget;
|
||||
return m_display_widget->getWindowInfo();
|
||||
}
|
||||
|
||||
g_host_display->DestroySurface();
|
||||
|
||||
destroyDisplayWidget(surfaceless);
|
||||
|
||||
// if we're going to surfaceless, we're done here
|
||||
if (surfaceless)
|
||||
return nullptr;
|
||||
return WindowInfo();
|
||||
|
||||
createDisplayWidget(fullscreen, render_to_main, is_exclusive_fullscreen);
|
||||
createDisplayWidget(fullscreen, render_to_main);
|
||||
|
||||
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo();
|
||||
if (!wi.has_value())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("Failed to get new window info from widget"));
|
||||
destroyDisplayWidget(true);
|
||||
return nullptr;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
g_emu_thread->connectDisplaySignals(m_display_widget);
|
||||
|
||||
if (!g_host_display->ChangeWindow(wi.value()))
|
||||
pxFailRel("Failed to recreate surface on new widget.");
|
||||
|
||||
if (is_exclusive_fullscreen)
|
||||
setDisplayFullscreen(fullscreen_mode);
|
||||
|
||||
updateWindowTitle();
|
||||
updateWindowState();
|
||||
|
||||
updateDisplayWidgetCursor();
|
||||
m_display_widget->setFocus();
|
||||
|
||||
return m_display_widget;
|
||||
return wi;
|
||||
}
|
||||
|
||||
void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main, bool is_exclusive_fullscreen)
|
||||
void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main)
|
||||
{
|
||||
// If we're rendering to main and were hidden (e.g. coming back from fullscreen),
|
||||
// make sure we're visible before trying to add ourselves. Otherwise Wayland breaks.
|
||||
|
@ -2374,10 +2341,7 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main, bool
|
|||
restoreDisplayWindowGeometryFromConfig();
|
||||
}
|
||||
|
||||
if (!is_exclusive_fullscreen)
|
||||
container->showFullScreen();
|
||||
else
|
||||
container->showNormal();
|
||||
}
|
||||
else if (!render_to_main)
|
||||
{
|
||||
|
@ -2558,23 +2522,6 @@ void MainWindow::restoreDisplayWindowGeometryFromConfig()
|
|||
}
|
||||
}
|
||||
|
||||
void MainWindow::setDisplayFullscreen(const std::string& fullscreen_mode)
|
||||
{
|
||||
u32 width, height;
|
||||
float refresh_rate;
|
||||
if (HostDisplay::ParseFullscreenMode(fullscreen_mode, &width, &height, &refresh_rate))
|
||||
{
|
||||
if (g_host_display->SetFullscreen(true, width, height, refresh_rate))
|
||||
{
|
||||
Host::AddOSDMessage("Acquired exclusive fullscreen.", Host::OSD_INFO_DURATION);
|
||||
}
|
||||
else
|
||||
{
|
||||
Host::AddOSDMessage("Failed to acquire exclusive fullscreen.", Host::OSD_WARNING_DURATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDialog* MainWindow::getSettingsDialog()
|
||||
{
|
||||
if (!m_settings_dialog)
|
||||
|
|
|
@ -128,8 +128,8 @@ public Q_SLOTS:
|
|||
private Q_SLOTS:
|
||||
void onUpdateCheckComplete();
|
||||
|
||||
DisplayWidget* createDisplay(bool fullscreen, bool render_to_main);
|
||||
DisplayWidget* updateDisplay(bool fullscreen, bool render_to_main, bool surfaceless);
|
||||
std::optional<WindowInfo> createDisplayWindow(bool fullscreen, bool render_to_main);
|
||||
std::optional<WindowInfo> updateDisplayWindow(bool fullscreen, bool render_to_main, bool surfaceless);
|
||||
void displayResizeRequested(qint32 width, qint32 height);
|
||||
void relativeMouseModeRequested(bool enabled);
|
||||
void destroyDisplay();
|
||||
|
@ -233,10 +233,9 @@ private:
|
|||
QWidget* getDisplayContainer() const;
|
||||
void saveDisplayWindowGeometryToConfig();
|
||||
void restoreDisplayWindowGeometryFromConfig();
|
||||
void createDisplayWidget(bool fullscreen, bool render_to_main, bool is_exclusive_fullscreen);
|
||||
void createDisplayWidget(bool fullscreen, bool render_to_main);
|
||||
void destroyDisplayWidget(bool show_game_list);
|
||||
void updateDisplayWidgetCursor();
|
||||
void setDisplayFullscreen(const std::string& fullscreen_mode);
|
||||
|
||||
SettingsDialog* getSettingsDialog();
|
||||
void doSettings(const char* category = nullptr);
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
#include "pcsx2/GS.h"
|
||||
#include "pcsx2/GS/GS.h"
|
||||
#include "pcsx2/GSDumpReplayer.h"
|
||||
#include "pcsx2/HostDisplay.h"
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/INISettingsInterface.h"
|
||||
#include "pcsx2/PAD/Host/PAD.h"
|
||||
|
@ -191,9 +190,11 @@ void EmuThread::startFullscreenUI(bool fullscreen)
|
|||
return;
|
||||
}
|
||||
|
||||
if (VMManager::HasValidVM())
|
||||
if (VMManager::HasValidVM() || GetMTGS().IsOpen())
|
||||
return;
|
||||
|
||||
// this should just set the flag so it gets automatically started
|
||||
ImGuiManager::InitializeFullscreenUI();
|
||||
m_run_fullscreen_ui = true;
|
||||
if (fullscreen)
|
||||
m_is_fullscreen = true;
|
||||
|
@ -216,13 +217,13 @@ void EmuThread::stopFullscreenUI()
|
|||
QMetaObject::invokeMethod(this, &EmuThread::stopFullscreenUI, Qt::QueuedConnection);
|
||||
|
||||
// wait until the host display is gone
|
||||
while (g_host_display)
|
||||
while (GetMTGS().IsOpen())
|
||||
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_host_display)
|
||||
if (!GetMTGS().IsOpen())
|
||||
return;
|
||||
|
||||
pxAssertRel(!VMManager::HasValidVM(), "VM is not valid at FSUI shutdown time");
|
||||
|
@ -590,7 +591,7 @@ void EmuThread::checkForSettingChanges(const Pcsx2Config& old_config)
|
|||
{
|
||||
QMetaObject::invokeMethod(g_main_window, &MainWindow::checkForSettingChanges, Qt::QueuedConnection);
|
||||
|
||||
if (g_host_display)
|
||||
if (GetMTGS().IsOpen())
|
||||
{
|
||||
const bool render_to_main = shouldRenderToMain();
|
||||
if (!m_is_fullscreen && m_is_rendering_to_main != render_to_main)
|
||||
|
@ -787,7 +788,7 @@ void EmuThread::connectDisplaySignals(DisplayWidget* widget)
|
|||
|
||||
void EmuThread::onDisplayWindowResized(int width, int height, float scale)
|
||||
{
|
||||
if (!g_host_display)
|
||||
if (!GetMTGS().IsOpen())
|
||||
return;
|
||||
|
||||
GetMTGS().ResizeDisplayWindow(width, height, scale);
|
||||
|
@ -892,115 +893,41 @@ void EmuThread::endCapture()
|
|||
GetMTGS().RunOnGSThread(&GSEndCapture);
|
||||
}
|
||||
|
||||
void EmuThread::updateDisplay()
|
||||
std::optional<WindowInfo> EmuThread::acquireRenderWindow()
|
||||
{
|
||||
pxAssertRel(!isOnEmuThread(), "Not on emu thread");
|
||||
|
||||
// finished with the display for now
|
||||
g_host_display->DoneCurrent();
|
||||
|
||||
// but we should get it back after this call
|
||||
onUpdateDisplayRequested(m_is_fullscreen, !m_is_fullscreen && m_is_rendering_to_main, m_is_surfaceless);
|
||||
if (!g_host_display->MakeCurrent())
|
||||
{
|
||||
pxFailRel("Failed to recreate context after updating");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool EmuThread::acquireHostDisplay(RenderAPI api, bool clear_state_on_fail)
|
||||
{
|
||||
pxAssertRel(!g_host_display, "Host display does not exist on create");
|
||||
m_is_rendering_to_main = shouldRenderToMain();
|
||||
m_is_surfaceless = false;
|
||||
|
||||
g_host_display = HostDisplay::CreateForAPI(api);
|
||||
if (!g_host_display)
|
||||
return false;
|
||||
|
||||
DisplayWidget* widget = emit onCreateDisplayRequested(m_is_fullscreen, m_is_rendering_to_main);
|
||||
if (!widget)
|
||||
{
|
||||
g_host_display.reset();
|
||||
return false;
|
||||
return emit onCreateDisplayRequested(m_is_fullscreen, m_is_rendering_to_main);
|
||||
}
|
||||
|
||||
connectDisplaySignals(widget);
|
||||
|
||||
if (!g_host_display->MakeCurrent())
|
||||
std::optional<WindowInfo> EmuThread::updateRenderWindow()
|
||||
{
|
||||
Console.Error("Failed to make render context current");
|
||||
releaseHostDisplay(clear_state_on_fail);
|
||||
return false;
|
||||
return emit onUpdateDisplayRequested(m_is_fullscreen, !m_is_fullscreen && m_is_rendering_to_main, m_is_surfaceless);
|
||||
}
|
||||
|
||||
if (!g_host_display->SetupDevice() || !ImGuiManager::Initialize())
|
||||
void EmuThread::releaseRenderWindow()
|
||||
{
|
||||
Console.Error("Failed to initialize device/imgui");
|
||||
releaseHostDisplay(clear_state_on_fail);
|
||||
return false;
|
||||
}
|
||||
|
||||
Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", HostDisplay::RenderAPIToString(g_host_display->GetRenderAPI()));
|
||||
Console.Indent().WriteLn(g_host_display->GetDriverInfo());
|
||||
|
||||
if (m_run_fullscreen_ui && !ImGuiManager::InitializeFullscreenUI())
|
||||
{
|
||||
Console.Error("Failed to initialize fullscreen UI");
|
||||
releaseHostDisplay(clear_state_on_fail);
|
||||
m_run_fullscreen_ui = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EmuThread::releaseHostDisplay(bool clear_state)
|
||||
{
|
||||
ImGuiManager::Shutdown(clear_state);
|
||||
|
||||
g_host_display.reset();
|
||||
emit onDestroyDisplayRequested();
|
||||
}
|
||||
|
||||
bool Host::AcquireHostDisplay(RenderAPI api, bool clear_state_on_fail)
|
||||
std::optional<WindowInfo> Host::AcquireRenderWindow(RenderAPI api)
|
||||
{
|
||||
return g_emu_thread->acquireHostDisplay(api, clear_state_on_fail);
|
||||
return g_emu_thread->acquireRenderWindow();
|
||||
}
|
||||
|
||||
void Host::ReleaseHostDisplay(bool clear_state)
|
||||
std::optional<WindowInfo> Host::UpdateRenderWindow()
|
||||
{
|
||||
g_emu_thread->releaseHostDisplay(clear_state);
|
||||
return g_emu_thread->updateRenderWindow();
|
||||
}
|
||||
|
||||
HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
|
||||
void Host::ReleaseRenderWindow()
|
||||
{
|
||||
const HostDisplay::PresentResult result = g_host_display->BeginPresent(frame_skip);
|
||||
if (result != HostDisplay::PresentResult::OK)
|
||||
{
|
||||
// if we're skipping a frame, we need to reset imgui's state, since
|
||||
// we won't be calling EndPresentFrame().
|
||||
ImGuiManager::SkipFrame();
|
||||
return g_emu_thread->releaseRenderWindow();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Host::EndPresentFrame()
|
||||
void Host::BeginPresentFrame()
|
||||
{
|
||||
if (GSDumpReplayer::IsReplayingDump())
|
||||
GSDumpReplayer::RenderUI();
|
||||
|
||||
FullscreenUI::Render();
|
||||
ImGuiManager::RenderOSD();
|
||||
g_host_display->EndPresent();
|
||||
ImGuiManager::NewFrame();
|
||||
}
|
||||
|
||||
void Host::ResizeHostDisplay(u32 new_window_width, u32 new_window_height, float new_window_scale)
|
||||
{
|
||||
g_host_display->ResizeWindow(new_window_width, new_window_height, new_window_scale);
|
||||
ImGuiManager::WindowResized();
|
||||
}
|
||||
|
||||
void Host::RequestResizeHostDisplay(s32 width, s32 height)
|
||||
|
@ -1008,12 +935,6 @@ void Host::RequestResizeHostDisplay(s32 width, s32 height)
|
|||
g_emu_thread->onResizeDisplayRequested(width, height);
|
||||
}
|
||||
|
||||
void Host::UpdateHostDisplay()
|
||||
{
|
||||
g_emu_thread->updateDisplay();
|
||||
ImGuiManager::WindowResized();
|
||||
}
|
||||
|
||||
void Host::OnVMStarting()
|
||||
{
|
||||
CommonHost::OnVMStarting();
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <optional>
|
||||
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/HostDisplay.h"
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/Frontend/InputManager.h"
|
||||
#include "pcsx2/VMManager.h"
|
||||
|
@ -70,10 +69,10 @@ public:
|
|||
bool shouldRenderToMain() const;
|
||||
|
||||
/// Called back from the GS thread when the display state changes (e.g. fullscreen, render to main).
|
||||
bool acquireHostDisplay(RenderAPI api, bool clear_state_on_fail);
|
||||
std::optional<WindowInfo> acquireRenderWindow();
|
||||
std::optional<WindowInfo> updateRenderWindow();
|
||||
void connectDisplaySignals(DisplayWidget* widget);
|
||||
void releaseHostDisplay(bool clear_state);
|
||||
void updateDisplay();
|
||||
void releaseRenderWindow();
|
||||
|
||||
void startBackgroundControllerPollTimer();
|
||||
void stopBackgroundControllerPollTimer();
|
||||
|
@ -118,8 +117,8 @@ public Q_SLOTS:
|
|||
Q_SIGNALS:
|
||||
bool messageConfirmed(const QString& title, const QString& message);
|
||||
|
||||
DisplayWidget* onCreateDisplayRequested(bool fullscreen, bool render_to_main);
|
||||
DisplayWidget* onUpdateDisplayRequested(bool fullscreen, bool render_to_main, bool surfaceless);
|
||||
std::optional<WindowInfo> onCreateDisplayRequested(bool fullscreen, bool render_to_main);
|
||||
std::optional<WindowInfo> onUpdateDisplayRequested(bool fullscreen, bool render_to_main, bool surfaceless);
|
||||
void onResizeDisplayRequested(qint32 width, qint32 height);
|
||||
void onDestroyDisplayRequested();
|
||||
void onRelativeMouseModeRequested(bool enabled);
|
||||
|
|
|
@ -26,18 +26,6 @@
|
|||
#include "pcsx2/GS/GSCapture.h"
|
||||
#include "pcsx2/GS/GSUtil.h"
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "Frontend/VulkanHostDisplay.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Frontend/D3D11HostDisplay.h"
|
||||
#include "Frontend/D3D12HostDisplay.h"
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#include "GS/Renderers/Metal/GSMetalCPPAccessible.h"
|
||||
#endif
|
||||
|
||||
struct RendererInfo
|
||||
{
|
||||
const char* name;
|
||||
|
@ -965,37 +953,9 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
|
|||
m_ui.disableFramebufferFetch->setDisabled(is_sw_dx);
|
||||
|
||||
// populate adapters
|
||||
HostDisplay::AdapterAndModeList modes;
|
||||
switch (type)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
case GSRendererType::DX11:
|
||||
modes = D3D11HostDisplay::StaticGetAdapterAndModeList();
|
||||
break;
|
||||
case GSRendererType::DX12:
|
||||
modes = D3D12HostDisplay::StaticGetAdapterAndModeList();
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
case GSRendererType::VK:
|
||||
modes = VulkanHostDisplay::StaticGetAdapterAndModeList(nullptr);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
case GSRendererType::Metal:
|
||||
modes = GetMetalAdapterAndModeList();
|
||||
break;
|
||||
#endif
|
||||
|
||||
case GSRendererType::OGL:
|
||||
case GSRendererType::SW:
|
||||
case GSRendererType::Null:
|
||||
case GSRendererType::Auto:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
std::vector<std::string> adapters;
|
||||
std::vector<std::string> fullscreen_modes;
|
||||
GSGetAdaptersAndFullscreenModes(type, &adapters, &fullscreen_modes);
|
||||
|
||||
// fill+select adapters
|
||||
{
|
||||
|
@ -1003,7 +963,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
|
|||
|
||||
std::string current_adapter = Host::GetBaseStringSettingValue("EmuCore/GS", "Adapter", "");
|
||||
m_ui.adapter->clear();
|
||||
m_ui.adapter->setEnabled(!modes.adapter_names.empty());
|
||||
m_ui.adapter->setEnabled(!adapters.empty());
|
||||
m_ui.adapter->addItem(tr("(Default)"));
|
||||
m_ui.adapter->setCurrentIndex(0);
|
||||
|
||||
|
@ -1019,7 +979,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
|
|||
}
|
||||
}
|
||||
|
||||
for (const std::string& adapter : modes.adapter_names)
|
||||
for (const std::string& adapter : adapters)
|
||||
{
|
||||
m_ui.adapter->addItem(QString::fromStdString(adapter));
|
||||
if (current_adapter == adapter)
|
||||
|
@ -1047,7 +1007,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
|
|||
}
|
||||
}
|
||||
|
||||
for (const std::string& fs_mode : modes.fullscreen_modes)
|
||||
for (const std::string& fs_mode : fullscreen_modes)
|
||||
{
|
||||
m_ui.fullscreenModes->addItem(QString::fromStdString(fs_mode));
|
||||
if (current_mode == fs_mode)
|
||||
|
|
|
@ -98,7 +98,6 @@ set(pcsx2Sources
|
|||
Gif_Unit.cpp
|
||||
GS.cpp
|
||||
Host.cpp
|
||||
HostDisplay.cpp
|
||||
Hw.cpp
|
||||
HwRead.cpp
|
||||
HwWrite.cpp
|
||||
|
@ -180,7 +179,6 @@ set(pcsx2Headers
|
|||
GS.h
|
||||
Hardware.h
|
||||
Host.h
|
||||
HostDisplay.h
|
||||
Hw.h
|
||||
IopBios.h
|
||||
IopCounters.h
|
||||
|
@ -785,42 +783,7 @@ set(pcsx2FrontendHeaders
|
|||
Frontend/ImGuiOverlays.h
|
||||
)
|
||||
|
||||
if(USE_OPENGL)
|
||||
list(APPEND pcsx2FrontendSources
|
||||
Frontend/OpenGLHostDisplay.cpp
|
||||
Frontend/imgui_impl_opengl3.cpp
|
||||
)
|
||||
list(APPEND pcsx2FrontendHeaders
|
||||
Frontend/OpenGLHostDisplay.h
|
||||
Frontend/imgui_impl_opengl3.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if(USE_VULKAN)
|
||||
list(APPEND pcsx2FrontendSources
|
||||
Frontend/VulkanHostDisplay.cpp
|
||||
Frontend/imgui_impl_vulkan.cpp
|
||||
)
|
||||
list(APPEND pcsx2FrontendHeaders
|
||||
Frontend/imgui_impl_vulkan.h
|
||||
Frontend/VulkanHostDisplay.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND pcsx2FrontendSources
|
||||
Frontend/D3D11HostDisplay.cpp
|
||||
Frontend/D3D12HostDisplay.cpp
|
||||
Frontend/imgui_impl_dx11.cpp
|
||||
Frontend/imgui_impl_dx12.cpp
|
||||
)
|
||||
list(APPEND pcsx2FrontendHeaders
|
||||
Frontend/D3D11HostDisplay.h
|
||||
Frontend/D3D12HostDisplay.h
|
||||
Frontend/imgui_impl_dx11.h
|
||||
Frontend/imgui_impl_dx12.h
|
||||
)
|
||||
elseif(APPLE)
|
||||
if(APPLE)
|
||||
list(APPEND pcsx2GSSources
|
||||
GS/Renderers/Metal/GSDeviceMTL.mm
|
||||
GS/Renderers/Metal/GSMTLDeviceInfo.mm
|
||||
|
@ -834,12 +797,6 @@ elseif(APPLE)
|
|||
GS/Renderers/Metal/GSMTLShaderCommon.h
|
||||
GS/Renderers/Metal/GSTextureMTL.h
|
||||
)
|
||||
list(APPEND pcsx2FrontendSources
|
||||
Frontend/MetalHostDisplay.mm
|
||||
)
|
||||
list(APPEND pcsx2FrontendHeaders
|
||||
Frontend/MetalHostDisplay.h
|
||||
)
|
||||
endif()
|
||||
|
||||
list(APPEND pcsx2FrontendSources
|
||||
|
|
|
@ -25,13 +25,13 @@
|
|||
#include "IopCounters.h"
|
||||
|
||||
#include "GS.h"
|
||||
#include "GS/GS.h"
|
||||
#include "VUmicro.h"
|
||||
#include "PerformanceMetrics.h"
|
||||
#include "Patch.h"
|
||||
|
||||
#include "ps2/HwInternal.h"
|
||||
#include "Sio.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "SPU2/spu2.h"
|
||||
#include "PAD/Host/PAD.h"
|
||||
#include "Recording/InputRecording.h"
|
||||
|
@ -357,7 +357,7 @@ static double AdjustToHostRefreshRate(double vertical_frequency, double frame_li
|
|||
}
|
||||
|
||||
float host_refresh_rate;
|
||||
if (!g_host_display->GetHostRefreshRate(&host_refresh_rate))
|
||||
if (!GSGetHostRefreshRate(&host_refresh_rate))
|
||||
{
|
||||
Console.Warning("Cannot sync to host refresh since the query failed.");
|
||||
SPU2::SetDeviceSampleRateMultiplier(1.0);
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include "GS.h"
|
||||
#include "GS/Renderers/HW/GSTextureReplacements.h"
|
||||
#include "Host.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "HostSettings.h"
|
||||
#include "IconsFontAwesome5.h"
|
||||
#include "MemoryCardFile.h"
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include "Frontend/InputManager.h"
|
||||
#include "GS.h"
|
||||
#include "Host.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "IconsFontAwesome5.h"
|
||||
#include "Recording/InputRecording.h"
|
||||
#include "SPU2/spu2.h"
|
||||
|
|
|
@ -1,740 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "Frontend/D3D11HostDisplay.h"
|
||||
#include "GS/Renderers/DX11/D3D.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_dx11.h"
|
||||
#include <array>
|
||||
#include <dxgi1_5.h>
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/RedtapeWindows.h"
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#pragma comment(lib, "d3d11.lib")
|
||||
#pragma comment(lib, "dxgi.lib")
|
||||
|
||||
class D3D11HostDisplayTexture : public HostDisplayTexture
|
||||
{
|
||||
public:
|
||||
D3D11HostDisplayTexture(wil::com_ptr_nothrow<ID3D11Texture2D> texture, wil::com_ptr_nothrow<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; }
|
||||
|
||||
__fi ID3D11Texture2D* GetD3DTexture() const { return m_texture.get(); }
|
||||
__fi ID3D11ShaderResourceView* GetD3DSRV() const { return m_srv.get(); }
|
||||
__fi ID3D11ShaderResourceView* const* GetD3DSRVArray() const { return m_srv.addressof(); }
|
||||
__fi bool IsDynamic() const { return m_dynamic; }
|
||||
|
||||
private:
|
||||
wil::com_ptr_nothrow<ID3D11Texture2D> m_texture;
|
||||
wil::com_ptr_nothrow<ID3D11ShaderResourceView> m_srv;
|
||||
u32 m_width;
|
||||
u32 m_height;
|
||||
bool m_dynamic;
|
||||
};
|
||||
|
||||
D3D11HostDisplay::D3D11HostDisplay() = default;
|
||||
|
||||
D3D11HostDisplay::~D3D11HostDisplay()
|
||||
{
|
||||
D3D11HostDisplay::DestroySurface();
|
||||
m_context.reset();
|
||||
m_device.reset();
|
||||
}
|
||||
|
||||
RenderAPI D3D11HostDisplay::GetRenderAPI() const
|
||||
{
|
||||
return RenderAPI::D3D11;
|
||||
}
|
||||
|
||||
void* D3D11HostDisplay::GetDevice() const
|
||||
{
|
||||
return m_device.get();
|
||||
}
|
||||
|
||||
void* D3D11HostDisplay::GetContext() const
|
||||
{
|
||||
return m_context.get();
|
||||
}
|
||||
|
||||
void* D3D11HostDisplay::GetSurface() const
|
||||
{
|
||||
return m_swap_chain.get();
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::HasDevice() const
|
||||
{
|
||||
return static_cast<bool>(m_device);
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::HasSurface() const
|
||||
{
|
||||
return static_cast<bool>(m_swap_chain);
|
||||
}
|
||||
|
||||
std::unique_ptr<HostDisplayTexture> D3D11HostDisplay::CreateTexture(
|
||||
u32 width, u32 height, const void* data, u32 data_stride, bool dynamic /* = false */)
|
||||
{
|
||||
const CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, width, height, 1u, 1u, 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 = m_device->CreateTexture2D(&desc, data ? &srd : nullptr, texture.addressof());
|
||||
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 = m_device->CreateShaderResourceView(texture.get(), &srv_desc, srv.addressof());
|
||||
if (FAILED(hr))
|
||||
return {};
|
||||
|
||||
return std::make_unique<D3D11HostDisplayTexture>(std::move(texture), std::move(srv), width, height, dynamic);
|
||||
}
|
||||
|
||||
void D3D11HostDisplay::UpdateTexture(
|
||||
HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_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, texture_data, texture_data_stride, texture_data_stride * height);
|
||||
}
|
||||
else
|
||||
{
|
||||
D3D11_MAPPED_SUBRESOURCE sr;
|
||||
HRESULT hr = m_context->Map(d3d11_texture->GetD3DTexture(), 0, D3D11_MAP_WRITE_DISCARD, 0, &sr);
|
||||
pxAssertMsg(SUCCEEDED(hr), "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*>(texture_data);
|
||||
if (sr.RowPitch == texture_data_stride)
|
||||
{
|
||||
std::memcpy(dst_ptr, src_ptr, texture_data_stride * height);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (u32 row = 0; row < height; row++)
|
||||
{
|
||||
std::memcpy(dst_ptr, src_ptr, width * sizeof(u32));
|
||||
src_ptr += texture_data_stride;
|
||||
dst_ptr += sr.RowPitch;
|
||||
}
|
||||
}
|
||||
|
||||
m_context->Unmap(d3d11_texture->GetD3DTexture(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::GetHostRefreshRate(float* refresh_rate)
|
||||
{
|
||||
if (m_swap_chain && IsFullscreen())
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC desc;
|
||||
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)) && desc.BufferDesc.RefreshRate.Numerator > 0 &&
|
||||
desc.BufferDesc.RefreshRate.Denominator > 0)
|
||||
{
|
||||
DevCon.WriteLn("using fs rr: %u %u", desc.BufferDesc.RefreshRate.Numerator, desc.BufferDesc.RefreshRate.Denominator);
|
||||
*refresh_rate =
|
||||
static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) / static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return HostDisplay::GetHostRefreshRate(refresh_rate);
|
||||
}
|
||||
|
||||
void D3D11HostDisplay::SetVSync(VsyncMode mode)
|
||||
{
|
||||
m_vsync_mode = mode;
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::CreateDevice(const WindowInfo& wi, VsyncMode vsync)
|
||||
{
|
||||
UINT create_flags = 0;
|
||||
if (EmuConfig.GS.UseDebugDevice)
|
||||
create_flags |= D3D11_CREATE_DEVICE_DEBUG;
|
||||
|
||||
m_dxgi_factory = D3D::CreateFactory(EmuConfig.GS.UseDebugDevice);
|
||||
if (!m_dxgi_factory)
|
||||
return false;
|
||||
|
||||
ComPtr<IDXGIAdapter1> dxgi_adapter = D3D::GetAdapterByName(m_dxgi_factory.get(), EmuConfig.GS.Adapter);
|
||||
|
||||
static constexpr std::array<D3D_FEATURE_LEVEL, 3> requested_feature_levels = {
|
||||
{D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0}};
|
||||
|
||||
HRESULT hr = D3D11CreateDevice(dxgi_adapter.get(), dxgi_adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, nullptr,
|
||||
create_flags, requested_feature_levels.data(), static_cast<UINT>(requested_feature_levels.size()), D3D11_SDK_VERSION,
|
||||
m_device.put(), nullptr, m_context.put());
|
||||
|
||||
// we re-grab these later, see below
|
||||
dxgi_adapter.reset();
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("Failed to create D3D device: 0x%08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (EmuConfig.GS.UseDebugDevice && IsDebuggerPresent())
|
||||
{
|
||||
ComPtr<ID3D11InfoQueue> info;
|
||||
if (m_device.try_query_to(&info))
|
||||
{
|
||||
info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, TRUE);
|
||||
info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
ComPtr<IDXGIDevice> dxgi_device;
|
||||
if (m_device.try_query_to(&dxgi_device) && SUCCEEDED(dxgi_device->GetParent(IID_PPV_ARGS(dxgi_adapter.put()))))
|
||||
Console.WriteLn(fmt::format("D3D Adapter: {}", D3D::GetAdapterName(dxgi_adapter.get())));
|
||||
else
|
||||
Console.Error("Failed to obtain D3D adapter name.");
|
||||
|
||||
BOOL allow_tearing_supported = false;
|
||||
hr = m_dxgi_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, sizeof(allow_tearing_supported));
|
||||
m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
|
||||
|
||||
m_window_info = wi;
|
||||
m_vsync_mode = vsync;
|
||||
|
||||
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain(nullptr))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::SetupDevice()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::MakeCurrent()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::DoneCurrent()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
|
||||
{
|
||||
if (m_window_info.type != WindowInfo::Type::Win32)
|
||||
return false;
|
||||
|
||||
m_using_flip_model_swap_chain = !EmuConfig.GS.UseBlitSwapChain || fullscreen_mode;
|
||||
|
||||
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
|
||||
RECT client_rc{};
|
||||
GetClientRect(window_hwnd, &client_rc);
|
||||
const u32 width = static_cast<u32>(client_rc.right - client_rc.left);
|
||||
const u32 height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
|
||||
swap_chain_desc.Width = width;
|
||||
swap_chain_desc.Height = height;
|
||||
swap_chain_desc.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.SwapEffect = m_using_flip_model_swap_chain ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
|
||||
|
||||
m_using_allow_tearing = (m_allow_tearing_supported && m_using_flip_model_swap_chain && !fullscreen_mode);
|
||||
if (m_using_allow_tearing)
|
||||
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
||||
|
||||
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
|
||||
if (fullscreen_mode)
|
||||
{
|
||||
swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
||||
swap_chain_desc.Width = fullscreen_mode->Width;
|
||||
swap_chain_desc.Height = fullscreen_mode->Height;
|
||||
fs_desc.RefreshRate = fullscreen_mode->RefreshRate;
|
||||
fs_desc.ScanlineOrdering = fullscreen_mode->ScanlineOrdering;
|
||||
fs_desc.Scaling = fullscreen_mode->Scaling;
|
||||
fs_desc.Windowed = FALSE;
|
||||
}
|
||||
|
||||
Console.WriteLn("Creating a %dx%d %s %s swap chain", swap_chain_desc.Width, swap_chain_desc.Height,
|
||||
m_using_flip_model_swap_chain ? "flip-discard" : "discard", fullscreen_mode ? "full-screen" : "windowed");
|
||||
|
||||
HRESULT hr = m_dxgi_factory->CreateSwapChainForHwnd(
|
||||
m_device.get(), window_hwnd, &swap_chain_desc, fullscreen_mode ? &fs_desc : nullptr, nullptr, m_swap_chain.put());
|
||||
if (FAILED(hr) && m_using_flip_model_swap_chain)
|
||||
{
|
||||
Console.Warning("Failed to create a flip-discard swap chain, trying discard.");
|
||||
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
|
||||
swap_chain_desc.Flags = 0;
|
||||
m_using_flip_model_swap_chain = false;
|
||||
m_using_allow_tearing = false;
|
||||
|
||||
hr = m_dxgi_factory->CreateSwapChainForHwnd(
|
||||
m_device.get(), window_hwnd, &swap_chain_desc, fullscreen_mode ? &fs_desc : nullptr, nullptr, m_swap_chain.put());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("CreateSwapChainForHwnd failed: 0x%08X", hr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
hr = m_dxgi_factory->MakeWindowAssociation(window_hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
|
||||
if (FAILED(hr))
|
||||
Console.Warning("MakeWindowAssociation() to disable ALT+ENTER failed");
|
||||
|
||||
return CreateSwapChainRTV();
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::CreateSwapChainRTV()
|
||||
{
|
||||
ComPtr<ID3D11Texture2D> backbuffer;
|
||||
HRESULT hr = m_swap_chain->GetBuffer(0, IID_PPV_ARGS(backbuffer.put()));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("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.put());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("CreateRenderTargetView for swap chain failed: 0x%08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_window_info.surface_width = backbuffer_desc.Width;
|
||||
m_window_info.surface_height = backbuffer_desc.Height;
|
||||
DevCon.WriteLn("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height);
|
||||
|
||||
if (m_window_info.type == WindowInfo::Type::Win32)
|
||||
{
|
||||
BOOL fullscreen = FALSE;
|
||||
DXGI_SWAP_CHAIN_DESC desc;
|
||||
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen && SUCCEEDED(m_swap_chain->GetDesc(&desc)))
|
||||
{
|
||||
m_window_info.surface_refresh_rate =
|
||||
static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) / static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_window_info.surface_refresh_rate = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::ChangeWindow(const WindowInfo& new_wi)
|
||||
{
|
||||
DestroySurface();
|
||||
|
||||
m_window_info = new_wi;
|
||||
return CreateSwapChain(nullptr);
|
||||
}
|
||||
|
||||
void D3D11HostDisplay::DestroySurface()
|
||||
{
|
||||
if (IsFullscreen())
|
||||
SetFullscreen(false, 0, 0, 0.0f);
|
||||
|
||||
m_swap_chain_rtv.reset();
|
||||
m_swap_chain.reset();
|
||||
}
|
||||
|
||||
std::string D3D11HostDisplay::GetDriverInfo() const
|
||||
{
|
||||
std::string ret = "Unknown Feature Level";
|
||||
|
||||
static constexpr std::array<std::tuple<D3D_FEATURE_LEVEL, const char*>, 4> feature_level_names = {{
|
||||
{D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_0"},
|
||||
{D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_1"},
|
||||
{D3D_FEATURE_LEVEL_11_0, "D3D_FEATURE_LEVEL_11_0"},
|
||||
{D3D_FEATURE_LEVEL_11_1, "D3D_FEATURE_LEVEL_11_1"},
|
||||
}};
|
||||
|
||||
const D3D_FEATURE_LEVEL fl = m_device->GetFeatureLevel();
|
||||
for (size_t i = 0; i < std::size(feature_level_names); i++)
|
||||
{
|
||||
if (fl == std::get<0>(feature_level_names[i]))
|
||||
{
|
||||
ret = std::get<1>(feature_level_names[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret += "\n";
|
||||
|
||||
ComPtr<IDXGIDevice> dxgi_dev;
|
||||
if (m_device.try_query_to(&dxgi_dev))
|
||||
{
|
||||
ComPtr<IDXGIAdapter> dxgi_adapter;
|
||||
if (SUCCEEDED(dxgi_dev->GetAdapter(dxgi_adapter.put())))
|
||||
{
|
||||
DXGI_ADAPTER_DESC desc;
|
||||
if (SUCCEEDED(dxgi_adapter->GetDesc(&desc)))
|
||||
{
|
||||
ret += StringUtil::StdStringFromFormat("VID: 0x%04X PID: 0x%04X\n", desc.VendorId, desc.DeviceId);
|
||||
ret += StringUtil::WideStringToUTF8String(desc.Description);
|
||||
ret += "\n";
|
||||
|
||||
const std::string driver_version(D3D::GetDriverVersionFromLUID(desc.AdapterLuid));
|
||||
if (!driver_version.empty())
|
||||
{
|
||||
ret += "Driver Version: ";
|
||||
ret += driver_version;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void D3D11HostDisplay::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
||||
{
|
||||
if (!m_swap_chain)
|
||||
return;
|
||||
|
||||
m_window_info.surface_scale = new_window_scale;
|
||||
|
||||
if (m_window_info.surface_width == new_window_width && m_window_info.surface_height == new_window_height)
|
||||
return;
|
||||
|
||||
m_swap_chain_rtv.reset();
|
||||
|
||||
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
|
||||
if (FAILED(hr))
|
||||
Console.Error("ResizeBuffers() failed: 0x%08X", hr);
|
||||
|
||||
if (!CreateSwapChainRTV())
|
||||
pxFailRel("Failed to recreate swap chain RTV after resize");
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::SupportsFullscreen() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::IsFullscreen()
|
||||
{
|
||||
BOOL is_fullscreen = FALSE;
|
||||
return (m_swap_chain && SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen);
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
|
||||
{
|
||||
if (!m_swap_chain)
|
||||
return false;
|
||||
|
||||
BOOL is_fullscreen = FALSE;
|
||||
HRESULT hr = m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr);
|
||||
if (!fullscreen)
|
||||
{
|
||||
// leaving fullscreen
|
||||
if (is_fullscreen)
|
||||
return SUCCEEDED(m_swap_chain->SetFullscreenState(FALSE, nullptr));
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
IDXGIOutput* output;
|
||||
if (FAILED(hr = m_swap_chain->GetContainingOutput(&output)))
|
||||
return false;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC current_desc;
|
||||
hr = m_swap_chain->GetDesc(¤t_desc);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
DXGI_MODE_DESC new_mode = current_desc.BufferDesc;
|
||||
new_mode.Width = width;
|
||||
new_mode.Height = height;
|
||||
new_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(refresh_rate * 1000.0f));
|
||||
new_mode.RefreshRate.Denominator = 1000u;
|
||||
|
||||
DXGI_MODE_DESC closest_mode;
|
||||
if (FAILED(hr = output->FindClosestMatchingMode(&new_mode, &closest_mode, nullptr)) ||
|
||||
new_mode.Format != current_desc.BufferDesc.Format)
|
||||
{
|
||||
Console.Error("Failed to find closest matching mode, hr=%08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (new_mode.Width == current_desc.BufferDesc.Width && new_mode.Height == current_desc.BufferDesc.Height &&
|
||||
new_mode.RefreshRate.Numerator == current_desc.BufferDesc.RefreshRate.Numerator &&
|
||||
new_mode.RefreshRate.Denominator == current_desc.BufferDesc.RefreshRate.Denominator)
|
||||
{
|
||||
DevCon.WriteLn("Fullscreen mode already set");
|
||||
return true;
|
||||
}
|
||||
|
||||
m_swap_chain_rtv.reset();
|
||||
m_swap_chain.reset();
|
||||
|
||||
if (!CreateSwapChain(&closest_mode))
|
||||
{
|
||||
Console.Error("Failed to create a fullscreen swap chain");
|
||||
if (!CreateSwapChain(nullptr))
|
||||
pxFailRel("Failed to recreate windowed swap chain");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::CreateImGuiContext()
|
||||
{
|
||||
return ImGui_ImplDX11_Init(m_device.get(), m_context.get());
|
||||
}
|
||||
|
||||
void D3D11HostDisplay::DestroyImGuiContext()
|
||||
{
|
||||
ImGui_ImplDX11_Shutdown();
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::UpdateImGuiFontTexture()
|
||||
{
|
||||
ImGui_ImplDX11_CreateFontsTexture();
|
||||
return true;
|
||||
}
|
||||
|
||||
HostDisplay::PresentResult D3D11HostDisplay::BeginPresent(bool frame_skip)
|
||||
{
|
||||
if (frame_skip || !m_swap_chain)
|
||||
return PresentResult::FrameSkipped;
|
||||
|
||||
// When using vsync, the time here seems to include the time for the buffer to become available.
|
||||
// This blows our our GPU usage number considerably, so read the timestamp before the final blit
|
||||
// in this configuration. It does reduce accuracy a little, but better than seeing 100% all of
|
||||
// the time, when it's more like a couple of percent.
|
||||
if (m_vsync_mode != VsyncMode::Off && m_gpu_timing_enabled)
|
||||
PopTimestampQuery();
|
||||
|
||||
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.addressof(), nullptr);
|
||||
|
||||
const CD3D11_VIEWPORT vp(0.0f, 0.0f, static_cast<float>(m_window_info.surface_width), static_cast<float>(m_window_info.surface_height));
|
||||
const CD3D11_RECT scissor(0, 0, m_window_info.surface_width, m_window_info.surface_height);
|
||||
m_context->RSSetViewports(1, &vp);
|
||||
m_context->RSSetScissorRects(1, &scissor);
|
||||
return PresentResult::OK;
|
||||
}
|
||||
|
||||
void D3D11HostDisplay::EndPresent()
|
||||
{
|
||||
ImGui::Render();
|
||||
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
// See note in BeginPresent() for why it's conditional on vsync-off.
|
||||
const bool vsync_on = m_vsync_mode != VsyncMode::Off;
|
||||
if (!vsync_on && m_gpu_timing_enabled)
|
||||
PopTimestampQuery();
|
||||
|
||||
if (!vsync_on && m_using_allow_tearing)
|
||||
m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
|
||||
else
|
||||
m_swap_chain->Present(static_cast<UINT>(vsync_on), 0);
|
||||
|
||||
if (m_gpu_timing_enabled)
|
||||
KickTimestampQuery();
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::CreateTimestampQueries()
|
||||
{
|
||||
for (u32 i = 0; i < NUM_TIMESTAMP_QUERIES; i++)
|
||||
{
|
||||
for (u32 j = 0; j < 3; j++)
|
||||
{
|
||||
const CD3D11_QUERY_DESC qdesc((j == 0) ? D3D11_QUERY_TIMESTAMP_DISJOINT : D3D11_QUERY_TIMESTAMP);
|
||||
const HRESULT hr = m_device->CreateQuery(&qdesc, m_timestamp_queries[i][j].put());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
m_timestamp_queries = {};
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KickTimestampQuery();
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D11HostDisplay::DestroyTimestampQueries()
|
||||
{
|
||||
if (!m_timestamp_queries[0][0])
|
||||
return;
|
||||
|
||||
if (m_timestamp_query_started)
|
||||
m_context->End(m_timestamp_queries[m_write_timestamp_query][1].get());
|
||||
|
||||
m_timestamp_queries = {};
|
||||
m_read_timestamp_query = 0;
|
||||
m_write_timestamp_query = 0;
|
||||
m_waiting_timestamp_queries = 0;
|
||||
m_timestamp_query_started = 0;
|
||||
}
|
||||
|
||||
void D3D11HostDisplay::PopTimestampQuery()
|
||||
{
|
||||
while (m_waiting_timestamp_queries > 0)
|
||||
{
|
||||
D3D11_QUERY_DATA_TIMESTAMP_DISJOINT disjoint;
|
||||
const HRESULT disjoint_hr = m_context->GetData(
|
||||
m_timestamp_queries[m_read_timestamp_query][0].get(), &disjoint, sizeof(disjoint), D3D11_ASYNC_GETDATA_DONOTFLUSH);
|
||||
if (disjoint_hr != S_OK)
|
||||
break;
|
||||
|
||||
if (disjoint.Disjoint)
|
||||
{
|
||||
DevCon.WriteLn("GPU timing disjoint, resetting.");
|
||||
m_read_timestamp_query = 0;
|
||||
m_write_timestamp_query = 0;
|
||||
m_waiting_timestamp_queries = 0;
|
||||
m_timestamp_query_started = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
u64 start = 0, end = 0;
|
||||
const HRESULT start_hr = m_context->GetData(
|
||||
m_timestamp_queries[m_read_timestamp_query][1].get(), &start, sizeof(start), D3D11_ASYNC_GETDATA_DONOTFLUSH);
|
||||
const HRESULT end_hr =
|
||||
m_context->GetData(m_timestamp_queries[m_read_timestamp_query][2].get(), &end, sizeof(end), D3D11_ASYNC_GETDATA_DONOTFLUSH);
|
||||
if (start_hr == S_OK && end_hr == S_OK)
|
||||
{
|
||||
m_accumulated_gpu_time +=
|
||||
static_cast<float>(static_cast<double>(end - start) / (static_cast<double>(disjoint.Frequency) / 1000.0));
|
||||
m_read_timestamp_query = (m_read_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
|
||||
m_waiting_timestamp_queries--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_timestamp_query_started)
|
||||
{
|
||||
m_context->End(m_timestamp_queries[m_write_timestamp_query][2].get());
|
||||
m_context->End(m_timestamp_queries[m_write_timestamp_query][0].get());
|
||||
m_write_timestamp_query = (m_write_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
|
||||
m_timestamp_query_started = false;
|
||||
m_waiting_timestamp_queries++;
|
||||
}
|
||||
}
|
||||
|
||||
void D3D11HostDisplay::KickTimestampQuery()
|
||||
{
|
||||
if (m_timestamp_query_started || !m_timestamp_queries[0][0] || m_waiting_timestamp_queries == NUM_TIMESTAMP_QUERIES)
|
||||
return;
|
||||
|
||||
m_context->Begin(m_timestamp_queries[m_write_timestamp_query][0].get());
|
||||
m_context->End(m_timestamp_queries[m_write_timestamp_query][1].get());
|
||||
m_timestamp_query_started = true;
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::SetGPUTimingEnabled(bool enabled)
|
||||
{
|
||||
if (m_gpu_timing_enabled == enabled)
|
||||
return true;
|
||||
|
||||
m_gpu_timing_enabled = enabled;
|
||||
if (m_gpu_timing_enabled)
|
||||
{
|
||||
return CreateTimestampQueries();
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyTimestampQueries();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
float D3D11HostDisplay::GetAndResetAccumulatedGPUTime()
|
||||
{
|
||||
const float value = m_accumulated_gpu_time;
|
||||
m_accumulated_gpu_time = 0.0f;
|
||||
return value;
|
||||
}
|
||||
|
||||
HostDisplay::AdapterAndModeList D3D11HostDisplay::StaticGetAdapterAndModeList()
|
||||
{
|
||||
auto factory = D3D::CreateFactory(false);
|
||||
if (!factory)
|
||||
return {};
|
||||
|
||||
return GetAdapterAndModeList(factory.get());
|
||||
}
|
||||
|
||||
HostDisplay::AdapterAndModeList D3D11HostDisplay::GetAdapterAndModeList(IDXGIFactory5* dxgi_factory)
|
||||
{
|
||||
AdapterAndModeList adapter_info;
|
||||
adapter_info.adapter_names = D3D::GetAdapterNames(dxgi_factory);
|
||||
|
||||
auto adapter = D3D::GetChosenOrFirstAdapter(dxgi_factory, EmuConfig.GS.Adapter);
|
||||
if (adapter)
|
||||
{
|
||||
ComPtr<IDXGIOutput> output;
|
||||
if (SUCCEEDED(adapter->EnumOutputs(0, &output)))
|
||||
{
|
||||
UINT num_modes = 0;
|
||||
if (SUCCEEDED(output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, nullptr)))
|
||||
{
|
||||
std::vector<DXGI_MODE_DESC> modes(num_modes);
|
||||
if (SUCCEEDED(output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, modes.data())))
|
||||
{
|
||||
for (const DXGI_MODE_DESC& mode : modes)
|
||||
{
|
||||
adapter_info.fullscreen_modes.push_back(GetFullscreenModeString(mode.Width, mode.Height,
|
||||
static_cast<float>(mode.RefreshRate.Numerator) / static_cast<float>(mode.RefreshRate.Denominator)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return adapter_info;
|
||||
}
|
||||
|
||||
HostDisplay::AdapterAndModeList D3D11HostDisplay::GetAdapterAndModeList()
|
||||
{
|
||||
return GetAdapterAndModeList(m_dxgi_factory.get());
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "HostDisplay.h"
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include "common/RedtapeWilCom.h"
|
||||
#include "common/WindowInfo.h"
|
||||
#include <array>
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_5.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
class D3D11HostDisplay final : public HostDisplay
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
using ComPtr = wil::com_ptr_nothrow<T>;
|
||||
|
||||
D3D11HostDisplay();
|
||||
~D3D11HostDisplay();
|
||||
|
||||
RenderAPI GetRenderAPI() const override;
|
||||
void* GetDevice() const override;
|
||||
void* GetContext() const override;
|
||||
void* GetSurface() const override;
|
||||
|
||||
bool HasDevice() const override;
|
||||
bool HasSurface() const override;
|
||||
|
||||
bool CreateDevice(const WindowInfo& wi, VsyncMode vsync) override;
|
||||
bool SetupDevice() override;
|
||||
|
||||
bool MakeCurrent() override;
|
||||
bool DoneCurrent() override;
|
||||
|
||||
bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
||||
bool SupportsFullscreen() const override;
|
||||
bool IsFullscreen() override;
|
||||
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||
AdapterAndModeList GetAdapterAndModeList() override;
|
||||
void DestroySurface() override;
|
||||
std::string GetDriverInfo() const override;
|
||||
|
||||
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic = false) override;
|
||||
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride) override;
|
||||
|
||||
bool GetHostRefreshRate(float* refresh_rate) override;
|
||||
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
static AdapterAndModeList StaticGetAdapterAndModeList();
|
||||
|
||||
protected:
|
||||
static constexpr u32 DISPLAY_CONSTANT_BUFFER_SIZE = 16;
|
||||
static constexpr u8 NUM_TIMESTAMP_QUERIES = 5;
|
||||
|
||||
static AdapterAndModeList GetAdapterAndModeList(IDXGIFactory5* dxgi_factory);
|
||||
|
||||
bool CreateImGuiContext() override;
|
||||
void DestroyImGuiContext() override;
|
||||
bool UpdateImGuiFontTexture() override;
|
||||
|
||||
bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode);
|
||||
bool CreateSwapChainRTV();
|
||||
|
||||
bool CreateTimestampQueries();
|
||||
void DestroyTimestampQueries();
|
||||
void PopTimestampQuery();
|
||||
void KickTimestampQuery();
|
||||
|
||||
ComPtr<ID3D11Device> m_device;
|
||||
ComPtr<ID3D11DeviceContext> m_context;
|
||||
|
||||
ComPtr<IDXGIFactory5> m_dxgi_factory;
|
||||
ComPtr<IDXGISwapChain1> m_swap_chain;
|
||||
ComPtr<ID3D11RenderTargetView> m_swap_chain_rtv;
|
||||
|
||||
bool m_allow_tearing_supported = false;
|
||||
bool m_using_flip_model_swap_chain = true;
|
||||
bool m_using_allow_tearing = false;
|
||||
|
||||
std::array<std::array<ComPtr<ID3D11Query>, 3>, NUM_TIMESTAMP_QUERIES> m_timestamp_queries = {};
|
||||
u8 m_read_timestamp_query = 0;
|
||||
u8 m_write_timestamp_query = 0;
|
||||
u8 m_waiting_timestamp_queries = 0;
|
||||
bool m_timestamp_query_started = false;
|
||||
float m_accumulated_gpu_time = 0.0f;
|
||||
bool m_gpu_timing_enabled = false;
|
||||
};
|
||||
|
|
@ -1,573 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "Frontend/D3D12HostDisplay.h"
|
||||
#include "GS/Renderers/DX11/D3D.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/D3D12/Context.h"
|
||||
#include "common/D3D12/Util.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_dx12.h"
|
||||
#include <array>
|
||||
#include <dxgi1_5.h>
|
||||
|
||||
class D3D12HostDisplayTexture : public HostDisplayTexture
|
||||
{
|
||||
public:
|
||||
explicit D3D12HostDisplayTexture(D3D12::Texture texture)
|
||||
: m_texture(std::move(texture))
|
||||
{
|
||||
}
|
||||
~D3D12HostDisplayTexture() override = default;
|
||||
|
||||
void* GetHandle() const override { return const_cast<D3D12::Texture*>(&m_texture); }
|
||||
u32 GetWidth() const override { return m_texture.GetWidth(); }
|
||||
u32 GetHeight() const override { return m_texture.GetHeight(); }
|
||||
|
||||
const D3D12::Texture& GetTexture() const { return m_texture; }
|
||||
D3D12::Texture& GetTexture() { return m_texture; }
|
||||
|
||||
private:
|
||||
D3D12::Texture m_texture;
|
||||
};
|
||||
|
||||
D3D12HostDisplay::D3D12HostDisplay() = default;
|
||||
|
||||
D3D12HostDisplay::~D3D12HostDisplay()
|
||||
{
|
||||
if (g_d3d12_context)
|
||||
{
|
||||
g_d3d12_context->WaitForGPUIdle();
|
||||
D3D12HostDisplay::DestroySurface();
|
||||
g_d3d12_context->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
RenderAPI D3D12HostDisplay::GetRenderAPI() const
|
||||
{
|
||||
return RenderAPI::D3D12;
|
||||
}
|
||||
|
||||
void* D3D12HostDisplay::GetDevice() const
|
||||
{
|
||||
return g_d3d12_context->GetDevice();
|
||||
}
|
||||
|
||||
void* D3D12HostDisplay::GetContext() const
|
||||
{
|
||||
return g_d3d12_context.get();
|
||||
}
|
||||
|
||||
void* D3D12HostDisplay::GetSurface() const
|
||||
{
|
||||
return m_swap_chain.get();
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::HasDevice() const
|
||||
{
|
||||
return static_cast<bool>(g_d3d12_context);
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::HasSurface() const
|
||||
{
|
||||
return static_cast<bool>(m_swap_chain);
|
||||
}
|
||||
|
||||
std::unique_ptr<HostDisplayTexture> D3D12HostDisplay::CreateTexture(
|
||||
u32 width, u32 height, const void* data, u32 data_stride, bool dynamic /* = false */)
|
||||
{
|
||||
D3D12::Texture tex;
|
||||
if (!tex.Create(width, height, 1, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN,
|
||||
D3D12_RESOURCE_FLAG_NONE))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (data && !tex.LoadData(g_d3d12_context->GetInitCommandList(), 0, 0, 0, width, height, data, data_stride))
|
||||
return {};
|
||||
|
||||
return std::make_unique<D3D12HostDisplayTexture>(std::move(tex));
|
||||
}
|
||||
|
||||
void D3D12HostDisplay::UpdateTexture(
|
||||
HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride)
|
||||
{
|
||||
static_cast<D3D12HostDisplayTexture*>(texture)->GetTexture().LoadData(
|
||||
g_d3d12_context->GetCommandList(), 0, x, y, width, height, texture_data, texture_data_stride);
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::GetHostRefreshRate(float* refresh_rate)
|
||||
{
|
||||
if (m_swap_chain && IsFullscreen())
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC desc;
|
||||
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)) && desc.BufferDesc.RefreshRate.Numerator > 0 &&
|
||||
desc.BufferDesc.RefreshRate.Denominator > 0)
|
||||
{
|
||||
DevCon.WriteLn("using fs rr: %u %u", desc.BufferDesc.RefreshRate.Numerator, desc.BufferDesc.RefreshRate.Denominator);
|
||||
*refresh_rate =
|
||||
static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) / static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return HostDisplay::GetHostRefreshRate(refresh_rate);
|
||||
}
|
||||
|
||||
void D3D12HostDisplay::SetVSync(VsyncMode mode)
|
||||
{
|
||||
m_vsync_mode = mode;
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::CreateDevice(const WindowInfo& wi, VsyncMode vsync)
|
||||
{
|
||||
m_dxgi_factory = D3D::CreateFactory(EmuConfig.GS.UseDebugDevice);
|
||||
if (!m_dxgi_factory)
|
||||
return false;
|
||||
|
||||
ComPtr<IDXGIAdapter1> dxgi_adapter = D3D::GetAdapterByName(m_dxgi_factory.get(), EmuConfig.GS.Adapter);
|
||||
|
||||
if (!D3D12::Context::Create(m_dxgi_factory.get(), dxgi_adapter.get(), EmuConfig.GS.UseDebugDevice))
|
||||
return false;
|
||||
|
||||
BOOL allow_tearing_supported = false;
|
||||
HRESULT hr =
|
||||
m_dxgi_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, sizeof(allow_tearing_supported));
|
||||
m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
|
||||
|
||||
m_window_info = wi;
|
||||
m_vsync_mode = vsync;
|
||||
|
||||
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain(nullptr))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::SetupDevice()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::MakeCurrent()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::DoneCurrent()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
|
||||
{
|
||||
if (m_window_info.type != WindowInfo::Type::Win32)
|
||||
return false;
|
||||
|
||||
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
|
||||
RECT client_rc{};
|
||||
GetClientRect(window_hwnd, &client_rc);
|
||||
const u32 width = static_cast<u32>(client_rc.right - client_rc.left);
|
||||
const u32 height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
|
||||
swap_chain_desc.Width = width;
|
||||
swap_chain_desc.Height = height;
|
||||
swap_chain_desc.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.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
|
||||
m_using_allow_tearing = (m_allow_tearing_supported && !fullscreen_mode);
|
||||
if (m_using_allow_tearing)
|
||||
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
||||
|
||||
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
|
||||
if (fullscreen_mode)
|
||||
{
|
||||
swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
||||
swap_chain_desc.Width = fullscreen_mode->Width;
|
||||
swap_chain_desc.Height = fullscreen_mode->Height;
|
||||
fs_desc.RefreshRate = fullscreen_mode->RefreshRate;
|
||||
fs_desc.ScanlineOrdering = fullscreen_mode->ScanlineOrdering;
|
||||
fs_desc.Scaling = fullscreen_mode->Scaling;
|
||||
fs_desc.Windowed = FALSE;
|
||||
}
|
||||
|
||||
DevCon.WriteLn(
|
||||
"Creating a %dx%d %s swap chain", swap_chain_desc.Width, swap_chain_desc.Height, fullscreen_mode ? "full-screen" : "windowed");
|
||||
|
||||
HRESULT hr = m_dxgi_factory->CreateSwapChainForHwnd(g_d3d12_context->GetCommandQueue(), window_hwnd, &swap_chain_desc,
|
||||
fullscreen_mode ? &fs_desc : nullptr, nullptr, m_swap_chain.put());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("CreateSwapChainForHwnd failed: 0x%08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = m_dxgi_factory->MakeWindowAssociation(window_hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
|
||||
if (FAILED(hr))
|
||||
Console.Warning("MakeWindowAssociation() to disable ALT+ENTER failed");
|
||||
|
||||
return CreateSwapChainRTV();
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::CreateSwapChainRTV()
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC swap_chain_desc;
|
||||
HRESULT hr = m_swap_chain->GetDesc(&swap_chain_desc);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
for (u32 i = 0; i < swap_chain_desc.BufferCount; i++)
|
||||
{
|
||||
ComPtr<ID3D12Resource> backbuffer;
|
||||
hr = m_swap_chain->GetBuffer(i, IID_PPV_ARGS(backbuffer.put()));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("GetBuffer for RTV failed: 0x%08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12::Texture tex;
|
||||
if (!tex.Adopt(std::move(backbuffer), DXGI_FORMAT_UNKNOWN, swap_chain_desc.BufferDesc.Format, DXGI_FORMAT_UNKNOWN,
|
||||
D3D12_RESOURCE_STATE_PRESENT))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_swap_chain_buffers.push_back(std::move(tex));
|
||||
}
|
||||
|
||||
m_window_info.surface_width = swap_chain_desc.BufferDesc.Width;
|
||||
m_window_info.surface_height = swap_chain_desc.BufferDesc.Height;
|
||||
DevCon.WriteLn("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height);
|
||||
|
||||
if (m_window_info.type == WindowInfo::Type::Win32)
|
||||
{
|
||||
BOOL fullscreen = FALSE;
|
||||
DXGI_SWAP_CHAIN_DESC desc;
|
||||
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen && SUCCEEDED(m_swap_chain->GetDesc(&desc)))
|
||||
{
|
||||
m_window_info.surface_refresh_rate =
|
||||
static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) / static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_window_info.surface_refresh_rate = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
m_current_swap_chain_buffer = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D12HostDisplay::DestroySwapChainRTVs()
|
||||
{
|
||||
for (D3D12::Texture& buffer : m_swap_chain_buffers)
|
||||
buffer.Destroy(false);
|
||||
m_swap_chain_buffers.clear();
|
||||
m_current_swap_chain_buffer = 0;
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::ChangeWindow(const WindowInfo& new_wi)
|
||||
{
|
||||
DestroySurface();
|
||||
|
||||
m_window_info = new_wi;
|
||||
return CreateSwapChain(nullptr);
|
||||
}
|
||||
|
||||
void D3D12HostDisplay::DestroySurface()
|
||||
{
|
||||
// For some reason if we don't execute the command list here, the swap chain is in use.. not sure where.
|
||||
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::Sleep);
|
||||
|
||||
if (IsFullscreen())
|
||||
SetFullscreen(false, 0, 0, 0.0f);
|
||||
|
||||
DestroySwapChainRTVs();
|
||||
m_swap_chain.reset();
|
||||
}
|
||||
|
||||
std::string D3D12HostDisplay::GetDriverInfo() const
|
||||
{
|
||||
std::string ret = "Unknown Feature Level";
|
||||
|
||||
static constexpr std::array<std::tuple<D3D_FEATURE_LEVEL, const char*>, 4> feature_level_names = {{
|
||||
{D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_0"},
|
||||
{D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_1"},
|
||||
{D3D_FEATURE_LEVEL_11_0, "D3D_FEATURE_LEVEL_11_0"},
|
||||
{D3D_FEATURE_LEVEL_11_1, "D3D_FEATURE_LEVEL_11_1"},
|
||||
}};
|
||||
|
||||
const D3D_FEATURE_LEVEL fl = g_d3d12_context->GetFeatureLevel();
|
||||
for (size_t i = 0; i < std::size(feature_level_names); i++)
|
||||
{
|
||||
if (fl == std::get<0>(feature_level_names[i]))
|
||||
{
|
||||
ret = std::get<1>(feature_level_names[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret += "\n";
|
||||
|
||||
IDXGIAdapter* adapter = g_d3d12_context->GetAdapter();
|
||||
DXGI_ADAPTER_DESC desc;
|
||||
if (adapter && SUCCEEDED(adapter->GetDesc(&desc)))
|
||||
{
|
||||
ret += StringUtil::StdStringFromFormat("VID: 0x%04X PID: 0x%04X\n", desc.VendorId, desc.DeviceId);
|
||||
ret += StringUtil::WideStringToUTF8String(desc.Description);
|
||||
ret += "\n";
|
||||
|
||||
const std::string driver_version(D3D::GetDriverVersionFromLUID(desc.AdapterLuid));
|
||||
if (!driver_version.empty())
|
||||
{
|
||||
ret += "Driver Version: ";
|
||||
ret += driver_version;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void D3D12HostDisplay::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
||||
{
|
||||
if (!m_swap_chain)
|
||||
return;
|
||||
|
||||
m_window_info.surface_scale = new_window_scale;
|
||||
|
||||
if (m_window_info.surface_width == new_window_width && m_window_info.surface_height == new_window_height)
|
||||
return;
|
||||
|
||||
// For some reason if we don't execute the command list here, the swap chain is in use.. not sure where.
|
||||
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::Sleep);
|
||||
|
||||
DestroySwapChainRTVs();
|
||||
|
||||
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
|
||||
if (FAILED(hr))
|
||||
Console.Error("ResizeBuffers() failed: 0x%08X", hr);
|
||||
|
||||
if (!CreateSwapChainRTV())
|
||||
pxFailRel("Failed to recreate swap chain RTV after resize");
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::SupportsFullscreen() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::IsFullscreen()
|
||||
{
|
||||
BOOL is_fullscreen = FALSE;
|
||||
return (m_swap_chain && SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen);
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
|
||||
{
|
||||
if (!m_swap_chain)
|
||||
return false;
|
||||
|
||||
BOOL is_fullscreen = FALSE;
|
||||
HRESULT hr = m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr);
|
||||
if (!fullscreen)
|
||||
{
|
||||
// leaving fullscreen
|
||||
if (is_fullscreen)
|
||||
return SUCCEEDED(m_swap_chain->SetFullscreenState(FALSE, nullptr));
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
IDXGIOutput* output;
|
||||
if (FAILED(hr = m_swap_chain->GetContainingOutput(&output)))
|
||||
return false;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC current_desc;
|
||||
hr = m_swap_chain->GetDesc(¤t_desc);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
DXGI_MODE_DESC new_mode = current_desc.BufferDesc;
|
||||
new_mode.Width = width;
|
||||
new_mode.Height = height;
|
||||
new_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(refresh_rate * 1000.0f));
|
||||
new_mode.RefreshRate.Denominator = 1000u;
|
||||
|
||||
DXGI_MODE_DESC closest_mode;
|
||||
if (FAILED(hr = output->FindClosestMatchingMode(&new_mode, &closest_mode, nullptr)) ||
|
||||
new_mode.Format != current_desc.BufferDesc.Format)
|
||||
{
|
||||
Console.Error("Failed to find closest matching mode, hr=%08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (new_mode.Width == current_desc.BufferDesc.Width && new_mode.Height == current_desc.BufferDesc.Width &&
|
||||
new_mode.RefreshRate.Numerator == current_desc.BufferDesc.RefreshRate.Numerator &&
|
||||
new_mode.RefreshRate.Denominator == current_desc.BufferDesc.RefreshRate.Denominator)
|
||||
{
|
||||
DevCon.WriteLn("Fullscreen mode already set");
|
||||
return true;
|
||||
}
|
||||
|
||||
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::Sleep);
|
||||
DestroySwapChainRTVs();
|
||||
m_swap_chain.reset();
|
||||
|
||||
if (!CreateSwapChain(&closest_mode))
|
||||
{
|
||||
Console.Error("Failed to create a fullscreen swap chain");
|
||||
if (!CreateSwapChain(nullptr))
|
||||
pxFailRel("Failed to recreate windowed swap chain");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
HostDisplay::AdapterAndModeList D3D12HostDisplay::GetAdapterAndModeList()
|
||||
{
|
||||
return GetAdapterAndModeList(m_dxgi_factory.get());
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::CreateImGuiContext()
|
||||
{
|
||||
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_info.surface_width);
|
||||
ImGui::GetIO().DisplaySize.y = static_cast<float>(m_window_info.surface_height);
|
||||
|
||||
if (!ImGui_ImplDX12_Init(DXGI_FORMAT_R8G8B8A8_UNORM))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D12HostDisplay::DestroyImGuiContext()
|
||||
{
|
||||
g_d3d12_context->WaitForGPUIdle();
|
||||
|
||||
ImGui_ImplDX12_Shutdown();
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::UpdateImGuiFontTexture()
|
||||
{
|
||||
return ImGui_ImplDX12_CreateFontsTexture();
|
||||
}
|
||||
|
||||
HostDisplay::PresentResult D3D12HostDisplay::BeginPresent(bool frame_skip)
|
||||
{
|
||||
if (m_device_lost)
|
||||
return HostDisplay::PresentResult::DeviceLost;
|
||||
|
||||
if (frame_skip || !m_swap_chain)
|
||||
return PresentResult::FrameSkipped;
|
||||
|
||||
static constexpr std::array<float, 4> clear_color = {};
|
||||
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
|
||||
|
||||
ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList();
|
||||
swap_chain_buf.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
cmdlist->ClearRenderTargetView(swap_chain_buf.GetWriteDescriptor(), clear_color.data(), 0, nullptr);
|
||||
cmdlist->OMSetRenderTargets(1, &swap_chain_buf.GetWriteDescriptor().cpu_handle, FALSE, nullptr);
|
||||
|
||||
const D3D12_VIEWPORT vp{
|
||||
0.0f, 0.0f, static_cast<float>(m_window_info.surface_width), static_cast<float>(m_window_info.surface_height), 0.0f, 1.0f};
|
||||
const D3D12_RECT scissor{0, 0, static_cast<LONG>(m_window_info.surface_width), static_cast<LONG>(m_window_info.surface_height)};
|
||||
cmdlist->RSSetViewports(1, &vp);
|
||||
cmdlist->RSSetScissorRects(1, &scissor);
|
||||
return PresentResult::OK;
|
||||
}
|
||||
|
||||
void D3D12HostDisplay::EndPresent()
|
||||
{
|
||||
ImGui::Render();
|
||||
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
|
||||
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
|
||||
|
||||
swap_chain_buf.TransitionToState(g_d3d12_context->GetCommandList(), D3D12_RESOURCE_STATE_PRESENT);
|
||||
if (!g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::None))
|
||||
{
|
||||
m_device_lost = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const bool vsync = static_cast<UINT>(m_vsync_mode != VsyncMode::Off);
|
||||
if (!vsync && m_using_allow_tearing)
|
||||
m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
|
||||
else
|
||||
m_swap_chain->Present(static_cast<UINT>(vsync), 0);
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::SetGPUTimingEnabled(bool enabled)
|
||||
{
|
||||
g_d3d12_context->SetEnableGPUTiming(enabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
float D3D12HostDisplay::GetAndResetAccumulatedGPUTime()
|
||||
{
|
||||
return g_d3d12_context->GetAndResetAccumulatedGPUTime();
|
||||
}
|
||||
|
||||
HostDisplay::AdapterAndModeList D3D12HostDisplay::StaticGetAdapterAndModeList()
|
||||
{
|
||||
auto factory = D3D::CreateFactory(false);
|
||||
if (!factory)
|
||||
return {};
|
||||
|
||||
return GetAdapterAndModeList(factory.get());
|
||||
}
|
||||
|
||||
HostDisplay::AdapterAndModeList D3D12HostDisplay::GetAdapterAndModeList(IDXGIFactory5* dxgi_factory)
|
||||
{
|
||||
AdapterAndModeList adapter_info;
|
||||
adapter_info.adapter_names = D3D::GetAdapterNames(dxgi_factory);
|
||||
|
||||
auto adapter = D3D::GetChosenOrFirstAdapter(dxgi_factory, EmuConfig.GS.Adapter);
|
||||
if (adapter)
|
||||
{
|
||||
ComPtr<IDXGIOutput> output;
|
||||
if (SUCCEEDED(adapter->EnumOutputs(0, &output)))
|
||||
{
|
||||
UINT num_modes = 0;
|
||||
if (SUCCEEDED(output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, nullptr)))
|
||||
{
|
||||
std::vector<DXGI_MODE_DESC> modes(num_modes);
|
||||
if (SUCCEEDED(output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, modes.data())))
|
||||
{
|
||||
for (const DXGI_MODE_DESC& mode : modes)
|
||||
{
|
||||
adapter_info.fullscreen_modes.push_back(GetFullscreenModeString(mode.Width, mode.Height,
|
||||
static_cast<float>(mode.RefreshRate.Numerator) / static_cast<float>(mode.RefreshRate.Denominator)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return adapter_info;
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/RedtapeWindows.h"
|
||||
|
||||
#include "common/D3D12/DescriptorHeapManager.h"
|
||||
#include "common/D3D12/Texture.h"
|
||||
#include "common/WindowInfo.h"
|
||||
|
||||
#include "HostDisplay.h"
|
||||
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_5.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <wil/com.h>
|
||||
|
||||
class D3D12HostDisplay : public HostDisplay
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
using ComPtr = wil::com_ptr_nothrow<T>;
|
||||
|
||||
D3D12HostDisplay();
|
||||
~D3D12HostDisplay();
|
||||
|
||||
RenderAPI GetRenderAPI() const override;
|
||||
void* GetDevice() const override;
|
||||
void* GetContext() const override;
|
||||
void* GetSurface() const override;
|
||||
|
||||
bool HasDevice() const override;
|
||||
bool HasSurface() const override;
|
||||
|
||||
bool CreateDevice(const WindowInfo& wi, VsyncMode vsync) override;
|
||||
bool SetupDevice() override;
|
||||
|
||||
bool MakeCurrent() override;
|
||||
bool DoneCurrent() override;
|
||||
|
||||
bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
||||
bool SupportsFullscreen() const override;
|
||||
bool IsFullscreen() override;
|
||||
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||
AdapterAndModeList GetAdapterAndModeList() override;
|
||||
void DestroySurface() override;
|
||||
std::string GetDriverInfo() const override;
|
||||
|
||||
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic = false) override;
|
||||
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride) override;
|
||||
|
||||
bool GetHostRefreshRate(float* refresh_rate) override;
|
||||
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
static AdapterAndModeList StaticGetAdapterAndModeList();
|
||||
|
||||
protected:
|
||||
static AdapterAndModeList GetAdapterAndModeList(IDXGIFactory5* dxgi_factory);
|
||||
|
||||
bool CreateImGuiContext() override;
|
||||
void DestroyImGuiContext() override;
|
||||
bool UpdateImGuiFontTexture() override;
|
||||
|
||||
bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode);
|
||||
bool CreateSwapChainRTV();
|
||||
void DestroySwapChainRTVs();
|
||||
|
||||
ComPtr<IDXGIFactory5> m_dxgi_factory;
|
||||
ComPtr<IDXGISwapChain1> m_swap_chain;
|
||||
std::vector<D3D12::Texture> m_swap_chain_buffers;
|
||||
u32 m_current_swap_chain_buffer = 0;
|
||||
|
||||
bool m_allow_tearing_supported = false;
|
||||
bool m_using_allow_tearing = false;
|
||||
bool m_device_lost = false;
|
||||
};
|
|
@ -37,8 +37,9 @@
|
|||
#include "CDVD/CDVDcommon.h"
|
||||
#include "CDVD/CDVDdiscReader.h"
|
||||
#include "GS.h"
|
||||
#include "GS/Renderers/Common/GSDevice.h"
|
||||
#include "GS/Renderers/Common/GSTexture.h"
|
||||
#include "Host.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "HostSettings.h"
|
||||
#include "INISettingsInterface.h"
|
||||
#include "MemoryCardFile.h"
|
||||
|
@ -196,6 +197,7 @@ namespace FullscreenUI
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
// Utility
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
static void ReleaseTexture(std::unique_ptr<GSTexture>& tex);
|
||||
static std::string TimeToPrintableString(time_t t);
|
||||
static void StartAsyncOp(std::function<void(::ProgressCallback*)> callback, std::string name);
|
||||
static void AsyncOpThreadEntryPoint(std::function<void(::ProgressCallback*)> callback, FullscreenUI::ProgressCallback* progress);
|
||||
|
@ -244,12 +246,12 @@ namespace FullscreenUI
|
|||
static bool LoadResources();
|
||||
static void DestroyResources();
|
||||
|
||||
static std::shared_ptr<HostDisplayTexture> s_app_icon_texture;
|
||||
static std::array<std::shared_ptr<HostDisplayTexture>, static_cast<u32>(GameDatabaseSchema::Compatibility::Perfect)>
|
||||
static std::shared_ptr<GSTexture> s_app_icon_texture;
|
||||
static std::array<std::shared_ptr<GSTexture>, static_cast<u32>(GameDatabaseSchema::Compatibility::Perfect)>
|
||||
s_game_compatibility_textures;
|
||||
static std::shared_ptr<HostDisplayTexture> s_fallback_disc_texture;
|
||||
static std::shared_ptr<HostDisplayTexture> s_fallback_exe_texture;
|
||||
static std::vector<std::unique_ptr<HostDisplayTexture>> s_cleanup_textures;
|
||||
static std::shared_ptr<GSTexture> s_fallback_disc_texture;
|
||||
static std::shared_ptr<GSTexture> s_fallback_exe_texture;
|
||||
static std::vector<std::unique_ptr<GSTexture>> s_cleanup_textures;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Landing
|
||||
|
@ -397,7 +399,7 @@ namespace FullscreenUI
|
|||
std::string title;
|
||||
std::string summary;
|
||||
std::string path;
|
||||
std::unique_ptr<HostDisplayTexture> preview_texture;
|
||||
std::unique_ptr<GSTexture> preview_texture;
|
||||
time_t timestamp;
|
||||
s32 slot;
|
||||
};
|
||||
|
@ -433,9 +435,9 @@ namespace FullscreenUI
|
|||
static void DrawGameListSettingsPage(const ImVec2& heading_size);
|
||||
static void SwitchToGameList();
|
||||
static void PopulateGameListEntryList();
|
||||
static HostDisplayTexture* GetTextureForGameListEntryType(GameList::EntryType type);
|
||||
static HostDisplayTexture* GetGameListCover(const GameList::Entry* entry);
|
||||
static HostDisplayTexture* GetCoverForCurrentGame();
|
||||
static GSTexture* GetTextureForGameListEntryType(GameList::EntryType type);
|
||||
static GSTexture* GetGameListCover(const GameList::Entry* entry);
|
||||
static GSTexture* GetCoverForCurrentGame();
|
||||
|
||||
// Lazily populated cover images.
|
||||
static std::unordered_map<std::string, std::string> s_cover_image_map;
|
||||
|
@ -465,6 +467,12 @@ namespace FullscreenUI
|
|||
// Utility
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FullscreenUI::ReleaseTexture(std::unique_ptr<GSTexture>& tex)
|
||||
{
|
||||
if (tex)
|
||||
g_gs_device->Recycle(tex.release());
|
||||
}
|
||||
|
||||
std::string FullscreenUI::TimeToPrintableString(time_t t)
|
||||
{
|
||||
struct tm lt = {};
|
||||
|
@ -765,8 +773,8 @@ void FullscreenUI::Render()
|
|||
if (!s_initialized)
|
||||
return;
|
||||
|
||||
for (std::unique_ptr<HostDisplayTexture>& tex : s_cleanup_textures)
|
||||
tex.reset();
|
||||
for (std::unique_ptr<GSTexture>& tex : s_cleanup_textures)
|
||||
g_gs_device->Recycle(tex.release());
|
||||
s_cleanup_textures.clear();
|
||||
ImGuiFullscreen::UploadAsyncTextures();
|
||||
|
||||
|
@ -883,7 +891,7 @@ void FullscreenUI::DestroyResources()
|
|||
for (auto& tex : s_game_compatibility_textures)
|
||||
tex.reset();
|
||||
for (auto& tex : s_cleanup_textures)
|
||||
tex.reset();
|
||||
g_gs_device->Recycle(tex.release());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1089,7 +1097,7 @@ void FullscreenUI::DrawLandingWindow()
|
|||
const float image_size = LayoutScale(380.f);
|
||||
ImGui::SetCursorPos(
|
||||
ImVec2((ImGui::GetWindowWidth() * 0.5f) - (image_size * 0.5f), (ImGui::GetWindowHeight() * 0.5f) - (image_size * 0.5f)));
|
||||
ImGui::Image(s_app_icon_texture->GetHandle(), ImVec2(image_size, image_size));
|
||||
ImGui::Image(s_app_icon_texture->GetNativeHandle(), ImVec2(image_size, image_size));
|
||||
}
|
||||
EndFullscreenColumnWindow();
|
||||
|
||||
|
@ -2360,10 +2368,7 @@ void FullscreenUI::SwitchToGameSettings(const GameList::Entry* entry)
|
|||
|
||||
void FullscreenUI::PopulateGraphicsAdapterList()
|
||||
{
|
||||
HostDisplay::AdapterAndModeList ml(g_host_display->GetAdapterAndModeList());
|
||||
s_graphics_adapter_list_cache = std::move(ml.adapter_names);
|
||||
s_fullscreen_mode_list_cache = std::move(ml.fullscreen_modes);
|
||||
s_fullscreen_mode_list_cache.insert(s_fullscreen_mode_list_cache.begin(), "Borderless Fullscreen");
|
||||
GSGetAdaptersAndFullscreenModes(GSConfig.Renderer, &s_graphics_adapter_list_cache, &s_fullscreen_mode_list_cache);
|
||||
}
|
||||
|
||||
void FullscreenUI::PopulateGameListDirectoryCache(SettingsInterface* si)
|
||||
|
@ -3179,7 +3184,8 @@ void FullscreenUI::DrawGraphicsSettingsPage()
|
|||
static constexpr const char* s_round_sprite_options[] = {"Off (Default)", "Half", "Full"};
|
||||
|
||||
DrawIntListSetting(bsi, "CRC Fix Level", "Applies manual fixes to difficult-to-emulate effects in the hardware renderers.",
|
||||
"EmuCore/GS", "crc_hack_level", static_cast<int>(CRCHackLevel::Automatic), s_crc_fix_options, std::size(s_crc_fix_options), -1);
|
||||
"EmuCore/GS", "crc_hack_level", static_cast<int>(CRCHackLevel::Automatic), s_crc_fix_options, std::size(s_crc_fix_options),
|
||||
-1);
|
||||
DrawIntListSetting(bsi, "Half-Bottom Override", "Control the half-screen fix detection on texture shuffling.", "EmuCore/GS",
|
||||
"UserHacks_Half_Bottom_Override", -1, s_generic_options, std::size(s_generic_options), -1);
|
||||
DrawIntListSetting(bsi, "CPU Sprite Render Size", "Uses software renderer to draw texture decompression-like sprites.",
|
||||
|
@ -3310,7 +3316,8 @@ void FullscreenUI::DrawGraphicsSettingsPage()
|
|||
"EmuCore/GS", "SkipDuplicateFrames", false);
|
||||
DrawToggleSetting(bsi, "Disable Threaded Presentation",
|
||||
"Presents frames on a worker thread, instead of on the GS thread. Can improve frame times on some systems, at the cost of "
|
||||
"potentially worse frame pacing.", "EmuCore/GS", "DisableThreadedPresentation", false);
|
||||
"potentially worse frame pacing.",
|
||||
"EmuCore/GS", "DisableThreadedPresentation", false);
|
||||
if (hw_fixes_visible)
|
||||
{
|
||||
DrawIntListSetting(bsi, "Hardware Download Mode", "Changes synchronization behavior for GS downloads.", "EmuCore/GS",
|
||||
|
@ -3743,9 +3750,8 @@ void FullscreenUI::DrawControllerSettingsPage()
|
|||
DrawToggleSetting(bsi, ICON_FA_WIFI " SDL DualShock 4 / DualSense Enhanced Mode",
|
||||
"Provides vibration and LED control support over Bluetooth.", "InputSources", "SDLControllerEnhancedMode", false,
|
||||
bsi->GetBoolValue("InputSources", "SDL", true), false);
|
||||
DrawToggleSetting(bsi, ICON_FA_COG " SDL Raw Input",
|
||||
"Allow SDL to use raw access to input devices.", "InputSources", "SDLRawInput", false,
|
||||
bsi->GetBoolValue("InputSources", "SDL", true), false);
|
||||
DrawToggleSetting(bsi, ICON_FA_COG " SDL Raw Input", "Allow SDL to use raw access to input devices.", "InputSources", "SDLRawInput",
|
||||
false, bsi->GetBoolValue("InputSources", "SDL", true), false);
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
DrawToggleSetting(bsi, ICON_FA_COG " Enable XInput Input Source",
|
||||
|
@ -4317,13 +4323,13 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
|
|||
#endif
|
||||
|
||||
|
||||
HostDisplayTexture* const cover = GetCoverForCurrentGame();
|
||||
GSTexture* const cover = GetCoverForCurrentGame();
|
||||
const ImVec2 image_min(
|
||||
display_size.x - LayoutScale(10.0f + image_width) - rp_height, display_size.y - LayoutScale(10.0f + image_height) - rp_height);
|
||||
const ImVec2 image_max(image_min.x + LayoutScale(image_width) + rp_height, image_min.y + LayoutScale(image_height) + rp_height);
|
||||
const ImRect image_rect(CenterImage(
|
||||
ImRect(image_min, image_max), ImVec2(static_cast<float>(cover->GetWidth()), static_cast<float>(cover->GetHeight()))));
|
||||
dl->AddImage(cover->GetHandle(), image_rect.Min, image_rect.Max);
|
||||
dl->AddImage(cover->GetNativeHandle(), image_rect.Min, image_rect.Max);
|
||||
}
|
||||
|
||||
// current time / play time
|
||||
|
@ -4549,10 +4555,14 @@ bool FullscreenUI::InitializeSaveStateListEntry(
|
|||
std::vector<u32> screenshot_pixels;
|
||||
if (SaveState_ReadScreenshot(li->path, &screenshot_width, &screenshot_height, &screenshot_pixels))
|
||||
{
|
||||
li->preview_texture = g_host_display->CreateTexture(
|
||||
screenshot_width, screenshot_height, screenshot_pixels.data(), sizeof(u32) * screenshot_width, false);
|
||||
if (!li->preview_texture)
|
||||
li->preview_texture =
|
||||
std::unique_ptr<GSTexture>(g_gs_device->CreateTexture(screenshot_width, screenshot_height, 1, GSTexture::Format::Color));
|
||||
if (!li->preview_texture || !li->preview_texture->Update(GSVector4i(0, 0, screenshot_width, screenshot_height),
|
||||
screenshot_pixels.data(), sizeof(u32) * screenshot_width))
|
||||
{
|
||||
Console.Error("Failed to upload save state image to GPU");
|
||||
ReleaseTexture(li->preview_texture);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -4849,12 +4859,11 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading)
|
|||
bb.Min += style.FramePadding;
|
||||
bb.Max -= style.FramePadding;
|
||||
|
||||
const HostDisplayTexture* const screenshot =
|
||||
entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get();
|
||||
const GSTexture* const screenshot = entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get();
|
||||
const ImRect image_rect(CenterImage(ImRect(bb.Min, bb.Min + image_size),
|
||||
ImVec2(static_cast<float>(screenshot->GetWidth()), static_cast<float>(screenshot->GetHeight()))));
|
||||
|
||||
ImGui::GetWindowDrawList()->AddImage(screenshot->GetHandle(), image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f),
|
||||
ImGui::GetWindowDrawList()->AddImage(screenshot->GetNativeHandle(), image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f),
|
||||
ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
|
||||
|
||||
const ImVec2 title_pos(bb.Min.x, bb.Min.y + image_height + title_spacing);
|
||||
|
@ -4956,14 +4965,14 @@ void FullscreenUI::DrawResumeStateSelector()
|
|||
ImGui::TextWrapped("A resume save state created at %s was found.\n\nDo you want to load this save and continue?",
|
||||
TimeToPrintableString(entry.timestamp).c_str());
|
||||
|
||||
const HostDisplayTexture* image = entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get();
|
||||
const GSTexture* image = entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get();
|
||||
const float image_height = LayoutScale(250.0f);
|
||||
const float image_width = image_height * (static_cast<float>(image->GetWidth()) / static_cast<float>(image->GetHeight()));
|
||||
const ImVec2 pos(ImGui::GetCursorScreenPos() +
|
||||
ImVec2((ImGui::GetCurrentWindow()->WorkRect.GetWidth() - image_width) * 0.5f, LayoutScale(20.0f)));
|
||||
const ImRect image_bb(pos, pos + ImVec2(image_width, image_height));
|
||||
ImGui::GetWindowDrawList()->AddImage(
|
||||
static_cast<ImTextureID>(entry.preview_texture ? entry.preview_texture->GetHandle() : GetPlaceholderTexture()->GetHandle()),
|
||||
ImGui::GetWindowDrawList()->AddImage(static_cast<ImTextureID>(entry.preview_texture ? entry.preview_texture->GetNativeHandle() :
|
||||
GetPlaceholderTexture()->GetNativeHandle()),
|
||||
image_bb.Min, image_bb.Max);
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + image_height + LayoutScale(40.0f));
|
||||
|
@ -5226,7 +5235,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
|||
if (!visible)
|
||||
continue;
|
||||
|
||||
HostDisplayTexture* cover_texture = GetGameListCover(entry);
|
||||
GSTexture* cover_texture = GetGameListCover(entry);
|
||||
|
||||
summary.clear();
|
||||
if (entry->serial.empty())
|
||||
|
@ -5240,7 +5249,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
|||
const ImRect image_rect(CenterImage(ImRect(bb.Min, bb.Min + image_size),
|
||||
ImVec2(static_cast<float>(cover_texture->GetWidth()), static_cast<float>(cover_texture->GetHeight()))));
|
||||
|
||||
ImGui::GetWindowDrawList()->AddImage(cover_texture->GetHandle(), image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f),
|
||||
ImGui::GetWindowDrawList()->AddImage(cover_texture->GetNativeHandle(), image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f),
|
||||
ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
|
||||
|
||||
const float midpoint = bb.Min.y + g_large_font->FontSize + LayoutScale(4.0f);
|
||||
|
@ -5280,7 +5289,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
|||
|
||||
if (BeginFullscreenColumnWindow(-530.0f, 0.0f, "game_list_info", UIPrimaryDarkColor))
|
||||
{
|
||||
const HostDisplayTexture* cover_texture =
|
||||
const GSTexture* cover_texture =
|
||||
selected_entry ? GetGameListCover(selected_entry) : GetTextureForGameListEntryType(GameList::EntryType::Count);
|
||||
if (cover_texture)
|
||||
{
|
||||
|
@ -5288,8 +5297,8 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
|||
ImVec2(static_cast<float>(cover_texture->GetWidth()), static_cast<float>(cover_texture->GetHeight()))));
|
||||
|
||||
ImGui::SetCursorPos(LayoutScale(ImVec2(128.0f, 20.0f)) + image_rect.Min);
|
||||
ImGui::Image(selected_entry ? GetGameListCover(selected_entry)->GetHandle() :
|
||||
GetTextureForGameListEntryType(GameList::EntryType::Count)->GetHandle(),
|
||||
ImGui::Image(selected_entry ? GetGameListCover(selected_entry)->GetNativeHandle() :
|
||||
GetTextureForGameListEntryType(GameList::EntryType::Count)->GetNativeHandle(),
|
||||
image_rect.GetSize());
|
||||
}
|
||||
|
||||
|
@ -5337,7 +5346,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
|||
std::string flag_texture(fmt::format("icons/flags/{}.png", GameList::RegionToString(selected_entry->region)));
|
||||
ImGui::TextUnformatted("Region: ");
|
||||
ImGui::SameLine();
|
||||
ImGui::Image(GetCachedTextureAsync(flag_texture.c_str())->GetHandle(), LayoutScale(23.0f, 16.0f));
|
||||
ImGui::Image(GetCachedTextureAsync(flag_texture.c_str())->GetNativeHandle(), LayoutScale(23.0f, 16.0f));
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(" (%s)", GameList::RegionToString(selected_entry->region));
|
||||
}
|
||||
|
@ -5347,7 +5356,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
|||
ImGui::SameLine();
|
||||
if (selected_entry->compatibility_rating != GameDatabaseSchema::Compatibility::Unknown)
|
||||
{
|
||||
ImGui::Image(s_game_compatibility_textures[static_cast<u32>(selected_entry->compatibility_rating) - 1]->GetHandle(),
|
||||
ImGui::Image(s_game_compatibility_textures[static_cast<u32>(selected_entry->compatibility_rating) - 1]->GetNativeHandle(),
|
||||
LayoutScale(64.0f, 16.0f));
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
@ -5451,11 +5460,11 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size)
|
|||
bb.Min += style.FramePadding;
|
||||
bb.Max -= style.FramePadding;
|
||||
|
||||
const HostDisplayTexture* const cover_texture = GetGameListCover(entry);
|
||||
const GSTexture* const cover_texture = GetGameListCover(entry);
|
||||
const ImRect image_rect(CenterImage(ImRect(bb.Min, bb.Min + image_size),
|
||||
ImVec2(static_cast<float>(cover_texture->GetWidth()), static_cast<float>(cover_texture->GetHeight()))));
|
||||
|
||||
ImGui::GetWindowDrawList()->AddImage(cover_texture->GetHandle(), image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f),
|
||||
ImGui::GetWindowDrawList()->AddImage(cover_texture->GetNativeHandle(), image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f),
|
||||
ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
|
||||
|
||||
const ImRect title_bb(ImVec2(bb.Min.x, bb.Min.y + image_height + title_spacing), bb.Max);
|
||||
|
@ -5777,7 +5786,7 @@ void FullscreenUI::SwitchToGameList()
|
|||
QueueResetFocus();
|
||||
}
|
||||
|
||||
HostDisplayTexture* FullscreenUI::GetGameListCover(const GameList::Entry* entry)
|
||||
GSTexture* FullscreenUI::GetGameListCover(const GameList::Entry* entry)
|
||||
{
|
||||
// lookup and grab cover image
|
||||
auto cover_it = s_cover_image_map.find(entry->path);
|
||||
|
@ -5787,11 +5796,11 @@ HostDisplayTexture* FullscreenUI::GetGameListCover(const GameList::Entry* entry)
|
|||
cover_it = s_cover_image_map.emplace(entry->path, std::move(cover_path)).first;
|
||||
}
|
||||
|
||||
HostDisplayTexture* tex = (!cover_it->second.empty()) ? GetCachedTextureAsync(cover_it->second.c_str()) : nullptr;
|
||||
GSTexture* tex = (!cover_it->second.empty()) ? GetCachedTextureAsync(cover_it->second.c_str()) : nullptr;
|
||||
return tex ? tex : GetTextureForGameListEntryType(entry->type);
|
||||
}
|
||||
|
||||
HostDisplayTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::EntryType type)
|
||||
GSTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::EntryType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
|
@ -5805,7 +5814,7 @@ HostDisplayTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::Entry
|
|||
}
|
||||
}
|
||||
|
||||
HostDisplayTexture* FullscreenUI::GetCoverForCurrentGame()
|
||||
GSTexture* FullscreenUI::GetCoverForCurrentGame()
|
||||
{
|
||||
auto lock = GameList::GetLock();
|
||||
|
||||
|
@ -6064,11 +6073,11 @@ void FullscreenUI::DrawAchievement(const Achievements::Achievement& cheevo)
|
|||
const std::string& badge_path = Achievements::GetAchievementBadgePath(cheevo);
|
||||
if (!badge_path.empty())
|
||||
{
|
||||
HostDisplayTexture* badge = GetCachedTextureAsync(badge_path.c_str());
|
||||
GSTexture* badge = GetCachedTextureAsync(badge_path.c_str());
|
||||
if (badge)
|
||||
{
|
||||
ImGui::GetWindowDrawList()->AddImage(
|
||||
badge->GetHandle(), bb.Min, bb.Min + image_size, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
|
||||
ImGui::GetWindowDrawList()->AddImage(badge->GetNativeHandle(), bb.Min, bb.Min + image_size, ImVec2(0.0f, 0.0f),
|
||||
ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6183,11 +6192,11 @@ void FullscreenUI::DrawAchievementsWindow()
|
|||
const std::string& icon_path = Achievements::GetGameIcon();
|
||||
if (!icon_path.empty())
|
||||
{
|
||||
HostDisplayTexture* badge = GetCachedTexture(icon_path.c_str());
|
||||
GSTexture* badge = GetCachedTexture(icon_path.c_str());
|
||||
if (badge)
|
||||
{
|
||||
ImGui::GetWindowDrawList()->AddImage(
|
||||
badge->GetHandle(), icon_min, icon_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
|
||||
badge->GetNativeHandle(), icon_min, icon_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6318,12 +6327,12 @@ void FullscreenUI::DrawPrimedAchievementsIcons()
|
|||
if (badge_path.empty())
|
||||
return true;
|
||||
|
||||
HostDisplayTexture* badge = GetCachedTextureAsync(badge_path.c_str());
|
||||
GSTexture* badge = GetCachedTextureAsync(badge_path.c_str());
|
||||
if (!badge)
|
||||
return true;
|
||||
|
||||
ImDrawList* dl = ImGui::GetBackgroundDrawList();
|
||||
dl->AddImage(badge->GetHandle(), position, position + image_size);
|
||||
dl->AddImage(badge->GetNativeHandle(), position, position + image_size);
|
||||
position.x -= x_advance;
|
||||
return true;
|
||||
});
|
||||
|
@ -6367,12 +6376,12 @@ void FullscreenUI::DrawPrimedAchievementsList()
|
|||
if (badge_path.empty())
|
||||
return true;
|
||||
|
||||
HostDisplayTexture* badge = GetCachedTextureAsync(badge_path.c_str());
|
||||
GSTexture* badge = GetCachedTextureAsync(badge_path.c_str());
|
||||
if (!badge)
|
||||
return true;
|
||||
|
||||
ImDrawList* dl = ImGui::GetBackgroundDrawList();
|
||||
dl->AddImage(badge->GetHandle(), position, position + image_size);
|
||||
dl->AddImage(badge->GetNativeHandle(), position, position + image_size);
|
||||
|
||||
const char* achievement_title = achievement.title.c_str();
|
||||
const char* achievement_tile_end = achievement_title + achievement.title.length();
|
||||
|
@ -6580,11 +6589,11 @@ void FullscreenUI::DrawLeaderboardsWindow()
|
|||
const std::string& icon_path = Achievements::GetGameIcon();
|
||||
if (!icon_path.empty())
|
||||
{
|
||||
HostDisplayTexture* badge = GetCachedTexture(icon_path.c_str());
|
||||
GSTexture* badge = GetCachedTexture(icon_path.c_str());
|
||||
if (badge)
|
||||
{
|
||||
ImGui::GetWindowDrawList()->AddImage(
|
||||
badge->GetHandle(), icon_min, icon_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
|
||||
badge->GetNativeHandle(), icon_min, icon_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
class HostDisplayTexture;
|
||||
|
||||
struct Pcsx2Config;
|
||||
|
||||
namespace FullscreenUI
|
||||
|
|
|
@ -29,7 +29,9 @@
|
|||
#include "common/Threading.h"
|
||||
#include "common/Timer.h"
|
||||
#include "fmt/core.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "Host.h"
|
||||
#include "GS/Renderers/Common/GSDevice.h"
|
||||
#include "GS/Renderers/Common/GSTexture.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "imgui_stdlib.h"
|
||||
#include <array>
|
||||
|
@ -43,7 +45,7 @@ namespace ImGuiFullscreen
|
|||
using MessageDialogCallbackVariant = std::variant<InfoMessageDialogCallback, ConfirmMessageDialogCallback>;
|
||||
|
||||
static std::optional<Common::RGBA8Image> LoadTextureImage(const char* path);
|
||||
static std::shared_ptr<HostDisplayTexture> UploadTexture(const char* path, const Common::RGBA8Image& image);
|
||||
static std::shared_ptr<GSTexture> UploadTexture(const char* path, const Common::RGBA8Image& image);
|
||||
static void TextureLoaderThread();
|
||||
|
||||
static void DrawFileSelector();
|
||||
|
@ -89,8 +91,8 @@ namespace ImGuiFullscreen
|
|||
static u32 s_close_button_state = 0;
|
||||
static bool s_focus_reset_queued = false;
|
||||
|
||||
static LRUCache<std::string, std::shared_ptr<HostDisplayTexture>> s_texture_cache(128, true);
|
||||
static std::shared_ptr<HostDisplayTexture> s_placeholder_texture;
|
||||
static LRUCache<std::string, std::shared_ptr<GSTexture>> s_texture_cache(128, true);
|
||||
static std::shared_ptr<GSTexture> s_placeholder_texture;
|
||||
static std::atomic_bool s_texture_load_thread_quit{false};
|
||||
static std::mutex s_texture_load_mutex;
|
||||
static std::condition_variable s_texture_load_cv;
|
||||
|
@ -247,7 +249,7 @@ void ImGuiFullscreen::Shutdown(bool clear_state)
|
|||
}
|
||||
}
|
||||
|
||||
const std::shared_ptr<HostDisplayTexture>& ImGuiFullscreen::GetPlaceholderTexture()
|
||||
const std::shared_ptr<GSTexture>& ImGuiFullscreen::GetPlaceholderTexture()
|
||||
{
|
||||
return s_placeholder_texture;
|
||||
}
|
||||
|
@ -278,26 +280,32 @@ std::optional<Common::RGBA8Image> ImGuiFullscreen::LoadTextureImage(const char*
|
|||
return image;
|
||||
}
|
||||
|
||||
std::shared_ptr<HostDisplayTexture> ImGuiFullscreen::UploadTexture(const char* path, const Common::RGBA8Image& image)
|
||||
std::shared_ptr<GSTexture> ImGuiFullscreen::UploadTexture(const char* path, const Common::RGBA8Image& image)
|
||||
{
|
||||
std::unique_ptr<HostDisplayTexture> texture =
|
||||
g_host_display->CreateTexture(image.GetWidth(), image.GetHeight(), image.GetPixels(), image.GetByteStride());
|
||||
GSTexture* texture = g_gs_device->CreateTexture(image.GetWidth(), image.GetHeight(), 1, GSTexture::Format::Color);
|
||||
if (!texture)
|
||||
{
|
||||
Console.Error("failed to create %ux%u texture for resource", image.GetWidth(), image.GetHeight());
|
||||
return {};
|
||||
}
|
||||
|
||||
DevCon.WriteLn("Uploaded texture resource '%s' (%ux%u)", path, image.GetWidth(), image.GetHeight());
|
||||
return std::shared_ptr<HostDisplayTexture>(std::move(texture));
|
||||
if (!texture->Update(GSVector4i(0, 0, image.GetWidth(), image.GetHeight()), image.GetPixels(), image.GetByteStride()))
|
||||
{
|
||||
Console.Error("Failed to upload %ux%u texture for resource", image.GetWidth(), image.GetHeight());
|
||||
g_gs_device->Recycle(texture);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<HostDisplayTexture> ImGuiFullscreen::LoadTexture(const char* path)
|
||||
DevCon.WriteLn("Uploaded texture resource '%s' (%ux%u)", path, image.GetWidth(), image.GetHeight());
|
||||
return std::shared_ptr<GSTexture>(texture, [](GSTexture* tex) { g_gs_device->Recycle(tex); });
|
||||
}
|
||||
|
||||
std::shared_ptr<GSTexture> ImGuiFullscreen::LoadTexture(const char* path)
|
||||
{
|
||||
std::optional<Common::RGBA8Image> image(LoadTextureImage(path));
|
||||
if (image.has_value())
|
||||
{
|
||||
std::shared_ptr<HostDisplayTexture> ret(UploadTexture(path, image.value()));
|
||||
std::shared_ptr<GSTexture> ret(UploadTexture(path, image.value()));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -305,21 +313,21 @@ std::shared_ptr<HostDisplayTexture> ImGuiFullscreen::LoadTexture(const char* pat
|
|||
return s_placeholder_texture;
|
||||
}
|
||||
|
||||
HostDisplayTexture* ImGuiFullscreen::GetCachedTexture(const char* name)
|
||||
GSTexture* ImGuiFullscreen::GetCachedTexture(const char* name)
|
||||
{
|
||||
std::shared_ptr<HostDisplayTexture>* tex_ptr = s_texture_cache.Lookup(name);
|
||||
std::shared_ptr<GSTexture>* tex_ptr = s_texture_cache.Lookup(name);
|
||||
if (!tex_ptr)
|
||||
{
|
||||
std::shared_ptr<HostDisplayTexture> tex(LoadTexture(name));
|
||||
std::shared_ptr<GSTexture> tex(LoadTexture(name));
|
||||
tex_ptr = s_texture_cache.Insert(name, std::move(tex));
|
||||
}
|
||||
|
||||
return tex_ptr->get();
|
||||
}
|
||||
|
||||
HostDisplayTexture* ImGuiFullscreen::GetCachedTextureAsync(const char* name)
|
||||
GSTexture* ImGuiFullscreen::GetCachedTextureAsync(const char* name)
|
||||
{
|
||||
std::shared_ptr<HostDisplayTexture>* tex_ptr = s_texture_cache.Lookup(name);
|
||||
std::shared_ptr<GSTexture>* tex_ptr = s_texture_cache.Lookup(name);
|
||||
if (!tex_ptr)
|
||||
{
|
||||
// insert the placeholder
|
||||
|
@ -348,7 +356,7 @@ void ImGuiFullscreen::UploadAsyncTextures()
|
|||
s_texture_upload_queue.pop_front();
|
||||
lock.unlock();
|
||||
|
||||
std::shared_ptr<HostDisplayTexture> tex = UploadTexture(it.first.c_str(), it.second);
|
||||
std::shared_ptr<GSTexture> tex = UploadTexture(it.first.c_str(), it.second);
|
||||
if (tex)
|
||||
s_texture_cache.Insert(std::move(it.first), std::move(tex));
|
||||
|
||||
|
@ -2378,9 +2386,9 @@ void ImGuiFullscreen::DrawNotifications(ImVec2& position, float spacing)
|
|||
const ImVec2 badge_max(badge_min.x + badge_size, badge_min.y + badge_size);
|
||||
if (!notif.badge_path.empty())
|
||||
{
|
||||
HostDisplayTexture* tex = GetCachedTexture(notif.badge_path.c_str());
|
||||
GSTexture* tex = GetCachedTexture(notif.badge_path.c_str());
|
||||
if (tex)
|
||||
dl->AddImage(static_cast<ImTextureID>(tex->GetHandle()), badge_min, badge_max);
|
||||
dl->AddImage(tex->GetNativeHandle(), badge_min, badge_max);
|
||||
}
|
||||
|
||||
const ImVec2 title_min(badge_max.x + horizontal_spacing, box_min.y + vertical_padding);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
class HostDisplayTexture;
|
||||
class GSTexture;
|
||||
|
||||
namespace ImGuiFullscreen
|
||||
{
|
||||
|
@ -116,10 +116,10 @@ namespace ImGuiFullscreen
|
|||
void Shutdown(bool clear_state);
|
||||
|
||||
/// Texture cache.
|
||||
const std::shared_ptr<HostDisplayTexture>& GetPlaceholderTexture();
|
||||
std::shared_ptr<HostDisplayTexture> LoadTexture(const char* path);
|
||||
HostDisplayTexture* GetCachedTexture(const char* name);
|
||||
HostDisplayTexture* GetCachedTextureAsync(const char* name);
|
||||
const std::shared_ptr<GSTexture>& GetPlaceholderTexture();
|
||||
std::shared_ptr<GSTexture> LoadTexture(const char* path);
|
||||
GSTexture* GetCachedTexture(const char* name);
|
||||
GSTexture* GetCachedTextureAsync(const char* name);
|
||||
bool InvalidateCachedTexture(const std::string& path);
|
||||
void UploadAsyncTextures();
|
||||
|
||||
|
|
|
@ -37,8 +37,8 @@
|
|||
#include "Frontend/InputManager.h"
|
||||
#include "GS.h"
|
||||
#include "GS/GS.h"
|
||||
#include "GS/Renderers/Common/GSDevice.h"
|
||||
#include "Host.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "IconsFontAwesome5.h"
|
||||
#include "PerformanceMetrics.h"
|
||||
#include "Recording/InputRecording.h"
|
||||
|
@ -90,40 +90,32 @@ bool ImGuiManager::Initialize()
|
|||
return false;
|
||||
}
|
||||
|
||||
s_global_scale = std::max(0.5f, g_host_display->GetWindowScale() * (EmuConfig.GS.OsdScale / 100.0f));
|
||||
s_global_scale = std::max(0.5f, g_gs_device->GetWindowScale() * (GSConfig.OsdScale / 100.0f));
|
||||
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.IniFilename = nullptr;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_HasGamepad;
|
||||
io.BackendUsingLegacyKeyArrays = 0;
|
||||
io.BackendUsingLegacyNavInputArray = 0;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
|
||||
|
||||
io.DisplayFramebufferScale = ImVec2(1, 1); // We already scale things ourselves, this would double-apply scaling
|
||||
io.DisplaySize.x = static_cast<float>(g_host_display->GetWindowWidth());
|
||||
io.DisplaySize.y = static_cast<float>(g_host_display->GetWindowHeight());
|
||||
io.DisplaySize.x = static_cast<float>(g_gs_device->GetWindowWidth());
|
||||
io.DisplaySize.y = static_cast<float>(g_gs_device->GetWindowHeight());
|
||||
|
||||
SetKeyMap();
|
||||
SetStyle();
|
||||
|
||||
const bool add_fullscreen_fonts = s_fullscreen_ui_was_initialized;
|
||||
pxAssertRel(!FullscreenUI::IsInitialized(), "Fullscreen UI is not initialized on ImGui init");
|
||||
if (add_fullscreen_fonts)
|
||||
ImGuiFullscreen::UpdateLayoutScale();
|
||||
|
||||
if (!g_host_display->CreateImGuiContext())
|
||||
if (!AddImGuiFonts(add_fullscreen_fonts) || !g_gs_device->UpdateImGuiFontTexture())
|
||||
{
|
||||
pxFailRel("Failed to create ImGui device context");
|
||||
g_host_display->DestroyImGuiContext();
|
||||
ImGui::DestroyContext();
|
||||
UnloadFontData();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AddImGuiFonts(add_fullscreen_fonts) || !g_host_display->UpdateImGuiFontTexture())
|
||||
{
|
||||
pxFailRel("Failed to create ImGui font text");
|
||||
g_host_display->DestroyImGuiContext();
|
||||
Host::ReportErrorAsync("ImGuiManager", "Failed to create ImGui font text");
|
||||
ImGui::DestroyContext();
|
||||
UnloadFontData();
|
||||
return false;
|
||||
|
@ -143,7 +135,7 @@ bool ImGuiManager::Initialize()
|
|||
|
||||
bool ImGuiManager::InitializeFullscreenUI()
|
||||
{
|
||||
s_fullscreen_ui_was_initialized = FullscreenUI::Initialize();
|
||||
s_fullscreen_ui_was_initialized = !ImGui::GetCurrentContext() || FullscreenUI::Initialize();
|
||||
return s_fullscreen_ui_was_initialized;
|
||||
}
|
||||
|
||||
|
@ -154,8 +146,6 @@ void ImGuiManager::Shutdown(bool clear_state)
|
|||
if (clear_state)
|
||||
s_fullscreen_ui_was_initialized = false;
|
||||
|
||||
if (g_host_display)
|
||||
g_host_display->DestroyImGuiContext();
|
||||
if (ImGui::GetCurrentContext())
|
||||
ImGui::DestroyContext();
|
||||
|
||||
|
@ -170,8 +160,8 @@ void ImGuiManager::Shutdown(bool clear_state)
|
|||
|
||||
void ImGuiManager::WindowResized()
|
||||
{
|
||||
const u32 new_width = g_host_display ? g_host_display->GetWindowWidth() : 0;
|
||||
const u32 new_height = g_host_display ? g_host_display->GetWindowHeight() : 0;
|
||||
const u32 new_width = g_gs_device ? g_gs_device->GetWindowWidth() : 0;
|
||||
const u32 new_height = g_gs_device ? g_gs_device->GetWindowHeight() : 0;
|
||||
|
||||
ImGui::GetIO().DisplaySize = ImVec2(static_cast<float>(new_width), static_cast<float>(new_height));
|
||||
|
||||
|
@ -184,7 +174,7 @@ void ImGuiManager::WindowResized()
|
|||
|
||||
void ImGuiManager::UpdateScale()
|
||||
{
|
||||
const float window_scale = g_host_display ? g_host_display->GetWindowScale() : 1.0f;
|
||||
const float window_scale = g_gs_device ? g_gs_device->GetWindowScale() : 1.0f;
|
||||
const float scale = std::max(window_scale * (EmuConfig.GS.OsdScale / 100.0f), 0.5f);
|
||||
|
||||
if (scale == s_global_scale && (!HasFullscreenFonts() || !ImGuiFullscreen::UpdateLayoutScale()))
|
||||
|
@ -199,7 +189,7 @@ void ImGuiManager::UpdateScale()
|
|||
if (!AddImGuiFonts(HasFullscreenFonts()))
|
||||
pxFailRel("Failed to create ImGui font text");
|
||||
|
||||
if (!g_host_display->UpdateImGuiFontTexture())
|
||||
if (!g_gs_device->UpdateImGuiFontTexture())
|
||||
pxFailRel("Failed to recreate font texture after scale+resize");
|
||||
|
||||
NewFrame();
|
||||
|
@ -483,7 +473,7 @@ bool ImGuiManager::AddFullscreenFontsIfMissing()
|
|||
AddImGuiFonts(false);
|
||||
}
|
||||
|
||||
g_host_display->UpdateImGuiFontTexture();
|
||||
g_gs_device->UpdateImGuiFontTexture();
|
||||
NewFrame();
|
||||
|
||||
return HasFullscreenFonts();
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
#include "GS/GSCapture.h"
|
||||
#include "GS/GSVector.h"
|
||||
#include "Host.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "IconsFontAwesome5.h"
|
||||
#include "PerformanceMetrics.h"
|
||||
#include "PAD/Host/PAD.h"
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "HostDisplay.h"
|
||||
|
||||
#ifndef __OBJC__
|
||||
#error "This header is for use with Objective-C++ only.
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
#include "GS/Renderers/Metal/GSMTLDeviceInfo.h"
|
||||
#include <mutex>
|
||||
#include <AppKit/AppKit.h>
|
||||
#include <Metal/Metal.h>
|
||||
#include <QuartzCore/QuartzCore.h>
|
||||
|
||||
class MetalHostDisplay final : public HostDisplay
|
||||
{
|
||||
enum class UsePresentDrawable : u8
|
||||
{
|
||||
Never = 0,
|
||||
Always = 1,
|
||||
IfVsync = 2,
|
||||
};
|
||||
MRCOwned<NSView*> m_view;
|
||||
MRCOwned<CAMetalLayer*> m_layer;
|
||||
GSMTLDevice m_dev;
|
||||
MRCOwned<id<MTLCommandQueue>> m_queue;
|
||||
MRCOwned<id<MTLTexture>> m_font_tex;
|
||||
MRCOwned<id<CAMetalDrawable>> m_current_drawable;
|
||||
MRCOwned<MTLRenderPassDescriptor*> m_pass_desc;
|
||||
u32 m_capture_start_frame;
|
||||
UsePresentDrawable m_use_present_drawable;
|
||||
bool m_gpu_timing_enabled = false;
|
||||
double m_accumulated_gpu_time = 0;
|
||||
double m_last_gpu_time_end = 0;
|
||||
std::mutex m_mtx;
|
||||
|
||||
void AttachSurfaceOnMainThread();
|
||||
void DetachSurfaceOnMainThread();
|
||||
|
||||
public:
|
||||
MetalHostDisplay();
|
||||
~MetalHostDisplay();
|
||||
RenderAPI GetRenderAPI() const override;
|
||||
void* GetDevice() const override;
|
||||
void* GetContext() const override;
|
||||
void* GetSurface() const override;
|
||||
|
||||
bool HasDevice() const override;
|
||||
bool HasSurface() const override;
|
||||
bool CreateDevice(const WindowInfo& wi, VsyncMode vsync) override;
|
||||
bool SetupDevice() override;
|
||||
bool MakeCurrent() override;
|
||||
bool DoneCurrent() override;
|
||||
void DestroySurface() override;
|
||||
bool ChangeWindow(const WindowInfo& wi) override;
|
||||
bool SupportsFullscreen() const override;
|
||||
bool IsFullscreen() override;
|
||||
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||
AdapterAndModeList GetAdapterAndModeList() override;
|
||||
std::string GetDriverInfo() const override;
|
||||
|
||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
||||
|
||||
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic = false) override;
|
||||
void UpdateTexture(id<MTLTexture> texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride);
|
||||
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride) override;
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
bool CreateImGuiContext() override;
|
||||
void DestroyImGuiContext() override;
|
||||
bool UpdateImGuiFontTexture() override;
|
||||
|
||||
bool GetHostRefreshRate(float* refresh_rate) override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
void AccumulateCommandBufferTime(id<MTLCommandBuffer> buffer);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,467 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "MetalHostDisplay.h"
|
||||
#include "GS/Renderers/Metal/GSMetalCPPAccessible.h"
|
||||
#include "GS/Renderers/Metal/GSDeviceMTL.h"
|
||||
#include <imgui.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
class MetalHostDisplayTexture final : public HostDisplayTexture
|
||||
{
|
||||
MRCOwned<id<MTLTexture>> m_tex;
|
||||
u32 m_width, m_height;
|
||||
public:
|
||||
MetalHostDisplayTexture(MRCOwned<id<MTLTexture>> tex, u32 width, u32 height)
|
||||
: m_tex(std::move(tex))
|
||||
, m_width(width)
|
||||
, m_height(height)
|
||||
{
|
||||
}
|
||||
|
||||
void* GetHandle() const override { return (__bridge void*)m_tex; };
|
||||
u32 GetWidth() const override { return m_width; }
|
||||
u32 GetHeight() const override { return m_height; }
|
||||
};
|
||||
|
||||
HostDisplay* MakeMetalHostDisplay()
|
||||
{
|
||||
return new MetalHostDisplay();
|
||||
}
|
||||
|
||||
MetalHostDisplay::MetalHostDisplay()
|
||||
{
|
||||
}
|
||||
|
||||
MetalHostDisplay::~MetalHostDisplay()
|
||||
{
|
||||
MetalHostDisplay::DestroySurface();
|
||||
m_queue = nullptr;
|
||||
m_dev.Reset();
|
||||
}
|
||||
|
||||
HostDisplay::AdapterAndModeList GetMetalAdapterAndModeList()
|
||||
{ @autoreleasepool {
|
||||
HostDisplay::AdapterAndModeList list;
|
||||
auto devs = MRCTransfer(MTLCopyAllDevices());
|
||||
for (id<MTLDevice> dev in devs.Get())
|
||||
list.adapter_names.push_back([[dev name] UTF8String]);
|
||||
return list;
|
||||
}}
|
||||
|
||||
template <typename Fn>
|
||||
static void OnMainThread(Fn&& fn)
|
||||
{
|
||||
if ([NSThread isMainThread])
|
||||
fn();
|
||||
else
|
||||
dispatch_sync(dispatch_get_main_queue(), fn);
|
||||
}
|
||||
|
||||
RenderAPI MetalHostDisplay::GetRenderAPI() const
|
||||
{
|
||||
return RenderAPI::Metal;
|
||||
}
|
||||
|
||||
void* MetalHostDisplay::GetDevice() const { return const_cast<void*>(static_cast<const void*>(&m_dev)); }
|
||||
void* MetalHostDisplay::GetContext() const { return (__bridge void*)m_queue; }
|
||||
void* MetalHostDisplay::GetSurface() const { return (__bridge void*)m_layer; }
|
||||
bool MetalHostDisplay::HasDevice() const { return m_dev.IsOk(); }
|
||||
bool MetalHostDisplay::HasSurface() const { return static_cast<bool>(m_layer);}
|
||||
|
||||
void MetalHostDisplay::AttachSurfaceOnMainThread()
|
||||
{
|
||||
ASSERT([NSThread isMainThread]);
|
||||
m_layer = MRCRetain([CAMetalLayer layer]);
|
||||
[m_layer setDrawableSize:CGSizeMake(m_window_info.surface_width, m_window_info.surface_height)];
|
||||
[m_layer setDevice:m_dev.dev];
|
||||
m_view = MRCRetain((__bridge NSView*)m_window_info.window_handle);
|
||||
[m_view setWantsLayer:YES];
|
||||
[m_view setLayer:m_layer];
|
||||
}
|
||||
|
||||
void MetalHostDisplay::DetachSurfaceOnMainThread()
|
||||
{
|
||||
ASSERT([NSThread isMainThread]);
|
||||
[m_view setLayer:nullptr];
|
||||
[m_view setWantsLayer:NO];
|
||||
m_view = nullptr;
|
||||
m_layer = nullptr;
|
||||
}
|
||||
|
||||
bool MetalHostDisplay::CreateDevice(const WindowInfo& wi, VsyncMode vsync)
|
||||
{ @autoreleasepool {
|
||||
m_window_info = wi;
|
||||
pxAssertRel(!m_dev.dev, "Device already created!");
|
||||
NSString* ns_adapter_name = [NSString stringWithUTF8String:EmuConfig.GS.Adapter.c_str()];
|
||||
auto devs = MRCTransfer(MTLCopyAllDevices());
|
||||
for (id<MTLDevice> dev in devs.Get())
|
||||
{
|
||||
if ([[dev name] isEqualToString:ns_adapter_name])
|
||||
m_dev = GSMTLDevice(MRCRetain(dev));
|
||||
}
|
||||
if (!m_dev.dev)
|
||||
{
|
||||
if (!EmuConfig.GS.Adapter.empty())
|
||||
Console.Warning("Metal: Couldn't find adapter %s, using default", EmuConfig.GS.Adapter.c_str());
|
||||
m_dev = GSMTLDevice(MRCTransfer(MTLCreateSystemDefaultDevice()));
|
||||
if (!m_dev.dev)
|
||||
Host::ReportErrorAsync("No Metal Devices Available", "No Metal-supporting GPUs were found. PCSX2 requires a Metal GPU (available on all macs from 2012 onwards).");
|
||||
}
|
||||
m_queue = MRCTransfer([m_dev.dev newCommandQueue]);
|
||||
|
||||
m_pass_desc = MRCTransfer([MTLRenderPassDescriptor new]);
|
||||
[m_pass_desc colorAttachments][0].loadAction = MTLLoadActionClear;
|
||||
[m_pass_desc colorAttachments][0].clearColor = MTLClearColorMake(0, 0, 0, 0);
|
||||
[m_pass_desc colorAttachments][0].storeAction = MTLStoreActionStore;
|
||||
|
||||
if (char* env = getenv("MTL_USE_PRESENT_DRAWABLE"))
|
||||
m_use_present_drawable = static_cast<UsePresentDrawable>(atoi(env));
|
||||
else if (@available(macOS 13.0, *))
|
||||
m_use_present_drawable = UsePresentDrawable::Always;
|
||||
else // Before Ventura, presentDrawable acts like vsync is on when windowed
|
||||
m_use_present_drawable = UsePresentDrawable::IfVsync;
|
||||
|
||||
m_capture_start_frame = 0;
|
||||
if (char* env = getenv("MTL_CAPTURE"))
|
||||
{
|
||||
m_capture_start_frame = atoi(env);
|
||||
}
|
||||
if (m_capture_start_frame)
|
||||
{
|
||||
Console.WriteLn("Metal will capture frame %u", m_capture_start_frame);
|
||||
}
|
||||
|
||||
if (m_dev.IsOk() && m_queue)
|
||||
{
|
||||
OnMainThread([this]
|
||||
{
|
||||
AttachSurfaceOnMainThread();
|
||||
});
|
||||
SetVSync(vsync);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}}
|
||||
|
||||
bool MetalHostDisplay::SetupDevice()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetalHostDisplay::MakeCurrent() { return true; }
|
||||
bool MetalHostDisplay::DoneCurrent() { return true; }
|
||||
|
||||
void MetalHostDisplay::DestroySurface()
|
||||
{
|
||||
if (!m_layer)
|
||||
return;
|
||||
OnMainThread([this]{ DetachSurfaceOnMainThread(); });
|
||||
m_layer = nullptr;
|
||||
}
|
||||
|
||||
bool MetalHostDisplay::ChangeWindow(const WindowInfo& wi)
|
||||
{
|
||||
OnMainThread([this, &wi]
|
||||
{
|
||||
DetachSurfaceOnMainThread();
|
||||
m_window_info = wi;
|
||||
AttachSurfaceOnMainThread();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetalHostDisplay::SupportsFullscreen() const { return false; }
|
||||
bool MetalHostDisplay::IsFullscreen() { return false; }
|
||||
bool MetalHostDisplay::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) { return false; }
|
||||
|
||||
HostDisplay::AdapterAndModeList MetalHostDisplay::GetAdapterAndModeList()
|
||||
{
|
||||
return GetMetalAdapterAndModeList();
|
||||
}
|
||||
|
||||
std::string MetalHostDisplay::GetDriverInfo() const
|
||||
{ @autoreleasepool {
|
||||
std::string desc([[m_dev.dev description] UTF8String]);
|
||||
desc += "\n Texture Swizzle: " + std::string(m_dev.features.texture_swizzle ? "Supported" : "Unsupported");
|
||||
desc += "\n Unified Memory: " + std::string(m_dev.features.unified_memory ? "Supported" : "Unsupported");
|
||||
desc += "\n Framebuffer Fetch: " + std::string(m_dev.features.framebuffer_fetch ? "Supported" : "Unsupported");
|
||||
desc += "\n Primitive ID: " + std::string(m_dev.features.primid ? "Supported" : "Unsupported");
|
||||
desc += "\n Shader Version: " + std::string(to_string(m_dev.features.shader_version));
|
||||
desc += "\n Max Texture Size: " + std::to_string(m_dev.features.max_texsize);
|
||||
return desc;
|
||||
}}
|
||||
|
||||
void MetalHostDisplay::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
||||
{
|
||||
m_window_info.surface_scale = new_window_scale;
|
||||
if (m_window_info.surface_width == static_cast<u32>(new_window_width) && m_window_info.surface_height == static_cast<u32>(new_window_height))
|
||||
return;
|
||||
m_window_info.surface_width = new_window_width;
|
||||
m_window_info.surface_height = new_window_height;
|
||||
@autoreleasepool
|
||||
{
|
||||
[m_layer setDrawableSize:CGSizeMake(new_window_width, new_window_height)];
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<HostDisplayTexture> MetalHostDisplay::CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic)
|
||||
{ @autoreleasepool {
|
||||
MTLTextureDescriptor* desc = [MTLTextureDescriptor
|
||||
texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
|
||||
width:width
|
||||
height:height
|
||||
mipmapped:false];
|
||||
[desc setUsage:MTLTextureUsageShaderRead];
|
||||
[desc setStorageMode:MTLStorageModePrivate];
|
||||
MRCOwned<id<MTLTexture>> tex = MRCTransfer([m_dev.dev newTextureWithDescriptor:desc]);
|
||||
if (!tex)
|
||||
return nullptr; // Something broke yay
|
||||
[tex setLabel:@"MetalHostDisplay Texture"];
|
||||
if (data)
|
||||
UpdateTexture(tex, 0, 0, width, height, data, data_stride);
|
||||
return std::make_unique<MetalHostDisplayTexture>(std::move(tex), width, height);
|
||||
}}
|
||||
|
||||
void MetalHostDisplay::UpdateTexture(id<MTLTexture> texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride)
|
||||
{
|
||||
id<MTLCommandBuffer> cmdbuf = [m_queue commandBuffer];
|
||||
id<MTLBlitCommandEncoder> enc = [cmdbuf blitCommandEncoder];
|
||||
size_t bytes = data_stride * height;
|
||||
MRCOwned<id<MTLBuffer>> buf = MRCTransfer([m_dev.dev newBufferWithLength:bytes options:MTLResourceStorageModeShared | MTLResourceCPUCacheModeWriteCombined]);
|
||||
memcpy([buf contents], data, bytes);
|
||||
[enc copyFromBuffer:buf
|
||||
sourceOffset:0
|
||||
sourceBytesPerRow:data_stride
|
||||
sourceBytesPerImage:bytes
|
||||
sourceSize:MTLSizeMake(width, height, 1)
|
||||
toTexture:texture
|
||||
destinationSlice:0
|
||||
destinationLevel:0
|
||||
destinationOrigin:MTLOriginMake(0, 0, 0)];
|
||||
[enc endEncoding];
|
||||
[cmdbuf commit];
|
||||
}
|
||||
|
||||
void MetalHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride)
|
||||
{ @autoreleasepool {
|
||||
UpdateTexture((__bridge id<MTLTexture>)texture->GetHandle(), x, y, width, height, data, data_stride);
|
||||
}}
|
||||
|
||||
static bool s_capture_next = false;
|
||||
|
||||
HostDisplay::PresentResult MetalHostDisplay::BeginPresent(bool frame_skip)
|
||||
{ @autoreleasepool {
|
||||
GSDeviceMTL* dev = static_cast<GSDeviceMTL*>(g_gs_device.get());
|
||||
if (dev && m_capture_start_frame && dev->FrameNo() == m_capture_start_frame)
|
||||
s_capture_next = true;
|
||||
if (frame_skip || m_window_info.type == WindowInfo::Type::Surfaceless || !g_gs_device)
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
id<MTLCommandBuffer> buf = dev->GetRenderCmdBuf();
|
||||
m_current_drawable = MRCRetain([m_layer nextDrawable]);
|
||||
dev->EndRenderPass();
|
||||
if (!m_current_drawable)
|
||||
{
|
||||
[buf pushDebugGroup:@"Present Skipped"];
|
||||
[buf popDebugGroup];
|
||||
dev->FlushEncoders();
|
||||
ImGui::EndFrame();
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
[m_pass_desc colorAttachments][0].texture = [m_current_drawable texture];
|
||||
id<MTLRenderCommandEncoder> enc = [buf renderCommandEncoderWithDescriptor:m_pass_desc];
|
||||
[enc setLabel:@"Present"];
|
||||
dev->m_current_render.encoder = MRCRetain(enc);
|
||||
return PresentResult::OK;
|
||||
}}
|
||||
|
||||
void MetalHostDisplay::EndPresent()
|
||||
{ @autoreleasepool {
|
||||
GSDeviceMTL* dev = static_cast<GSDeviceMTL*>(g_gs_device.get());
|
||||
pxAssertDev(dev && dev->m_current_render.encoder && dev->m_current_render_cmdbuf, "BeginPresent cmdbuf was destroyed");
|
||||
ImGui::Render();
|
||||
dev->RenderImGui(ImGui::GetDrawData());
|
||||
dev->EndRenderPass();
|
||||
if (m_current_drawable)
|
||||
{
|
||||
const bool use_present_drawable = m_use_present_drawable == UsePresentDrawable::Always ||
|
||||
(m_use_present_drawable == UsePresentDrawable::IfVsync && m_vsync_mode != VsyncMode::Off);
|
||||
|
||||
if (use_present_drawable)
|
||||
[dev->m_current_render_cmdbuf presentDrawable:m_current_drawable];
|
||||
else
|
||||
[dev->m_current_render_cmdbuf addScheduledHandler:[drawable = std::move(m_current_drawable)](id<MTLCommandBuffer>){
|
||||
[drawable present];
|
||||
}];
|
||||
}
|
||||
dev->FlushEncoders();
|
||||
dev->FrameCompleted();
|
||||
m_current_drawable = nullptr;
|
||||
if (m_capture_start_frame)
|
||||
{
|
||||
if (@available(macOS 10.15, iOS 13, *))
|
||||
{
|
||||
static NSString* const path = @"/tmp/PCSX2MTLCapture.gputrace";
|
||||
static u32 frames;
|
||||
if (frames)
|
||||
{
|
||||
--frames;
|
||||
if (!frames)
|
||||
{
|
||||
[[MTLCaptureManager sharedCaptureManager] stopCapture];
|
||||
Console.WriteLn("Metal Trace Capture to /tmp/PCSX2MTLCapture.gputrace finished");
|
||||
[[NSWorkspace sharedWorkspace] selectFile:path
|
||||
inFileViewerRootedAtPath:@"/tmp/"];
|
||||
}
|
||||
}
|
||||
else if (s_capture_next)
|
||||
{
|
||||
s_capture_next = false;
|
||||
MTLCaptureManager* mgr = [MTLCaptureManager sharedCaptureManager];
|
||||
if ([mgr supportsDestination:MTLCaptureDestinationGPUTraceDocument])
|
||||
{
|
||||
MTLCaptureDescriptor* desc = [[MTLCaptureDescriptor new] autorelease];
|
||||
[desc setCaptureObject:m_dev.dev];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path])
|
||||
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
|
||||
[desc setOutputURL:[NSURL fileURLWithPath:path]];
|
||||
[desc setDestination:MTLCaptureDestinationGPUTraceDocument];
|
||||
NSError* err = nullptr;
|
||||
[mgr startCaptureWithDescriptor:desc error:&err];
|
||||
if (err)
|
||||
{
|
||||
Console.Error("Metal Trace Capture failed: %s", [[err localizedDescription] UTF8String]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLn("Metal Trace Capture to /tmp/PCSX2MTLCapture.gputrace started");
|
||||
frames = 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error("Metal Trace Capture Failed: MTLCaptureManager doesn't support GPU trace documents! (Did you forget to run with METAL_CAPTURE_ENABLED=1?)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
void MetalHostDisplay::SetVSync(VsyncMode mode)
|
||||
{
|
||||
[m_layer setDisplaySyncEnabled:mode != VsyncMode::Off];
|
||||
m_vsync_mode = mode;
|
||||
}
|
||||
|
||||
bool MetalHostDisplay::CreateImGuiContext()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendRendererName = "pcsx2_imgui_metal";
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
return true;
|
||||
}
|
||||
|
||||
void MetalHostDisplay::DestroyImGuiContext()
|
||||
{
|
||||
ImGui::GetIO().Fonts->SetTexID(nullptr);
|
||||
}
|
||||
|
||||
bool MetalHostDisplay::UpdateImGuiFontTexture()
|
||||
{ @autoreleasepool {
|
||||
u8* data;
|
||||
int width, height;
|
||||
ImFontAtlas* fonts = ImGui::GetIO().Fonts;
|
||||
fonts->GetTexDataAsAlpha8(&data, &width, &height);
|
||||
MTLTextureDescriptor* desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatA8Unorm width:width height:height mipmapped:false];
|
||||
[desc setUsage:MTLTextureUsageShaderRead];
|
||||
[desc setStorageMode:MTLStorageModePrivate];
|
||||
if (@available(macOS 10.15, *))
|
||||
if (m_dev.features.texture_swizzle)
|
||||
[desc setSwizzle:MTLTextureSwizzleChannelsMake(MTLTextureSwizzleOne, MTLTextureSwizzleOne, MTLTextureSwizzleOne, MTLTextureSwizzleAlpha)];
|
||||
m_font_tex = MRCTransfer([m_dev.dev newTextureWithDescriptor:desc]);
|
||||
[m_font_tex setLabel:@"ImGui Font"];
|
||||
UpdateTexture(m_font_tex, 0, 0, width, height, data, width);
|
||||
fonts->SetTexID((__bridge void*)m_font_tex);
|
||||
return static_cast<bool>(m_font_tex);
|
||||
}}
|
||||
|
||||
bool MetalHostDisplay::GetHostRefreshRate(float* refresh_rate)
|
||||
{
|
||||
OnMainThread([this, refresh_rate]
|
||||
{
|
||||
u32 did = [[[[[m_view window] screen] deviceDescription] valueForKey:@"NSScreenNumber"] unsignedIntValue];
|
||||
if (CGDisplayModeRef mode = CGDisplayCopyDisplayMode(did))
|
||||
{
|
||||
*refresh_rate = CGDisplayModeGetRefreshRate(mode);
|
||||
CGDisplayModeRelease(mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
*refresh_rate = 0;
|
||||
}
|
||||
});
|
||||
return *refresh_rate != 0;
|
||||
}
|
||||
|
||||
bool MetalHostDisplay::SetGPUTimingEnabled(bool enabled)
|
||||
{
|
||||
if (enabled == m_gpu_timing_enabled)
|
||||
return true;
|
||||
if (@available(macOS 10.15, iOS 10.3, *))
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_mtx);
|
||||
m_gpu_timing_enabled = enabled;
|
||||
m_accumulated_gpu_time = 0;
|
||||
m_last_gpu_time_end = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float MetalHostDisplay::GetAndResetAccumulatedGPUTime()
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_mtx);
|
||||
float time = m_accumulated_gpu_time * 1000;
|
||||
m_accumulated_gpu_time = 0;
|
||||
return time;
|
||||
}
|
||||
|
||||
void MetalHostDisplay::AccumulateCommandBufferTime(id<MTLCommandBuffer> buffer)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_mtx);
|
||||
if (!m_gpu_timing_enabled)
|
||||
return;
|
||||
// We do the check before enabling m_gpu_timing_enabled
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
||||
// It's unlikely, but command buffers can overlap or run out of order
|
||||
// This doesn't handle every case (fully out of order), but it should at least handle overlapping
|
||||
double begin = std::max(m_last_gpu_time_end, [buffer GPUStartTime]);
|
||||
double end = [buffer GPUEndTime];
|
||||
if (end > begin)
|
||||
{
|
||||
m_accumulated_gpu_time += end - begin;
|
||||
m_last_gpu_time_end = end;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
#endif // __APPLE__
|
|
@ -1,487 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "OpenGLHostDisplay.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/ScopedGuard.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/GL/Program.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
|
||||
class OpenGLHostDisplayTexture : public HostDisplayTexture
|
||||
{
|
||||
public:
|
||||
OpenGLHostDisplayTexture(GLuint texture, u32 width, u32 height)
|
||||
: m_texture(texture)
|
||||
, m_width(width)
|
||||
, m_height(height)
|
||||
{
|
||||
}
|
||||
~OpenGLHostDisplayTexture() override = default;
|
||||
|
||||
void* GetHandle() const override { return reinterpret_cast<void*>(static_cast<uintptr_t>(m_texture)); }
|
||||
u32 GetWidth() const override { return m_width; }
|
||||
u32 GetHeight() const override { return m_height; }
|
||||
|
||||
GLuint GetGLID() const { return m_texture; }
|
||||
|
||||
private:
|
||||
GLuint m_texture;
|
||||
u32 m_width;
|
||||
u32 m_height;
|
||||
};
|
||||
|
||||
OpenGLHostDisplay::OpenGLHostDisplay() = default;
|
||||
|
||||
OpenGLHostDisplay::~OpenGLHostDisplay()
|
||||
{
|
||||
if (m_gl_context)
|
||||
{
|
||||
m_gl_context->DoneCurrent();
|
||||
m_gl_context.reset();
|
||||
}
|
||||
}
|
||||
|
||||
RenderAPI OpenGLHostDisplay::GetRenderAPI() const
|
||||
{
|
||||
return m_gl_context->IsGLES() ? RenderAPI::OpenGLES : RenderAPI::OpenGL;
|
||||
}
|
||||
|
||||
void* OpenGLHostDisplay::GetDevice() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* OpenGLHostDisplay::GetContext() const
|
||||
{
|
||||
return m_gl_context.get();
|
||||
}
|
||||
|
||||
void* OpenGLHostDisplay::GetSurface() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<HostDisplayTexture> OpenGLHostDisplay::CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic /* = false */)
|
||||
{
|
||||
// clear error
|
||||
glGetError();
|
||||
|
||||
// don't worry, I'm planning on removing this eventually - we'll use GSTexture instead.
|
||||
glActiveTexture(GL_TEXTURE7);
|
||||
|
||||
GLuint id;
|
||||
glGenTextures(1, &id);
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
|
||||
if (GLAD_GL_ARB_texture_storage || GLAD_GL_ES_VERSION_3_0)
|
||||
{
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
GLenum error = glGetError();
|
||||
if (error != GL_NO_ERROR)
|
||||
{
|
||||
Console.Error("Failed to create texture: 0x%X", error);
|
||||
glDeleteTextures(1, &id);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<OpenGLHostDisplayTexture>(id, width, height);
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE7);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, texture_data_stride / sizeof(u32));
|
||||
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA8, GL_UNSIGNED_BYTE, texture_data);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::SetVSync(VsyncMode mode)
|
||||
{
|
||||
if (m_vsync_mode == mode || m_gl_context->GetWindowInfo().type == WindowInfo::Type::Surfaceless)
|
||||
return;
|
||||
|
||||
// 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);
|
||||
|
||||
if (mode != VsyncMode::Adaptive || !m_gl_context->SetSwapInterval(-1))
|
||||
m_gl_context->SetSwapInterval(static_cast<s32>(mode != VsyncMode::Off));
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
|
||||
m_vsync_mode = mode;
|
||||
}
|
||||
|
||||
const char* OpenGLHostDisplay::GetGLSLVersionString() const
|
||||
{
|
||||
if (GetRenderAPI() == RenderAPI::OpenGLES)
|
||||
{
|
||||
if (GLAD_GL_ES_VERSION_3_0)
|
||||
return "#version 300 es";
|
||||
else
|
||||
return "#version 100";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GLAD_GL_VERSION_3_3)
|
||||
return "#version 330";
|
||||
else
|
||||
return "#version 130";
|
||||
}
|
||||
}
|
||||
|
||||
std::string OpenGLHostDisplay::GetGLSLVersionHeader() const
|
||||
{
|
||||
std::string header = GetGLSLVersionString();
|
||||
header += "\n\n";
|
||||
if (GetRenderAPI() == RenderAPI::OpenGLES)
|
||||
{
|
||||
header += "precision highp float;\n";
|
||||
header += "precision highp int;\n\n";
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::HasDevice() const
|
||||
{
|
||||
return static_cast<bool>(m_gl_context);
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::HasSurface() const
|
||||
{
|
||||
return m_window_info.type != WindowInfo::Type::Surfaceless;
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::CreateDevice(const WindowInfo& wi, VsyncMode vsync)
|
||||
{
|
||||
m_gl_context = GL::Context::Create(wi);
|
||||
if (!m_gl_context)
|
||||
{
|
||||
Console.Error("Failed to create any GL context");
|
||||
m_gl_context.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_window_info = m_gl_context->GetWindowInfo();
|
||||
m_vsync_mode = vsync;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::SetupDevice()
|
||||
{
|
||||
// We do use 8-bit formats, and higher alignment for 32-bit formats won't hurt anything.
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
SetSwapInterval();
|
||||
GL::Program::ResetLastProgram();
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::SetSwapInterval()
|
||||
{
|
||||
const int interval = ((m_vsync_mode == VsyncMode::Adaptive) ? -1 : ((m_vsync_mode == VsyncMode::On) ? 1 : 0));
|
||||
m_gl_context->SetSwapInterval(interval);
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::MakeCurrent()
|
||||
{
|
||||
if (!m_gl_context->MakeCurrent())
|
||||
{
|
||||
Console.Error("Failed to make GL context current");
|
||||
return false;
|
||||
}
|
||||
|
||||
SetSwapInterval();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::DoneCurrent()
|
||||
{
|
||||
return m_gl_context->DoneCurrent();
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::ChangeWindow(const WindowInfo& new_wi)
|
||||
{
|
||||
pxAssert(m_gl_context);
|
||||
|
||||
if (!m_gl_context->ChangeSurface(new_wi))
|
||||
{
|
||||
Console.Error("Failed to change surface");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_window_info = m_gl_context->GetWindowInfo();
|
||||
|
||||
if (new_wi.type != WindowInfo::Type::Surfaceless)
|
||||
{
|
||||
// reset vsync rate, since it (usually) gets lost
|
||||
if (m_vsync_mode != VsyncMode::Adaptive || !m_gl_context->SetSwapInterval(-1))
|
||||
m_gl_context->SetSwapInterval(static_cast<s32>(m_vsync_mode != VsyncMode::Off));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
||||
{
|
||||
if (!m_gl_context)
|
||||
return;
|
||||
|
||||
m_window_info.surface_scale = new_window_scale;
|
||||
if (m_window_info.surface_width == static_cast<u32>(new_window_width) &&
|
||||
m_window_info.surface_height == static_cast<u32>(new_window_height))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_gl_context->ResizeSurface(static_cast<u32>(new_window_width), static_cast<u32>(new_window_height));
|
||||
m_window_info = m_gl_context->GetWindowInfo();
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::SupportsFullscreen() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::IsFullscreen()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HostDisplay::AdapterAndModeList OpenGLHostDisplay::GetAdapterAndModeList()
|
||||
{
|
||||
AdapterAndModeList aml;
|
||||
|
||||
if (m_gl_context)
|
||||
{
|
||||
for (const GL::Context::FullscreenModeInfo& fmi : m_gl_context->EnumerateFullscreenModes())
|
||||
aml.fullscreen_modes.push_back(GetFullscreenModeString(fmi.width, fmi.height, fmi.refresh_rate));
|
||||
}
|
||||
|
||||
return aml;
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::DestroySurface()
|
||||
{
|
||||
if (!m_gl_context)
|
||||
return;
|
||||
|
||||
m_window_info = {};
|
||||
if (!m_gl_context->ChangeSurface(m_window_info))
|
||||
Console.Error("Failed to switch to surfaceless");
|
||||
}
|
||||
|
||||
std::string OpenGLHostDisplay::GetDriverInfo() const
|
||||
{
|
||||
const char* gl_vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
|
||||
const char* gl_renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
|
||||
const char* gl_version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
||||
return StringUtil::StdStringFromFormat(
|
||||
"%s Context:\n%s\n%s %s", m_gl_context->IsGLES() ? "OpenGL ES" : "OpenGL", gl_version, gl_vendor, gl_renderer);
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::CreateImGuiContext()
|
||||
{
|
||||
return ImGui_ImplOpenGL3_Init(GetGLSLVersionString());
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::DestroyImGuiContext()
|
||||
{
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::UpdateImGuiFontTexture()
|
||||
{
|
||||
return ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||
}
|
||||
|
||||
HostDisplay::PresentResult OpenGLHostDisplay::BeginPresent(bool frame_skip)
|
||||
{
|
||||
if (frame_skip || m_window_info.type == WindowInfo::Type::Surfaceless)
|
||||
return PresentResult::FrameSkipped;
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glViewport(0, 0, m_window_info.surface_width, m_window_info.surface_height);
|
||||
|
||||
return PresentResult::OK;
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::EndPresent()
|
||||
{
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
|
||||
ImGui::Render();
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
GL::Program::ResetLastProgram();
|
||||
|
||||
if (m_gpu_timing_enabled)
|
||||
PopTimestampQuery();
|
||||
|
||||
m_gl_context->SwapBuffers();
|
||||
|
||||
if (m_gpu_timing_enabled)
|
||||
KickTimestampQuery();
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::CreateTimestampQueries()
|
||||
{
|
||||
const bool gles = m_gl_context->IsGLES();
|
||||
const auto GenQueries = gles ? glGenQueriesEXT : glGenQueries;
|
||||
|
||||
GenQueries(static_cast<u32>(m_timestamp_queries.size()), m_timestamp_queries.data());
|
||||
KickTimestampQuery();
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::DestroyTimestampQueries()
|
||||
{
|
||||
if (m_timestamp_queries[0] == 0)
|
||||
return;
|
||||
|
||||
const bool gles = m_gl_context->IsGLES();
|
||||
const auto DeleteQueries = gles ? glDeleteQueriesEXT : glDeleteQueries;
|
||||
|
||||
if (m_timestamp_query_started)
|
||||
{
|
||||
const auto EndQuery = gles ? glEndQueryEXT : glEndQuery;
|
||||
EndQuery(GL_TIME_ELAPSED);
|
||||
}
|
||||
|
||||
DeleteQueries(static_cast<u32>(m_timestamp_queries.size()), m_timestamp_queries.data());
|
||||
m_timestamp_queries.fill(0);
|
||||
m_read_timestamp_query = 0;
|
||||
m_write_timestamp_query = 0;
|
||||
m_waiting_timestamp_queries = 0;
|
||||
m_timestamp_query_started = false;
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::PopTimestampQuery()
|
||||
{
|
||||
const bool gles = m_gl_context->IsGLES();
|
||||
|
||||
if (gles)
|
||||
{
|
||||
GLint disjoint = 0;
|
||||
glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint);
|
||||
if (disjoint)
|
||||
{
|
||||
DevCon.WriteLn("GPU timing disjoint, resetting.");
|
||||
if (m_timestamp_query_started)
|
||||
glEndQueryEXT(GL_TIME_ELAPSED);
|
||||
|
||||
m_read_timestamp_query = 0;
|
||||
m_write_timestamp_query = 0;
|
||||
m_waiting_timestamp_queries = 0;
|
||||
m_timestamp_query_started = false;
|
||||
}
|
||||
}
|
||||
|
||||
while (m_waiting_timestamp_queries > 0)
|
||||
{
|
||||
const auto GetQueryObjectiv = gles ? glGetQueryObjectivEXT : glGetQueryObjectiv;
|
||||
const auto GetQueryObjectui64v = gles ? glGetQueryObjectui64vEXT : glGetQueryObjectui64v;
|
||||
|
||||
GLint available = 0;
|
||||
GetQueryObjectiv(m_timestamp_queries[m_read_timestamp_query], GL_QUERY_RESULT_AVAILABLE, &available);
|
||||
|
||||
if (!available)
|
||||
break;
|
||||
|
||||
u64 result = 0;
|
||||
GetQueryObjectui64v(m_timestamp_queries[m_read_timestamp_query], GL_QUERY_RESULT, &result);
|
||||
m_accumulated_gpu_time += static_cast<float>(static_cast<double>(result) / 1000000.0);
|
||||
m_read_timestamp_query = (m_read_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
|
||||
m_waiting_timestamp_queries--;
|
||||
}
|
||||
|
||||
if (m_timestamp_query_started)
|
||||
{
|
||||
const auto EndQuery = gles ? glEndQueryEXT : glEndQuery;
|
||||
EndQuery(GL_TIME_ELAPSED);
|
||||
|
||||
m_write_timestamp_query = (m_write_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
|
||||
m_timestamp_query_started = false;
|
||||
m_waiting_timestamp_queries++;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::KickTimestampQuery()
|
||||
{
|
||||
if (m_timestamp_query_started || m_waiting_timestamp_queries == NUM_TIMESTAMP_QUERIES)
|
||||
return;
|
||||
|
||||
const bool gles = m_gl_context->IsGLES();
|
||||
const auto BeginQuery = gles ? glBeginQueryEXT : glBeginQuery;
|
||||
|
||||
BeginQuery(GL_TIME_ELAPSED, m_timestamp_queries[m_write_timestamp_query]);
|
||||
m_timestamp_query_started = true;
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::SetGPUTimingEnabled(bool enabled)
|
||||
{
|
||||
if (m_gpu_timing_enabled == enabled)
|
||||
return true;
|
||||
|
||||
if (enabled && m_gl_context->IsGLES() && !GLAD_GL_EXT_disjoint_timer_query)
|
||||
return false;
|
||||
|
||||
m_gpu_timing_enabled = enabled;
|
||||
if (m_gpu_timing_enabled)
|
||||
CreateTimestampQueries();
|
||||
else
|
||||
DestroyTimestampQueries();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float OpenGLHostDisplay::GetAndResetAccumulatedGPUTime()
|
||||
{
|
||||
const float value = m_accumulated_gpu_time;
|
||||
m_accumulated_gpu_time = 0.0f;
|
||||
return value;
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glad.h>
|
||||
|
||||
#include "HostDisplay.h"
|
||||
#include "common/GL/Context.h"
|
||||
#include "common/WindowInfo.h"
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <memory>
|
||||
|
||||
class OpenGLHostDisplay final : public HostDisplay
|
||||
{
|
||||
public:
|
||||
OpenGLHostDisplay();
|
||||
~OpenGLHostDisplay();
|
||||
|
||||
RenderAPI GetRenderAPI() const override;
|
||||
void* GetDevice() const override;
|
||||
void* GetContext() const override;
|
||||
void* GetSurface() const override;
|
||||
|
||||
bool HasDevice() const override;
|
||||
bool HasSurface() const override;
|
||||
|
||||
bool CreateDevice(const WindowInfo& wi, VsyncMode vsync) override;
|
||||
bool SetupDevice() override;
|
||||
|
||||
bool MakeCurrent() override;
|
||||
bool DoneCurrent() override;
|
||||
|
||||
bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
||||
bool SupportsFullscreen() const override;
|
||||
bool IsFullscreen() override;
|
||||
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||
AdapterAndModeList GetAdapterAndModeList() override;
|
||||
void DestroySurface() override;
|
||||
std::string GetDriverInfo() 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* texture_data, u32 texture_data_stride) override;
|
||||
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
protected:
|
||||
static constexpr u8 NUM_TIMESTAMP_QUERIES = 5;
|
||||
|
||||
const char* GetGLSLVersionString() const;
|
||||
std::string GetGLSLVersionHeader() const;
|
||||
|
||||
bool CreateImGuiContext() override;
|
||||
void DestroyImGuiContext() override;
|
||||
bool UpdateImGuiFontTexture() override;
|
||||
|
||||
void SetSwapInterval();
|
||||
|
||||
void CreateTimestampQueries();
|
||||
void DestroyTimestampQueries();
|
||||
void PopTimestampQuery();
|
||||
void KickTimestampQuery();
|
||||
|
||||
std::unique_ptr<GL::Context> m_gl_context;
|
||||
|
||||
std::array<GLuint, NUM_TIMESTAMP_QUERIES> m_timestamp_queries = {};
|
||||
u8 m_read_timestamp_query = 0;
|
||||
u8 m_write_timestamp_query = 0;
|
||||
u8 m_waiting_timestamp_queries = 0;
|
||||
bool m_timestamp_query_started = false;
|
||||
float m_accumulated_gpu_time = 0.0f;
|
||||
bool m_gpu_timing_enabled = false;
|
||||
};
|
||||
|
|
@ -1,470 +0,0 @@
|
|||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "VulkanHostDisplay.h"
|
||||
#include "ShaderCacheVersion.h"
|
||||
#include "common/Align.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/ScopedGuard.h"
|
||||
#include "common/Vulkan/Builders.h"
|
||||
#include "common/Vulkan/Context.h"
|
||||
#include "common/Vulkan/ShaderCache.h"
|
||||
#include "common/Vulkan/StreamBuffer.h"
|
||||
#include "common/Vulkan/SwapChain.h"
|
||||
#include "common/Vulkan/Util.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_vulkan.h"
|
||||
#include <array>
|
||||
|
||||
class VulkanHostDisplayTexture : public HostDisplayTexture
|
||||
{
|
||||
public:
|
||||
explicit VulkanHostDisplayTexture(Vulkan::Texture texture)
|
||||
: m_texture(std::move(texture))
|
||||
{
|
||||
}
|
||||
~VulkanHostDisplayTexture() override = default;
|
||||
|
||||
void* GetHandle() const override { return const_cast<Vulkan::Texture*>(&m_texture); }
|
||||
u32 GetWidth() const override { return m_texture.GetWidth(); }
|
||||
u32 GetHeight() const override { return m_texture.GetHeight(); }
|
||||
|
||||
const Vulkan::Texture& GetTexture() const { return m_texture; }
|
||||
Vulkan::Texture& GetTexture() { return m_texture; }
|
||||
|
||||
private:
|
||||
Vulkan::Texture m_texture;
|
||||
};
|
||||
|
||||
static VkPresentModeKHR GetPreferredPresentModeForVsyncMode(VsyncMode mode)
|
||||
{
|
||||
if (mode == VsyncMode::On)
|
||||
return VK_PRESENT_MODE_FIFO_KHR;
|
||||
else if (mode == VsyncMode::Adaptive)
|
||||
return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
|
||||
else
|
||||
return VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
}
|
||||
|
||||
VulkanHostDisplay::VulkanHostDisplay() = default;
|
||||
|
||||
VulkanHostDisplay::~VulkanHostDisplay()
|
||||
{
|
||||
if (g_vulkan_context)
|
||||
{
|
||||
g_vulkan_context->WaitForGPUIdle();
|
||||
m_swap_chain.reset();
|
||||
|
||||
Vulkan::ShaderCache::Destroy();
|
||||
Vulkan::Context::Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
RenderAPI VulkanHostDisplay::GetRenderAPI() const
|
||||
{
|
||||
return RenderAPI::Vulkan;
|
||||
}
|
||||
|
||||
void* VulkanHostDisplay::GetDevice() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* VulkanHostDisplay::GetContext() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* VulkanHostDisplay::GetSurface() const
|
||||
{
|
||||
return m_swap_chain.get();
|
||||
}
|
||||
|
||||
bool VulkanHostDisplay::ChangeWindow(const WindowInfo& new_wi)
|
||||
{
|
||||
g_vulkan_context->WaitForGPUIdle();
|
||||
|
||||
if (new_wi.type == WindowInfo::Type::Surfaceless)
|
||||
{
|
||||
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::Sleep);
|
||||
m_swap_chain.reset();
|
||||
m_window_info = new_wi;
|
||||
return true;
|
||||
}
|
||||
|
||||
// recreate surface in existing swap chain if it already exists
|
||||
if (m_swap_chain)
|
||||
{
|
||||
if (m_swap_chain->RecreateSurface(new_wi))
|
||||
{
|
||||
m_window_info = m_swap_chain->GetWindowInfo();
|
||||
return true;
|
||||
}
|
||||
|
||||
m_swap_chain.reset();
|
||||
}
|
||||
|
||||
WindowInfo wi_copy(new_wi);
|
||||
VkSurfaceKHR surface =
|
||||
Vulkan::SwapChain::CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), g_vulkan_context->GetPhysicalDevice(), &wi_copy);
|
||||
if (surface == VK_NULL_HANDLE)
|
||||
{
|
||||
Console.Error("Failed to create new surface for swap chain");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_swap_chain = Vulkan::SwapChain::Create(wi_copy, surface, GetPreferredPresentModeForVsyncMode(m_vsync_mode));
|
||||
if (!m_swap_chain)
|
||||
{
|
||||
Console.Error("Failed to create swap chain");
|
||||
Vulkan::SwapChain::DestroyVulkanSurface(g_vulkan_context->GetVulkanInstance(), &wi_copy, surface);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_window_info = m_swap_chain->GetWindowInfo();
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanHostDisplay::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
||||
{
|
||||
if (m_swap_chain->GetWidth() == static_cast<u32>(new_window_width) && m_swap_chain->GetHeight() == static_cast<u32>(new_window_height))
|
||||
{
|
||||
// skip unnecessary resizes
|
||||
m_window_info.surface_scale = new_window_scale;
|
||||
return;
|
||||
}
|
||||
|
||||
g_vulkan_context->WaitForGPUIdle();
|
||||
|
||||
if (!m_swap_chain->ResizeSwapChain(new_window_width, new_window_height, new_window_scale))
|
||||
{
|
||||
// AcquireNextImage() will fail, and we'll recreate the surface.
|
||||
Console.Error("Failed to resize swap chain. Next present will fail.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_window_info = m_swap_chain->GetWindowInfo();
|
||||
}
|
||||
|
||||
bool VulkanHostDisplay::SupportsFullscreen() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VulkanHostDisplay::IsFullscreen()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VulkanHostDisplay::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HostDisplay::AdapterAndModeList VulkanHostDisplay::GetAdapterAndModeList()
|
||||
{
|
||||
return StaticGetAdapterAndModeList(m_window_info.type != WindowInfo::Type::Surfaceless ? &m_window_info : nullptr);
|
||||
}
|
||||
|
||||
void VulkanHostDisplay::DestroySurface()
|
||||
{
|
||||
g_vulkan_context->WaitForGPUIdle();
|
||||
m_swap_chain.reset();
|
||||
}
|
||||
|
||||
std::string VulkanHostDisplay::GetDriverInfo() const
|
||||
{
|
||||
std::string ret;
|
||||
const u32 api_version = g_vulkan_context->GetDeviceProperties().apiVersion;
|
||||
const u32 driver_version = g_vulkan_context->GetDeviceProperties().driverVersion;
|
||||
if (g_vulkan_context->GetOptionalExtensions().vk_khr_driver_properties)
|
||||
{
|
||||
const VkPhysicalDeviceDriverProperties& props = g_vulkan_context->GetDeviceDriverProperties();
|
||||
ret = StringUtil::StdStringFromFormat("Driver %u.%u.%u\nVulkan %u.%u.%u\nConformance Version %u.%u.%u.%u\n%s\n%s\n%s",
|
||||
VK_VERSION_MAJOR(driver_version), VK_VERSION_MINOR(driver_version), VK_VERSION_PATCH(driver_version),
|
||||
VK_API_VERSION_MAJOR(api_version), VK_API_VERSION_MINOR(api_version), VK_API_VERSION_PATCH(api_version),
|
||||
props.conformanceVersion.major, props.conformanceVersion.minor, props.conformanceVersion.subminor,
|
||||
props.conformanceVersion.patch, props.driverInfo, props.driverName, g_vulkan_context->GetDeviceProperties().deviceName);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = StringUtil::StdStringFromFormat("Driver %u.%u.%u\nVulkan %u.%u.%u\n%s", VK_VERSION_MAJOR(driver_version),
|
||||
VK_VERSION_MINOR(driver_version), VK_VERSION_PATCH(driver_version), VK_API_VERSION_MAJOR(api_version),
|
||||
VK_API_VERSION_MINOR(api_version), VK_API_VERSION_PATCH(api_version), g_vulkan_context->GetDeviceProperties().deviceName);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool UploadBufferToTexture(
|
||||
Vulkan::Texture* texture, VkCommandBuffer cmdbuf, u32 width, u32 height, const void* data, u32 data_stride)
|
||||
{
|
||||
const u32 texel_size = Vulkan::Util::GetTexelSize(texture->GetFormat());
|
||||
const u32 row_size = texel_size * width;
|
||||
const u32 upload_stride = Common::AlignUpPow2(row_size, g_vulkan_context->GetBufferCopyRowPitchAlignment());
|
||||
const u32 upload_size = upload_stride * height;
|
||||
pxAssert(row_size <= data_stride);
|
||||
|
||||
Vulkan::StreamBuffer& buf = g_vulkan_context->GetTextureUploadBuffer();
|
||||
if (!buf.ReserveMemory(upload_size, g_vulkan_context->GetBufferCopyOffsetAlignment()))
|
||||
{
|
||||
Console.WriteLn("Executing command buffer for UploadBufferToTexture()");
|
||||
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::None);
|
||||
if (!buf.ReserveMemory(upload_size, g_vulkan_context->GetBufferCopyOffsetAlignment()))
|
||||
{
|
||||
Console.WriteLn("Failed to allocate %u bytes in stream buffer for UploadBufferToTexture()", upload_size);
|
||||
return false;
|
||||
}
|
||||
cmdbuf = g_vulkan_context->GetCurrentInitCommandBuffer();
|
||||
}
|
||||
|
||||
const u32 buf_offset = buf.GetCurrentOffset();
|
||||
StringUtil::StrideMemCpy(buf.GetCurrentHostPointer(), upload_stride, data, data_stride, row_size, height);
|
||||
buf.CommitMemory(upload_size);
|
||||
|
||||
texture->UpdateFromBuffer(cmdbuf, 0, 0, 0, 0, width, height, height, upload_stride / texel_size, buf.GetBuffer(), buf_offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<HostDisplayTexture> VulkanHostDisplay::CreateTexture(
|
||||
u32 width, u32 height, const void* data, u32 data_stride, bool dynamic /* = false */)
|
||||
{
|
||||
static constexpr VkFormat vk_format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
static constexpr VkImageUsageFlags usage =
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
|
||||
Vulkan::Texture texture;
|
||||
if (!texture.Create(width, height, 1, 1, vk_format, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, usage))
|
||||
return {};
|
||||
|
||||
texture.TransitionToLayout(g_vulkan_context->GetCurrentInitCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
if (data)
|
||||
{
|
||||
if (!UploadBufferToTexture(&texture, g_vulkan_context->GetCurrentInitCommandBuffer(), width, height, data, data_stride))
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
// clear it instead so we don't read uninitialized data (and keep the validation layer happy!)
|
||||
static constexpr VkClearColorValue ccv = {};
|
||||
static constexpr VkImageSubresourceRange isr = {VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u};
|
||||
vkCmdClearColorImage(g_vulkan_context->GetCurrentInitCommandBuffer(), texture.GetImage(), texture.GetLayout(), &ccv, 1u, &isr);
|
||||
}
|
||||
|
||||
texture.TransitionToLayout(g_vulkan_context->GetCurrentInitCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
return std::make_unique<VulkanHostDisplayTexture>(std::move(texture));
|
||||
}
|
||||
|
||||
void VulkanHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride)
|
||||
{
|
||||
UploadBufferToTexture(&static_cast<VulkanHostDisplayTexture*>(texture)->GetTexture(), g_vulkan_context->GetCurrentCommandBuffer(),
|
||||
width, height, data, data_stride);
|
||||
}
|
||||
|
||||
void VulkanHostDisplay::SetVSync(VsyncMode mode)
|
||||
{
|
||||
if (!m_swap_chain || m_vsync_mode == mode)
|
||||
return;
|
||||
|
||||
// This swap chain should not be used by the current buffer, thus safe to destroy.
|
||||
g_vulkan_context->WaitForGPUIdle();
|
||||
m_swap_chain->SetVSync(GetPreferredPresentModeForVsyncMode(mode));
|
||||
m_vsync_mode = mode;
|
||||
}
|
||||
|
||||
bool VulkanHostDisplay::CreateDevice(const WindowInfo& wi, VsyncMode vsync)
|
||||
{
|
||||
WindowInfo local_wi(wi);
|
||||
const bool debug_device = EmuConfig.GS.UseDebugDevice;
|
||||
if (!Vulkan::Context::Create(EmuConfig.GS.Adapter, &local_wi, &m_swap_chain, GetPreferredPresentModeForVsyncMode(vsync),
|
||||
!EmuConfig.GS.DisableThreadedPresentation, debug_device, debug_device))
|
||||
{
|
||||
Console.Error("Failed to create Vulkan context");
|
||||
m_window_info = {};
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE: This is assigned afterwards, because some platforms can modify the window info (e.g. Metal).
|
||||
m_window_info = m_swap_chain ? m_swap_chain->GetWindowInfo() : local_wi;
|
||||
m_vsync_mode = vsync;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanHostDisplay::SetupDevice()
|
||||
{
|
||||
Vulkan::ShaderCache::Create(EmuConfig.GS.DisableShaderCache ? std::string_view() : std::string_view(EmuFolders::Cache),
|
||||
SHADER_CACHE_VERSION, EmuConfig.GS.UseDebugDevice);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanHostDisplay::HasDevice() const
|
||||
{
|
||||
return static_cast<bool>(g_vulkan_context);
|
||||
}
|
||||
|
||||
bool VulkanHostDisplay::HasSurface() const
|
||||
{
|
||||
return static_cast<bool>(m_swap_chain);
|
||||
}
|
||||
|
||||
bool VulkanHostDisplay::CreateImGuiContext()
|
||||
{
|
||||
const VkRenderPass render_pass =
|
||||
m_swap_chain ? m_swap_chain->GetClearRenderPass() : g_vulkan_context->GetRenderPass(VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_UNDEFINED);
|
||||
if (render_pass == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
return ImGui_ImplVulkan_Init(render_pass);
|
||||
}
|
||||
|
||||
void VulkanHostDisplay::DestroyImGuiContext()
|
||||
{
|
||||
g_vulkan_context->WaitForGPUIdle();
|
||||
ImGui_ImplVulkan_Shutdown();
|
||||
}
|
||||
|
||||
bool VulkanHostDisplay::UpdateImGuiFontTexture()
|
||||
{
|
||||
return ImGui_ImplVulkan_CreateFontsTexture();
|
||||
}
|
||||
|
||||
bool VulkanHostDisplay::MakeCurrent()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanHostDisplay::DoneCurrent()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
HostDisplay::PresentResult VulkanHostDisplay::BeginPresent(bool frame_skip)
|
||||
{
|
||||
if (frame_skip || !m_swap_chain)
|
||||
return PresentResult::FrameSkipped;
|
||||
|
||||
// Previous frame needs to be presented before we can acquire the swap chain.
|
||||
g_vulkan_context->WaitForPresentComplete();
|
||||
|
||||
// Check if the device was lost.
|
||||
if (g_vulkan_context->CheckLastSubmitFail())
|
||||
return PresentResult::DeviceLost;
|
||||
|
||||
VkResult res = m_swap_chain->AcquireNextImage();
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
m_swap_chain->ReleaseCurrentImage();
|
||||
|
||||
if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
|
||||
{
|
||||
ResizeWindow(0, 0, m_window_info.surface_scale);
|
||||
res = m_swap_chain->AcquireNextImage();
|
||||
}
|
||||
else if (res == VK_ERROR_SURFACE_LOST_KHR)
|
||||
{
|
||||
Console.Warning("Surface lost, attempting to recreate");
|
||||
if (!m_swap_chain->RecreateSurface(m_window_info))
|
||||
{
|
||||
Console.Error("Failed to recreate surface after loss");
|
||||
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::None);
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
|
||||
res = m_swap_chain->AcquireNextImage();
|
||||
}
|
||||
|
||||
// This can happen when multiple resize events happen in quick succession.
|
||||
// In this case, just wait until the next frame to try again.
|
||||
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
|
||||
{
|
||||
// Still submit the command buffer, otherwise we'll end up with several frames waiting.
|
||||
LOG_VULKAN_ERROR(res, "vkAcquireNextImageKHR() failed: ");
|
||||
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::None);
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
}
|
||||
|
||||
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
|
||||
|
||||
// Swap chain images start in undefined
|
||||
Vulkan::Texture& swap_chain_texture = m_swap_chain->GetCurrentTexture();
|
||||
swap_chain_texture.OverrideImageLayout(VK_IMAGE_LAYOUT_UNDEFINED);
|
||||
swap_chain_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
const VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
|
||||
const VkRenderPassBeginInfo rp = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr, m_swap_chain->GetClearRenderPass(),
|
||||
m_swap_chain->GetCurrentFramebuffer(), {{0, 0}, {swap_chain_texture.GetWidth(), swap_chain_texture.GetHeight()}}, 1u, &clear_value};
|
||||
vkCmdBeginRenderPass(g_vulkan_context->GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
const VkViewport vp{
|
||||
0.0f, 0.0f, static_cast<float>(swap_chain_texture.GetWidth()), static_cast<float>(swap_chain_texture.GetHeight()), 0.0f, 1.0f};
|
||||
const VkRect2D scissor{{0, 0}, {static_cast<u32>(swap_chain_texture.GetWidth()), static_cast<u32>(swap_chain_texture.GetHeight())}};
|
||||
vkCmdSetViewport(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &vp);
|
||||
vkCmdSetScissor(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &scissor);
|
||||
return PresentResult::OK;
|
||||
}
|
||||
|
||||
void VulkanHostDisplay::EndPresent()
|
||||
{
|
||||
ImGui::Render();
|
||||
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
|
||||
vkCmdEndRenderPass(g_vulkan_context->GetCurrentCommandBuffer());
|
||||
m_swap_chain->GetCurrentTexture().TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
|
||||
g_vulkan_context->SubmitCommandBuffer(m_swap_chain.get(), !m_swap_chain->IsPresentModeSynchronizing());
|
||||
g_vulkan_context->MoveToNextCommandBuffer();
|
||||
}
|
||||
|
||||
bool VulkanHostDisplay::SetGPUTimingEnabled(bool enabled)
|
||||
{
|
||||
return g_vulkan_context->SetEnableGPUTiming(enabled);
|
||||
}
|
||||
|
||||
float VulkanHostDisplay::GetAndResetAccumulatedGPUTime()
|
||||
{
|
||||
return g_vulkan_context->GetAndResetAccumulatedGPUTime();
|
||||
}
|
||||
|
||||
HostDisplay::AdapterAndModeList VulkanHostDisplay::StaticGetAdapterAndModeList(const WindowInfo* wi)
|
||||
{
|
||||
AdapterAndModeList ret;
|
||||
std::vector<Vulkan::SwapChain::FullscreenModeInfo> fsmodes;
|
||||
|
||||
if (g_vulkan_context)
|
||||
{
|
||||
ret.adapter_names = Vulkan::Context::EnumerateGPUNames(g_vulkan_context->GetVulkanInstance());
|
||||
if (wi)
|
||||
{
|
||||
fsmodes = Vulkan::SwapChain::GetSurfaceFullscreenModes(
|
||||
g_vulkan_context->GetVulkanInstance(), g_vulkan_context->GetPhysicalDevice(), *wi);
|
||||
}
|
||||
}
|
||||
else if (Vulkan::LoadVulkanLibrary())
|
||||
{
|
||||
ScopedGuard lib_guard([]() { Vulkan::UnloadVulkanLibrary(); });
|
||||
|
||||
VkInstance instance = Vulkan::Context::CreateVulkanInstance(nullptr, false, false);
|
||||
if (instance != VK_NULL_HANDLE)
|
||||
{
|
||||
ScopedGuard instance_guard([&instance]() { vkDestroyInstance(instance, nullptr); });
|
||||
|
||||
if (Vulkan::LoadVulkanInstanceFunctions(instance))
|
||||
ret.adapter_names = Vulkan::Context::EnumerateGPUNames(instance);
|
||||
}
|
||||
}
|
||||
|
||||
if (!fsmodes.empty())
|
||||
{
|
||||
ret.fullscreen_modes.reserve(fsmodes.size());
|
||||
for (const Vulkan::SwapChain::FullscreenModeInfo& fmi : fsmodes)
|
||||
{
|
||||
ret.fullscreen_modes.push_back(GetFullscreenModeString(fmi.width, fmi.height, fmi.refresh_rate));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
#pragma once
|
||||
#include "common/Vulkan/Loader.h"
|
||||
#include "common/Vulkan/StreamBuffer.h"
|
||||
#include "common/Vulkan/SwapChain.h"
|
||||
#include "common/WindowInfo.h"
|
||||
#include "pcsx2/HostDisplay.h"
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class StreamBuffer;
|
||||
class SwapChain;
|
||||
} // namespace Vulkan
|
||||
|
||||
class VulkanHostDisplay final : public HostDisplay
|
||||
{
|
||||
public:
|
||||
VulkanHostDisplay();
|
||||
~VulkanHostDisplay();
|
||||
|
||||
RenderAPI GetRenderAPI() const override;
|
||||
void* GetDevice() const override;
|
||||
void* GetContext() const override;
|
||||
void* GetSurface() const override;
|
||||
|
||||
bool HasDevice() const override;
|
||||
bool HasSurface() const override;
|
||||
|
||||
bool CreateDevice(const WindowInfo& wi, VsyncMode vsync) override;
|
||||
bool SetupDevice() override;
|
||||
|
||||
bool MakeCurrent() override;
|
||||
bool DoneCurrent() override;
|
||||
|
||||
bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
||||
bool SupportsFullscreen() const override;
|
||||
bool IsFullscreen() override;
|
||||
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||
AdapterAndModeList GetAdapterAndModeList() override;
|
||||
void DestroySurface() override;
|
||||
std::string GetDriverInfo() const override;
|
||||
|
||||
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic = false) override;
|
||||
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride) override;
|
||||
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
static AdapterAndModeList StaticGetAdapterAndModeList(const WindowInfo* wi);
|
||||
|
||||
protected:
|
||||
bool CreateImGuiContext() override;
|
||||
void DestroyImGuiContext() override;
|
||||
bool UpdateImGuiFontTexture() override;
|
||||
|
||||
std::unique_ptr<Vulkan::SwapChain> m_swap_chain;
|
||||
};
|
|
@ -1,531 +0,0 @@
|
|||
// dear imgui: Renderer Backend for DirectX11
|
||||
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices.
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||
// 2021-05-19: DirectX11: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
||||
// 2021-02-18: DirectX11: Change blending equation to preserve alpha in output buffer.
|
||||
// 2019-08-01: DirectX11: Fixed code querying the Geometry Shader state (would generally error with Debug layer enabled).
|
||||
// 2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore.
|
||||
// 2019-05-29: DirectX11: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||
// 2019-04-30: DirectX11: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
|
||||
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
||||
// 2018-08-01: DirectX11: Querying for IDXGIFactory instead of IDXGIFactory1 to increase compatibility.
|
||||
// 2018-07-13: DirectX11: Fixed unreleased resources in Init and Shutdown functions.
|
||||
// 2018-06-08: Misc: Extracted imgui_impl_dx11.cpp/.h away from the old combined DX11+Win32 example.
|
||||
// 2018-06-08: DirectX11: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
|
||||
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself.
|
||||
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||
// 2016-05-07: DirectX11: Disabling depth-write.
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_dx11.h"
|
||||
|
||||
// DirectX
|
||||
#include <stdio.h>
|
||||
#include <d3d11.h>
|
||||
#include <d3dcompiler.h>
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
||||
#endif
|
||||
|
||||
// DirectX11 data
|
||||
struct ImGui_ImplDX11_Data
|
||||
{
|
||||
ID3D11Device* pd3dDevice;
|
||||
ID3D11DeviceContext* pd3dDeviceContext;
|
||||
IDXGIFactory* pFactory;
|
||||
ID3D11Buffer* pVB;
|
||||
ID3D11Buffer* pIB;
|
||||
ID3D11VertexShader* pVertexShader;
|
||||
ID3D11InputLayout* pInputLayout;
|
||||
ID3D11Buffer* pVertexConstantBuffer;
|
||||
ID3D11PixelShader* pPixelShader;
|
||||
ID3D11SamplerState* pFontSampler;
|
||||
ID3D11ShaderResourceView* pFontTextureView;
|
||||
ID3D11RasterizerState* pRasterizerState;
|
||||
ID3D11BlendState* pBlendState;
|
||||
ID3D11DepthStencilState* pDepthStencilState;
|
||||
int VertexBufferSize;
|
||||
int IndexBufferSize;
|
||||
|
||||
ImGui_ImplDX11_Data() { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; }
|
||||
};
|
||||
|
||||
struct VERTEX_CONSTANT_BUFFER
|
||||
{
|
||||
float mvp[4][4];
|
||||
};
|
||||
|
||||
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||
static ImGui_ImplDX11_Data* ImGui_ImplDX11_GetBackendData()
|
||||
{
|
||||
return ImGui::GetCurrentContext() ? (ImGui_ImplDX11_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
|
||||
}
|
||||
|
||||
// Functions
|
||||
static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx)
|
||||
{
|
||||
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||
|
||||
// Setup viewport
|
||||
D3D11_VIEWPORT vp;
|
||||
memset(&vp, 0, sizeof(D3D11_VIEWPORT));
|
||||
vp.Width = draw_data->DisplaySize.x;
|
||||
vp.Height = draw_data->DisplaySize.y;
|
||||
vp.MinDepth = 0.0f;
|
||||
vp.MaxDepth = 1.0f;
|
||||
vp.TopLeftX = vp.TopLeftY = 0;
|
||||
ctx->RSSetViewports(1, &vp);
|
||||
|
||||
// Setup shader and vertex buffers
|
||||
unsigned int stride = sizeof(ImDrawVert);
|
||||
unsigned int offset = 0;
|
||||
ctx->IASetInputLayout(bd->pInputLayout);
|
||||
ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset);
|
||||
ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
|
||||
ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
ctx->VSSetShader(bd->pVertexShader, NULL, 0);
|
||||
ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer);
|
||||
ctx->PSSetShader(bd->pPixelShader, NULL, 0);
|
||||
ctx->PSSetSamplers(0, 1, &bd->pFontSampler);
|
||||
ctx->GSSetShader(NULL, NULL, 0);
|
||||
ctx->HSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used..
|
||||
ctx->DSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used..
|
||||
ctx->CSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used..
|
||||
|
||||
// Setup blend state
|
||||
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
|
||||
ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff);
|
||||
ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0);
|
||||
ctx->RSSetState(bd->pRasterizerState);
|
||||
}
|
||||
|
||||
// Render function
|
||||
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
|
||||
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||
ID3D11DeviceContext* ctx = bd->pd3dDeviceContext;
|
||||
|
||||
// Create and grow vertex/index buffers if needed
|
||||
if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
|
||||
{
|
||||
if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; }
|
||||
bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||
D3D11_BUFFER_DESC desc;
|
||||
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
|
||||
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert);
|
||||
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
desc.MiscFlags = 0;
|
||||
if (bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVB) < 0)
|
||||
return;
|
||||
}
|
||||
if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount)
|
||||
{
|
||||
if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; }
|
||||
bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||
D3D11_BUFFER_DESC desc;
|
||||
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
|
||||
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx);
|
||||
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
if (bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pIB) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Upload vertex/index data into a single contiguous GPU buffer
|
||||
D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource;
|
||||
if (ctx->Map(bd->pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK)
|
||||
return;
|
||||
if (ctx->Map(bd->pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK)
|
||||
return;
|
||||
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData;
|
||||
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtx_dst += cmd_list->VtxBuffer.Size;
|
||||
idx_dst += cmd_list->IdxBuffer.Size;
|
||||
}
|
||||
ctx->Unmap(bd->pVB, 0);
|
||||
ctx->Unmap(bd->pIB, 0);
|
||||
|
||||
// Setup orthographic projection matrix into our constant buffer
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||
{
|
||||
D3D11_MAPPED_SUBRESOURCE mapped_resource;
|
||||
if (ctx->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
|
||||
return;
|
||||
VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData;
|
||||
float L = draw_data->DisplayPos.x;
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||
float T = draw_data->DisplayPos.y;
|
||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
||||
float mvp[4][4] =
|
||||
{
|
||||
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, 0.5f, 0.0f },
|
||||
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
|
||||
};
|
||||
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
|
||||
ctx->Unmap(bd->pVertexConstantBuffer, 0);
|
||||
}
|
||||
|
||||
// Setup desired DX state
|
||||
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
|
||||
|
||||
// Render command lists
|
||||
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||
int global_idx_offset = 0;
|
||||
int global_vtx_offset = 0;
|
||||
ImVec2 clip_off = draw_data->DisplayPos;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != NULL)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
|
||||
else
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
|
||||
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
|
||||
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||
continue;
|
||||
|
||||
// Apply scissor/clipping rectangle
|
||||
const D3D11_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
|
||||
ctx->RSSetScissorRects(1, &r);
|
||||
|
||||
// Bind texture, Draw
|
||||
ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->GetTexID();
|
||||
ctx->PSSetShaderResources(0, 1, &texture_srv);
|
||||
ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
|
||||
}
|
||||
}
|
||||
global_idx_offset += cmd_list->IdxBuffer.Size;
|
||||
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGui_ImplDX11_CreateFontsTexture()
|
||||
{
|
||||
// Build texture atlas
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
if (bd->pFontTextureView)
|
||||
bd->pFontTextureView->Release();
|
||||
|
||||
// Upload texture to graphics system
|
||||
{
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Width = width;
|
||||
desc.Height = height;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.Usage = D3D11_USAGE_DEFAULT;
|
||||
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
desc.CPUAccessFlags = 0;
|
||||
|
||||
ID3D11Texture2D* pTexture = NULL;
|
||||
D3D11_SUBRESOURCE_DATA subResource;
|
||||
subResource.pSysMem = pixels;
|
||||
subResource.SysMemPitch = desc.Width * 4;
|
||||
subResource.SysMemSlicePitch = 0;
|
||||
bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
|
||||
IM_ASSERT(pTexture != NULL);
|
||||
|
||||
// Create texture view
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
||||
ZeroMemory(&srvDesc, sizeof(srvDesc));
|
||||
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Texture2D.MipLevels = desc.MipLevels;
|
||||
srvDesc.Texture2D.MostDetailedMip = 0;
|
||||
bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &bd->pFontTextureView);
|
||||
pTexture->Release();
|
||||
}
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView);
|
||||
|
||||
// Create texture sampler
|
||||
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
||||
if (!bd->pFontSampler)
|
||||
{
|
||||
D3D11_SAMPLER_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
desc.MipLODBias = 0.f;
|
||||
desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
|
||||
desc.MinLOD = 0.f;
|
||||
desc.MaxLOD = 0.f;
|
||||
bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler);
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX11_CreateDeviceObjects()
|
||||
{
|
||||
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||
if (!bd->pd3dDevice)
|
||||
return false;
|
||||
if (bd->pFontSampler)
|
||||
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
|
||||
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
|
||||
// If you would like to use this DX11 sample code but remove this dependency you can:
|
||||
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
|
||||
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
|
||||
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
|
||||
|
||||
// Create the vertex shader
|
||||
{
|
||||
static const char* vertexShader =
|
||||
"cbuffer vertexBuffer : register(b0) \
|
||||
{\
|
||||
float4x4 ProjectionMatrix; \
|
||||
};\
|
||||
struct VS_INPUT\
|
||||
{\
|
||||
float2 pos : POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
\
|
||||
struct PS_INPUT\
|
||||
{\
|
||||
float4 pos : SV_POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
\
|
||||
PS_INPUT main(VS_INPUT input)\
|
||||
{\
|
||||
PS_INPUT output;\
|
||||
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
|
||||
output.col = input.col;\
|
||||
output.uv = input.uv;\
|
||||
return output;\
|
||||
}";
|
||||
|
||||
ID3DBlob* vertexShaderBlob;
|
||||
if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &vertexShaderBlob, NULL)))
|
||||
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
||||
if (bd->pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), NULL, &bd->pVertexShader) != S_OK)
|
||||
{
|
||||
vertexShaderBlob->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the input layout
|
||||
D3D11_INPUT_ELEMENT_DESC local_layout[] =
|
||||
{
|
||||
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||
};
|
||||
if (bd->pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &bd->pInputLayout) != S_OK)
|
||||
{
|
||||
vertexShaderBlob->Release();
|
||||
return false;
|
||||
}
|
||||
vertexShaderBlob->Release();
|
||||
|
||||
// Create the constant buffer
|
||||
{
|
||||
D3D11_BUFFER_DESC desc;
|
||||
desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER);
|
||||
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
desc.MiscFlags = 0;
|
||||
bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVertexConstantBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the pixel shader
|
||||
{
|
||||
static const char* pixelShader =
|
||||
"struct PS_INPUT\
|
||||
{\
|
||||
float4 pos : SV_POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
sampler sampler0;\
|
||||
Texture2D texture0;\
|
||||
\
|
||||
float4 main(PS_INPUT input) : SV_Target\
|
||||
{\
|
||||
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
|
||||
return out_col; \
|
||||
}";
|
||||
|
||||
ID3DBlob* pixelShaderBlob;
|
||||
if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &pixelShaderBlob, NULL)))
|
||||
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
||||
if (bd->pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), NULL, &bd->pPixelShader) != S_OK)
|
||||
{
|
||||
pixelShaderBlob->Release();
|
||||
return false;
|
||||
}
|
||||
pixelShaderBlob->Release();
|
||||
}
|
||||
|
||||
// Create the blending setup
|
||||
{
|
||||
D3D11_BLEND_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.AlphaToCoverageEnable = false;
|
||||
desc.RenderTarget[0].BlendEnable = true;
|
||||
desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
|
||||
desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
|
||||
desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
|
||||
desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
|
||||
desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
|
||||
desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
|
||||
desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
|
||||
bd->pd3dDevice->CreateBlendState(&desc, &bd->pBlendState);
|
||||
}
|
||||
|
||||
// Create the rasterizer state
|
||||
{
|
||||
D3D11_RASTERIZER_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.FillMode = D3D11_FILL_SOLID;
|
||||
desc.CullMode = D3D11_CULL_NONE;
|
||||
desc.ScissorEnable = true;
|
||||
desc.DepthClipEnable = true;
|
||||
bd->pd3dDevice->CreateRasterizerState(&desc, &bd->pRasterizerState);
|
||||
}
|
||||
|
||||
// Create depth-stencil State
|
||||
{
|
||||
D3D11_DEPTH_STENCIL_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.DepthEnable = false;
|
||||
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
|
||||
desc.DepthFunc = D3D11_COMPARISON_ALWAYS;
|
||||
desc.StencilEnable = false;
|
||||
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
|
||||
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
|
||||
desc.BackFace = desc.FrontFace;
|
||||
bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState);
|
||||
}
|
||||
|
||||
ImGui_ImplDX11_CreateFontsTexture();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplDX11_InvalidateDeviceObjects()
|
||||
{
|
||||
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||
if (!bd->pd3dDevice)
|
||||
return;
|
||||
|
||||
if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = NULL; }
|
||||
if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied data->pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||
if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; }
|
||||
if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; }
|
||||
if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = NULL; }
|
||||
if (bd->pDepthStencilState) { bd->pDepthStencilState->Release(); bd->pDepthStencilState = NULL; }
|
||||
if (bd->pRasterizerState) { bd->pRasterizerState->Release(); bd->pRasterizerState = NULL; }
|
||||
if (bd->pPixelShader) { bd->pPixelShader->Release(); bd->pPixelShader = NULL; }
|
||||
if (bd->pVertexConstantBuffer) { bd->pVertexConstantBuffer->Release(); bd->pVertexConstantBuffer = NULL; }
|
||||
if (bd->pInputLayout) { bd->pInputLayout->Release(); bd->pInputLayout = NULL; }
|
||||
if (bd->pVertexShader) { bd->pVertexShader->Release(); bd->pVertexShader = NULL; }
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
|
||||
|
||||
// Setup backend capabilities flags
|
||||
ImGui_ImplDX11_Data* bd = IM_NEW(ImGui_ImplDX11_Data)();
|
||||
io.BackendRendererUserData = (void*)bd;
|
||||
io.BackendRendererName = "imgui_impl_dx11";
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
|
||||
// Get factory from device
|
||||
IDXGIDevice* pDXGIDevice = NULL;
|
||||
IDXGIAdapter* pDXGIAdapter = NULL;
|
||||
IDXGIFactory* pFactory = NULL;
|
||||
|
||||
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
|
||||
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
|
||||
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
|
||||
{
|
||||
bd->pd3dDevice = device;
|
||||
bd->pd3dDeviceContext = device_context;
|
||||
bd->pFactory = pFactory;
|
||||
}
|
||||
if (pDXGIDevice) pDXGIDevice->Release();
|
||||
if (pDXGIAdapter) pDXGIAdapter->Release();
|
||||
bd->pd3dDevice->AddRef();
|
||||
bd->pd3dDeviceContext->AddRef();
|
||||
|
||||
return ImGui_ImplDX11_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
void ImGui_ImplDX11_Shutdown()
|
||||
{
|
||||
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
|
||||
if (bd == NULL)
|
||||
return;
|
||||
|
||||
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
if (bd->pFactory) { bd->pFactory->Release(); }
|
||||
if (bd->pd3dDevice) { bd->pd3dDevice->Release(); }
|
||||
if (bd->pd3dDeviceContext) { bd->pd3dDeviceContext->Release(); }
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendRendererName = NULL;
|
||||
io.BackendRendererUserData = NULL;
|
||||
IM_DELETE(bd);
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
// dear imgui: Renderer Backend for DirectX11
|
||||
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
|
||||
struct ID3D11Device;
|
||||
struct ID3D11DeviceContext;
|
||||
|
||||
bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context);
|
||||
void ImGui_ImplDX11_Shutdown();
|
||||
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// Use if you want to reset your rendering device without losing Dear ImGui state.
|
||||
void ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
bool ImGui_ImplDX11_CreateDeviceObjects();
|
||||
void ImGui_ImplDX11_CreateFontsTexture();
|
|
@ -1,536 +0,0 @@
|
|||
// dear imgui: Renderer Backend for DirectX12
|
||||
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices.
|
||||
|
||||
// Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
|
||||
// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
|
||||
// To build this on 32-bit systems:
|
||||
// - [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in the 'example_win32_direct12/example_win32_direct12.vcxproj' project file)
|
||||
// - [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like.
|
||||
// - [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!)
|
||||
// - [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in the example_win32_direct12/build_win32.bat file)
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||
// 2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
||||
// 2021-02-18: DirectX12: Change blending equation to preserve alpha in output buffer.
|
||||
// 2021-01-11: DirectX12: Improve Windows 7 compatibility (for D3D12On7) by loading d3d12.dll dynamically.
|
||||
// 2020-09-16: DirectX12: Avoid rendering calls with zero-sized scissor rectangle since it generates a validation layer warning.
|
||||
// 2020-09-08: DirectX12: Clarified support for building on 32-bit systems by redefining ImTextureID.
|
||||
// 2019-10-18: DirectX12: *BREAKING CHANGE* Added extra ID3D12DescriptorHeap parameter to ImGui_ImplDX12_Init() function.
|
||||
// 2019-05-29: DirectX12: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||
// 2019-04-30: DirectX12: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||
// 2019-03-29: Misc: Various minor tidying up.
|
||||
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
|
||||
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
||||
// 2018-06-12: DirectX12: Moved the ID3D12GraphicsCommandList* parameter from NewFrame() to RenderDrawData().
|
||||
// 2018-06-08: Misc: Extracted imgui_impl_dx12.cpp/.h away from the old combined DX12+Win32 example.
|
||||
// 2018-06-08: DirectX12: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle (to ease support for future multi-viewport).
|
||||
// 2018-02-22: Merged into master with all Win32 code synchronized to other examples.
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/D3D12/Context.h"
|
||||
#include "common/D3D12/Texture.h"
|
||||
#include "common/D3D12/StreamBuffer.h"
|
||||
#include "common/RedtapeWindows.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_dx12.h"
|
||||
|
||||
// DirectX
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_4.h>
|
||||
#include <d3dcompiler.h>
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
||||
#endif
|
||||
|
||||
// If we're doing more than this... wtf?
|
||||
static constexpr u32 VERTEX_BUFFER_SIZE = 8 * 1024 * 1024;
|
||||
static constexpr u32 INDEX_BUFFER_SIZE = 4 * 1024 * 1024;
|
||||
|
||||
struct ImGui_ImplDX12_Data
|
||||
{
|
||||
D3D12::StreamBuffer VertexStreamBuffer;
|
||||
D3D12::StreamBuffer IndexStreamBuffer;
|
||||
D3D12::Texture FontTexture;
|
||||
ID3D12RootSignature* pRootSignature = nullptr;
|
||||
ID3D12PipelineState* pPipelineState = nullptr;
|
||||
DXGI_FORMAT RTVFormat = DXGI_FORMAT_UNKNOWN;
|
||||
};
|
||||
|
||||
struct VERTEX_CONSTANT_BUFFER
|
||||
{
|
||||
float mvp[4][4];
|
||||
};
|
||||
|
||||
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||
static ImGui_ImplDX12_Data* ImGui_ImplDX12_GetBackendData()
|
||||
{
|
||||
return ImGui::GetCurrentContext() ? (ImGui_ImplDX12_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
|
||||
}
|
||||
|
||||
// Functions
|
||||
static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx)
|
||||
{
|
||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||
|
||||
// Setup orthographic projection matrix into our constant buffer
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right).
|
||||
VERTEX_CONSTANT_BUFFER vertex_constant_buffer;
|
||||
{
|
||||
float L = draw_data->DisplayPos.x;
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||
float T = draw_data->DisplayPos.y;
|
||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
||||
float mvp[4][4] =
|
||||
{
|
||||
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, 0.5f, 0.0f },
|
||||
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
|
||||
};
|
||||
memcpy(&vertex_constant_buffer.mvp, mvp, sizeof(mvp));
|
||||
}
|
||||
|
||||
// Setup viewport
|
||||
D3D12_VIEWPORT vp;
|
||||
memset(&vp, 0, sizeof(D3D12_VIEWPORT));
|
||||
vp.Width = draw_data->DisplaySize.x;
|
||||
vp.Height = draw_data->DisplaySize.y;
|
||||
vp.MinDepth = 0.0f;
|
||||
vp.MaxDepth = 1.0f;
|
||||
vp.TopLeftX = vp.TopLeftY = 0.0f;
|
||||
ctx->RSSetViewports(1, &vp);
|
||||
|
||||
// Bind shader and vertex buffers
|
||||
unsigned int stride = sizeof(ImDrawVert);
|
||||
D3D12_VERTEX_BUFFER_VIEW vbv;
|
||||
memset(&vbv, 0, sizeof(D3D12_VERTEX_BUFFER_VIEW));
|
||||
vbv.BufferLocation = bd->VertexStreamBuffer.GetCurrentGPUPointer();
|
||||
vbv.SizeInBytes = bd->VertexStreamBuffer.GetCurrentSpace();
|
||||
vbv.StrideInBytes = stride;
|
||||
ctx->IASetVertexBuffers(0, 1, &vbv);
|
||||
D3D12_INDEX_BUFFER_VIEW ibv;
|
||||
memset(&ibv, 0, sizeof(D3D12_INDEX_BUFFER_VIEW));
|
||||
ibv.BufferLocation = bd->IndexStreamBuffer.GetCurrentGPUPointer();
|
||||
ibv.SizeInBytes = bd->IndexStreamBuffer.GetCurrentSpace();
|
||||
ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
|
||||
ctx->IASetIndexBuffer(&ibv);
|
||||
ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
ctx->SetPipelineState(bd->pPipelineState);
|
||||
ctx->SetGraphicsRootSignature(bd->pRootSignature);
|
||||
ctx->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0);
|
||||
|
||||
// Setup blend factor
|
||||
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
|
||||
ctx->OMSetBlendFactor(blend_factor);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline void SafeRelease(T*& res)
|
||||
{
|
||||
if (res)
|
||||
res->Release();
|
||||
res = NULL;
|
||||
}
|
||||
|
||||
// Render function
|
||||
void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
|
||||
// FIXME: I'm assuming that this only gets called once per frame!
|
||||
// If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator.
|
||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||
|
||||
const u32 needed_vb = draw_data->TotalVtxCount * sizeof(ImDrawVert);
|
||||
const u32 needed_ib = draw_data->TotalIdxCount * sizeof(ImDrawIdx);
|
||||
|
||||
if (!bd->VertexStreamBuffer.ReserveMemory(needed_vb, sizeof(ImDrawVert)) ||
|
||||
!bd->IndexStreamBuffer.ReserveMemory(needed_ib, sizeof(ImDrawIdx)))
|
||||
{
|
||||
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::None);
|
||||
if (!bd->VertexStreamBuffer.ReserveMemory(needed_vb, sizeof(ImDrawVert)) ||
|
||||
!bd->IndexStreamBuffer.ReserveMemory(needed_ib, sizeof(ImDrawIdx)))
|
||||
{
|
||||
pxFailRel("Failed to allocate space for imgui vertices/indices");
|
||||
}
|
||||
}
|
||||
|
||||
// Upload vertex/index data into a single contiguous GPU buffer
|
||||
ImDrawVert* vtx_dst = (ImDrawVert*)bd->VertexStreamBuffer.GetCurrentHostPointer();
|
||||
ImDrawIdx* idx_dst = (ImDrawIdx*)bd->IndexStreamBuffer.GetCurrentHostPointer();
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtx_dst += cmd_list->VtxBuffer.Size;
|
||||
idx_dst += cmd_list->IdxBuffer.Size;
|
||||
}
|
||||
|
||||
// Setup desired DX state (must happen before commit, because it uses the offsets)
|
||||
ID3D12GraphicsCommandList* ctx = g_d3d12_context->GetCommandList();
|
||||
ImGui_ImplDX12_SetupRenderState(draw_data, ctx);
|
||||
bd->VertexStreamBuffer.CommitMemory(needed_vb);
|
||||
bd->IndexStreamBuffer.CommitMemory(needed_ib);
|
||||
|
||||
// Render command lists
|
||||
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||
int global_vtx_offset = 0;
|
||||
int global_idx_offset = 0;
|
||||
ImVec2 clip_off = draw_data->DisplayPos;
|
||||
const D3D12::Texture* last_texture = nullptr;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != NULL)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplDX12_SetupRenderState(draw_data, ctx);
|
||||
else
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
|
||||
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
|
||||
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||
continue;
|
||||
|
||||
// Apply Scissor/clipping rectangle, Bind texture, Draw
|
||||
const D3D12_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
|
||||
|
||||
const D3D12::Texture* tex = (D3D12::Texture*)pcmd->GetTexID();
|
||||
if (tex && last_texture != tex)
|
||||
{
|
||||
D3D12::DescriptorHandle handle;
|
||||
if (!g_d3d12_context->GetDescriptorAllocator().Allocate(1, &handle))
|
||||
{
|
||||
// ugh.
|
||||
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::None);
|
||||
ctx = g_d3d12_context->GetCommandList();
|
||||
ImGui_ImplDX12_SetupRenderState(draw_data, ctx);
|
||||
if (!g_d3d12_context->GetDescriptorAllocator().Allocate(1, &handle))
|
||||
pxFailRel("Failed to allocate descriptor after cmdlist kick");
|
||||
}
|
||||
|
||||
g_d3d12_context->GetDevice()->CopyDescriptorsSimple(1, handle, tex->GetSRVDescriptor(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
|
||||
ctx->SetGraphicsRootDescriptorTable(1, handle);
|
||||
last_texture = tex;
|
||||
}
|
||||
|
||||
ctx->RSSetScissorRects(1, &r);
|
||||
ctx->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
|
||||
}
|
||||
}
|
||||
global_idx_offset += cmd_list->IdxBuffer.Size;
|
||||
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX12_CreateFontsTexture()
|
||||
{
|
||||
// Build texture atlas
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
// Upload texture to graphics system
|
||||
if (bd->FontTexture.GetWidth() != width || bd->FontTexture.GetHeight() != height)
|
||||
{
|
||||
if (!bd->FontTexture.Create(width, height, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN,
|
||||
D3D12_RESOURCE_FLAG_NONE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bd->FontTexture.LoadData(g_d3d12_context->GetInitCommandList(), 0, 0, 0, width, height, pixels, width * sizeof(u32)))
|
||||
return false;
|
||||
|
||||
io.Fonts->SetTexID((ImTextureID)&bd->FontTexture);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX12_CreateDeviceObjects()
|
||||
{
|
||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||
if (bd->pPipelineState)
|
||||
ImGui_ImplDX12_DestroyDeviceObjects();
|
||||
|
||||
// Create the root signature
|
||||
{
|
||||
D3D12_DESCRIPTOR_RANGE descRange = {};
|
||||
descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
||||
descRange.NumDescriptors = 1;
|
||||
descRange.BaseShaderRegister = 0;
|
||||
descRange.RegisterSpace = 0;
|
||||
descRange.OffsetInDescriptorsFromTableStart = 0;
|
||||
|
||||
D3D12_ROOT_PARAMETER param[2] = {};
|
||||
|
||||
param[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
|
||||
param[0].Constants.ShaderRegister = 0;
|
||||
param[0].Constants.RegisterSpace = 0;
|
||||
param[0].Constants.Num32BitValues = 16;
|
||||
param[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
|
||||
|
||||
param[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
||||
param[1].DescriptorTable.NumDescriptorRanges = 1;
|
||||
param[1].DescriptorTable.pDescriptorRanges = &descRange;
|
||||
param[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
||||
|
||||
// Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling.
|
||||
D3D12_STATIC_SAMPLER_DESC staticSampler = {};
|
||||
staticSampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
staticSampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSampler.MipLODBias = 0.f;
|
||||
staticSampler.MaxAnisotropy = 0;
|
||||
staticSampler.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
|
||||
staticSampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
|
||||
staticSampler.MinLOD = 0.f;
|
||||
staticSampler.MaxLOD = 0.f;
|
||||
staticSampler.ShaderRegister = 0;
|
||||
staticSampler.RegisterSpace = 0;
|
||||
staticSampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
||||
|
||||
D3D12_ROOT_SIGNATURE_DESC desc = {};
|
||||
desc.NumParameters = _countof(param);
|
||||
desc.pParameters = param;
|
||||
desc.NumStaticSamplers = 1;
|
||||
desc.pStaticSamplers = &staticSampler;
|
||||
desc.Flags =
|
||||
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
|
||||
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
|
||||
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
|
||||
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
|
||||
|
||||
auto blob = g_d3d12_context->SerializeRootSignature(&desc);
|
||||
if (!blob)
|
||||
return false;
|
||||
|
||||
g_d3d12_context->GetDevice()->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignature));
|
||||
}
|
||||
|
||||
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
|
||||
// If you would like to use this DX12 sample code but remove this dependency you can:
|
||||
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
|
||||
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
|
||||
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
|
||||
|
||||
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
|
||||
memset(&psoDesc, 0, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
|
||||
psoDesc.NodeMask = 1;
|
||||
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
|
||||
psoDesc.pRootSignature = bd->pRootSignature;
|
||||
psoDesc.SampleMask = UINT_MAX;
|
||||
psoDesc.NumRenderTargets = 1;
|
||||
psoDesc.RTVFormats[0] = bd->RTVFormat;
|
||||
psoDesc.SampleDesc.Count = 1;
|
||||
psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
|
||||
|
||||
ID3DBlob* vertexShaderBlob;
|
||||
ID3DBlob* pixelShaderBlob;
|
||||
|
||||
// Create the vertex shader
|
||||
{
|
||||
static const char* vertexShader =
|
||||
"cbuffer vertexBuffer : register(b0) \
|
||||
{\
|
||||
float4x4 ProjectionMatrix; \
|
||||
};\
|
||||
struct VS_INPUT\
|
||||
{\
|
||||
float2 pos : POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
\
|
||||
struct PS_INPUT\
|
||||
{\
|
||||
float4 pos : SV_POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
\
|
||||
PS_INPUT main(VS_INPUT input)\
|
||||
{\
|
||||
PS_INPUT output;\
|
||||
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
|
||||
output.col = input.col;\
|
||||
output.uv = input.uv;\
|
||||
return output;\
|
||||
}";
|
||||
|
||||
if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_5_0", 0, 0, &vertexShaderBlob, NULL)))
|
||||
return false; // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
||||
psoDesc.VS = { vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize() };
|
||||
|
||||
// Create the input layout
|
||||
static D3D12_INPUT_ELEMENT_DESC local_layout[] =
|
||||
{
|
||||
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
||||
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
||||
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
||||
};
|
||||
psoDesc.InputLayout = { local_layout, 3 };
|
||||
}
|
||||
|
||||
// Create the pixel shader
|
||||
{
|
||||
static const char* pixelShader =
|
||||
"struct PS_INPUT\
|
||||
{\
|
||||
float4 pos : SV_POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
SamplerState sampler0 : register(s0);\
|
||||
Texture2D texture0 : register(t0);\
|
||||
\
|
||||
float4 main(PS_INPUT input) : SV_Target\
|
||||
{\
|
||||
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
|
||||
return out_col; \
|
||||
}";
|
||||
|
||||
if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_5_0", 0, 0, &pixelShaderBlob, NULL)))
|
||||
{
|
||||
vertexShaderBlob->Release();
|
||||
return false; // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
||||
}
|
||||
psoDesc.PS = { pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize() };
|
||||
}
|
||||
|
||||
// Create the blending setup
|
||||
{
|
||||
D3D12_BLEND_DESC& desc = psoDesc.BlendState;
|
||||
desc.AlphaToCoverageEnable = false;
|
||||
desc.RenderTarget[0].BlendEnable = true;
|
||||
desc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA;
|
||||
desc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
|
||||
desc.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD;
|
||||
desc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE;
|
||||
desc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA;
|
||||
desc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD;
|
||||
desc.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
|
||||
}
|
||||
|
||||
// Create the rasterizer state
|
||||
{
|
||||
D3D12_RASTERIZER_DESC& desc = psoDesc.RasterizerState;
|
||||
desc.FillMode = D3D12_FILL_MODE_SOLID;
|
||||
desc.CullMode = D3D12_CULL_MODE_NONE;
|
||||
desc.FrontCounterClockwise = FALSE;
|
||||
desc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
|
||||
desc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
|
||||
desc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
|
||||
desc.DepthClipEnable = true;
|
||||
desc.MultisampleEnable = FALSE;
|
||||
desc.AntialiasedLineEnable = FALSE;
|
||||
desc.ForcedSampleCount = 0;
|
||||
desc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
|
||||
}
|
||||
|
||||
// Create depth-stencil State
|
||||
{
|
||||
D3D12_DEPTH_STENCIL_DESC& desc = psoDesc.DepthStencilState;
|
||||
desc.DepthEnable = false;
|
||||
desc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
|
||||
desc.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
|
||||
desc.StencilEnable = false;
|
||||
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
|
||||
desc.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
|
||||
desc.BackFace = desc.FrontFace;
|
||||
}
|
||||
|
||||
HRESULT result_pipeline_state = g_d3d12_context->GetDevice()->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&bd->pPipelineState));
|
||||
vertexShaderBlob->Release();
|
||||
pixelShaderBlob->Release();
|
||||
if (result_pipeline_state != S_OK)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplDX12_DestroyDeviceObjects()
|
||||
{
|
||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||
if (!bd)
|
||||
return;
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
SafeRelease(bd->pRootSignature);
|
||||
SafeRelease(bd->pPipelineState);
|
||||
bd->FontTexture.Destroy(false);
|
||||
bd->VertexStreamBuffer.Destroy(false);
|
||||
bd->IndexStreamBuffer.Destroy(false);
|
||||
io.Fonts->SetTexID(NULL); // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX12_Init(DXGI_FORMAT rtv_format)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
|
||||
|
||||
// Setup backend capabilities flags
|
||||
ImGui_ImplDX12_Data* bd = IM_NEW(ImGui_ImplDX12_Data)();
|
||||
io.BackendRendererUserData = (void*)bd;
|
||||
io.BackendRendererName = "imgui_impl_dx12";
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
|
||||
bd->RTVFormat = rtv_format;
|
||||
|
||||
if (!bd->VertexStreamBuffer.Create(VERTEX_BUFFER_SIZE) || !bd->IndexStreamBuffer.Create(INDEX_BUFFER_SIZE))
|
||||
return false;
|
||||
|
||||
return ImGui_ImplDX12_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
void ImGui_ImplDX12_Shutdown()
|
||||
{
|
||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
ImGui_ImplDX12_DestroyDeviceObjects();
|
||||
io.BackendRendererName = NULL;
|
||||
io.BackendRendererUserData = NULL;
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
void ImGui_ImplDX12_NewFrame()
|
||||
{
|
||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplDX12_Init()?");
|
||||
|
||||
if (!bd->pPipelineState)
|
||||
ImGui_ImplDX12_CreateDeviceObjects();
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// dear imgui: Renderer Backend for DirectX12
|
||||
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
|
||||
bool ImGui_ImplDX12_Init(DXGI_FORMAT rtv_format);
|
||||
void ImGui_ImplDX12_Shutdown();
|
||||
void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// Use if you want to reset your rendering device without losing Dear ImGui state.
|
||||
void ImGui_ImplDX12_DestroyDeviceObjects();
|
||||
bool ImGui_ImplDX12_CreateDeviceObjects();
|
||||
bool ImGui_ImplDX12_CreateFontsTexture();
|
|
@ -1,577 +0,0 @@
|
|||
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
|
||||
// - Desktop GL: 2.x 3.x 4.x
|
||||
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
|
||||
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2022-05-13: OpenGL: Fix state corruption on OpenGL ES 2.0 due to not preserving GL_ELEMENT_ARRAY_BUFFER_BINDING and vertex attribute states.
|
||||
// 2021-12-15: OpenGL: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers.
|
||||
// 2021-08-23: OpenGL: Fixed ES 3.0 shader ("#version 300 es") use normal precision floats to avoid wobbly rendering at HD resolutions.
|
||||
// 2021-08-19: OpenGL: Embed and use our own minimal GL loader (imgui_impl_opengl3_loader.h), removing requirement and support for third-party loader.
|
||||
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||
// 2021-06-25: OpenGL: Use OES_vertex_array extension on Emscripten + backup/restore current state.
|
||||
// 2021-06-21: OpenGL: Destroy individual vertex/fragment shader objects right after they are linked into the main shader.
|
||||
// 2021-05-24: OpenGL: Access GL_CLIP_ORIGIN when "GL_ARB_clip_control" extension is detected, inside of just OpenGL 4.5 version.
|
||||
// 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
||||
// 2021-04-06: OpenGL: Don't try to read GL_CLIP_ORIGIN unless we're OpenGL 4.5 or greater.
|
||||
// 2021-02-18: OpenGL: Change blending equation to preserve alpha in output buffer.
|
||||
// 2021-01-03: OpenGL: Backup, setup and restore GL_STENCIL_TEST state.
|
||||
// 2020-10-23: OpenGL: Backup, setup and restore GL_PRIMITIVE_RESTART state.
|
||||
// 2020-10-15: OpenGL: Use glGetString(GL_VERSION) instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x)
|
||||
// 2020-09-17: OpenGL: Fix to avoid compiling/calling glBindSampler() on ES or pre 3.3 context which have the defines set by a loader.
|
||||
// 2020-07-10: OpenGL: Added support for glad2 OpenGL loader.
|
||||
// 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX.
|
||||
// 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix.
|
||||
// 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset.
|
||||
// 2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader.
|
||||
// 2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader.
|
||||
// 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders.
|
||||
// 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility.
|
||||
// 2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call.
|
||||
// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||
// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||
// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop.
|
||||
// 2019-03-15: OpenGL: Added a GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early.
|
||||
// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0).
|
||||
// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader.
|
||||
// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
|
||||
// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450).
|
||||
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
||||
// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN.
|
||||
// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
|
||||
// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
|
||||
// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
|
||||
// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.
|
||||
// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.
|
||||
// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
|
||||
// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.
|
||||
// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer.
|
||||
// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150".
|
||||
// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.
|
||||
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.
|
||||
// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.
|
||||
// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.
|
||||
// 2017-05-01: OpenGL: Fixed save and restore of current blend func state.
|
||||
// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.
|
||||
// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
|
||||
// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)
|
||||
|
||||
//----------------------------------------
|
||||
// OpenGL GLSL GLSL
|
||||
// version version string
|
||||
//----------------------------------------
|
||||
// 2.0 110 "#version 110"
|
||||
// 2.1 120 "#version 120"
|
||||
// 3.0 130 "#version 130"
|
||||
// 3.1 140 "#version 140"
|
||||
// 3.2 150 "#version 150"
|
||||
// 3.3 330 "#version 330 core"
|
||||
// 4.0 400 "#version 400 core"
|
||||
// 4.1 410 "#version 410 core"
|
||||
// 4.2 420 "#version 410 core"
|
||||
// 4.3 430 "#version 430 core"
|
||||
// ES 2.0 100 "#version 100" = WebGL 1.0
|
||||
// ES 3.0 300 "#version 300 es" = WebGL 2.0
|
||||
//----------------------------------------
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include <stdio.h>
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
|
||||
#include <stddef.h> // intptr_t
|
||||
#else
|
||||
#include <stdint.h> // intptr_t
|
||||
#endif
|
||||
|
||||
// Clang warnings with -Weverything
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||
#if __has_warning("-Wzero-as-null-pointer-constant")
|
||||
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// GL includes
|
||||
#include <glad.h>
|
||||
|
||||
// OpenGL Data
|
||||
struct ImGui_ImplOpenGL3_Data
|
||||
{
|
||||
GLuint GlVersion; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
|
||||
char GlslVersionString[32]; // Specified by user or detected based on compile time GL settings.
|
||||
GLuint FontTexture;
|
||||
GLuint ShaderHandle;
|
||||
GLint AttribLocationTex; // Uniforms location
|
||||
GLint AttribLocationProjMtx;
|
||||
GLuint AttribLocationVtxPos; // Vertex attributes location
|
||||
GLuint AttribLocationVtxUV;
|
||||
GLuint AttribLocationVtxColor;
|
||||
unsigned int VboHandle, ElementsHandle, VaoHandle, SamplerHandle;
|
||||
GLsizeiptr VertexBufferSize;
|
||||
GLsizeiptr IndexBufferSize;
|
||||
|
||||
ImGui_ImplOpenGL3_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||
static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData()
|
||||
{
|
||||
return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
|
||||
}
|
||||
|
||||
// Functions
|
||||
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
|
||||
|
||||
// Setup backend capabilities flags
|
||||
ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)();
|
||||
io.BackendRendererUserData = (void*)bd;
|
||||
io.BackendRendererName = "imgui_impl_opengl3";
|
||||
|
||||
// Query for GL version (e.g. 320 for GL 3.2)
|
||||
GLint major = 0;
|
||||
GLint minor = 0;
|
||||
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
||||
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
||||
if (major == 0 && minor == 0)
|
||||
{
|
||||
// Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
|
||||
const char* gl_version = (const char*)glGetString(GL_VERSION);
|
||||
sscanf(gl_version, "%d.%d", &major, &minor);
|
||||
}
|
||||
bd->GlVersion = (GLuint)(major * 100 + minor * 10);
|
||||
|
||||
if (bd->GlVersion >= 320)
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
|
||||
// Store GLSL version string so we can refer to it later in case we recreate shaders.
|
||||
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
|
||||
if (glsl_version == NULL)
|
||||
glsl_version = "#version 130";
|
||||
|
||||
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(bd->GlslVersionString));
|
||||
strcpy(bd->GlslVersionString, glsl_version);
|
||||
strcat(bd->GlslVersionString, "\n");
|
||||
return ImGui_ImplOpenGL3_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_Shutdown()
|
||||
{
|
||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
||||
io.BackendRendererName = NULL;
|
||||
io.BackendRendererUserData = NULL;
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height)
|
||||
{
|
||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||
|
||||
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
if (glPolygonMode)
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
|
||||
// Setup viewport, orthographic projection matrix
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
|
||||
float L = draw_data->DisplayPos.x;
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||
float T = draw_data->DisplayPos.y;
|
||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
||||
const float ortho_projection[4][4] =
|
||||
{
|
||||
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, -1.0f, 0.0f },
|
||||
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
|
||||
};
|
||||
glUseProgram(bd->ShaderHandle);
|
||||
glUniform1i(bd->AttribLocationTex, 0);
|
||||
glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
|
||||
glBindSampler(0, bd->SamplerHandle);
|
||||
|
||||
// Bind vertex/index buffers and setup attributes for ImDrawVert
|
||||
glBindVertexArray(bd->VaoHandle);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, bd->VboHandle);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd->ElementsHandle);
|
||||
glEnableVertexAttribArray(bd->AttribLocationVtxPos);
|
||||
glEnableVertexAttribArray(bd->AttribLocationVtxUV);
|
||||
glEnableVertexAttribArray(bd->AttribLocationVtxColor);
|
||||
glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
|
||||
glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
|
||||
glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
|
||||
}
|
||||
|
||||
// OpenGL3 Render function.
|
||||
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly.
|
||||
// This is in order to be able to run within an OpenGL engine that doesn't do so.
|
||||
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
||||
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
|
||||
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
|
||||
if (fb_width <= 0 || fb_height <= 0)
|
||||
return;
|
||||
|
||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||
|
||||
// Setup desired GL state
|
||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height);
|
||||
|
||||
// Will project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
|
||||
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
|
||||
|
||||
// Render command lists
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
|
||||
// Upload vertex/index buffers
|
||||
GLsizeiptr vtx_buffer_size = (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert);
|
||||
GLsizeiptr idx_buffer_size = (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx);
|
||||
if (bd->VertexBufferSize < vtx_buffer_size)
|
||||
{
|
||||
bd->VertexBufferSize = vtx_buffer_size;
|
||||
glBufferData(GL_ARRAY_BUFFER, bd->VertexBufferSize, NULL, GL_STREAM_DRAW);
|
||||
}
|
||||
if (bd->IndexBufferSize < idx_buffer_size)
|
||||
{
|
||||
bd->IndexBufferSize = idx_buffer_size;
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, NULL, GL_STREAM_DRAW);
|
||||
}
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data);
|
||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data);
|
||||
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != NULL)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height);
|
||||
else
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
|
||||
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
|
||||
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||
continue;
|
||||
|
||||
// Apply scissor/clipping rectangle (Y is inverted in OpenGL)
|
||||
glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y));
|
||||
|
||||
// Bind texture, Draw
|
||||
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID());
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
bool ImGui_ImplOpenGL3_CreateFontsTexture()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||
|
||||
// Build texture atlas
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
|
||||
|
||||
// Upload texture to graphics system
|
||||
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
||||
if (bd->FontTexture == 0)
|
||||
glGenTextures(1, &bd->FontTexture);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, bd->FontTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_DestroyFontsTexture()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||
if (bd->FontTexture)
|
||||
{
|
||||
glDeleteTextures(1, &bd->FontTexture);
|
||||
io.Fonts->SetTexID(0);
|
||||
bd->FontTexture = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
|
||||
static bool CheckShader(GLuint handle, const char* desc)
|
||||
{
|
||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||
GLint status = 0, log_length = 0;
|
||||
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
|
||||
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
|
||||
if ((GLboolean)status == GL_FALSE)
|
||||
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s! With GLSL: %s\n", desc, bd->GlslVersionString);
|
||||
if (log_length > 1)
|
||||
{
|
||||
ImVector<char> buf;
|
||||
buf.resize((int)(log_length + 1));
|
||||
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
|
||||
fprintf(stderr, "%s\n", buf.begin());
|
||||
}
|
||||
return (GLboolean)status == GL_TRUE;
|
||||
}
|
||||
|
||||
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
|
||||
static bool CheckProgram(GLuint handle, const char* desc)
|
||||
{
|
||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||
GLint status = 0, log_length = 0;
|
||||
glGetProgramiv(handle, GL_LINK_STATUS, &status);
|
||||
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
|
||||
if ((GLboolean)status == GL_FALSE)
|
||||
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! With GLSL %s\n", desc, bd->GlslVersionString);
|
||||
if (log_length > 1)
|
||||
{
|
||||
ImVector<char> buf;
|
||||
buf.resize((int)(log_length + 1));
|
||||
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
|
||||
fprintf(stderr, "%s\n", buf.begin());
|
||||
}
|
||||
return (GLboolean)status == GL_TRUE;
|
||||
}
|
||||
|
||||
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
|
||||
{
|
||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||
|
||||
// Parse GLSL version string
|
||||
int glsl_version = 130;
|
||||
sscanf(bd->GlslVersionString, "#version %d", &glsl_version);
|
||||
|
||||
const GLchar* vertex_shader_glsl_120 =
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"attribute vec2 Position;\n"
|
||||
"attribute vec2 UV;\n"
|
||||
"attribute vec4 Color;\n"
|
||||
"varying vec2 Frag_UV;\n"
|
||||
"varying vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* vertex_shader_glsl_130 =
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"in vec2 Position;\n"
|
||||
"in vec2 UV;\n"
|
||||
"in vec4 Color;\n"
|
||||
"out vec2 Frag_UV;\n"
|
||||
"out vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* vertex_shader_glsl_300_es =
|
||||
"precision highp float;\n"
|
||||
"layout (location = 0) in vec2 Position;\n"
|
||||
"layout (location = 1) in vec2 UV;\n"
|
||||
"layout (location = 2) in vec4 Color;\n"
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"out vec2 Frag_UV;\n"
|
||||
"out vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* vertex_shader_glsl_410_core =
|
||||
"layout (location = 0) in vec2 Position;\n"
|
||||
"layout (location = 1) in vec2 UV;\n"
|
||||
"layout (location = 2) in vec4 Color;\n"
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"out vec2 Frag_UV;\n"
|
||||
"out vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader_glsl_120 =
|
||||
"#ifdef GL_ES\n"
|
||||
" precision mediump float;\n"
|
||||
"#endif\n"
|
||||
"uniform sampler2D Texture;\n"
|
||||
"varying vec2 Frag_UV;\n"
|
||||
"varying vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader_glsl_130 =
|
||||
"uniform sampler2D Texture;\n"
|
||||
"in vec2 Frag_UV;\n"
|
||||
"in vec4 Frag_Color;\n"
|
||||
"out vec4 Out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader_glsl_300_es =
|
||||
"precision mediump float;\n"
|
||||
"uniform sampler2D Texture;\n"
|
||||
"in vec2 Frag_UV;\n"
|
||||
"in vec4 Frag_Color;\n"
|
||||
"layout (location = 0) out vec4 Out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader_glsl_410_core =
|
||||
"in vec2 Frag_UV;\n"
|
||||
"in vec4 Frag_Color;\n"
|
||||
"uniform sampler2D Texture;\n"
|
||||
"layout (location = 0) out vec4 Out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
// Select shaders matching our GLSL versions
|
||||
const GLchar* vertex_shader = NULL;
|
||||
const GLchar* fragment_shader = NULL;
|
||||
if (glsl_version < 130)
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_120;
|
||||
fragment_shader = fragment_shader_glsl_120;
|
||||
}
|
||||
else if (glsl_version >= 410)
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_410_core;
|
||||
fragment_shader = fragment_shader_glsl_410_core;
|
||||
}
|
||||
else if (glsl_version == 300)
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_300_es;
|
||||
fragment_shader = fragment_shader_glsl_300_es;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_130;
|
||||
fragment_shader = fragment_shader_glsl_130;
|
||||
}
|
||||
|
||||
// Create shaders
|
||||
const GLchar* vertex_shader_with_version[2] = { bd->GlslVersionString, vertex_shader };
|
||||
GLuint vert_handle = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vert_handle, 2, vertex_shader_with_version, NULL);
|
||||
glCompileShader(vert_handle);
|
||||
CheckShader(vert_handle, "vertex shader");
|
||||
|
||||
const GLchar* fragment_shader_with_version[2] = { bd->GlslVersionString, fragment_shader };
|
||||
GLuint frag_handle = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(frag_handle, 2, fragment_shader_with_version, NULL);
|
||||
glCompileShader(frag_handle);
|
||||
CheckShader(frag_handle, "fragment shader");
|
||||
|
||||
// Link
|
||||
bd->ShaderHandle = glCreateProgram();
|
||||
glAttachShader(bd->ShaderHandle, vert_handle);
|
||||
glAttachShader(bd->ShaderHandle, frag_handle);
|
||||
glLinkProgram(bd->ShaderHandle);
|
||||
CheckProgram(bd->ShaderHandle, "shader program");
|
||||
|
||||
glDetachShader(bd->ShaderHandle, vert_handle);
|
||||
glDetachShader(bd->ShaderHandle, frag_handle);
|
||||
glDeleteShader(vert_handle);
|
||||
glDeleteShader(frag_handle);
|
||||
|
||||
bd->AttribLocationTex = glGetUniformLocation(bd->ShaderHandle, "Texture");
|
||||
bd->AttribLocationProjMtx = glGetUniformLocation(bd->ShaderHandle, "ProjMtx");
|
||||
bd->AttribLocationVtxPos = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Position");
|
||||
bd->AttribLocationVtxUV = (GLuint)glGetAttribLocation(bd->ShaderHandle, "UV");
|
||||
bd->AttribLocationVtxColor = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Color");
|
||||
|
||||
// Create buffers
|
||||
glGenBuffers(1, &bd->VboHandle);
|
||||
glGenBuffers(1, &bd->ElementsHandle);
|
||||
glGenVertexArrays(1, &bd->VaoHandle);
|
||||
|
||||
// Create sampler
|
||||
if (bd->SamplerHandle == 0)
|
||||
{
|
||||
glGenSamplers(1, &bd->SamplerHandle);
|
||||
glSamplerParameteri(bd->SamplerHandle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(bd->SamplerHandle, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(bd->SamplerHandle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(bd->SamplerHandle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
|
||||
{
|
||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||
if (bd->VaoHandle) { glDeleteVertexArrays(1, &bd->VaoHandle); bd->VaoHandle = 0; }
|
||||
if (bd->VboHandle) { glDeleteBuffers(1, &bd->VboHandle); bd->VboHandle = 0; }
|
||||
if (bd->ElementsHandle) { glDeleteBuffers(1, &bd->ElementsHandle); bd->ElementsHandle = 0; }
|
||||
if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; }
|
||||
if (bd->SamplerHandle) { glDeleteSamplers(1, &bd->SamplerHandle); bd->SamplerHandle = 0; }
|
||||
ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
|
@ -1,16 +0,0 @@
|
|||
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
|
||||
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
|
||||
// Backend API
|
||||
bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
|
||||
void ImGui_ImplOpenGL3_Shutdown();
|
||||
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// (Optional) Called by Init/NewFrame/Shutdown
|
||||
bool ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||
void ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||
bool ImGui_ImplOpenGL3_CreateDeviceObjects();
|
||||
void ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
|
@ -1,706 +0,0 @@
|
|||
// dear imgui: Renderer Backend for Vulkan
|
||||
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices.
|
||||
// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions.
|
||||
|
||||
// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'.
|
||||
// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
|
||||
// To build this on 32-bit systems and support texture changes:
|
||||
// - [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in our .vcxproj files)
|
||||
// - [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like.
|
||||
// - [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!)
|
||||
// - [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in our batch files)
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
|
||||
// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification.
|
||||
// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
|
||||
|
||||
// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app.
|
||||
// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h.
|
||||
// You will use those if you want to use this rendering backend in your engine/app.
|
||||
// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by
|
||||
// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code.
|
||||
// Read comments in imgui_impl_vulkan.h.
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2021-10-15: Vulkan: Call vkCmdSetScissor() at the end of render a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame.
|
||||
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||
// 2021-03-22: Vulkan: Fix mapped memory validation error when buffer sizes are not multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize.
|
||||
// 2021-02-18: Vulkan: Change blending equation to preserve alpha in output buffer.
|
||||
// 2021-01-27: Vulkan: Added support for custom function load and IMGUI_IMPL_VULKAN_NO_PROTOTYPES by using ImGui_ImplVulkan_LoadFunctions().
|
||||
// 2020-11-11: Vulkan: Added support for specifying which subpass to reference during VkPipeline creation.
|
||||
// 2020-09-07: Vulkan: Added VkPipeline parameter to ImGui_ImplVulkan_RenderDrawData (default to one passed to ImGui_ImplVulkan_Init).
|
||||
// 2020-05-04: Vulkan: Fixed crash if initial frame has no vertices.
|
||||
// 2020-04-26: Vulkan: Fixed edge case where render callbacks wouldn't be called if the ImDrawData didn't have vertices.
|
||||
// 2019-08-01: Vulkan: Added support for specifying multisample count. Set ImGui_ImplVulkan_InitInfo::MSAASamples to one of the VkSampleCountFlagBits values to use, default is non-multisampled as before.
|
||||
// 2019-05-29: Vulkan: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||
// 2019-04-30: Vulkan: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||
// 2019-04-04: *BREAKING CHANGE*: Vulkan: Added ImageCount/MinImageCount fields in ImGui_ImplVulkan_InitInfo, required for initialization (was previously a hard #define IMGUI_VK_QUEUED_FRAMES 2). Added ImGui_ImplVulkan_SetMinImageCount().
|
||||
// 2019-04-04: Vulkan: Added VkInstance argument to ImGui_ImplVulkanH_CreateWindow() optional helper.
|
||||
// 2019-04-04: Vulkan: Avoid passing negative coordinates to vkCmdSetScissor, which debug validation layers do not like.
|
||||
// 2019-04-01: Vulkan: Support for 32-bit index buffer (#define ImDrawIdx unsigned int).
|
||||
// 2019-02-16: Vulkan: Viewport and clipping rectangles correctly using draw_data->FramebufferScale to allow retina display.
|
||||
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
||||
// 2018-08-25: Vulkan: Fixed mishandled VkSurfaceCapabilitiesKHR::maxImageCount=0 case.
|
||||
// 2018-06-22: Inverted the parameters to ImGui_ImplVulkan_RenderDrawData() to be consistent with other backends.
|
||||
// 2018-06-08: Misc: Extracted imgui_impl_vulkan.cpp/.h away from the old combined GLFW+Vulkan example.
|
||||
// 2018-06-08: Vulkan: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
|
||||
// 2018-03-03: Vulkan: Various refactor, created a couple of ImGui_ImplVulkanH_XXX helper that the example can use and that viewport support will use.
|
||||
// 2018-03-01: Vulkan: Renamed ImGui_ImplVulkan_Init_Info to ImGui_ImplVulkan_InitInfo and fields to match more closely Vulkan terminology.
|
||||
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback, ImGui_ImplVulkan_Render() calls ImGui_ImplVulkan_RenderDrawData() itself.
|
||||
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||
// 2017-05-15: Vulkan: Fix scissor offset being negative. Fix new Vulkan validation warnings. Set required depth member for buffer image copy.
|
||||
// 2016-11-13: Vulkan: Fix validation layer warnings and errors and redeclare gl_PerVertex.
|
||||
// 2016-10-18: Vulkan: Add location decorators & change to use structs as in/out in glsl, update embedded spv (produced with glslangValidator -x). Null the released resources.
|
||||
// 2016-08-27: Vulkan: Fix Vulkan example for use when a depth buffer is active.
|
||||
|
||||
#include "imgui_impl_vulkan.h"
|
||||
|
||||
#include "common/Vulkan/Builders.h"
|
||||
#include "common/Vulkan/Context.h"
|
||||
#include "common/Vulkan/Texture.h"
|
||||
#include "common/Vulkan/StreamBuffer.h"
|
||||
#include "common/Vulkan/Util.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
// Visual Studio warnings
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable: 4127) // condition expression is constant
|
||||
#endif
|
||||
|
||||
// If we're doing more than this... wtf?
|
||||
static constexpr u32 VERTEX_BUFFER_SIZE = 8 * 1024 * 1024;
|
||||
static constexpr u32 INDEX_BUFFER_SIZE = 4 * 1024 * 1024;
|
||||
|
||||
// Vulkan data
|
||||
struct ImGui_ImplVulkan_Data
|
||||
{
|
||||
VkRenderPass RenderPass = VK_NULL_HANDLE;
|
||||
VkPipelineCreateFlags PipelineCreateFlags = 0;
|
||||
VkDescriptorSetLayout DescriptorSetLayout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout PipelineLayout = VK_NULL_HANDLE;
|
||||
VkPipeline Pipeline = VK_NULL_HANDLE;
|
||||
VkShaderModule ShaderModuleVert = VK_NULL_HANDLE;
|
||||
VkShaderModule ShaderModuleFrag = VK_NULL_HANDLE;
|
||||
|
||||
VkSampler FontSampler = VK_NULL_HANDLE;
|
||||
|
||||
Vulkan::StreamBuffer VertexStreamBuffer;
|
||||
Vulkan::StreamBuffer IndexStreamBuffer;
|
||||
Vulkan::Texture FontTexture;
|
||||
};
|
||||
|
||||
// Forward Declarations
|
||||
static bool ImGui_ImplVulkan_CreateDeviceObjects();
|
||||
static void ImGui_ImplVulkan_DestroyDeviceObjects();
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SHADERS
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// glsl_shader.vert, compiled with:
|
||||
// # glslangValidator -V -x -o glsl_shader.vert.u32 glsl_shader.vert
|
||||
/*
|
||||
#version 450 core
|
||||
layout(location = 0) in vec2 aPos;
|
||||
layout(location = 1) in vec2 aUV;
|
||||
layout(location = 2) in vec4 aColor;
|
||||
layout(push_constant) uniform uPushConstant { vec2 uScale; vec2 uTranslate; } pc;
|
||||
|
||||
out gl_PerVertex { vec4 gl_Position; };
|
||||
layout(location = 0) out struct { vec4 Color; vec2 UV; } Out;
|
||||
|
||||
void main()
|
||||
{
|
||||
Out.Color = aColor;
|
||||
Out.UV = aUV;
|
||||
gl_Position = vec4(aPos * pc.uScale + pc.uTranslate, 0, 1);
|
||||
}
|
||||
*/
|
||||
static uint32_t __glsl_shader_vert_spv[] =
|
||||
{
|
||||
0x07230203,0x00010000,0x00080001,0x0000002e,0x00000000,0x00020011,0x00000001,0x0006000b,
|
||||
0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
|
||||
0x000a000f,0x00000000,0x00000004,0x6e69616d,0x00000000,0x0000000b,0x0000000f,0x00000015,
|
||||
0x0000001b,0x0000001c,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d,
|
||||
0x00000000,0x00030005,0x00000009,0x00000000,0x00050006,0x00000009,0x00000000,0x6f6c6f43,
|
||||
0x00000072,0x00040006,0x00000009,0x00000001,0x00005655,0x00030005,0x0000000b,0x0074754f,
|
||||
0x00040005,0x0000000f,0x6c6f4361,0x0000726f,0x00030005,0x00000015,0x00565561,0x00060005,
|
||||
0x00000019,0x505f6c67,0x65567265,0x78657472,0x00000000,0x00060006,0x00000019,0x00000000,
|
||||
0x505f6c67,0x7469736f,0x006e6f69,0x00030005,0x0000001b,0x00000000,0x00040005,0x0000001c,
|
||||
0x736f5061,0x00000000,0x00060005,0x0000001e,0x73755075,0x6e6f4368,0x6e617473,0x00000074,
|
||||
0x00050006,0x0000001e,0x00000000,0x61635375,0x0000656c,0x00060006,0x0000001e,0x00000001,
|
||||
0x61725475,0x616c736e,0x00006574,0x00030005,0x00000020,0x00006370,0x00040047,0x0000000b,
|
||||
0x0000001e,0x00000000,0x00040047,0x0000000f,0x0000001e,0x00000002,0x00040047,0x00000015,
|
||||
0x0000001e,0x00000001,0x00050048,0x00000019,0x00000000,0x0000000b,0x00000000,0x00030047,
|
||||
0x00000019,0x00000002,0x00040047,0x0000001c,0x0000001e,0x00000000,0x00050048,0x0000001e,
|
||||
0x00000000,0x00000023,0x00000000,0x00050048,0x0000001e,0x00000001,0x00000023,0x00000008,
|
||||
0x00030047,0x0000001e,0x00000002,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,
|
||||
0x00030016,0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040017,
|
||||
0x00000008,0x00000006,0x00000002,0x0004001e,0x00000009,0x00000007,0x00000008,0x00040020,
|
||||
0x0000000a,0x00000003,0x00000009,0x0004003b,0x0000000a,0x0000000b,0x00000003,0x00040015,
|
||||
0x0000000c,0x00000020,0x00000001,0x0004002b,0x0000000c,0x0000000d,0x00000000,0x00040020,
|
||||
0x0000000e,0x00000001,0x00000007,0x0004003b,0x0000000e,0x0000000f,0x00000001,0x00040020,
|
||||
0x00000011,0x00000003,0x00000007,0x0004002b,0x0000000c,0x00000013,0x00000001,0x00040020,
|
||||
0x00000014,0x00000001,0x00000008,0x0004003b,0x00000014,0x00000015,0x00000001,0x00040020,
|
||||
0x00000017,0x00000003,0x00000008,0x0003001e,0x00000019,0x00000007,0x00040020,0x0000001a,
|
||||
0x00000003,0x00000019,0x0004003b,0x0000001a,0x0000001b,0x00000003,0x0004003b,0x00000014,
|
||||
0x0000001c,0x00000001,0x0004001e,0x0000001e,0x00000008,0x00000008,0x00040020,0x0000001f,
|
||||
0x00000009,0x0000001e,0x0004003b,0x0000001f,0x00000020,0x00000009,0x00040020,0x00000021,
|
||||
0x00000009,0x00000008,0x0004002b,0x00000006,0x00000028,0x00000000,0x0004002b,0x00000006,
|
||||
0x00000029,0x3f800000,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,
|
||||
0x00000005,0x0004003d,0x00000007,0x00000010,0x0000000f,0x00050041,0x00000011,0x00000012,
|
||||
0x0000000b,0x0000000d,0x0003003e,0x00000012,0x00000010,0x0004003d,0x00000008,0x00000016,
|
||||
0x00000015,0x00050041,0x00000017,0x00000018,0x0000000b,0x00000013,0x0003003e,0x00000018,
|
||||
0x00000016,0x0004003d,0x00000008,0x0000001d,0x0000001c,0x00050041,0x00000021,0x00000022,
|
||||
0x00000020,0x0000000d,0x0004003d,0x00000008,0x00000023,0x00000022,0x00050085,0x00000008,
|
||||
0x00000024,0x0000001d,0x00000023,0x00050041,0x00000021,0x00000025,0x00000020,0x00000013,
|
||||
0x0004003d,0x00000008,0x00000026,0x00000025,0x00050081,0x00000008,0x00000027,0x00000024,
|
||||
0x00000026,0x00050051,0x00000006,0x0000002a,0x00000027,0x00000000,0x00050051,0x00000006,
|
||||
0x0000002b,0x00000027,0x00000001,0x00070050,0x00000007,0x0000002c,0x0000002a,0x0000002b,
|
||||
0x00000028,0x00000029,0x00050041,0x00000011,0x0000002d,0x0000001b,0x0000000d,0x0003003e,
|
||||
0x0000002d,0x0000002c,0x000100fd,0x00010038
|
||||
};
|
||||
|
||||
// glsl_shader.frag, compiled with:
|
||||
// # glslangValidator -V -x -o glsl_shader.frag.u32 glsl_shader.frag
|
||||
/*
|
||||
#version 450 core
|
||||
layout(location = 0) out vec4 fColor;
|
||||
layout(set=0, binding=0) uniform sampler2D sTexture;
|
||||
layout(location = 0) in struct { vec4 Color; vec2 UV; } In;
|
||||
void main()
|
||||
{
|
||||
fColor = In.Color * texture(sTexture, In.UV.st);
|
||||
}
|
||||
*/
|
||||
static uint32_t __glsl_shader_frag_spv[] =
|
||||
{
|
||||
0x07230203,0x00010000,0x00080001,0x0000001e,0x00000000,0x00020011,0x00000001,0x0006000b,
|
||||
0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
|
||||
0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000009,0x0000000d,0x00030010,
|
||||
0x00000004,0x00000007,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d,
|
||||
0x00000000,0x00040005,0x00000009,0x6c6f4366,0x0000726f,0x00030005,0x0000000b,0x00000000,
|
||||
0x00050006,0x0000000b,0x00000000,0x6f6c6f43,0x00000072,0x00040006,0x0000000b,0x00000001,
|
||||
0x00005655,0x00030005,0x0000000d,0x00006e49,0x00050005,0x00000016,0x78655473,0x65727574,
|
||||
0x00000000,0x00040047,0x00000009,0x0000001e,0x00000000,0x00040047,0x0000000d,0x0000001e,
|
||||
0x00000000,0x00040047,0x00000016,0x00000022,0x00000000,0x00040047,0x00000016,0x00000021,
|
||||
0x00000000,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,
|
||||
0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040020,0x00000008,0x00000003,
|
||||
0x00000007,0x0004003b,0x00000008,0x00000009,0x00000003,0x00040017,0x0000000a,0x00000006,
|
||||
0x00000002,0x0004001e,0x0000000b,0x00000007,0x0000000a,0x00040020,0x0000000c,0x00000001,
|
||||
0x0000000b,0x0004003b,0x0000000c,0x0000000d,0x00000001,0x00040015,0x0000000e,0x00000020,
|
||||
0x00000001,0x0004002b,0x0000000e,0x0000000f,0x00000000,0x00040020,0x00000010,0x00000001,
|
||||
0x00000007,0x00090019,0x00000013,0x00000006,0x00000001,0x00000000,0x00000000,0x00000000,
|
||||
0x00000001,0x00000000,0x0003001b,0x00000014,0x00000013,0x00040020,0x00000015,0x00000000,
|
||||
0x00000014,0x0004003b,0x00000015,0x00000016,0x00000000,0x0004002b,0x0000000e,0x00000018,
|
||||
0x00000001,0x00040020,0x00000019,0x00000001,0x0000000a,0x00050036,0x00000002,0x00000004,
|
||||
0x00000000,0x00000003,0x000200f8,0x00000005,0x00050041,0x00000010,0x00000011,0x0000000d,
|
||||
0x0000000f,0x0004003d,0x00000007,0x00000012,0x00000011,0x0004003d,0x00000014,0x00000017,
|
||||
0x00000016,0x00050041,0x00000019,0x0000001a,0x0000000d,0x00000018,0x0004003d,0x0000000a,
|
||||
0x0000001b,0x0000001a,0x00050057,0x00000007,0x0000001c,0x00000017,0x0000001b,0x00050085,
|
||||
0x00000007,0x0000001d,0x00000012,0x0000001c,0x0003003e,0x00000009,0x0000001d,0x000100fd,
|
||||
0x00010038
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FUNCTIONS
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||
// FIXME: multi-context support is not tested and probably dysfunctional in this backend.
|
||||
static ImGui_ImplVulkan_Data* ImGui_ImplVulkan_GetBackendData()
|
||||
{
|
||||
return ImGui::GetCurrentContext() ? (ImGui_ImplVulkan_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
|
||||
}
|
||||
|
||||
static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkCommandBuffer command_buffer, int fb_width, int fb_height)
|
||||
{
|
||||
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
|
||||
|
||||
// Bind pipeline:
|
||||
{
|
||||
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
}
|
||||
|
||||
// Bind Vertex And Index Buffer:
|
||||
if (draw_data->TotalVtxCount > 0)
|
||||
{
|
||||
VkBuffer vertex_buffers[1] = { bd->VertexStreamBuffer.GetBuffer() };
|
||||
VkDeviceSize vertex_offset[1] = { bd->VertexStreamBuffer.GetCurrentOffset() };
|
||||
vkCmdBindVertexBuffers(command_buffer, 0, 1, vertex_buffers, vertex_offset);
|
||||
vkCmdBindIndexBuffer(command_buffer, bd->IndexStreamBuffer.GetBuffer(), bd->IndexStreamBuffer.GetCurrentOffset(), sizeof(ImDrawIdx) == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32);
|
||||
}
|
||||
|
||||
// Setup viewport:
|
||||
{
|
||||
VkViewport viewport;
|
||||
viewport.x = 0;
|
||||
viewport.y = 0;
|
||||
viewport.width = (float)fb_width;
|
||||
viewport.height = (float)fb_height;
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
|
||||
}
|
||||
|
||||
// Setup scale and translation:
|
||||
// Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||
{
|
||||
float scale[2];
|
||||
scale[0] = 2.0f / draw_data->DisplaySize.x;
|
||||
scale[1] = 2.0f / draw_data->DisplaySize.y;
|
||||
float translate[2];
|
||||
translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0];
|
||||
translate[1] = -1.0f - draw_data->DisplayPos.y * scale[1];
|
||||
vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale);
|
||||
vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate);
|
||||
}
|
||||
}
|
||||
|
||||
// Render function
|
||||
void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
||||
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
|
||||
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
|
||||
if (fb_width <= 0 || fb_height <= 0)
|
||||
return;
|
||||
|
||||
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
|
||||
if (draw_data->TotalVtxCount > 0)
|
||||
{
|
||||
// Create or resize the vertex/index buffers
|
||||
const size_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert);
|
||||
const size_t index_size = draw_data->TotalIdxCount * sizeof(ImDrawIdx);
|
||||
if (!bd->VertexStreamBuffer.ReserveMemory(vertex_size, sizeof(ImDrawVert)) ||
|
||||
!bd->IndexStreamBuffer.ReserveMemory(index_size, sizeof(ImDrawIdx)))
|
||||
{
|
||||
// this is annoying, because we can't restart the render pass...
|
||||
return;
|
||||
}
|
||||
|
||||
// Upload vertex/index data into a single contiguous GPU buffer
|
||||
ImDrawVert* vtx_dst = (ImDrawVert*)bd->VertexStreamBuffer.GetCurrentHostPointer();
|
||||
ImDrawIdx* idx_dst = (ImDrawIdx*)bd->IndexStreamBuffer.GetCurrentHostPointer();
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtx_dst += cmd_list->VtxBuffer.Size;
|
||||
idx_dst += cmd_list->IdxBuffer.Size;
|
||||
}
|
||||
|
||||
// Setup desired Vulkan state (must come before buffer commit)
|
||||
ImGui_ImplVulkan_SetupRenderState(draw_data, bd->Pipeline, g_vulkan_context->GetCurrentCommandBuffer(), fb_width, fb_height);
|
||||
bd->VertexStreamBuffer.CommitMemory(vertex_size);
|
||||
bd->IndexStreamBuffer.CommitMemory(index_size);
|
||||
}
|
||||
|
||||
// Will project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
|
||||
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
|
||||
|
||||
// Render command lists
|
||||
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||
int global_vtx_offset = 0;
|
||||
int global_idx_offset = 0;
|
||||
const Vulkan::Texture* last_texture = nullptr;
|
||||
VkCommandBuffer command_buffer = g_vulkan_context->GetCurrentCommandBuffer();
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != NULL)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplVulkan_SetupRenderState(draw_data, bd->Pipeline, command_buffer, fb_width, fb_height);
|
||||
else
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
|
||||
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
|
||||
|
||||
// Clamp to viewport as vkCmdSetScissor() won't accept values that are off bounds
|
||||
if (clip_min.x < 0.0f) { clip_min.x = 0.0f; }
|
||||
if (clip_min.y < 0.0f) { clip_min.y = 0.0f; }
|
||||
if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; }
|
||||
if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; }
|
||||
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||
continue;
|
||||
|
||||
// Apply scissor/clipping rectangle
|
||||
VkRect2D scissor;
|
||||
scissor.offset.x = (int32_t)(clip_min.x);
|
||||
scissor.offset.y = (int32_t)(clip_min.y);
|
||||
scissor.extent.width = (uint32_t)(clip_max.x - clip_min.x);
|
||||
scissor.extent.height = (uint32_t)(clip_max.y - clip_min.y);
|
||||
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
|
||||
|
||||
// Bind DescriptorSet with font or user texture
|
||||
const Vulkan::Texture* tex = (const Vulkan::Texture*)pcmd->TextureId;
|
||||
if (tex && last_texture != tex)
|
||||
{
|
||||
// if we can't get a descriptor set, we'll we're in trouble, since we can't restart the render pass from here.
|
||||
VkDescriptorSet ds = g_vulkan_context->AllocateDescriptorSet(bd->DescriptorSetLayout);
|
||||
if (ds == VK_NULL_HANDLE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Vulkan::DescriptorSetUpdateBuilder dsb;
|
||||
dsb.AddCombinedImageSamplerDescriptorWrite(ds, 0, tex->GetView(), bd->FontSampler);
|
||||
dsb.Update(g_vulkan_context->GetDevice());
|
||||
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &ds, 0, nullptr);
|
||||
last_texture = tex;
|
||||
}
|
||||
|
||||
// Draw
|
||||
vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
|
||||
}
|
||||
}
|
||||
global_idx_offset += cmd_list->IdxBuffer.Size;
|
||||
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
||||
}
|
||||
|
||||
// Note: at this point both vkCmdSetViewport() and vkCmdSetScissor() have been called.
|
||||
// Our last values will leak into user/application rendering IF:
|
||||
// - Your app uses a pipeline with VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR dynamic state
|
||||
// - And you forgot to call vkCmdSetViewport() and vkCmdSetScissor() yourself to explicitely set that state.
|
||||
// If you use VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR you are responsible for setting the values before rendering.
|
||||
// In theory we should aim to backup/restore those values but I am not sure this is possible.
|
||||
// We perform a call to vkCmdSetScissor() to set back a full viewport which is likely to fix things for 99% users but technically this is not perfect. (See github #4644)
|
||||
VkRect2D scissor = { { 0, 0 }, { (uint32_t)fb_width, (uint32_t)fb_height } };
|
||||
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
|
||||
}
|
||||
|
||||
bool ImGui_ImplVulkan_CreateFontsTexture()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
|
||||
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
if (bd->FontTexture.GetWidth() != static_cast<u32>(width) || bd->FontTexture.GetHeight() != static_cast<u32>(height))
|
||||
{
|
||||
if (!bd->FontTexture.Create(width, height, 1, 1, VK_FORMAT_R8G8B8A8_UNORM,
|
||||
VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t upload_size = width * height * 4 * sizeof(unsigned char);
|
||||
const VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, nullptr, 0,
|
||||
static_cast<VkDeviceSize>(upload_size), VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_SHARING_MODE_EXCLUSIVE, 0, nullptr};
|
||||
VmaAllocationCreateInfo aci = {};
|
||||
aci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||
aci.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
|
||||
|
||||
VmaAllocationInfo ai;
|
||||
VkBuffer buffer;
|
||||
VmaAllocation allocation;
|
||||
VkResult res = vmaCreateBuffer(g_vulkan_context->GetAllocator(), &bci, &aci, &buffer, &allocation, &ai);
|
||||
if (res != VK_SUCCESS)
|
||||
return false;
|
||||
|
||||
std::memcpy(ai.pMappedData, pixels, upload_size);
|
||||
vmaFlushAllocation(g_vulkan_context->GetAllocator(), allocation, 0, upload_size);
|
||||
bd->FontTexture.TransitionToLayout(g_vulkan_context->GetCurrentInitCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
bd->FontTexture.UpdateFromBuffer(g_vulkan_context->GetCurrentInitCommandBuffer(), 0, 0, 0, 0, width, height, height, width, buffer, 0);
|
||||
bd->FontTexture.TransitionToLayout(g_vulkan_context->GetCurrentInitCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
// Immediately queue it for freeing after the command buffer finishes, since it's only needed for the copy.
|
||||
g_vulkan_context->DeferBufferDestruction(buffer, allocation);
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts->SetTexID((ImTextureID)&bd->FontTexture);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ImGui_ImplVulkan_CreateShaderModules(VkDevice device)
|
||||
{
|
||||
// Create the shader modules
|
||||
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
|
||||
if (bd->ShaderModuleVert == VK_NULL_HANDLE)
|
||||
{
|
||||
VkShaderModuleCreateInfo vert_info = {};
|
||||
vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
vert_info.codeSize = sizeof(__glsl_shader_vert_spv);
|
||||
vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv;
|
||||
VkResult err = vkCreateShaderModule(device, &vert_info, nullptr, &bd->ShaderModuleVert);
|
||||
if (err != VK_SUCCESS)
|
||||
return false;
|
||||
}
|
||||
if (bd->ShaderModuleFrag == VK_NULL_HANDLE)
|
||||
{
|
||||
VkShaderModuleCreateInfo frag_info = {};
|
||||
frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
frag_info.codeSize = sizeof(__glsl_shader_frag_spv);
|
||||
frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv;
|
||||
VkResult err = vkCreateShaderModule(device, &frag_info, nullptr, &bd->ShaderModuleFrag);
|
||||
if (err != VK_SUCCESS)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ImGui_ImplVulkan_CreateFontSampler(VkDevice device)
|
||||
{
|
||||
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
|
||||
if (bd->FontSampler)
|
||||
return true;
|
||||
|
||||
// Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling.
|
||||
VkSamplerCreateInfo info = {};
|
||||
info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||
info.magFilter = VK_FILTER_LINEAR;
|
||||
info.minFilter = VK_FILTER_LINEAR;
|
||||
info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
||||
info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
||||
info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
||||
info.minLod = -1000;
|
||||
info.maxLod = 1000;
|
||||
info.maxAnisotropy = 1.0f;
|
||||
VkResult err = vkCreateSampler(device, &info, nullptr, &bd->FontSampler);
|
||||
return (err == VK_SUCCESS);
|
||||
}
|
||||
|
||||
static bool ImGui_ImplVulkan_CreateDescriptorSetLayout(VkDevice device)
|
||||
{
|
||||
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
|
||||
if (bd->DescriptorSetLayout)
|
||||
return true;
|
||||
|
||||
if (!ImGui_ImplVulkan_CreateFontSampler(device))
|
||||
return false;
|
||||
|
||||
VkSampler sampler[1] = { bd->FontSampler };
|
||||
VkDescriptorSetLayoutBinding binding[1] = {};
|
||||
binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
binding[0].descriptorCount = 1;
|
||||
binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
binding[0].pImmutableSamplers = sampler;
|
||||
VkDescriptorSetLayoutCreateInfo info = {};
|
||||
info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
info.bindingCount = 1;
|
||||
info.pBindings = binding;
|
||||
VkResult err = vkCreateDescriptorSetLayout(device, &info, nullptr, &bd->DescriptorSetLayout);
|
||||
return (err == VK_SUCCESS);
|
||||
}
|
||||
|
||||
static bool ImGui_ImplVulkan_CreatePipelineLayout(VkDevice device)
|
||||
{
|
||||
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
|
||||
if (bd->PipelineLayout)
|
||||
return true;
|
||||
|
||||
// Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix
|
||||
ImGui_ImplVulkan_CreateDescriptorSetLayout(device);
|
||||
VkPushConstantRange push_constants[1] = {};
|
||||
push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
push_constants[0].offset = sizeof(float) * 0;
|
||||
push_constants[0].size = sizeof(float) * 4;
|
||||
VkDescriptorSetLayout set_layout[1] = { bd->DescriptorSetLayout };
|
||||
VkPipelineLayoutCreateInfo layout_info = {};
|
||||
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
layout_info.setLayoutCount = 1;
|
||||
layout_info.pSetLayouts = set_layout;
|
||||
layout_info.pushConstantRangeCount = 1;
|
||||
layout_info.pPushConstantRanges = push_constants;
|
||||
VkResult err = vkCreatePipelineLayout(device, &layout_info, nullptr, &bd->PipelineLayout);
|
||||
return (err == VK_SUCCESS);
|
||||
}
|
||||
|
||||
static bool ImGui_ImplVulkan_CreatePipeline(VkDevice device, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkPipeline* pipeline)
|
||||
{
|
||||
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
|
||||
if (!ImGui_ImplVulkan_CreateShaderModules(device))
|
||||
return false;
|
||||
|
||||
VkPipelineShaderStageCreateInfo stage[2] = {};
|
||||
stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
stage[0].module = bd->ShaderModuleVert;
|
||||
stage[0].pName = "main";
|
||||
stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
stage[1].module = bd->ShaderModuleFrag;
|
||||
stage[1].pName = "main";
|
||||
|
||||
VkVertexInputBindingDescription binding_desc[1] = {};
|
||||
binding_desc[0].stride = sizeof(ImDrawVert);
|
||||
binding_desc[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
|
||||
VkVertexInputAttributeDescription attribute_desc[3] = {};
|
||||
attribute_desc[0].location = 0;
|
||||
attribute_desc[0].binding = binding_desc[0].binding;
|
||||
attribute_desc[0].format = VK_FORMAT_R32G32_SFLOAT;
|
||||
attribute_desc[0].offset = IM_OFFSETOF(ImDrawVert, pos);
|
||||
attribute_desc[1].location = 1;
|
||||
attribute_desc[1].binding = binding_desc[0].binding;
|
||||
attribute_desc[1].format = VK_FORMAT_R32G32_SFLOAT;
|
||||
attribute_desc[1].offset = IM_OFFSETOF(ImDrawVert, uv);
|
||||
attribute_desc[2].location = 2;
|
||||
attribute_desc[2].binding = binding_desc[0].binding;
|
||||
attribute_desc[2].format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
attribute_desc[2].offset = IM_OFFSETOF(ImDrawVert, col);
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertex_info = {};
|
||||
vertex_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
vertex_info.vertexBindingDescriptionCount = 1;
|
||||
vertex_info.pVertexBindingDescriptions = binding_desc;
|
||||
vertex_info.vertexAttributeDescriptionCount = 3;
|
||||
vertex_info.pVertexAttributeDescriptions = attribute_desc;
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo ia_info = {};
|
||||
ia_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
ia_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewport_info = {};
|
||||
viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
viewport_info.viewportCount = 1;
|
||||
viewport_info.scissorCount = 1;
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo raster_info = {};
|
||||
raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
raster_info.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
raster_info.cullMode = VK_CULL_MODE_NONE;
|
||||
raster_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
raster_info.lineWidth = 1.0f;
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo ms_info = {};
|
||||
ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
ms_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
VkPipelineColorBlendAttachmentState color_attachment[1] = {};
|
||||
color_attachment[0].blendEnable = VK_TRUE;
|
||||
color_attachment[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
color_attachment[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
color_attachment[0].colorBlendOp = VK_BLEND_OP_ADD;
|
||||
color_attachment[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
color_attachment[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
color_attachment[0].alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
color_attachment[0].colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo depth_info = {};
|
||||
depth_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo blend_info = {};
|
||||
blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
blend_info.attachmentCount = 1;
|
||||
blend_info.pAttachments = color_attachment;
|
||||
|
||||
VkDynamicState dynamic_states[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
|
||||
VkPipelineDynamicStateCreateInfo dynamic_state = {};
|
||||
dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
dynamic_state.dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states);
|
||||
dynamic_state.pDynamicStates = dynamic_states;
|
||||
|
||||
if (!ImGui_ImplVulkan_CreatePipelineLayout(device))
|
||||
return false;
|
||||
|
||||
VkGraphicsPipelineCreateInfo info = {};
|
||||
info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
info.flags = bd->PipelineCreateFlags;
|
||||
info.stageCount = 2;
|
||||
info.pStages = stage;
|
||||
info.pVertexInputState = &vertex_info;
|
||||
info.pInputAssemblyState = &ia_info;
|
||||
info.pViewportState = &viewport_info;
|
||||
info.pRasterizationState = &raster_info;
|
||||
info.pMultisampleState = &ms_info;
|
||||
info.pDepthStencilState = &depth_info;
|
||||
info.pColorBlendState = &blend_info;
|
||||
info.pDynamicState = &dynamic_state;
|
||||
info.layout = bd->PipelineLayout;
|
||||
info.renderPass = renderPass;
|
||||
info.subpass = 0;
|
||||
VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &info, nullptr, pipeline);
|
||||
return (err == VK_SUCCESS);
|
||||
}
|
||||
|
||||
bool ImGui_ImplVulkan_CreateDeviceObjects()
|
||||
{
|
||||
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
|
||||
|
||||
if (!bd->VertexStreamBuffer.Create(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VERTEX_BUFFER_SIZE) ||
|
||||
!bd->IndexStreamBuffer.Create(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, INDEX_BUFFER_SIZE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ImGui_ImplVulkan_CreatePipeline(g_vulkan_context->GetDevice(), VK_NULL_HANDLE, bd->RenderPass, &bd->Pipeline))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplVulkan_DestroyDeviceObjects()
|
||||
{
|
||||
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
|
||||
|
||||
bd->VertexStreamBuffer.Destroy(false);
|
||||
bd->IndexStreamBuffer.Destroy(false);
|
||||
bd->FontTexture.Destroy(false);
|
||||
|
||||
if (bd->ShaderModuleVert) { vkDestroyShaderModule(g_vulkan_context->GetDevice(), bd->ShaderModuleVert, nullptr); bd->ShaderModuleVert = VK_NULL_HANDLE; }
|
||||
if (bd->ShaderModuleFrag) { vkDestroyShaderModule(g_vulkan_context->GetDevice(), bd->ShaderModuleFrag, nullptr); bd->ShaderModuleFrag = VK_NULL_HANDLE; }
|
||||
if (bd->FontSampler) { vkDestroySampler(g_vulkan_context->GetDevice(), bd->FontSampler, nullptr); bd->FontSampler = VK_NULL_HANDLE; }
|
||||
if (bd->DescriptorSetLayout) { vkDestroyDescriptorSetLayout(g_vulkan_context->GetDevice(), bd->DescriptorSetLayout, nullptr); bd->DescriptorSetLayout = VK_NULL_HANDLE; }
|
||||
if (bd->PipelineLayout) { vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), bd->PipelineLayout, nullptr); bd->PipelineLayout = VK_NULL_HANDLE; }
|
||||
if (bd->Pipeline) { vkDestroyPipeline(g_vulkan_context->GetDevice(), bd->Pipeline, nullptr); bd->Pipeline = VK_NULL_HANDLE; }
|
||||
}
|
||||
|
||||
bool ImGui_ImplVulkan_Init(VkRenderPass render_pass)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
|
||||
|
||||
// Setup backend capabilities flags
|
||||
ImGui_ImplVulkan_Data* bd = IM_NEW(ImGui_ImplVulkan_Data)();
|
||||
io.BackendRendererUserData = (void*)bd;
|
||||
io.BackendRendererName = "imgui_impl_vulkan";
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
|
||||
IM_ASSERT(render_pass != VK_NULL_HANDLE);
|
||||
|
||||
bd->RenderPass = render_pass;
|
||||
|
||||
return ImGui_ImplVulkan_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
void ImGui_ImplVulkan_Shutdown()
|
||||
{
|
||||
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
|
||||
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
ImGui_ImplVulkan_DestroyDeviceObjects();
|
||||
io.BackendRendererName = NULL;
|
||||
io.BackendRendererUserData = NULL;
|
||||
IM_DELETE(bd);
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
// dear imgui: Renderer Backend for Vulkan
|
||||
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
#include "common/Vulkan/Loader.h"
|
||||
|
||||
// Called by user code
|
||||
bool ImGui_ImplVulkan_Init(VkRenderPass render_pass);
|
||||
void ImGui_ImplVulkan_Shutdown();
|
||||
void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data);
|
||||
bool ImGui_ImplVulkan_CreateFontsTexture();
|
||||
void ImGui_ImplVulkan_DestroyFontUploadObjects();
|
374
pcsx2/GS/GS.cpp
374
pcsx2/GS/GS.cpp
|
@ -32,8 +32,8 @@
|
|||
#include "common/StringUtil.h"
|
||||
#include "pcsx2/Config.h"
|
||||
#include "pcsx2/Counters.h"
|
||||
#include "pcsx2/Frontend/ImGuiManager.h"
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/HostDisplay.h"
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/Frontend/FullscreenUI.h"
|
||||
#include "pcsx2/Frontend/InputManager.h"
|
||||
|
@ -106,27 +106,6 @@ void GSshutdown()
|
|||
GSJoinSnapshotThreads();
|
||||
}
|
||||
|
||||
void GSclose()
|
||||
{
|
||||
GSTextureReplacements::Shutdown();
|
||||
|
||||
if (g_gs_renderer)
|
||||
{
|
||||
g_gs_renderer->Destroy();
|
||||
g_gs_renderer.reset();
|
||||
}
|
||||
if (g_gs_device)
|
||||
{
|
||||
g_gs_device->Destroy();
|
||||
g_gs_device.reset();
|
||||
}
|
||||
|
||||
if (g_host_display)
|
||||
g_host_display->SetGPUTimingEnabled(false);
|
||||
|
||||
Host::ReleaseHostDisplay(true);
|
||||
}
|
||||
|
||||
static RenderAPI GetAPIForRenderer(GSRendererType renderer)
|
||||
{
|
||||
switch (renderer)
|
||||
|
@ -155,11 +134,55 @@ static RenderAPI GetAPIForRenderer(GSRendererType renderer)
|
|||
}
|
||||
}
|
||||
|
||||
static bool DoGSOpen(GSRendererType renderer, u8* basemem)
|
||||
static void UpdateExclusiveFullscreen(bool force_off)
|
||||
{
|
||||
s_render_api = g_host_display->GetRenderAPI();
|
||||
if (!g_gs_device->SupportsExclusiveFullscreen())
|
||||
return;
|
||||
|
||||
switch (g_host_display->GetRenderAPI())
|
||||
std::string fullscreen_mode = Host::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", "");
|
||||
u32 width, height;
|
||||
float refresh_rate;
|
||||
|
||||
const bool wants_fullscreen = !force_off && Host::IsFullscreen() && !fullscreen_mode.empty() &&
|
||||
GSDevice::ParseFullscreenMode(fullscreen_mode, &width, &height, &refresh_rate);
|
||||
const bool is_fullscreen = g_gs_device->IsExclusiveFullscreen();
|
||||
if (wants_fullscreen == is_fullscreen)
|
||||
return;
|
||||
|
||||
if (wants_fullscreen)
|
||||
{
|
||||
Console.WriteLn("Trying to acquire exclusive fullscreen...");
|
||||
if (g_gs_device->SetExclusiveFullscreen(true, width, height, refresh_rate))
|
||||
{
|
||||
Host::AddKeyedOSDMessage(
|
||||
"UpdateExclusiveFullscreen", "Acquired exclusive fullscreen.", Host::OSD_INFO_DURATION);
|
||||
}
|
||||
else
|
||||
{
|
||||
Host::AddKeyedOSDMessage(
|
||||
"UpdateExclusiveFullscreen", "Failed to acquire exclusive fullscreen.", Host::OSD_WARNING_DURATION);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLn("Leaving exclusive fullscreen...");
|
||||
g_gs_device->SetExclusiveFullscreen(false, 0, 0, 0.0f);
|
||||
Host::AddKeyedOSDMessage("UpdateExclusiveFullscreen", "Lost exclusive fullscreen.", Host::OSD_INFO_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail)
|
||||
{
|
||||
s_render_api = GetAPIForRenderer(renderer);
|
||||
|
||||
std::optional<WindowInfo> wi = Host::AcquireRenderWindow(s_render_api);
|
||||
if (!wi.has_value())
|
||||
{
|
||||
Console.Error("Failed to acquire render window.");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (s_render_api)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
case RenderAPI::D3D11:
|
||||
|
@ -188,18 +211,59 @@ static bool DoGSOpen(GSRendererType renderer, u8* basemem)
|
|||
#endif
|
||||
|
||||
default:
|
||||
Console.Error("Unknown render API %u", static_cast<unsigned>(g_host_display->GetRenderAPI()));
|
||||
Console.Error("Unsupported render API %s", GSDevice::RenderAPIToString(s_render_api));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_gs_device->Create())
|
||||
const VsyncMode vsync_mode = Host::GetEffectiveVSyncMode();
|
||||
bool okay = g_gs_device->Create(wi.value(), vsync_mode);
|
||||
if (okay)
|
||||
{
|
||||
okay = ImGuiManager::Initialize();
|
||||
if (!okay)
|
||||
Console.Error("Failed to initialize ImGuiManager");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error("Failed to create GS device");
|
||||
}
|
||||
|
||||
if (!okay)
|
||||
{
|
||||
ImGuiManager::Shutdown(clear_state_on_fail);
|
||||
g_gs_device->Destroy();
|
||||
g_gs_device.reset();
|
||||
Host::ReleaseRenderWindow();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_gs_renderer)
|
||||
GSConfig.OsdShowGPU = EmuConfig.GS.OsdShowGPU && g_gs_device->SetGPUTimingEnabled(true);
|
||||
|
||||
s_render_api = g_gs_device->GetRenderAPI();
|
||||
Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", GSDevice::RenderAPIToString(s_render_api));
|
||||
Console.Indent().WriteLn(g_gs_device->GetDriverInfo());
|
||||
|
||||
// Switch to exclusive fullscreen if enabled.
|
||||
UpdateExclusiveFullscreen(false);
|
||||
|
||||
g_perfmon.Reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void CloseGSDevice(bool clear_state)
|
||||
{
|
||||
if (!g_gs_device)
|
||||
return;
|
||||
|
||||
UpdateExclusiveFullscreen(true);
|
||||
ImGuiManager::Shutdown(clear_state);
|
||||
g_gs_device->Destroy();
|
||||
g_gs_device.reset();
|
||||
|
||||
Host::ReleaseRenderWindow();
|
||||
}
|
||||
|
||||
static bool OpenGSRenderer(GSRendererType renderer, u8* basemem)
|
||||
{
|
||||
if (renderer == GSRendererType::Null)
|
||||
{
|
||||
|
@ -216,21 +280,24 @@ static bool DoGSOpen(GSRendererType renderer, u8* basemem)
|
|||
|
||||
g_gs_renderer->SetRegsMem(basemem);
|
||||
g_gs_renderer->ResetPCRTC();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Warning("(DoGSOpen) Using existing renderer.");
|
||||
}
|
||||
|
||||
GSConfig.OsdShowGPU = EmuConfig.GS.OsdShowGPU && g_host_display->SetGPUTimingEnabled(true);
|
||||
|
||||
g_perfmon.Reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config)
|
||||
static void CloseGSRenderer()
|
||||
{
|
||||
Console.WriteLn("Reopening GS with %s display", recreate_display ? "new" : "existing");
|
||||
GSTextureReplacements::Shutdown();
|
||||
|
||||
if (g_gs_renderer)
|
||||
{
|
||||
g_gs_renderer->Destroy();
|
||||
g_gs_renderer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool GSreopen(bool recreate_device, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config)
|
||||
{
|
||||
Console.WriteLn("Reopening GS with %s device and %s renderer", recreate_device ? "new" : "existing",
|
||||
recreate_renderer ? "new" : "existing");
|
||||
|
||||
if (recreate_renderer)
|
||||
g_gs_renderer->Flush(GSState::GSFlushReason::GSREOPEN);
|
||||
|
@ -238,6 +305,9 @@ bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::
|
|||
if (GSConfig.UserHacks_ReadTCOnClose)
|
||||
g_gs_renderer->ReadbackTextureCache();
|
||||
|
||||
u8* basemem = g_gs_renderer->GetRegsMem();
|
||||
const u32 gamecrc = g_gs_renderer->GetGameCRC();
|
||||
|
||||
freezeData fd = {};
|
||||
std::unique_ptr<u8[]> fd_data;
|
||||
if (recreate_renderer)
|
||||
|
@ -255,6 +325,8 @@ bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::
|
|||
Console.Error("(GSreopen) Failed to freeze GS");
|
||||
return false;
|
||||
}
|
||||
|
||||
CloseGSRenderer();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -263,69 +335,33 @@ bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::
|
|||
g_gs_renderer->PurgePool();
|
||||
}
|
||||
|
||||
if (recreate_display)
|
||||
if (recreate_device)
|
||||
{
|
||||
g_gs_device->ResetAPIState();
|
||||
if (Host::BeginPresentFrame(true) == HostDisplay::PresentResult::OK)
|
||||
Host::EndPresentFrame();
|
||||
}
|
||||
CloseGSDevice(false);
|
||||
|
||||
u8* basemem = g_gs_renderer->GetRegsMem();
|
||||
const u32 gamecrc = g_gs_renderer->GetGameCRC();
|
||||
if (recreate_renderer)
|
||||
if (!OpenGSDevice(GSConfig.Renderer, false) || (recreate_renderer && !OpenGSRenderer(GSConfig.Renderer, basemem)))
|
||||
{
|
||||
GSTextureReplacements::Shutdown();
|
||||
g_gs_renderer->Destroy();
|
||||
g_gs_renderer.reset();
|
||||
}
|
||||
Host::AddKeyedOSDMessage(
|
||||
"GSReopenFailed", "Failed to reopen, restoring old configuration.", Host::OSD_CRITICAL_ERROR_DURATION);
|
||||
|
||||
g_gs_device->Destroy();
|
||||
g_gs_device.reset();
|
||||
CloseGSDevice(false);
|
||||
|
||||
if (recreate_display)
|
||||
{
|
||||
Host::ReleaseHostDisplay(false);
|
||||
if (!Host::AcquireHostDisplay(GetAPIForRenderer(GSConfig.Renderer), false))
|
||||
{
|
||||
Console.Error("(GSreopen) Failed to reacquire host display");
|
||||
|
||||
// try to get the old one back
|
||||
if (!Host::AcquireHostDisplay(GetAPIForRenderer(old_config.Renderer), false))
|
||||
{
|
||||
pxFailRel("Failed to recreate old config host display");
|
||||
return false;
|
||||
}
|
||||
|
||||
Host::AddKeyedOSDMessage("GSReopenFailed", fmt::format("Failed to open {} display, switching back to {}.",
|
||||
HostDisplay::RenderAPIToString(GetAPIForRenderer(GSConfig.Renderer)),
|
||||
HostDisplay::RenderAPIToString(GetAPIForRenderer(old_config.Renderer)), Host::OSD_CRITICAL_ERROR_DURATION));
|
||||
GSConfig = old_config;
|
||||
}
|
||||
}
|
||||
|
||||
if (!DoGSOpen(GSConfig.Renderer, basemem))
|
||||
{
|
||||
Console.Error("(GSreopen) Failed to recreate GS");
|
||||
|
||||
// try the old config
|
||||
if (recreate_display && GSConfig.Renderer != old_config.Renderer)
|
||||
{
|
||||
Host::ReleaseHostDisplay(false);
|
||||
if (!Host::AcquireHostDisplay(GetAPIForRenderer(old_config.Renderer), false))
|
||||
{
|
||||
pxFailRel("Failed to recreate old config host display (part 2)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Host::AddKeyedOSDMessage("GSReopenFailed","Failed to reopen, restoring old configuration.", Host::OSD_CRITICAL_ERROR_DURATION);
|
||||
GSConfig = old_config;
|
||||
if (!DoGSOpen(GSConfig.Renderer, basemem))
|
||||
if (!OpenGSDevice(GSConfig.Renderer, false) || (recreate_renderer && !OpenGSRenderer(GSConfig.Renderer, basemem)))
|
||||
{
|
||||
pxFailRel("Failed to reopen GS on old config");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (recreate_renderer)
|
||||
{
|
||||
if (!OpenGSRenderer(GSConfig.Renderer, basemem))
|
||||
{
|
||||
Console.Error("(GSreopen) Failed to create new renderer");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (recreate_renderer)
|
||||
{
|
||||
|
@ -349,21 +385,32 @@ bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* b
|
|||
GSConfig = config;
|
||||
GSConfig.Renderer = renderer;
|
||||
|
||||
if (!Host::AcquireHostDisplay(GetAPIForRenderer(renderer), true))
|
||||
bool res = OpenGSDevice(renderer, true);
|
||||
if (res)
|
||||
{
|
||||
Console.Error("Failed to acquire host display");
|
||||
return false;
|
||||
res = OpenGSRenderer(renderer, basemem);
|
||||
if (!res)
|
||||
CloseGSDevice(true);
|
||||
}
|
||||
|
||||
if (!DoGSOpen(renderer, basemem))
|
||||
if (!res)
|
||||
{
|
||||
Host::ReleaseHostDisplay(true);
|
||||
Host::ReportErrorAsync(
|
||||
"Error", fmt::format("Failed to create render device. This may be due to your GPU not supporting the "
|
||||
"chosen renderer ({}), or because your graphics drivers need to be updated.",
|
||||
Pcsx2Config::GSOptions::GetRendererName(EmuConfig.GS.Renderer)));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSclose()
|
||||
{
|
||||
CloseGSRenderer();
|
||||
CloseGSDevice(true);
|
||||
}
|
||||
|
||||
void GSreset(bool hardware_reset)
|
||||
{
|
||||
try
|
||||
|
@ -539,7 +586,7 @@ void GSPresentCurrentFrame()
|
|||
|
||||
void GSThrottlePresentation()
|
||||
{
|
||||
if (g_host_display->GetVsyncMode() != VsyncMode::Off)
|
||||
if (g_gs_device->GetVsyncMode() != VsyncMode::Off)
|
||||
{
|
||||
// Let vsync take care of throttling.
|
||||
return;
|
||||
|
@ -547,7 +594,7 @@ void GSThrottlePresentation()
|
|||
|
||||
// Manually throttle presentation when vsync isn't enabled, so we don't try to render the
|
||||
// fullscreen UI at thousands of FPS and make the gpu go brrrrrrrr.
|
||||
const float surface_refresh_rate = g_host_display->GetWindowInfo().surface_refresh_rate;
|
||||
const float surface_refresh_rate = g_gs_device->GetWindowInfo().surface_refresh_rate;
|
||||
const float throttle_rate = (surface_refresh_rate > 0.0f) ? surface_refresh_rate : 60.0f;
|
||||
|
||||
const u64 sleep_period = static_cast<u64>(static_cast<double>(GetTickFrequency()) / static_cast<double>(throttle_rate));
|
||||
|
@ -564,11 +611,96 @@ void GSThrottlePresentation()
|
|||
Threading::SleepUntil(s_next_manual_present_time);
|
||||
}
|
||||
|
||||
void GSsetGameCRC(u32 crc)
|
||||
void GSSetGameCRC(u32 crc)
|
||||
{
|
||||
g_gs_renderer->SetGameCRC(crc);
|
||||
}
|
||||
|
||||
void GSResizeDisplayWindow(int width, int height, float scale)
|
||||
{
|
||||
g_gs_device->ResizeWindow(width, height, scale);
|
||||
ImGuiManager::WindowResized();
|
||||
}
|
||||
|
||||
void GSUpdateDisplayWindow()
|
||||
{
|
||||
UpdateExclusiveFullscreen(true);
|
||||
g_gs_device->DestroySurface();
|
||||
|
||||
const std::optional<WindowInfo> wi = Host::UpdateRenderWindow();
|
||||
if (!wi.has_value())
|
||||
{
|
||||
pxFailRel("Failed to get window info after update.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_gs_device->ChangeWindow(wi.value()))
|
||||
{
|
||||
pxFailRel("Failed to change window after update.");
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateExclusiveFullscreen(false);
|
||||
|
||||
ImGuiManager::WindowResized();
|
||||
}
|
||||
|
||||
void GSSetVSyncMode(VsyncMode mode)
|
||||
{
|
||||
g_gs_device->SetVSync(mode);
|
||||
}
|
||||
|
||||
bool GSGetHostRefreshRate(float* refresh_rate)
|
||||
{
|
||||
if (!g_gs_device)
|
||||
return false;
|
||||
|
||||
return g_gs_device->GetHostRefreshRate(refresh_rate);
|
||||
}
|
||||
|
||||
void GSGetAdaptersAndFullscreenModes(
|
||||
GSRendererType renderer, std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes)
|
||||
{
|
||||
switch (renderer)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
case GSRendererType::DX11:
|
||||
case GSRendererType::DX12:
|
||||
{
|
||||
auto factory = D3D::CreateFactory(false);
|
||||
if (factory)
|
||||
{
|
||||
if (adapters)
|
||||
*adapters = D3D::GetAdapterNames(factory.get());
|
||||
if (fullscreen_modes)
|
||||
*fullscreen_modes = D3D::GetFullscreenModes(factory.get(), EmuConfig.GS.Adapter);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
case GSRendererType::VK:
|
||||
{
|
||||
GSDeviceVK::GetAdaptersAndFullscreenModes(adapters, fullscreen_modes);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
case GSRendererType::Metal:
|
||||
{
|
||||
if (adapters)
|
||||
*adapters = GetMetalAdapterList();
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GSVideoMode GSgetDisplayMode()
|
||||
{
|
||||
GSRenderer* gs = g_gs_renderer.get();
|
||||
|
@ -594,7 +726,7 @@ void GSgetInternalResolution(int* width, int* height)
|
|||
void GSgetStats(std::string& info)
|
||||
{
|
||||
GSPerfMon& pm = g_perfmon;
|
||||
const char* api_name = HostDisplay::RenderAPIToString(s_render_api);
|
||||
const char* api_name = GSDevice::RenderAPIToString(s_render_api);
|
||||
if (GSConfig.Renderer == GSRendererType::SW)
|
||||
{
|
||||
const double fps = GetVerticalFrequency();
|
||||
|
@ -661,7 +793,7 @@ void GSgetTitleStats(std::string& info)
|
|||
static constexpr const char* deinterlace_modes[] = {
|
||||
"Automatic", "None", "Weave tff", "Weave bff", "Bob tff", "Bob bff", "Blend tff", "Blend bff", "Adaptive tff", "Adaptive bff"};
|
||||
|
||||
const char* api_name = HostDisplay::RenderAPIToString(s_render_api);
|
||||
const char* api_name = GSDevice::RenderAPIToString(s_render_api);
|
||||
const char* hw_sw_name = (GSConfig.Renderer == GSRendererType::Null) ? " Null" : (GSConfig.UseHardwareRenderer() ? " HW" : " SW");
|
||||
const char* deinterlace_mode = deinterlace_modes[static_cast<int>(GSConfig.InterlaceMode)];
|
||||
|
||||
|
@ -681,28 +813,12 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
|
|||
|
||||
// Handle OSD scale changes by pushing a window resize through.
|
||||
if (new_config.OsdScale != old_config.OsdScale)
|
||||
{
|
||||
g_gs_device->ResetAPIState();
|
||||
Host::ResizeHostDisplay(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight(), g_host_display->GetWindowScale());
|
||||
g_gs_device->RestoreAPIState();
|
||||
}
|
||||
ImGuiManager::WindowResized();
|
||||
|
||||
// Options which need a full teardown/recreate.
|
||||
if (!GSConfig.RestartOptionsAreEqual(old_config))
|
||||
{
|
||||
RenderAPI existing_api = g_host_display->GetRenderAPI();
|
||||
if (existing_api == RenderAPI::OpenGLES)
|
||||
existing_api = RenderAPI::OpenGL;
|
||||
|
||||
const bool do_full_restart = (
|
||||
existing_api != GetAPIForRenderer(GSConfig.Renderer) ||
|
||||
GSConfig.Adapter != old_config.Adapter ||
|
||||
GSConfig.UseDebugDevice != old_config.UseDebugDevice ||
|
||||
GSConfig.UseBlitSwapChain != old_config.UseBlitSwapChain ||
|
||||
GSConfig.DisableShaderCache != old_config.DisableShaderCache ||
|
||||
GSConfig.DisableThreadedPresentation != old_config.DisableThreadedPresentation
|
||||
);
|
||||
if (!GSreopen(do_full_restart, true, old_config))
|
||||
if (!GSreopen(true, true, old_config))
|
||||
pxFailRel("Failed to do full GS reopen");
|
||||
return;
|
||||
}
|
||||
|
@ -771,7 +887,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
|
|||
|
||||
if (GSConfig.OsdShowGPU != old_config.OsdShowGPU)
|
||||
{
|
||||
if (!g_host_display->SetGPUTimingEnabled(GSConfig.OsdShowGPU))
|
||||
if (!g_gs_device->SetGPUTimingEnabled(GSConfig.OsdShowGPU))
|
||||
GSConfig.OsdShowGPU = false;
|
||||
}
|
||||
}
|
||||
|
@ -784,7 +900,7 @@ void GSSwitchRenderer(GSRendererType new_renderer)
|
|||
if (!g_gs_renderer || GSConfig.Renderer == new_renderer)
|
||||
return;
|
||||
|
||||
RenderAPI existing_api = g_host_display->GetRenderAPI();
|
||||
RenderAPI existing_api = g_gs_device->GetRenderAPI();
|
||||
if (existing_api == RenderAPI::OpenGLES)
|
||||
existing_api = RenderAPI::OpenGL;
|
||||
|
||||
|
@ -796,22 +912,6 @@ void GSSwitchRenderer(GSRendererType new_renderer)
|
|||
pxFailRel("Failed to reopen GS for renderer switch.");
|
||||
}
|
||||
|
||||
void GSResetAPIState()
|
||||
{
|
||||
if (!g_gs_device)
|
||||
return;
|
||||
|
||||
g_gs_device->ResetAPIState();
|
||||
}
|
||||
|
||||
void GSRestoreAPIState()
|
||||
{
|
||||
if (!g_gs_device)
|
||||
return;
|
||||
|
||||
g_gs_device->RestoreAPIState();
|
||||
}
|
||||
|
||||
bool GSSaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,
|
||||
u32* width, u32* height, std::vector<u32>* pixels)
|
||||
{
|
||||
|
|
|
@ -20,8 +20,21 @@
|
|||
#include "pcsx2/Config.h"
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
enum class RenderAPI
|
||||
{
|
||||
None,
|
||||
D3D11,
|
||||
Metal,
|
||||
D3D12,
|
||||
Vulkan,
|
||||
OpenGL,
|
||||
OpenGLES
|
||||
};
|
||||
|
||||
// ST_WRITE is defined in libc, avoid this
|
||||
enum stateType
|
||||
|
@ -42,6 +55,13 @@ enum class GSVideoMode : u8
|
|||
HDTV_1080I
|
||||
};
|
||||
|
||||
enum class GSDisplayAlignment
|
||||
{
|
||||
Center,
|
||||
LeftOrTop,
|
||||
RightOrBottom
|
||||
};
|
||||
|
||||
extern Pcsx2Config::GSOptions GSConfig;
|
||||
|
||||
class HostDisplay;
|
||||
|
@ -53,7 +73,7 @@ s16 GSLookupBeforeDrawFunctionId(const std::string_view& name);
|
|||
int GSinit();
|
||||
void GSshutdown();
|
||||
bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* basemem);
|
||||
bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config);
|
||||
bool GSreopen(bool recreate_device, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config);
|
||||
void GSreset(bool hardware_reset);
|
||||
void GSclose();
|
||||
void GSgifSoftReset(u32 mask);
|
||||
|
@ -74,8 +94,15 @@ bool GSBeginCapture(std::string filename);
|
|||
void GSEndCapture();
|
||||
void GSPresentCurrentFrame();
|
||||
void GSThrottlePresentation();
|
||||
void GSsetGameCRC(u32 crc);
|
||||
void GSSetGameCRC(u32 crc);
|
||||
void GSSetDisplayAlignment(GSDisplayAlignment alignment);
|
||||
void GSResizeDisplayWindow(int width, int height, float scale);
|
||||
void GSUpdateDisplayWindow();
|
||||
void GSSetVSyncMode(VsyncMode mode);
|
||||
|
||||
bool GSGetHostRefreshRate(float* refresh_rate);
|
||||
void GSGetAdaptersAndFullscreenModes(
|
||||
GSRendererType renderer, std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes);
|
||||
GSVideoMode GSgetDisplayMode();
|
||||
void GSgetInternalResolution(int* width, int* height);
|
||||
void GSgetStats(std::string& info);
|
||||
|
@ -88,8 +115,6 @@ void GSTranslateWindowToDisplayCoordinates(float window_x, float window_y, float
|
|||
|
||||
void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config);
|
||||
void GSSwitchRenderer(GSRendererType new_renderer);
|
||||
void GSResetAPIState();
|
||||
void GSRestoreAPIState();
|
||||
bool GSSaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,
|
||||
u32* width, u32* height, std::vector<u32>* pixels);
|
||||
void GSJoinSnapshotThreads();
|
||||
|
@ -100,3 +125,28 @@ struct GSError
|
|||
struct GSRecoverableError : GSError
|
||||
{
|
||||
};
|
||||
|
||||
namespace Host
|
||||
{
|
||||
/// Called when the GS is creating a render device.
|
||||
std::optional<WindowInfo> AcquireRenderWindow(RenderAPI api);
|
||||
|
||||
/// Called on the MTGS thread when a request to update the display is received.
|
||||
/// This could be a fullscreen transition, for example.
|
||||
std::optional<WindowInfo> UpdateRenderWindow();
|
||||
|
||||
/// Called before drawing the OSD and other display elements.
|
||||
void BeginPresentFrame();
|
||||
|
||||
/// Called when the GS is finished with a render window.
|
||||
void ReleaseRenderWindow();
|
||||
|
||||
/// Returns true if the hosting application is currently fullscreen.
|
||||
bool IsFullscreen();
|
||||
|
||||
/// Alters fullscreen state of hosting application.
|
||||
void SetFullscreen(bool enabled);
|
||||
|
||||
/// Returns the desired vsync mode, depending on the runtime environment.
|
||||
VsyncMode GetEffectiveVSyncMode();
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include "Host.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
const char* shaderName(ShaderConvert value)
|
||||
|
@ -91,23 +93,129 @@ GSDevice::~GSDevice()
|
|||
pxAssert(m_pool.empty() && !m_merge && !m_weavebob && !m_blend && !m_mad && !m_target_tmp && !m_cas);
|
||||
}
|
||||
|
||||
bool GSDevice::Create()
|
||||
const char* GSDevice::RenderAPIToString(RenderAPI api)
|
||||
{
|
||||
switch (api)
|
||||
{
|
||||
// clang-format off
|
||||
#define CASE(x) case RenderAPI::x: return #x
|
||||
CASE(None);
|
||||
CASE(D3D11);
|
||||
CASE(D3D12);
|
||||
CASE(Metal);
|
||||
CASE(Vulkan);
|
||||
CASE(OpenGL);
|
||||
CASE(OpenGLES);
|
||||
#undef CASE
|
||||
// clang-format on
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
bool GSDevice::ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate)
|
||||
{
|
||||
if (!mode.empty())
|
||||
{
|
||||
std::string_view::size_type sep1 = mode.find('x');
|
||||
if (sep1 != std::string_view::npos)
|
||||
{
|
||||
std::optional<u32> owidth = StringUtil::FromChars<u32>(mode.substr(0, sep1));
|
||||
sep1++;
|
||||
|
||||
while (sep1 < mode.length() && std::isspace(mode[sep1]))
|
||||
sep1++;
|
||||
|
||||
if (owidth.has_value() && sep1 < mode.length())
|
||||
{
|
||||
std::string_view::size_type sep2 = mode.find('@', sep1);
|
||||
if (sep2 != std::string_view::npos)
|
||||
{
|
||||
std::optional<u32> oheight = StringUtil::FromChars<u32>(mode.substr(sep1, sep2 - sep1));
|
||||
sep2++;
|
||||
|
||||
while (sep2 < mode.length() && std::isspace(mode[sep2]))
|
||||
sep2++;
|
||||
|
||||
if (oheight.has_value() && sep2 < mode.length())
|
||||
{
|
||||
std::optional<float> orefresh_rate = StringUtil::FromChars<float>(mode.substr(sep2));
|
||||
if (orefresh_rate.has_value())
|
||||
{
|
||||
*width = owidth.value();
|
||||
*height = oheight.value();
|
||||
*refresh_rate = orefresh_rate.value();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
*refresh_rate = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string GSDevice::GetFullscreenModeString(u32 width, u32 height, float refresh_rate)
|
||||
{
|
||||
return StringUtil::StdStringFromFormat("%u x %u @ %f hz", width, height, refresh_rate);
|
||||
}
|
||||
|
||||
bool GSDevice::Create(const WindowInfo& wi, VsyncMode vsync)
|
||||
{
|
||||
m_window_info = wi;
|
||||
m_vsync_mode = vsync;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSDevice::Destroy()
|
||||
{
|
||||
if (m_imgui_font)
|
||||
{
|
||||
Recycle(m_imgui_font);
|
||||
m_imgui_font = nullptr;
|
||||
}
|
||||
|
||||
ClearCurrent();
|
||||
PurgePool();
|
||||
}
|
||||
|
||||
void GSDevice::ResetAPIState()
|
||||
bool GSDevice::GetHostRefreshRate(float* refresh_rate)
|
||||
{
|
||||
if (m_window_info.surface_refresh_rate > 0.0f)
|
||||
{
|
||||
*refresh_rate = m_window_info.surface_refresh_rate;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSDevice::RestoreAPIState()
|
||||
return WindowInfo::QueryRefreshRateForWindow(m_window_info, refresh_rate);
|
||||
}
|
||||
|
||||
bool GSDevice::UpdateImGuiFontTexture()
|
||||
{
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
ImGui::GetIO().Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
const GSVector4i r(0, 0, width, height);
|
||||
const int pitch = sizeof(u32) * width;
|
||||
|
||||
if (m_imgui_font && m_imgui_font->GetWidth() == width && m_imgui_font->GetHeight() == height)
|
||||
return m_imgui_font->Update(r, pixels, pitch);
|
||||
|
||||
GSTexture* new_font = CreateTexture(width, height, 1, GSTexture::Format::Color);
|
||||
if (!new_font || !new_font->Update(r, pixels, pitch))
|
||||
return false;
|
||||
|
||||
if (m_imgui_font)
|
||||
Recycle(m_imgui_font);
|
||||
|
||||
m_imgui_font = new_font;
|
||||
ImGui::GetIO().Fonts->SetTexID(new_font->GetNativeHandle());
|
||||
return true;
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format, bool clear, bool prefer_reuse)
|
||||
|
@ -182,11 +290,6 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
|
|||
return t;
|
||||
}
|
||||
|
||||
std::unique_ptr<GSDownloadTexture> GSDevice::CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void GSDevice::Recycle(GSTexture* t)
|
||||
{
|
||||
if (!t)
|
||||
|
@ -208,6 +311,12 @@ void GSDevice::Recycle(GSTexture* t)
|
|||
}
|
||||
}
|
||||
|
||||
bool GSDevice::UsesLowerLeftOrigin() const
|
||||
{
|
||||
const RenderAPI api = GetRenderAPI();
|
||||
return (api == RenderAPI::OpenGL || api == RenderAPI::OpenGLES);
|
||||
}
|
||||
|
||||
void GSDevice::AgePool()
|
||||
{
|
||||
m_frame++;
|
||||
|
@ -229,10 +338,6 @@ void GSDevice::PurgePool()
|
|||
m_pool_memory_usage = 0;
|
||||
}
|
||||
|
||||
void GSDevice::ClearSamplerCache()
|
||||
{
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear)
|
||||
{
|
||||
return FetchSurface(GSTexture::Type::RenderTarget, w, h, 1, format, clear, true);
|
||||
|
|
|
@ -17,15 +17,14 @@
|
|||
|
||||
#include "common/HashCombine.h"
|
||||
#include "common/WindowInfo.h"
|
||||
#include "GSFastList.h"
|
||||
#include "GSTexture.h"
|
||||
#include "GSVertex.h"
|
||||
#include "GS/GS.h"
|
||||
#include "GS/Renderers/Common/GSFastList.h"
|
||||
#include "GS/Renderers/Common/GSTexture.h"
|
||||
#include "GS/Renderers/Common/GSVertex.h"
|
||||
#include "GS/GSAlignedClass.h"
|
||||
#include "GS/GSExtra.h"
|
||||
#include <array>
|
||||
|
||||
class HostDisplay;
|
||||
|
||||
enum class ShaderConvert
|
||||
{
|
||||
COPY = 0,
|
||||
|
@ -694,6 +693,22 @@ struct alignas(16) GSHWDrawConfig
|
|||
class GSDevice : public GSAlignedClass<32>
|
||||
{
|
||||
public:
|
||||
enum class PresentResult
|
||||
{
|
||||
OK,
|
||||
FrameSkipped,
|
||||
DeviceLost
|
||||
};
|
||||
|
||||
enum class DebugMessageCategory
|
||||
{
|
||||
Cache,
|
||||
Reg,
|
||||
Debug,
|
||||
Message,
|
||||
Performance
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
struct FeatureSupport
|
||||
{
|
||||
|
@ -747,6 +762,7 @@ public:
|
|||
private:
|
||||
FastList<GSTexture*> m_pool;
|
||||
u64 m_pool_memory_usage = 0;
|
||||
|
||||
static const std::array<HWBlend, 3*3*3*3> m_blendMap;
|
||||
static const std::array<u8, 16> m_replaceDualSrcBlendMap;
|
||||
|
||||
|
@ -756,6 +772,11 @@ protected:
|
|||
static constexpr u32 MAX_POOLED_TEXTURES = 300;
|
||||
static constexpr u32 NUM_CAS_CONSTANTS = 12; // 8 plus src offset x/y, 16 byte alignment
|
||||
|
||||
WindowInfo m_window_info;
|
||||
VsyncMode m_vsync_mode = VsyncMode::Off;
|
||||
|
||||
GSTexture* m_imgui_font = nullptr;
|
||||
|
||||
GSTexture* m_merge = nullptr;
|
||||
GSTexture* m_weavebob = nullptr;
|
||||
GSTexture* m_blend = nullptr;
|
||||
|
@ -781,8 +802,8 @@ protected:
|
|||
|
||||
virtual void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) = 0;
|
||||
virtual void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) = 0;
|
||||
virtual void DoFXAA(GSTexture* sTex, GSTexture* dTex) {}
|
||||
virtual void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) {}
|
||||
virtual void DoFXAA(GSTexture* sTex, GSTexture* dTex) = 0;
|
||||
virtual void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) = 0;
|
||||
|
||||
/// Resolves CAS shader includes for the specified source.
|
||||
static bool GetCASShaderSource(std::string* source);
|
||||
|
@ -794,58 +815,111 @@ public:
|
|||
GSDevice();
|
||||
virtual ~GSDevice();
|
||||
|
||||
/// Returns a string representing the specified API.
|
||||
static const char* RenderAPIToString(RenderAPI api);
|
||||
|
||||
/// Parses a fullscreen mode into its components (width * height @ refresh hz)
|
||||
static bool ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate);
|
||||
|
||||
/// Converts a fullscreen mode to a string.
|
||||
static std::string GetFullscreenModeString(u32 width, u32 height, float refresh_rate);
|
||||
|
||||
__fi unsigned int GetFrameNumber() const { return m_frame; }
|
||||
__fi u64 GetPoolMemoryUsage() const { return m_pool_memory_usage; }
|
||||
|
||||
__fi FeatureSupport Features() const { return m_features; }
|
||||
|
||||
__fi const WindowInfo& GetWindowInfo() const { return m_window_info; }
|
||||
__fi s32 GetWindowWidth() const { return static_cast<s32>(m_window_info.surface_width); }
|
||||
__fi s32 GetWindowHeight() const { return static_cast<s32>(m_window_info.surface_height); }
|
||||
__fi GSVector2i GetWindowSize() const { return GSVector2i(static_cast<s32>(m_window_info.surface_width), static_cast<s32>(m_window_info.surface_height)); }
|
||||
__fi float GetWindowScale() const { return m_window_info.surface_scale; }
|
||||
__fi VsyncMode GetVsyncMode() const { return m_vsync_mode; }
|
||||
|
||||
__fi GSTexture* GetCurrent() const { return m_current; }
|
||||
|
||||
void Recycle(GSTexture* t);
|
||||
|
||||
enum
|
||||
{
|
||||
Windowed,
|
||||
Fullscreen,
|
||||
DontCare
|
||||
};
|
||||
/// Returns true if it's an OpenGL-based renderer.
|
||||
bool UsesLowerLeftOrigin() const;
|
||||
|
||||
enum class DebugMessageCategory
|
||||
{
|
||||
Cache,
|
||||
Reg,
|
||||
Debug,
|
||||
Message,
|
||||
Performance
|
||||
};
|
||||
/// Recreates the font, call when the window scaling changes.
|
||||
bool UpdateImGuiFontTexture();
|
||||
|
||||
virtual bool Create();
|
||||
virtual bool Create(const WindowInfo& wi, VsyncMode vsync);
|
||||
virtual void Destroy();
|
||||
|
||||
virtual void ResetAPIState();
|
||||
virtual void RestoreAPIState();
|
||||
/// Returns the graphics API used by this device.
|
||||
virtual RenderAPI GetRenderAPI() const = 0;
|
||||
|
||||
virtual void ClearRenderTarget(GSTexture* t, const GSVector4& c) {}
|
||||
virtual void ClearRenderTarget(GSTexture* t, u32 c) {}
|
||||
virtual void InvalidateRenderTarget(GSTexture* t) {}
|
||||
virtual void ClearDepth(GSTexture* t) {}
|
||||
virtual void ClearStencil(GSTexture* t, u8 c) {}
|
||||
/// Returns true if we have a window we're rendering into.
|
||||
virtual bool HasSurface() const = 0;
|
||||
|
||||
virtual void PushDebugGroup(const char* fmt, ...) {}
|
||||
virtual void PopDebugGroup() {}
|
||||
virtual void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) {}
|
||||
/// Destroys the surface we're currently drawing to.
|
||||
virtual void DestroySurface() = 0;
|
||||
|
||||
/// Switches to a new window/surface.
|
||||
virtual bool ChangeWindow(const WindowInfo& wi) = 0;
|
||||
|
||||
/// Call when the window size changes externally to recreate any resources.
|
||||
virtual void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) = 0;
|
||||
|
||||
/// Returns true if exclusive fullscreen is supported.
|
||||
virtual bool SupportsExclusiveFullscreen() const = 0;
|
||||
|
||||
/// Returns true if exclusive fullscreen is active.
|
||||
virtual bool IsExclusiveFullscreen() = 0;
|
||||
|
||||
/// Attempts to switch to the specified mode in exclusive fullscreen.
|
||||
virtual bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) = 0;
|
||||
|
||||
/// Returns false if the window was completely occluded. If frame_skip is set, the frame won't be
|
||||
/// displayed, but the GPU command queue will still be flushed.
|
||||
virtual PresentResult BeginPresent(bool frame_skip) = 0;
|
||||
|
||||
/// Presents the frame to the display.
|
||||
virtual void EndPresent() = 0;
|
||||
|
||||
/// Changes vsync mode for this display.
|
||||
virtual void SetVSync(VsyncMode mode) = 0;
|
||||
|
||||
/// Returns the effective refresh rate of this display.
|
||||
virtual bool GetHostRefreshRate(float* refresh_rate);
|
||||
|
||||
/// Returns a string of information about the graphics driver being used.
|
||||
virtual std::string GetDriverInfo() const = 0;
|
||||
|
||||
/// Enables/disables GPU frame timing.
|
||||
virtual bool SetGPUTimingEnabled(bool enabled) = 0;
|
||||
|
||||
/// Returns the amount of GPU time utilized since the last time this method was called.
|
||||
virtual float GetAndResetAccumulatedGPUTime() = 0;
|
||||
|
||||
virtual void ClearRenderTarget(GSTexture* t, const GSVector4& c) = 0;
|
||||
virtual void ClearRenderTarget(GSTexture* t, u32 c) = 0;
|
||||
virtual void InvalidateRenderTarget(GSTexture* t) = 0;
|
||||
virtual void ClearDepth(GSTexture* t) = 0;
|
||||
virtual void ClearStencil(GSTexture* t, u8 c) = 0;
|
||||
|
||||
virtual void PushDebugGroup(const char* fmt, ...) = 0;
|
||||
virtual void PopDebugGroup() = 0;
|
||||
virtual void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) = 0;
|
||||
|
||||
GSTexture* CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear = true);
|
||||
GSTexture* CreateDepthStencil(int w, int h, GSTexture::Format format, bool clear = true);
|
||||
GSTexture* CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse = false);
|
||||
GSTexture::Format GetDefaultTextureFormat(GSTexture::Type type);
|
||||
|
||||
virtual std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format);
|
||||
virtual std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) = 0;
|
||||
|
||||
virtual void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) {}
|
||||
virtual void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true) {}
|
||||
virtual void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha) {}
|
||||
virtual void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) = 0;
|
||||
virtual void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true) = 0;
|
||||
virtual void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha) = 0;
|
||||
|
||||
void StretchRect(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true);
|
||||
|
||||
/// Performs a screen blit for display. If dTex is null, it assumes you are writing to the system framebuffer/swap chain.
|
||||
virtual void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear) {}
|
||||
virtual void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear) = 0;
|
||||
|
||||
/// Same as doing StretchRect for each item, except tries to batch together rectangles in as few draws as possible.
|
||||
/// The provided list should be sorted by texture, the implementations only check if it's the same as the last.
|
||||
|
@ -855,18 +929,14 @@ public:
|
|||
static void SortMultiStretchRects(MultiStretchRect* rects, u32 num_rects);
|
||||
|
||||
/// Updates a GPU CLUT texture from a source texture.
|
||||
virtual void UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) {}
|
||||
virtual void UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) = 0;
|
||||
|
||||
/// Converts a colour format to an indexed format texture.
|
||||
virtual void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) {}
|
||||
virtual void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) = 0;
|
||||
|
||||
/// Converts a colour format to an indexed format texture.
|
||||
virtual void ConvertToIndexedTexture(GSTexture* sTex, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) {}
|
||||
virtual void RenderHW(GSHWDrawConfig& config) = 0;
|
||||
|
||||
virtual void RenderHW(GSHWDrawConfig& config) {}
|
||||
|
||||
__fi FeatureSupport Features() const { return m_features; }
|
||||
__fi GSTexture* GetCurrent() const { return m_current; }
|
||||
virtual void ClearSamplerCache() = 0;
|
||||
|
||||
void ClearCurrent();
|
||||
void Merge(GSTexture* sTex[3], GSVector4* sRect, GSVector4* dRect, const GSVector2i& fs, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c);
|
||||
|
@ -887,8 +957,6 @@ public:
|
|||
void AgePool();
|
||||
void PurgePool();
|
||||
|
||||
virtual void ClearSamplerCache();
|
||||
|
||||
__fi static constexpr bool IsDualSourceBlendFactor(u8 factor)
|
||||
{
|
||||
return (factor == SRC1_ALPHA || factor == INV_SRC1_ALPHA || factor == SRC1_COLOR
|
||||
|
|
|
@ -14,22 +14,27 @@
|
|||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "GSRenderer.h"
|
||||
|
||||
#include "Frontend/FullscreenUI.h"
|
||||
#include "Frontend/ImGuiManager.h"
|
||||
#include "GS/Renderers/Common/GSRenderer.h"
|
||||
#include "GS/GSCapture.h"
|
||||
#include "GS/GSGL.h"
|
||||
#include "GSDumpReplayer.h"
|
||||
#include "Host.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "PerformanceMetrics.h"
|
||||
#include "pcsx2/Config.h"
|
||||
#include "IconsFontAwesome5.h"
|
||||
#include "VMManager.h"
|
||||
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Image.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/Timer.h"
|
||||
|
||||
#include "fmt/core.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
|
@ -53,6 +58,9 @@ static GSVector4 s_last_draw_rect;
|
|||
// Last time we reset the renderer due to a GPU crash, if any.
|
||||
static Common::Timer::Value s_last_gpu_reset_time;
|
||||
|
||||
// Screen alignment
|
||||
static GSDisplayAlignment s_display_alignment = GSDisplayAlignment::Center;
|
||||
|
||||
GSRenderer::GSRenderer()
|
||||
: m_shader_time_start(Common::Timer::GetCurrentValue())
|
||||
{
|
||||
|
@ -230,9 +238,9 @@ bool GSRenderer::Merge(int field)
|
|||
g_gs_device->FXAA();
|
||||
|
||||
// Sharpens biinear at lower resolutions, almost nearest but with more uniform pixels.
|
||||
if (GSConfig.LinearPresent == GSPostBilinearMode::BilinearSharp && (g_host_display->GetWindowWidth() > fs.x || g_host_display->GetWindowHeight() > fs.y))
|
||||
if (GSConfig.LinearPresent == GSPostBilinearMode::BilinearSharp && (g_gs_device->GetWindowWidth() > fs.x || g_gs_device->GetWindowHeight() > fs.y))
|
||||
{
|
||||
g_gs_device->Resize(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight());
|
||||
g_gs_device->Resize(g_gs_device->GetWindowWidth(), g_gs_device->GetWindowHeight());
|
||||
}
|
||||
|
||||
if (m_scanmask_used)
|
||||
|
@ -271,7 +279,7 @@ static float GetCurrentAspectRatioFloat(bool is_progressive)
|
|||
return ars[static_cast<u32>(GSConfig.AspectRatio) + (3u * (is_progressive && GSConfig.AspectRatio == AspectRatioType::RAuto4_3_3_2))];
|
||||
}
|
||||
|
||||
static GSVector4 CalculateDrawDstRect(s32 window_width, s32 window_height, const GSVector4i& src_rect, const GSVector2i& src_size, HostDisplay::Alignment alignment, bool flip_y, bool is_progressive)
|
||||
static GSVector4 CalculateDrawDstRect(s32 window_width, s32 window_height, const GSVector4i& src_rect, const GSVector2i& src_size, GSDisplayAlignment alignment, bool flip_y, bool is_progressive)
|
||||
{
|
||||
const float f_width = static_cast<float>(window_width);
|
||||
const float f_height = static_cast<float>(window_height);
|
||||
|
@ -318,7 +326,7 @@ static GSVector4 CalculateDrawDstRect(s32 window_width, s32 window_height, const
|
|||
const GSVector2i fs = GSVector2i(static_cast<int>(static_cast<float>(resolution.x) * g_gs_renderer->GetUpscaleMultiplier()),
|
||||
static_cast<int>(static_cast<float>(resolution.y) * g_gs_renderer->GetUpscaleMultiplier()));
|
||||
|
||||
if (g_host_display->GetWindowWidth() > fs.x || g_host_display->GetWindowHeight() > fs.y)
|
||||
if (g_gs_device->GetWindowWidth() > fs.x || g_gs_device->GetWindowHeight() > fs.y)
|
||||
{
|
||||
t_width *= static_cast<float>(fs.x) / src_rect.width();
|
||||
t_height *= static_cast<float>(fs.y) / src_rect.height();
|
||||
|
@ -348,13 +356,13 @@ static GSVector4 CalculateDrawDstRect(s32 window_width, s32 window_height, const
|
|||
{
|
||||
switch (alignment)
|
||||
{
|
||||
case HostDisplay::Alignment::Center:
|
||||
case GSDisplayAlignment::Center:
|
||||
target_x = (f_width - target_width) * 0.5f;
|
||||
break;
|
||||
case HostDisplay::Alignment::RightOrBottom:
|
||||
case GSDisplayAlignment::RightOrBottom:
|
||||
target_x = (f_width - target_width);
|
||||
break;
|
||||
case HostDisplay::Alignment::LeftOrTop:
|
||||
case GSDisplayAlignment::LeftOrTop:
|
||||
default:
|
||||
target_x = 0.0f;
|
||||
break;
|
||||
|
@ -368,13 +376,13 @@ static GSVector4 CalculateDrawDstRect(s32 window_width, s32 window_height, const
|
|||
{
|
||||
switch (alignment)
|
||||
{
|
||||
case HostDisplay::Alignment::Center:
|
||||
case GSDisplayAlignment::Center:
|
||||
target_y = (f_height - target_height) * 0.5f;
|
||||
break;
|
||||
case HostDisplay::Alignment::RightOrBottom:
|
||||
case GSDisplayAlignment::RightOrBottom:
|
||||
target_y = (f_height - target_height);
|
||||
break;
|
||||
case HostDisplay::Alignment::LeftOrTop:
|
||||
case GSDisplayAlignment::LeftOrTop:
|
||||
default:
|
||||
target_y = 0.0f;
|
||||
break;
|
||||
|
@ -466,11 +474,21 @@ void GSJoinSnapshotThreads()
|
|||
|
||||
bool GSRenderer::BeginPresentFrame(bool frame_skip)
|
||||
{
|
||||
const HostDisplay::PresentResult result = Host::BeginPresentFrame(frame_skip);
|
||||
if (result == HostDisplay::PresentResult::OK)
|
||||
return true;
|
||||
else if (result == HostDisplay::PresentResult::FrameSkipped)
|
||||
Host::BeginPresentFrame();
|
||||
|
||||
const GSDevice::PresentResult res = g_gs_device->BeginPresent(frame_skip);
|
||||
if (res == GSDevice::PresentResult::FrameSkipped)
|
||||
{
|
||||
// If we're skipping a frame, we need to reset imgui's state, since
|
||||
// we won't be calling EndPresentFrame().
|
||||
ImGuiManager::SkipFrame();
|
||||
return false;
|
||||
}
|
||||
else if (res == GSDevice::PresentResult::OK)
|
||||
{
|
||||
// All good!
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we're constantly crashing on something in particular, we don't want to end up in an
|
||||
// endless reset loop.. that'd probably end up leaking memory and/or crashing us for other
|
||||
|
@ -498,6 +516,17 @@ bool GSRenderer::BeginPresentFrame(bool frame_skip)
|
|||
return false;
|
||||
}
|
||||
|
||||
void GSRenderer::EndPresentFrame()
|
||||
{
|
||||
if (GSDumpReplayer::IsReplayingDump())
|
||||
GSDumpReplayer::RenderUI();
|
||||
|
||||
FullscreenUI::Render();
|
||||
ImGuiManager::RenderOSD();
|
||||
g_gs_device->EndPresent();
|
||||
ImGuiManager::NewFrame();
|
||||
}
|
||||
|
||||
void GSRenderer::VSync(u32 field, bool registers_written)
|
||||
{
|
||||
Flush(GSFlushReason::VSYNC);
|
||||
|
@ -542,10 +571,9 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
|||
|
||||
if (skip_frame)
|
||||
{
|
||||
g_gs_device->ResetAPIState();
|
||||
if (BeginPresentFrame(true))
|
||||
Host::EndPresentFrame();
|
||||
g_gs_device->RestoreAPIState();
|
||||
EndPresentFrame();
|
||||
|
||||
PerformanceMetrics::Update(registers_written, fb_sprite_frame, true);
|
||||
return;
|
||||
}
|
||||
|
@ -564,8 +592,8 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
|||
{
|
||||
src_rect = CalculateDrawSrcRect(current);
|
||||
src_uv = GSVector4(src_rect) / GSVector4(current->GetSize()).xyxy();
|
||||
draw_rect = CalculateDrawDstRect(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight(),
|
||||
src_rect, current->GetSize(), g_host_display->GetDisplayAlignment(), g_host_display->UsesLowerLeftOrigin(),
|
||||
draw_rect = CalculateDrawDstRect(g_gs_device->GetWindowWidth(), g_gs_device->GetWindowHeight(),
|
||||
src_rect, current->GetSize(), s_display_alignment, g_gs_device->UsesLowerLeftOrigin(),
|
||||
GetVideoMode() == GSVideoMode::SDTV_480P || (GSConfig.PCRTCOverscan && GSConfig.PCRTCOffsets));
|
||||
s_last_draw_rect = draw_rect;
|
||||
|
||||
|
@ -576,8 +604,8 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
|||
{
|
||||
// sharpen only if the IR is higher than the display resolution
|
||||
const bool sharpen_only = (GSConfig.CASMode == GSCASMode::SharpenOnly ||
|
||||
(current->GetWidth() > g_host_display->GetWindowWidth() &&
|
||||
current->GetHeight() > g_host_display->GetWindowHeight()));
|
||||
(current->GetWidth() > g_gs_device->GetWindowWidth() &&
|
||||
current->GetHeight() > g_gs_device->GetWindowHeight()));
|
||||
g_gs_device->CAS(current, src_rect, src_uv, draw_rect, sharpen_only);
|
||||
}
|
||||
else if (!cas_log_once)
|
||||
|
@ -589,7 +617,6 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
|||
}
|
||||
}
|
||||
|
||||
g_gs_device->ResetAPIState();
|
||||
if (BeginPresentFrame(false))
|
||||
{
|
||||
if (current && !blank_frame)
|
||||
|
@ -601,12 +628,12 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
|||
s_tv_shader_indices[GSConfig.TVShader], shader_time, GSConfig.LinearPresent != GSPostBilinearMode::Off);
|
||||
}
|
||||
|
||||
Host::EndPresentFrame();
|
||||
EndPresentFrame();
|
||||
|
||||
if (GSConfig.OsdShowGPU)
|
||||
PerformanceMetrics::OnGPUPresent(g_host_display->GetAndResetAccumulatedGPUTime());
|
||||
PerformanceMetrics::OnGPUPresent(g_gs_device->GetAndResetAccumulatedGPUTime());
|
||||
}
|
||||
g_gs_device->RestoreAPIState();
|
||||
|
||||
PerformanceMetrics::Update(registers_written, fb_sprite_frame, false);
|
||||
|
||||
// snapshot
|
||||
|
@ -665,8 +692,8 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
|||
const bool aspect_correct = (GSConfig.ScreenshotSize != GSScreenshotSize::InternalResolutionUncorrected);
|
||||
|
||||
if (g_gs_device->GetCurrent() && SaveSnapshotToMemory(
|
||||
internal_resolution ? 0 : g_host_display->GetWindowWidth(),
|
||||
internal_resolution ? 0 : g_host_display->GetWindowHeight(),
|
||||
internal_resolution ? 0 : g_gs_device->GetWindowWidth(),
|
||||
internal_resolution ? 0 : g_gs_device->GetWindowHeight(),
|
||||
aspect_correct, true,
|
||||
&screenshot_width, &screenshot_height, &screenshot_pixels))
|
||||
{
|
||||
|
@ -805,7 +832,6 @@ void GSRenderer::StopGSDump()
|
|||
|
||||
void GSRenderer::PresentCurrentFrame()
|
||||
{
|
||||
g_gs_device->ResetAPIState();
|
||||
if (BeginPresentFrame(false))
|
||||
{
|
||||
GSTexture* current = g_gs_device->GetCurrent();
|
||||
|
@ -813,8 +839,8 @@ void GSRenderer::PresentCurrentFrame()
|
|||
{
|
||||
const GSVector4i src_rect(CalculateDrawSrcRect(current));
|
||||
const GSVector4 src_uv(GSVector4(src_rect) / GSVector4(current->GetSize()).xyxy());
|
||||
const GSVector4 draw_rect(CalculateDrawDstRect(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight(),
|
||||
src_rect, current->GetSize(), g_host_display->GetDisplayAlignment(), g_host_display->UsesLowerLeftOrigin(),
|
||||
const GSVector4 draw_rect(CalculateDrawDstRect(g_gs_device->GetWindowWidth(), g_gs_device->GetWindowHeight(),
|
||||
src_rect, current->GetSize(), s_display_alignment, g_gs_device->UsesLowerLeftOrigin(),
|
||||
GetVideoMode() == GSVideoMode::SDTV_480P || (GSConfig.PCRTCOverscan && GSConfig.PCRTCOffsets)));
|
||||
s_last_draw_rect = draw_rect;
|
||||
|
||||
|
@ -825,9 +851,8 @@ void GSRenderer::PresentCurrentFrame()
|
|||
s_tv_shader_indices[GSConfig.TVShader], shader_time, GSConfig.LinearPresent != GSPostBilinearMode::Off);
|
||||
}
|
||||
|
||||
Host::EndPresentFrame();
|
||||
EndPresentFrame();
|
||||
}
|
||||
g_gs_device->RestoreAPIState();
|
||||
}
|
||||
|
||||
void GSTranslateWindowToDisplayCoordinates(float window_x, float window_y, float* display_x, float* display_y)
|
||||
|
@ -847,6 +872,11 @@ void GSTranslateWindowToDisplayCoordinates(float window_x, float window_y, float
|
|||
*display_y = rel_y / draw_height;
|
||||
}
|
||||
|
||||
void GSSetDisplayAlignment(GSDisplayAlignment alignment)
|
||||
{
|
||||
s_display_alignment = alignment;
|
||||
}
|
||||
|
||||
bool GSRenderer::BeginCapture(std::string filename)
|
||||
{
|
||||
const GSVector2i capture_resolution(GSConfig.VideoCaptureAutoResolution ?
|
||||
|
@ -910,7 +940,7 @@ bool GSRenderer::SaveSnapshotToMemory(u32 window_width, u32 window_height, bool
|
|||
else
|
||||
{
|
||||
draw_rect = CalculateDrawDstRect(window_width, window_height, src_rect, current->GetSize(),
|
||||
HostDisplay::Alignment::LeftOrTop, false, is_progressive);
|
||||
GSDisplayAlignment::LeftOrTop, false, is_progressive);
|
||||
}
|
||||
const u32 draw_width = static_cast<u32>(draw_rect.z - draw_rect.x);
|
||||
const u32 draw_height = static_cast<u32>(draw_rect.w - draw_rect.y);
|
||||
|
|
|
@ -24,6 +24,7 @@ class GSRenderer : public GSState
|
|||
private:
|
||||
bool Merge(int field);
|
||||
bool BeginPresentFrame(bool frame_skip);
|
||||
void EndPresentFrame();
|
||||
|
||||
u64 m_shader_time_start = 0;
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "GS/Renderers/Common/GSDevice.h"
|
||||
#include "GS/Renderers/DX11/D3D.h"
|
||||
#include "GS/GSExtra.h"
|
||||
|
||||
|
@ -80,6 +81,45 @@ std::vector<std::string> D3D::GetAdapterNames(IDXGIFactory5* factory)
|
|||
return adapter_names;
|
||||
}
|
||||
|
||||
std::vector<std::string> D3D::GetFullscreenModes(IDXGIFactory5* factory, const std::string_view& adapter_name)
|
||||
{
|
||||
std::vector<std::string> modes;
|
||||
HRESULT hr;
|
||||
|
||||
wil::com_ptr_nothrow<IDXGIAdapter1> adapter = GetChosenOrFirstAdapter(factory, adapter_name);
|
||||
if (!adapter)
|
||||
return modes;
|
||||
|
||||
wil::com_ptr_nothrow<IDXGIOutput> output;
|
||||
if (FAILED(hr = adapter->EnumOutputs(0, &output)))
|
||||
{
|
||||
Console.Error("EnumOutputs() failed: %08X", hr);
|
||||
return modes;
|
||||
}
|
||||
|
||||
UINT num_modes = 0;
|
||||
if (FAILED(hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, nullptr)))
|
||||
{
|
||||
Console.Error("GetDisplayModeList() failed: %08X", hr);
|
||||
return modes;
|
||||
}
|
||||
|
||||
std::vector<DXGI_MODE_DESC> dmodes(num_modes);
|
||||
if (FAILED(hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, dmodes.data())))
|
||||
{
|
||||
Console.Error("GetDisplayModeList() (2) failed: %08X", hr);
|
||||
return modes;
|
||||
}
|
||||
|
||||
for (const DXGI_MODE_DESC& mode : dmodes)
|
||||
{
|
||||
modes.push_back(GSDevice::GetFullscreenModeString(mode.Width, mode.Height,
|
||||
static_cast<float>(mode.RefreshRate.Numerator) / static_cast<float>(mode.RefreshRate.Denominator)));
|
||||
}
|
||||
|
||||
return modes;
|
||||
}
|
||||
|
||||
wil::com_ptr_nothrow<IDXGIAdapter1> D3D::GetAdapterByName(IDXGIFactory5* factory, const std::string_view& name)
|
||||
{
|
||||
if (name.empty())
|
||||
|
|
|
@ -33,6 +33,9 @@ namespace D3D
|
|||
// returns a list of all adapter names
|
||||
std::vector<std::string> GetAdapterNames(IDXGIFactory5* factory);
|
||||
|
||||
// returns a list of fullscreen modes for the specified adapter
|
||||
std::vector<std::string> GetFullscreenModes(IDXGIFactory5* factory, const std::string_view& adapter_name);
|
||||
|
||||
// get an adapter based on name
|
||||
wil::com_ptr_nothrow<IDXGIAdapter1> GetAdapterByName(IDXGIFactory5* factory, const std::string_view& name);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -21,7 +21,7 @@
|
|||
#include "common/D3D11/ShaderCache.h"
|
||||
#include <unordered_map>
|
||||
#include <wil/com.h>
|
||||
#include <dxgi1_3.h>
|
||||
#include <dxgi1_5.h>
|
||||
#include <d3d11_1.h>
|
||||
|
||||
struct GSVertexShader11
|
||||
|
@ -116,32 +116,47 @@ private:
|
|||
MAX_SAMPLERS = 1,
|
||||
VERTEX_BUFFER_SIZE = 32 * 1024 * 1024,
|
||||
INDEX_BUFFER_SIZE = 16 * 1024 * 1024,
|
||||
NUM_TIMESTAMP_QUERIES = 5,
|
||||
};
|
||||
|
||||
int m_d3d_texsize;
|
||||
|
||||
void SetFeatures();
|
||||
|
||||
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) final;
|
||||
bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode);
|
||||
bool CreateSwapChainRTV();
|
||||
|
||||
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) final;
|
||||
bool CreateTimestampQueries();
|
||||
void DestroyTimestampQueries();
|
||||
void PopTimestampQuery();
|
||||
void KickTimestampQuery();
|
||||
|
||||
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) final;
|
||||
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) final;
|
||||
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
|
||||
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final;
|
||||
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) override;
|
||||
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) override;
|
||||
void DoFXAA(GSTexture* sTex, GSTexture* dTex) override;
|
||||
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) override;
|
||||
|
||||
bool CreateCASShaders();
|
||||
bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) final;
|
||||
bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) override;
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11Device> m_dev;
|
||||
wil::com_ptr_nothrow<ID3D11DeviceContext> m_ctx;
|
||||
bool CreateImGuiResources();
|
||||
void RenderImGui();
|
||||
|
||||
wil::com_ptr_nothrow<IDXGIFactory5> m_dxgi_factory;
|
||||
wil::com_ptr_nothrow<ID3D11Device1> m_dev;
|
||||
wil::com_ptr_nothrow<ID3D11DeviceContext1> m_ctx;
|
||||
wil::com_ptr_nothrow<ID3DUserDefinedAnnotation> m_annotation;
|
||||
wil::com_ptr_nothrow<IDXGISwapChain1> m_swapchain;
|
||||
|
||||
wil::com_ptr_nothrow<IDXGISwapChain1> m_swap_chain;
|
||||
wil::com_ptr_nothrow<ID3D11RenderTargetView> m_swap_chain_rtv;
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11Buffer> m_vb;
|
||||
wil::com_ptr_nothrow<ID3D11Buffer> m_ib;
|
||||
u32 m_vb_pos = 0; // bytes
|
||||
u32 m_ib_pos = 0; // indices/sizeof(u32)
|
||||
int m_d3d_texsize = 0;
|
||||
|
||||
bool m_allow_tearing_supported = false;
|
||||
bool m_using_flip_model_swap_chain = true;
|
||||
bool m_using_allow_tearing = false;
|
||||
|
||||
struct
|
||||
{
|
||||
|
@ -166,6 +181,13 @@ private:
|
|||
ID3D11DepthStencilView* dsv;
|
||||
} m_state;
|
||||
|
||||
std::array<std::array<wil::com_ptr_nothrow<ID3D11Query>, 3>, NUM_TIMESTAMP_QUERIES> m_timestamp_queries = {};
|
||||
float m_accumulated_gpu_time = 0.0f;
|
||||
u8 m_read_timestamp_query = 0;
|
||||
u8 m_write_timestamp_query = 0;
|
||||
u8 m_waiting_timestamp_queries = 0;
|
||||
bool m_timestamp_query_started = false;
|
||||
bool m_gpu_timing_enabled = false;
|
||||
|
||||
struct
|
||||
{
|
||||
|
@ -222,6 +244,15 @@ private:
|
|||
wil::com_ptr_nothrow<ID3D11ComputeShader> cs_sharpen;
|
||||
} m_cas;
|
||||
|
||||
struct
|
||||
{
|
||||
wil::com_ptr_nothrow<ID3D11InputLayout> il;
|
||||
wil::com_ptr_nothrow<ID3D11VertexShader> vs;
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> ps;
|
||||
wil::com_ptr_nothrow<ID3D11BlendState> bs;
|
||||
wil::com_ptr_nothrow<ID3D11Buffer> vs_cb;
|
||||
} m_imgui;
|
||||
|
||||
// Shaders...
|
||||
|
||||
std::unordered_map<u32, GSVertexShader11> m_vs;
|
||||
|
@ -237,8 +268,6 @@ private:
|
|||
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
|
||||
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
|
||||
|
||||
std::unique_ptr<GSTexture11> m_download_tex;
|
||||
|
||||
D3D11::ShaderCache m_shader_cache;
|
||||
std::string m_tfx_source;
|
||||
|
||||
|
@ -247,13 +276,32 @@ public:
|
|||
~GSDevice11() override;
|
||||
|
||||
__fi static GSDevice11* GetInstance() { return static_cast<GSDevice11*>(g_gs_device.get()); }
|
||||
__fi ID3D11Device* GetD3DDevice() const { return m_dev.get(); }
|
||||
__fi ID3D11DeviceContext* GetD3DContext() const { return m_ctx.get(); }
|
||||
__fi ID3D11Device1* GetD3DDevice() const { return m_dev.get(); }
|
||||
__fi ID3D11DeviceContext1* GetD3DContext() const { return m_ctx.get(); }
|
||||
|
||||
bool Create() override;
|
||||
bool Create(const WindowInfo& wi, VsyncMode vsync) override;
|
||||
void Destroy() override;
|
||||
|
||||
void ResetAPIState() override;
|
||||
void RestoreAPIState() override;
|
||||
RenderAPI GetRenderAPI() const override;
|
||||
|
||||
bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
||||
bool SupportsExclusiveFullscreen() const override;
|
||||
bool IsExclusiveFullscreen() override;
|
||||
bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||
bool HasSurface() const override;
|
||||
void DestroySurface() override;
|
||||
std::string GetDriverInfo() const override;
|
||||
|
||||
bool GetHostRefreshRate(float* refresh_rate) override;
|
||||
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
void DrawPrimitive();
|
||||
void DrawIndexedPrimitive();
|
||||
|
@ -261,6 +309,7 @@ public:
|
|||
|
||||
void ClearRenderTarget(GSTexture* t, const GSVector4& c) override;
|
||||
void ClearRenderTarget(GSTexture* t, u32 c) override;
|
||||
void InvalidateRenderTarget(GSTexture* t) override;
|
||||
void ClearDepth(GSTexture* t) override;
|
||||
void ClearStencil(GSTexture* t, u8 c) override;
|
||||
|
||||
|
@ -268,6 +317,9 @@ public:
|
|||
void PopDebugGroup() override;
|
||||
void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) override;
|
||||
|
||||
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
|
||||
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) override;
|
||||
|
||||
void CloneTexture(GSTexture* src, GSTexture** dest, const GSVector4i& rect);
|
||||
|
||||
void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override;
|
||||
|
@ -296,7 +348,7 @@ public:
|
|||
void IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY topology);
|
||||
|
||||
void VSSetShader(ID3D11VertexShader* vs, ID3D11Buffer* vs_cb);
|
||||
void GSSetShader(ID3D11GeometryShader* gs, ID3D11Buffer* gs_cb = NULL);
|
||||
void GSSetShader(ID3D11GeometryShader* gs, ID3D11Buffer* gs_cb = nullptr);
|
||||
|
||||
void PSSetShaderResources(GSTexture* sr0, GSTexture* sr1);
|
||||
void PSSetShaderResource(int i, GSTexture* sr);
|
||||
|
@ -306,7 +358,9 @@ public:
|
|||
|
||||
void OMSetDepthStencilState(ID3D11DepthStencilState* dss, u8 sref);
|
||||
void OMSetBlendState(ID3D11BlendState* bs, float bf);
|
||||
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = NULL);
|
||||
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = nullptr);
|
||||
void SetViewport(const GSVector2i& viewport);
|
||||
void SetScissor(const GSVector4i& scissor);
|
||||
|
||||
bool CreateTextureFX();
|
||||
void SetupVS(VSSelector sel, const GSHWDrawConfig::VSConstantBuffer* cb);
|
||||
|
@ -314,11 +368,11 @@ public:
|
|||
void SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstantBuffer* cb, PSSamplerSelector ssel);
|
||||
void SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, u8 afix);
|
||||
|
||||
void RenderHW(GSHWDrawConfig& config) final;
|
||||
void RenderHW(GSHWDrawConfig& config) override;
|
||||
|
||||
void ClearSamplerCache() final;
|
||||
void ClearSamplerCache() override;
|
||||
|
||||
ID3D11Device* operator->() { return m_dev.get(); }
|
||||
operator ID3D11Device*() { return m_dev.get(); }
|
||||
operator ID3D11DeviceContext*() { return m_ctx.get(); }
|
||||
ID3D11Device1* operator->() { return m_dev.get(); }
|
||||
operator ID3D11Device1*() { return m_dev.get(); }
|
||||
operator ID3D11DeviceContext1*() { return m_ctx.get(); }
|
||||
};
|
||||
|
|
|
@ -14,6 +14,16 @@
|
|||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "GS/GS.h"
|
||||
#include "GS/GSGL.h"
|
||||
#include "GS/GSPerfMon.h"
|
||||
#include "GS/GSUtil.h"
|
||||
#include "GS/Renderers/DX11/D3D.h"
|
||||
#include "GS/Renderers/DX12/GSDevice12.h"
|
||||
#include "Host.h"
|
||||
#include "ShaderCacheVersion.h"
|
||||
|
||||
#include "common/D3D12/Builders.h"
|
||||
#include "common/D3D12/Context.h"
|
||||
#include "common/D3D12/ShaderCache.h"
|
||||
|
@ -21,15 +31,10 @@
|
|||
#include "common/Align.h"
|
||||
#include "common/ScopedGuard.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "D3D12MemAlloc.h"
|
||||
#include "GS.h"
|
||||
#include "GSDevice12.h"
|
||||
#include "GS/GSGL.h"
|
||||
#include "GS/GSPerfMon.h"
|
||||
#include "GS/GSUtil.h"
|
||||
#include "Host.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "ShaderCacheVersion.h"
|
||||
#include "imgui.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <limits>
|
||||
|
||||
|
@ -94,16 +99,52 @@ D3D_SHADER_MACRO* GSDevice12::ShaderMacro::GetPtr(void)
|
|||
return (D3D_SHADER_MACRO*)mout.data();
|
||||
}
|
||||
|
||||
GSDevice12::GSDevice12()
|
||||
GSDevice12::GSDevice12() = default;
|
||||
|
||||
GSDevice12::~GSDevice12()
|
||||
{
|
||||
std::memset(&m_pipeline_selector, 0, sizeof(m_pipeline_selector));
|
||||
pxAssert(!g_d3d12_context);
|
||||
}
|
||||
|
||||
GSDevice12::~GSDevice12() {}
|
||||
|
||||
bool GSDevice12::Create()
|
||||
RenderAPI GSDevice12::GetRenderAPI() const
|
||||
{
|
||||
if (!GSDevice::Create() || !CheckFeatures())
|
||||
return RenderAPI::D3D12;
|
||||
}
|
||||
|
||||
bool GSDevice12::HasSurface() const
|
||||
{
|
||||
return static_cast<bool>(m_swap_chain);
|
||||
}
|
||||
|
||||
bool GSDevice12::Create(const WindowInfo& wi, VsyncMode vsync)
|
||||
{
|
||||
if (!GSDevice::Create(wi, vsync))
|
||||
return false;
|
||||
|
||||
m_dxgi_factory = D3D::CreateFactory(EmuConfig.GS.UseDebugDevice);
|
||||
if (!m_dxgi_factory)
|
||||
return false;
|
||||
|
||||
ComPtr<IDXGIAdapter1> dxgi_adapter = D3D::GetAdapterByName(m_dxgi_factory.get(), EmuConfig.GS.Adapter);
|
||||
|
||||
if (!D3D12::Context::Create(m_dxgi_factory.get(), dxgi_adapter.get(), EmuConfig.GS.UseDebugDevice))
|
||||
{
|
||||
Console.Error("Failed to create D3D12 context");
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOL allow_tearing_supported = false;
|
||||
HRESULT hr = m_dxgi_factory->CheckFeatureSupport(
|
||||
DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, sizeof(allow_tearing_supported));
|
||||
m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
|
||||
|
||||
if (!CheckFeatures())
|
||||
{
|
||||
Console.Error("Your GPU does not support the required D3D12 features.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain(nullptr))
|
||||
return false;
|
||||
|
||||
{
|
||||
|
@ -157,6 +198,9 @@ bool GSDevice12::Create()
|
|||
}
|
||||
|
||||
CompileCASPipelines();
|
||||
if (!CompileImGuiPipeline())
|
||||
return false;
|
||||
|
||||
InitializeState();
|
||||
InitializeSamplers();
|
||||
return true;
|
||||
|
@ -164,23 +208,379 @@ bool GSDevice12::Create()
|
|||
|
||||
void GSDevice12::Destroy()
|
||||
{
|
||||
if (!g_d3d12_context)
|
||||
return;
|
||||
GSDevice::Destroy();
|
||||
|
||||
if (g_d3d12_context)
|
||||
{
|
||||
EndRenderPass();
|
||||
ExecuteCommandList(true);
|
||||
DestroyResources();
|
||||
GSDevice::Destroy();
|
||||
|
||||
g_d3d12_context->WaitForGPUIdle();
|
||||
GSDevice12::DestroySurface();
|
||||
g_d3d12_context->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void GSDevice12::ResetAPIState()
|
||||
bool GSDevice12::GetHostRefreshRate(float* refresh_rate)
|
||||
{
|
||||
if (m_swap_chain && IsExclusiveFullscreen())
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC desc;
|
||||
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)) && desc.BufferDesc.RefreshRate.Numerator > 0 &&
|
||||
desc.BufferDesc.RefreshRate.Denominator > 0)
|
||||
{
|
||||
DevCon.WriteLn(
|
||||
"using fs rr: %u %u", desc.BufferDesc.RefreshRate.Numerator, desc.BufferDesc.RefreshRate.Denominator);
|
||||
*refresh_rate = static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
|
||||
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return GSDevice::GetHostRefreshRate(refresh_rate);
|
||||
}
|
||||
|
||||
void GSDevice12::SetVSync(VsyncMode mode)
|
||||
{
|
||||
m_vsync_mode = mode;
|
||||
}
|
||||
|
||||
|
||||
bool GSDevice12::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
|
||||
{
|
||||
if (m_window_info.type != WindowInfo::Type::Win32)
|
||||
return false;
|
||||
|
||||
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
|
||||
RECT client_rc{};
|
||||
GetClientRect(window_hwnd, &client_rc);
|
||||
const u32 width = static_cast<u32>(client_rc.right - client_rc.left);
|
||||
const u32 height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
|
||||
swap_chain_desc.Width = width;
|
||||
swap_chain_desc.Height = height;
|
||||
swap_chain_desc.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.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
|
||||
m_using_allow_tearing = (m_allow_tearing_supported && !fullscreen_mode);
|
||||
if (m_using_allow_tearing)
|
||||
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
||||
|
||||
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
|
||||
if (fullscreen_mode)
|
||||
{
|
||||
swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
||||
swap_chain_desc.Width = fullscreen_mode->Width;
|
||||
swap_chain_desc.Height = fullscreen_mode->Height;
|
||||
fs_desc.RefreshRate = fullscreen_mode->RefreshRate;
|
||||
fs_desc.ScanlineOrdering = fullscreen_mode->ScanlineOrdering;
|
||||
fs_desc.Scaling = fullscreen_mode->Scaling;
|
||||
fs_desc.Windowed = FALSE;
|
||||
}
|
||||
|
||||
DevCon.WriteLn("Creating a %dx%d %s swap chain", swap_chain_desc.Width, swap_chain_desc.Height,
|
||||
fullscreen_mode ? "full-screen" : "windowed");
|
||||
|
||||
HRESULT hr = m_dxgi_factory->CreateSwapChainForHwnd(g_d3d12_context->GetCommandQueue(), window_hwnd,
|
||||
&swap_chain_desc, fullscreen_mode ? &fs_desc : nullptr, nullptr, m_swap_chain.put());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("CreateSwapChainForHwnd failed: 0x%08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = m_dxgi_factory->MakeWindowAssociation(window_hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
|
||||
if (FAILED(hr))
|
||||
Console.Warning("MakeWindowAssociation() to disable ALT+ENTER failed");
|
||||
|
||||
return CreateSwapChainRTV();
|
||||
}
|
||||
|
||||
bool GSDevice12::CreateSwapChainRTV()
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC swap_chain_desc;
|
||||
HRESULT hr = m_swap_chain->GetDesc(&swap_chain_desc);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
for (u32 i = 0; i < swap_chain_desc.BufferCount; i++)
|
||||
{
|
||||
ComPtr<ID3D12Resource> backbuffer;
|
||||
hr = m_swap_chain->GetBuffer(i, IID_PPV_ARGS(backbuffer.put()));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("GetBuffer for RTV failed: 0x%08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12::Texture tex;
|
||||
if (!tex.Adopt(std::move(backbuffer), DXGI_FORMAT_UNKNOWN, swap_chain_desc.BufferDesc.Format,
|
||||
DXGI_FORMAT_UNKNOWN, D3D12_RESOURCE_STATE_PRESENT))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_swap_chain_buffers.push_back(std::move(tex));
|
||||
}
|
||||
|
||||
m_window_info.surface_width = swap_chain_desc.BufferDesc.Width;
|
||||
m_window_info.surface_height = swap_chain_desc.BufferDesc.Height;
|
||||
DevCon.WriteLn("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height);
|
||||
|
||||
if (m_window_info.type == WindowInfo::Type::Win32)
|
||||
{
|
||||
BOOL fullscreen = FALSE;
|
||||
DXGI_SWAP_CHAIN_DESC desc;
|
||||
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen &&
|
||||
SUCCEEDED(m_swap_chain->GetDesc(&desc)))
|
||||
{
|
||||
m_window_info.surface_refresh_rate = static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
|
||||
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_window_info.surface_refresh_rate = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
m_current_swap_chain_buffer = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSDevice12::DestroySwapChainRTVs()
|
||||
{
|
||||
for (D3D12::Texture& buffer : m_swap_chain_buffers)
|
||||
buffer.Destroy(false);
|
||||
m_swap_chain_buffers.clear();
|
||||
m_current_swap_chain_buffer = 0;
|
||||
}
|
||||
|
||||
bool GSDevice12::ChangeWindow(const WindowInfo& new_wi)
|
||||
{
|
||||
DestroySurface();
|
||||
|
||||
m_window_info = new_wi;
|
||||
if (new_wi.type == WindowInfo::Type::Surfaceless)
|
||||
return true;
|
||||
|
||||
return CreateSwapChain(nullptr);
|
||||
}
|
||||
|
||||
void GSDevice12::DestroySurface()
|
||||
{
|
||||
ExecuteCommandList(true);
|
||||
|
||||
if (IsExclusiveFullscreen())
|
||||
SetExclusiveFullscreen(false, 0, 0, 0.0f);
|
||||
|
||||
DestroySwapChainRTVs();
|
||||
m_swap_chain.reset();
|
||||
}
|
||||
|
||||
std::string GSDevice12::GetDriverInfo() const
|
||||
{
|
||||
std::string ret = "Unknown Feature Level";
|
||||
|
||||
static constexpr std::array<std::tuple<D3D_FEATURE_LEVEL, const char*>, 4> feature_level_names = {{
|
||||
{D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_0"},
|
||||
{D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_1"},
|
||||
{D3D_FEATURE_LEVEL_11_0, "D3D_FEATURE_LEVEL_11_0"},
|
||||
{D3D_FEATURE_LEVEL_11_1, "D3D_FEATURE_LEVEL_11_1"},
|
||||
}};
|
||||
|
||||
const D3D_FEATURE_LEVEL fl = g_d3d12_context->GetFeatureLevel();
|
||||
for (size_t i = 0; i < std::size(feature_level_names); i++)
|
||||
{
|
||||
if (fl == std::get<0>(feature_level_names[i]))
|
||||
{
|
||||
ret = std::get<1>(feature_level_names[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret += "\n";
|
||||
|
||||
IDXGIAdapter* adapter = g_d3d12_context->GetAdapter();
|
||||
DXGI_ADAPTER_DESC desc;
|
||||
if (adapter && SUCCEEDED(adapter->GetDesc(&desc)))
|
||||
{
|
||||
ret += StringUtil::StdStringFromFormat("VID: 0x%04X PID: 0x%04X\n", desc.VendorId, desc.DeviceId);
|
||||
ret += StringUtil::WideStringToUTF8String(desc.Description);
|
||||
ret += "\n";
|
||||
|
||||
const std::string driver_version(D3D::GetDriverVersionFromLUID(desc.AdapterLuid));
|
||||
if (!driver_version.empty())
|
||||
{
|
||||
ret += "Driver Version: ";
|
||||
ret += driver_version;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GSDevice12::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
||||
{
|
||||
if (!m_swap_chain)
|
||||
return;
|
||||
|
||||
m_window_info.surface_scale = new_window_scale;
|
||||
|
||||
if (m_window_info.surface_width == new_window_width && m_window_info.surface_height == new_window_height)
|
||||
return;
|
||||
|
||||
ExecuteCommandList(true);
|
||||
|
||||
DestroySwapChainRTVs();
|
||||
|
||||
HRESULT hr = m_swap_chain->ResizeBuffers(
|
||||
0, 0, 0, DXGI_FORMAT_UNKNOWN, m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
|
||||
if (FAILED(hr))
|
||||
Console.Error("ResizeBuffers() failed: 0x%08X", hr);
|
||||
|
||||
if (!CreateSwapChainRTV())
|
||||
pxFailRel("Failed to recreate swap chain RTV after resize");
|
||||
}
|
||||
|
||||
bool GSDevice12::SupportsExclusiveFullscreen() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSDevice12::IsExclusiveFullscreen()
|
||||
{
|
||||
BOOL is_fullscreen = FALSE;
|
||||
return (m_swap_chain && SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen);
|
||||
}
|
||||
|
||||
bool GSDevice12::SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
|
||||
{
|
||||
if (!m_swap_chain)
|
||||
return false;
|
||||
|
||||
BOOL is_fullscreen = FALSE;
|
||||
HRESULT hr = m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr);
|
||||
if (!fullscreen)
|
||||
{
|
||||
// leaving fullscreen
|
||||
if (is_fullscreen)
|
||||
return SUCCEEDED(m_swap_chain->SetFullscreenState(FALSE, nullptr));
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
IDXGIOutput* output;
|
||||
if (FAILED(hr = m_swap_chain->GetContainingOutput(&output)))
|
||||
return false;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC current_desc;
|
||||
hr = m_swap_chain->GetDesc(¤t_desc);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
DXGI_MODE_DESC new_mode = current_desc.BufferDesc;
|
||||
new_mode.Width = width;
|
||||
new_mode.Height = height;
|
||||
new_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(refresh_rate * 1000.0f));
|
||||
new_mode.RefreshRate.Denominator = 1000u;
|
||||
|
||||
DXGI_MODE_DESC closest_mode;
|
||||
if (FAILED(hr = output->FindClosestMatchingMode(&new_mode, &closest_mode, nullptr)) ||
|
||||
new_mode.Format != current_desc.BufferDesc.Format)
|
||||
{
|
||||
Console.Error("Failed to find closest matching mode, hr=%08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (new_mode.Width == current_desc.BufferDesc.Width && new_mode.Height == current_desc.BufferDesc.Width &&
|
||||
new_mode.RefreshRate.Numerator == current_desc.BufferDesc.RefreshRate.Numerator &&
|
||||
new_mode.RefreshRate.Denominator == current_desc.BufferDesc.RefreshRate.Denominator)
|
||||
{
|
||||
DevCon.WriteLn("Fullscreen mode already set");
|
||||
return true;
|
||||
}
|
||||
|
||||
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::Sleep);
|
||||
DestroySwapChainRTVs();
|
||||
m_swap_chain.reset();
|
||||
|
||||
if (!CreateSwapChain(&closest_mode))
|
||||
{
|
||||
Console.Error("Failed to create a fullscreen swap chain");
|
||||
if (!CreateSwapChain(nullptr))
|
||||
pxFailRel("Failed to recreate windowed swap chain");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GSDevice::PresentResult GSDevice12::BeginPresent(bool frame_skip)
|
||||
{
|
||||
EndRenderPass();
|
||||
|
||||
if (m_device_lost)
|
||||
return PresentResult::DeviceLost;
|
||||
|
||||
if (frame_skip || !m_swap_chain)
|
||||
return PresentResult::FrameSkipped;
|
||||
|
||||
static constexpr std::array<float, 4> clear_color = {};
|
||||
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
|
||||
|
||||
ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList();
|
||||
swap_chain_buf.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
cmdlist->ClearRenderTargetView(swap_chain_buf.GetWriteDescriptor(), clear_color.data(), 0, nullptr);
|
||||
cmdlist->OMSetRenderTargets(1, &swap_chain_buf.GetWriteDescriptor().cpu_handle, FALSE, nullptr);
|
||||
|
||||
const D3D12_VIEWPORT vp{0.0f, 0.0f, static_cast<float>(m_window_info.surface_width),
|
||||
static_cast<float>(m_window_info.surface_height), 0.0f, 1.0f};
|
||||
const D3D12_RECT scissor{
|
||||
0, 0, static_cast<LONG>(m_window_info.surface_width), static_cast<LONG>(m_window_info.surface_height)};
|
||||
cmdlist->RSSetViewports(1, &vp);
|
||||
cmdlist->RSSetScissorRects(1, &scissor);
|
||||
return PresentResult::OK;
|
||||
}
|
||||
|
||||
void GSDevice12::RestoreAPIState()
|
||||
void GSDevice12::EndPresent()
|
||||
{
|
||||
RenderImGui();
|
||||
|
||||
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
|
||||
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
|
||||
|
||||
swap_chain_buf.TransitionToState(g_d3d12_context->GetCommandList(), D3D12_RESOURCE_STATE_PRESENT);
|
||||
if (!g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::None))
|
||||
{
|
||||
m_device_lost = true;
|
||||
InvalidateCachedState();
|
||||
return;
|
||||
}
|
||||
|
||||
const bool vsync = static_cast<UINT>(m_vsync_mode != VsyncMode::Off);
|
||||
if (!vsync && m_using_allow_tearing)
|
||||
m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
|
||||
else
|
||||
m_swap_chain->Present(static_cast<UINT>(vsync), 0);
|
||||
|
||||
InvalidateCachedState();
|
||||
}
|
||||
|
||||
bool GSDevice12::SetGPUTimingEnabled(bool enabled)
|
||||
{
|
||||
g_d3d12_context->SetEnableGPUTiming(enabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
float GSDevice12::GetAndResetAccumulatedGPUTime()
|
||||
{
|
||||
return g_d3d12_context->GetAndResetAccumulatedGPUTime();
|
||||
}
|
||||
|
||||
void GSDevice12::PushDebugGroup(const char* fmt, ...)
|
||||
|
@ -453,7 +853,7 @@ void GSDevice12::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
|||
{
|
||||
DisplayConstantBuffer cb;
|
||||
cb.SetSource(sRect, sTex->GetSize());
|
||||
cb.SetTarget(dRect, dTex ? dTex->GetSize() : GSVector2i(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight()));
|
||||
cb.SetTarget(dRect, dTex ? dTex->GetSize() : GSVector2i(GetWindowWidth(), GetWindowHeight()));
|
||||
cb.SetTime(shaderTime);
|
||||
SetUtilityRootSignature();
|
||||
SetUtilityPushConstants(&cb, sizeof(cb));
|
||||
|
@ -661,8 +1061,7 @@ void GSDevice12::DoStretchRect(GSTexture12* sTex, const GSVector4& sRect, GSText
|
|||
|
||||
const bool is_present = (!dTex);
|
||||
const bool depth = (dTex && dTex->GetType() == GSTexture::Type::DepthStencil);
|
||||
const GSVector2i size(
|
||||
is_present ? GSVector2i(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight()) : dTex->GetSize());
|
||||
const GSVector2i size(is_present ? GSVector2i(GetWindowWidth(), GetWindowHeight()) : dTex->GetSize());
|
||||
const GSVector4i dtex_rc(0, 0, size.x, size.y);
|
||||
const GSVector4i dst_rc(GSVector4i(dRect).rintersect(dtex_rc));
|
||||
|
||||
|
@ -918,6 +1317,163 @@ bool GSDevice12::CompileCASPipelines()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSDevice12::CompileImGuiPipeline()
|
||||
{
|
||||
const std::optional<std::string> hlsl = Host::ReadResourceFileToString("shaders/dx11/imgui.fx");
|
||||
if (!hlsl.has_value())
|
||||
{
|
||||
Console.Error("Failed to read imgui.fx");
|
||||
return false;
|
||||
}
|
||||
|
||||
const ComPtr<ID3DBlob> vs = m_shader_cache.GetVertexShader(hlsl.value(), nullptr, "vs_main");
|
||||
const ComPtr<ID3DBlob> ps = m_shader_cache.GetPixelShader(hlsl.value(), nullptr, "ps_main");
|
||||
if (!vs || !ps)
|
||||
{
|
||||
Console.Error("Failed to compile ImGui shaders");
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12::GraphicsPipelineBuilder gpb;
|
||||
gpb.SetRootSignature(m_utility_root_signature.get());
|
||||
gpb.AddVertexAttribute("POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(ImDrawVert, pos));
|
||||
gpb.AddVertexAttribute("TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(ImDrawVert, uv));
|
||||
gpb.AddVertexAttribute("COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(ImDrawVert, col));
|
||||
gpb.SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE);
|
||||
gpb.SetVertexShader(vs.get());
|
||||
gpb.SetPixelShader(ps.get());
|
||||
gpb.SetNoCullRasterizationState();
|
||||
gpb.SetNoDepthTestState();
|
||||
gpb.SetBlendState(0, true, D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_INV_SRC_ALPHA, D3D12_BLEND_OP_ADD, D3D12_BLEND_ONE,
|
||||
D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD);
|
||||
gpb.SetRenderTarget(0, DXGI_FORMAT_R8G8B8A8_UNORM);
|
||||
|
||||
m_imgui_pipeline = gpb.Create(g_d3d12_context->GetDevice(), m_shader_cache, false);
|
||||
if (!m_imgui_pipeline)
|
||||
{
|
||||
Console.Error("Failed to compile ImGui pipeline");
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12::SetObjectName(m_imgui_pipeline.get(), "ImGui pipeline");
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSDevice12::RenderImGui()
|
||||
{
|
||||
ImGui::Render();
|
||||
const ImDrawData* draw_data = ImGui::GetDrawData();
|
||||
if (draw_data->CmdListsCount == 0)
|
||||
return;
|
||||
|
||||
const float L = 0.0f;
|
||||
const float R = static_cast<float>(m_window_info.surface_width);
|
||||
const float T = 0.0f;
|
||||
const float B = static_cast<float>(m_window_info.surface_height);
|
||||
|
||||
// clang-format off
|
||||
const float ortho_projection[4][4] =
|
||||
{
|
||||
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, 0.5f, 0.0f },
|
||||
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
SetUtilityRootSignature();
|
||||
SetUtilityPushConstants(ortho_projection, sizeof(ortho_projection));
|
||||
SetPipeline(m_imgui_pipeline.get());
|
||||
SetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
|
||||
if (m_utility_sampler_cpu != m_linear_sampler_cpu)
|
||||
{
|
||||
m_utility_sampler_cpu = m_linear_sampler_cpu;
|
||||
m_dirty_flags |= DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE;
|
||||
|
||||
// just skip if we run out.. we can't resume the present render pass :/
|
||||
if (!g_d3d12_context->GetSamplerAllocator().LookupSingle(&m_utility_sampler_gpu, m_linear_sampler_cpu))
|
||||
{
|
||||
Console.Warning("Skipping ImGui draw because of no descriptors");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// this is for presenting, we don't want to screw with the viewport/scissor set by display
|
||||
m_dirty_flags &= ~(DIRTY_FLAG_RENDER_TARGET | DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR);
|
||||
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
|
||||
u32 vertex_offset;
|
||||
{
|
||||
const u32 size = sizeof(ImDrawVert) * static_cast<u32>(cmd_list->VtxBuffer.Size);
|
||||
if (!m_vertex_stream_buffer.ReserveMemory(size, sizeof(ImDrawVert)))
|
||||
{
|
||||
Console.Warning("Skipping ImGui draw because of no vertex buffer space");
|
||||
return;
|
||||
}
|
||||
|
||||
vertex_offset = m_vertex_stream_buffer.GetCurrentOffset() / sizeof(ImDrawVert);
|
||||
std::memcpy(m_vertex_stream_buffer.GetCurrentHostPointer(), cmd_list->VtxBuffer.Data, size);
|
||||
m_vertex_stream_buffer.CommitMemory(size);
|
||||
}
|
||||
|
||||
u32 index_offset;
|
||||
{
|
||||
const u32 size = sizeof(ImDrawIdx) * static_cast<u32>(cmd_list->IdxBuffer.Size);
|
||||
if (!m_index_stream_buffer.ReserveMemory(size, sizeof(ImDrawIdx)))
|
||||
{
|
||||
Console.Warning("Skipping ImGui draw because of no vertex buffer space");
|
||||
return;
|
||||
}
|
||||
|
||||
index_offset = m_index_stream_buffer.GetCurrentOffset() / sizeof(ImDrawIdx);
|
||||
std::memcpy(m_index_stream_buffer.GetCurrentHostPointer(), cmd_list->IdxBuffer.Data, size);
|
||||
m_index_stream_buffer.CommitMemory(size);
|
||||
}
|
||||
|
||||
SetVertexBuffer(m_vertex_stream_buffer.GetGPUPointer(), m_vertex_stream_buffer.GetSize(), sizeof(ImDrawVert));
|
||||
SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(), m_index_stream_buffer.GetSize(), DXGI_FORMAT_R16_UINT);
|
||||
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
pxAssert(!pcmd->UserCallback);
|
||||
|
||||
const GSVector4 clip = GSVector4::load<false>(&pcmd->ClipRect);
|
||||
if ((clip.zwzw() <= clip.xyxy()).mask() != 0)
|
||||
continue;
|
||||
|
||||
SetScissor(GSVector4i(clip));
|
||||
|
||||
// Since we don't have the GSTexture...
|
||||
D3D12::Texture* tex = static_cast<D3D12::Texture*>(pcmd->GetTexID());
|
||||
D3D12::DescriptorHandle handle = tex ? tex->GetSRVDescriptor() : m_null_texture.GetSRVDescriptor();
|
||||
if (m_utility_texture_cpu != handle)
|
||||
{
|
||||
m_utility_texture_cpu = handle;
|
||||
m_dirty_flags |= DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE;
|
||||
|
||||
if (!GetTextureGroupDescriptors(&m_utility_texture_gpu, &handle, 1))
|
||||
{
|
||||
Console.Warning("Skipping ImGui draw because of no descriptors");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ApplyUtilityState())
|
||||
{
|
||||
g_d3d12_context->GetCommandList()->DrawIndexedInstanced(
|
||||
pcmd->ElemCount, 1, index_offset + pcmd->IdxOffset, vertex_offset + pcmd->VtxOffset, 0);
|
||||
}
|
||||
}
|
||||
|
||||
g_perfmon.Put(GSPerfMon::DrawCalls, cmd_list->CmdBuffer.Size);
|
||||
}
|
||||
}
|
||||
|
||||
bool GSDevice12::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants)
|
||||
{
|
||||
EndRenderPass();
|
||||
|
@ -1182,7 +1738,7 @@ bool GSDevice12::CreateRootSignatures()
|
|||
// Convert Pipeline Layout
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
rsb.SetInputAssemblerFlag();
|
||||
rsb.Add32BitConstants(0, CONVERT_PUSH_CONSTANTS_SIZE / sizeof(u32), static_cast<D3D12_SHADER_VISIBILITY>(D3D12_SHADER_VISIBILITY_VERTEX | D3D12_SHADER_VISIBILITY_PIXEL));
|
||||
rsb.Add32BitConstants(0, CONVERT_PUSH_CONSTANTS_SIZE / sizeof(u32), D3D12_SHADER_VISIBILITY_ALL);
|
||||
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, NUM_UTILITY_SAMPLERS, D3D12_SHADER_VISIBILITY_PIXEL);
|
||||
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, NUM_UTILITY_SAMPLERS, D3D12_SHADER_VISIBILITY_PIXEL);
|
||||
if (!(m_utility_root_signature = rsb.Create()))
|
||||
|
@ -1528,6 +2084,12 @@ void GSDevice12::DestroyResources()
|
|||
{
|
||||
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::Sleep);
|
||||
|
||||
m_convert_vs.reset();
|
||||
|
||||
m_cas_sharpen_pipeline.reset();
|
||||
m_cas_upscale_pipeline.reset();
|
||||
m_cas_root_signature.reset();
|
||||
|
||||
for (auto& it : m_tfx_pipelines)
|
||||
g_d3d12_context->DeferObjectDestruction(it.second.get());
|
||||
m_tfx_pipelines.clear();
|
||||
|
@ -1544,6 +2106,7 @@ void GSDevice12::DestroyResources()
|
|||
m_date_image_setup_pipelines = {};
|
||||
m_fxaa_pipeline.reset();
|
||||
m_shadeboost_pipeline.reset();
|
||||
m_imgui_pipeline.reset();
|
||||
|
||||
m_linear_sampler_cpu.Clear();
|
||||
m_point_sampler_cpu.Clear();
|
||||
|
@ -1563,6 +2126,8 @@ void GSDevice12::DestroyResources()
|
|||
m_tfx_root_signature.reset();
|
||||
|
||||
m_null_texture.Destroy(false);
|
||||
|
||||
m_shader_cache.Close();
|
||||
}
|
||||
|
||||
const ID3DBlob* GSDevice12::GetTFXVertexShader(GSHWDrawConfig::VSSelector sel)
|
||||
|
|
|
@ -138,6 +138,15 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
ComPtr<IDXGIFactory5> m_dxgi_factory;
|
||||
ComPtr<IDXGISwapChain1> m_swap_chain;
|
||||
std::vector<D3D12::Texture> m_swap_chain_buffers;
|
||||
u32 m_current_swap_chain_buffer = 0;
|
||||
|
||||
bool m_allow_tearing_supported = false;
|
||||
bool m_using_allow_tearing = false;
|
||||
bool m_device_lost = false;
|
||||
|
||||
ComPtr<ID3D12RootSignature> m_tfx_root_signature;
|
||||
ComPtr<ID3D12RootSignature> m_utility_root_signature;
|
||||
|
||||
|
@ -161,6 +170,7 @@ private:
|
|||
std::array<std::array<ComPtr<ID3D12PipelineState>, 2>, 2> m_date_image_setup_pipelines{}; // [depth][datm]
|
||||
ComPtr<ID3D12PipelineState> m_fxaa_pipeline;
|
||||
ComPtr<ID3D12PipelineState> m_shadeboost_pipeline;
|
||||
ComPtr<ID3D12PipelineState> m_imgui_pipeline;
|
||||
|
||||
std::unordered_map<u32, ComPtr<ID3DBlob>> m_tfx_vertex_shaders;
|
||||
std::unordered_map<u32, ComPtr<ID3DBlob>> m_tfx_geometry_shaders;
|
||||
|
@ -180,6 +190,10 @@ private:
|
|||
|
||||
void LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format, DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) const;
|
||||
|
||||
bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode);
|
||||
bool CreateSwapChainRTV();
|
||||
void DestroySwapChainRTVs();
|
||||
|
||||
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
|
||||
|
||||
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE,
|
||||
|
@ -215,6 +229,9 @@ private:
|
|||
bool CompilePostProcessingPipelines();
|
||||
bool CompileCASPipelines();
|
||||
|
||||
bool CompileImGuiPipeline();
|
||||
void RenderImGui();
|
||||
|
||||
void DestroyResources();
|
||||
|
||||
public:
|
||||
|
@ -223,11 +240,29 @@ public:
|
|||
|
||||
__fi static GSDevice12* GetInstance() { return static_cast<GSDevice12*>(g_gs_device.get()); }
|
||||
|
||||
bool Create() override;
|
||||
RenderAPI GetRenderAPI() const override;
|
||||
bool HasSurface() const override;
|
||||
|
||||
bool Create(const WindowInfo& wi, VsyncMode vsync) override;
|
||||
void Destroy() override;
|
||||
|
||||
void ResetAPIState() override;
|
||||
void RestoreAPIState() override;
|
||||
bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
||||
bool SupportsExclusiveFullscreen() const override;
|
||||
bool IsExclusiveFullscreen() override;
|
||||
bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||
void DestroySurface() override;
|
||||
std::string GetDriverInfo() const override;
|
||||
|
||||
bool GetHostRefreshRate(float* refresh_rate) override;
|
||||
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
void PushDebugGroup(const char* fmt, ...) override;
|
||||
void PopDebugGroup() override;
|
||||
|
|
|
@ -143,6 +143,12 @@ class GSDeviceMTL final : public GSDevice
|
|||
public:
|
||||
using DepthStencilSelector = GSHWDrawConfig::DepthStencilSelector;
|
||||
using SamplerSelector = GSHWDrawConfig::SamplerSelector;
|
||||
enum class UsePresentDrawable : u8
|
||||
{
|
||||
Never = 0,
|
||||
Always = 1,
|
||||
IfVsync = 2,
|
||||
};
|
||||
enum class LoadAction
|
||||
{
|
||||
DontCare,
|
||||
|
@ -219,7 +225,18 @@ public:
|
|||
MRCOwned<id<MTLFence>> m_draw_sync_fence;
|
||||
MRCOwned<MTLFunctionConstantValues*> m_fn_constants;
|
||||
MRCOwned<MTLVertexDescriptor*> m_hw_vertex;
|
||||
std::unique_ptr<GSTextureMTL> m_font;
|
||||
|
||||
// Previously in MetalHostDisplay.
|
||||
MRCOwned<NSView*> m_view;
|
||||
MRCOwned<CAMetalLayer*> m_layer;
|
||||
MRCOwned<id<CAMetalDrawable>> m_current_drawable;
|
||||
MRCOwned<MTLRenderPassDescriptor*> m_pass_desc;
|
||||
u32 m_capture_start_frame;
|
||||
UsePresentDrawable m_use_present_drawable;
|
||||
bool m_gpu_timing_enabled = false;
|
||||
double m_accumulated_gpu_time = 0;
|
||||
double m_last_gpu_time_end = 0;
|
||||
std::mutex m_mtx;
|
||||
|
||||
// Draw IDs are used to make sure we're not clobbering things
|
||||
u64 m_current_draw = 1;
|
||||
|
@ -361,15 +378,45 @@ public:
|
|||
MRCOwned<id<MTLFunction>> LoadShader(NSString* name);
|
||||
MRCOwned<id<MTLRenderPipelineState>> MakePipeline(MTLRenderPipelineDescriptor* desc, id<MTLFunction> vertex, id<MTLFunction> fragment, NSString* name);
|
||||
MRCOwned<id<MTLComputePipelineState>> MakeComputePipeline(id<MTLFunction> compute, NSString* name);
|
||||
bool Create() override;
|
||||
bool Create(const WindowInfo& wi, VsyncMode vsync) override;
|
||||
void Destroy() override;
|
||||
|
||||
void AttachSurfaceOnMainThread();
|
||||
void DetachSurfaceOnMainThread();
|
||||
|
||||
RenderAPI GetRenderAPI() const override;
|
||||
bool HasSurface() const override;
|
||||
void DestroySurface() override;
|
||||
bool ChangeWindow(const WindowInfo& wi) override;
|
||||
bool SupportsExclusiveFullscreen() const override;
|
||||
bool IsExclusiveFullscreen() override;
|
||||
bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||
std::string GetDriverInfo() const override;
|
||||
|
||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
||||
|
||||
void UpdateTexture(id<MTLTexture> texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride);
|
||||
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
bool GetHostRefreshRate(float* refresh_rate) override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
void AccumulateCommandBufferTime(id<MTLCommandBuffer> buffer);
|
||||
|
||||
void ClearRenderTarget(GSTexture* t, const GSVector4& c) override;
|
||||
void ClearRenderTarget(GSTexture* t, u32 c) override;
|
||||
void ClearDepth(GSTexture* t) override;
|
||||
void ClearStencil(GSTexture* t, u8 c) override;
|
||||
void InvalidateRenderTarget(GSTexture* t) override;
|
||||
|
||||
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) override;
|
||||
|
||||
void ClearSamplerCache() override;
|
||||
|
||||
void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override;
|
||||
void DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, id<MTLRenderPipelineState> pipeline, bool linear, LoadAction load_action, const void* frag_uniform, size_t frag_uniform_len);
|
||||
void DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds);
|
||||
|
|
|
@ -14,14 +14,14 @@
|
|||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "GSMetalCPPAccessible.h"
|
||||
#include "GSDeviceMTL.h"
|
||||
|
||||
#include "Frontend/MetalHostDisplay.h"
|
||||
#include "GSTextureMTL.h"
|
||||
#include "Host.h"
|
||||
#include "GS/Renderers/Metal/GSMetalCPPAccessible.h"
|
||||
#include "GS/Renderers/Metal/GSDeviceMTL.h"
|
||||
#include "GS/Renderers/Metal/GSTextureMTL.h"
|
||||
#include "GS/GSPerfMon.h"
|
||||
#include "HostDisplay.h"
|
||||
#include <imgui.h>
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "GSMTLSharedHeader.h"
|
||||
|
@ -36,6 +36,15 @@ GSDevice* MakeGSDeviceMTL()
|
|||
return new GSDeviceMTL();
|
||||
}
|
||||
|
||||
std::vector<std::string> GetMetalAdapterList()
|
||||
{ @autoreleasepool {
|
||||
std::vector<std::string> list;
|
||||
auto devs = MRCTransfer(MTLCopyAllDevices());
|
||||
for (id<MTLDevice> dev in devs.Get())
|
||||
list.push_back([[dev name] UTF8String]);
|
||||
return list;
|
||||
}}
|
||||
|
||||
bool GSDeviceMTL::UsageTracker::PrepareForAllocation(u64 last_draw, size_t amt)
|
||||
{
|
||||
auto removeme = std::find_if(m_usage.begin(), m_usage.end(), [last_draw](UsageEntry usage){ return usage.drawno > last_draw; });
|
||||
|
@ -82,11 +91,8 @@ GSDeviceMTL::GSDeviceMTL()
|
|||
}
|
||||
|
||||
GSDeviceMTL::~GSDeviceMTL()
|
||||
{ @autoreleasepool {
|
||||
FlushEncoders();
|
||||
std::lock_guard<std::mutex> guard(m_backref->first);
|
||||
m_backref->second = nullptr;
|
||||
}}
|
||||
{
|
||||
}
|
||||
|
||||
GSDeviceMTL::Map GSDeviceMTL::Allocate(UploadBuffer& buffer, size_t amt)
|
||||
{
|
||||
|
@ -241,7 +247,7 @@ void GSDeviceMTL::DrawCommandBufferFinished(u64 draw, id<MTLCommandBuffer> buffe
|
|||
// We can do the update non-atomically because we only ever update under the lock
|
||||
u64 newval = std::max(draw, m_last_finished_draw.load(std::memory_order_relaxed));
|
||||
m_last_finished_draw.store(newval, std::memory_order_release);
|
||||
static_cast<MetalHostDisplay*>(g_host_display.get())->AccumulateCommandBufferTime(buffer);
|
||||
AccumulateCommandBufferTime(buffer);
|
||||
}
|
||||
|
||||
void GSDeviceMTL::FlushEncoders()
|
||||
|
@ -706,19 +712,100 @@ static void setFnConstantI(MTLFunctionConstantValues* fc, unsigned int value, GS
|
|||
[fc setConstantValue:&value type:MTLDataTypeUInt atIndex:constant];
|
||||
}
|
||||
|
||||
bool GSDeviceMTL::Create()
|
||||
template <typename Fn>
|
||||
static void OnMainThread(Fn&& fn)
|
||||
{
|
||||
if ([NSThread isMainThread])
|
||||
fn();
|
||||
else
|
||||
dispatch_sync(dispatch_get_main_queue(), fn);
|
||||
}
|
||||
|
||||
RenderAPI GSDeviceMTL::GetRenderAPI() const
|
||||
{
|
||||
return RenderAPI::Metal;
|
||||
}
|
||||
|
||||
bool GSDeviceMTL::HasSurface() const { return static_cast<bool>(m_layer);}
|
||||
|
||||
void GSDeviceMTL::AttachSurfaceOnMainThread()
|
||||
{
|
||||
ASSERT([NSThread isMainThread]);
|
||||
m_layer = MRCRetain([CAMetalLayer layer]);
|
||||
[m_layer setDrawableSize:CGSizeMake(m_window_info.surface_width, m_window_info.surface_height)];
|
||||
[m_layer setDevice:m_dev.dev];
|
||||
m_view = MRCRetain((__bridge NSView*)m_window_info.window_handle);
|
||||
[m_view setWantsLayer:YES];
|
||||
[m_view setLayer:m_layer];
|
||||
}
|
||||
|
||||
void GSDeviceMTL::DetachSurfaceOnMainThread()
|
||||
{
|
||||
ASSERT([NSThread isMainThread]);
|
||||
[m_view setLayer:nullptr];
|
||||
[m_view setWantsLayer:NO];
|
||||
m_view = nullptr;
|
||||
m_layer = nullptr;
|
||||
}
|
||||
|
||||
bool GSDeviceMTL::Create(const WindowInfo& wi, VsyncMode vsync)
|
||||
{ @autoreleasepool {
|
||||
if (!GSDevice::Create())
|
||||
if (!GSDevice::Create(wi, vsync))
|
||||
return false;
|
||||
|
||||
if (g_host_display->GetRenderAPI() != RenderAPI::Metal)
|
||||
return false;
|
||||
NSString* ns_adapter_name = [NSString stringWithUTF8String:EmuConfig.GS.Adapter.c_str()];
|
||||
auto devs = MRCTransfer(MTLCopyAllDevices());
|
||||
for (id<MTLDevice> dev in devs.Get())
|
||||
{
|
||||
if ([[dev name] isEqualToString:ns_adapter_name])
|
||||
m_dev = GSMTLDevice(MRCRetain(dev));
|
||||
}
|
||||
if (!m_dev.dev)
|
||||
{
|
||||
if (!EmuConfig.GS.Adapter.empty())
|
||||
Console.Warning("Metal: Couldn't find adapter %s, using default", EmuConfig.GS.Adapter.c_str());
|
||||
m_dev = GSMTLDevice(MRCTransfer(MTLCreateSystemDefaultDevice()));
|
||||
if (!m_dev.dev)
|
||||
Host::ReportErrorAsync("No Metal Devices Available", "No Metal-supporting GPUs were found. PCSX2 requires a Metal GPU (available on all macs from 2012 onwards).");
|
||||
}
|
||||
m_queue = MRCTransfer([m_dev.dev newCommandQueue]);
|
||||
|
||||
if (!g_host_display->HasDevice() || !g_host_display->HasSurface())
|
||||
m_pass_desc = MRCTransfer([MTLRenderPassDescriptor new]);
|
||||
[m_pass_desc colorAttachments][0].loadAction = MTLLoadActionClear;
|
||||
[m_pass_desc colorAttachments][0].clearColor = MTLClearColorMake(0, 0, 0, 0);
|
||||
[m_pass_desc colorAttachments][0].storeAction = MTLStoreActionStore;
|
||||
|
||||
if (char* env = getenv("MTL_USE_PRESENT_DRAWABLE"))
|
||||
m_use_present_drawable = static_cast<UsePresentDrawable>(atoi(env));
|
||||
else if (@available(macOS 13.0, *))
|
||||
m_use_present_drawable = UsePresentDrawable::Always;
|
||||
else // Before Ventura, presentDrawable acts like vsync is on when windowed
|
||||
m_use_present_drawable = UsePresentDrawable::IfVsync;
|
||||
|
||||
m_capture_start_frame = 0;
|
||||
if (char* env = getenv("MTL_CAPTURE"))
|
||||
{
|
||||
m_capture_start_frame = atoi(env);
|
||||
}
|
||||
if (m_capture_start_frame)
|
||||
{
|
||||
Console.WriteLn("Metal will capture frame %u", m_capture_start_frame);
|
||||
}
|
||||
|
||||
if (m_dev.IsOk() && m_queue)
|
||||
{
|
||||
OnMainThread([this]
|
||||
{
|
||||
AttachSurfaceOnMainThread();
|
||||
});
|
||||
SetVSync(vsync);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
m_dev = *static_cast<const GSMTLDevice*>(g_host_display->GetDevice());
|
||||
m_queue = MRCRetain((__bridge id<MTLCommandQueue>)g_host_display->GetContext());
|
||||
MTLPixelFormat layer_px_fmt = [(__bridge CAMetalLayer*)g_host_display->GetSurface() pixelFormat];
|
||||
}
|
||||
|
||||
MTLPixelFormat layer_px_fmt = [m_layer pixelFormat];
|
||||
|
||||
m_features.broken_point_sampler = [[m_dev.dev name] containsString:@"AMD"];
|
||||
m_features.geometry_shader = false;
|
||||
|
@ -1042,8 +1129,6 @@ bool GSDeviceMTL::Create()
|
|||
pdesc.vertexDescriptor.layouts[0].stride = sizeof(ImDrawVert);
|
||||
pdesc.colorAttachments[0].pixelFormat = layer_px_fmt;
|
||||
m_imgui_pipeline = MakePipeline(pdesc, LoadShader(@"vs_imgui"), LoadShader(@"ps_imgui"), @"imgui");
|
||||
if (!m_dev.features.texture_swizzle)
|
||||
m_imgui_pipeline_a8 = MakePipeline(pdesc, LoadShader(@"vs_imgui"), LoadShader(@"ps_imgui_a8"), @"imgui_a8");
|
||||
|
||||
[initCommands commit];
|
||||
}
|
||||
|
@ -1054,6 +1139,255 @@ bool GSDeviceMTL::Create()
|
|||
return true;
|
||||
}}
|
||||
|
||||
void GSDeviceMTL::Destroy()
|
||||
{ @autoreleasepool {
|
||||
FlushEncoders();
|
||||
std::lock_guard<std::mutex> guard(m_backref->first);
|
||||
m_backref->second = nullptr;
|
||||
|
||||
GSDevice::Destroy();
|
||||
GSDeviceMTL::DestroySurface();
|
||||
m_queue = nullptr;
|
||||
m_dev.Reset();
|
||||
}}
|
||||
|
||||
void GSDeviceMTL::DestroySurface()
|
||||
{
|
||||
if (!m_layer)
|
||||
return;
|
||||
OnMainThread([this]{ DetachSurfaceOnMainThread(); });
|
||||
m_layer = nullptr;
|
||||
}
|
||||
|
||||
bool GSDeviceMTL::ChangeWindow(const WindowInfo& wi)
|
||||
{
|
||||
OnMainThread([this, &wi]
|
||||
{
|
||||
DetachSurfaceOnMainThread();
|
||||
m_window_info = wi;
|
||||
AttachSurfaceOnMainThread();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSDeviceMTL::SupportsExclusiveFullscreen() const { return false; }
|
||||
bool GSDeviceMTL::IsExclusiveFullscreen() { return false; }
|
||||
bool GSDeviceMTL::SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) { return false; }
|
||||
|
||||
std::string GSDeviceMTL::GetDriverInfo() const
|
||||
{ @autoreleasepool {
|
||||
std::string desc([[m_dev.dev description] UTF8String]);
|
||||
desc += "\n Texture Swizzle: " + std::string(m_dev.features.texture_swizzle ? "Supported" : "Unsupported");
|
||||
desc += "\n Unified Memory: " + std::string(m_dev.features.unified_memory ? "Supported" : "Unsupported");
|
||||
desc += "\n Framebuffer Fetch: " + std::string(m_dev.features.framebuffer_fetch ? "Supported" : "Unsupported");
|
||||
desc += "\n Primitive ID: " + std::string(m_dev.features.primid ? "Supported" : "Unsupported");
|
||||
desc += "\n Shader Version: " + std::string(to_string(m_dev.features.shader_version));
|
||||
desc += "\n Max Texture Size: " + std::to_string(m_dev.features.max_texsize);
|
||||
return desc;
|
||||
}}
|
||||
|
||||
void GSDeviceMTL::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
||||
{
|
||||
m_window_info.surface_scale = new_window_scale;
|
||||
if (m_window_info.surface_width == static_cast<u32>(new_window_width) && m_window_info.surface_height == static_cast<u32>(new_window_height))
|
||||
return;
|
||||
m_window_info.surface_width = new_window_width;
|
||||
m_window_info.surface_height = new_window_height;
|
||||
@autoreleasepool
|
||||
{
|
||||
[m_layer setDrawableSize:CGSizeMake(new_window_width, new_window_height)];
|
||||
}
|
||||
}
|
||||
|
||||
void GSDeviceMTL::UpdateTexture(id<MTLTexture> texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride)
|
||||
{
|
||||
id<MTLCommandBuffer> cmdbuf = [m_queue commandBuffer];
|
||||
id<MTLBlitCommandEncoder> enc = [cmdbuf blitCommandEncoder];
|
||||
size_t bytes = data_stride * height;
|
||||
MRCOwned<id<MTLBuffer>> buf = MRCTransfer([m_dev.dev newBufferWithLength:bytes options:MTLResourceStorageModeShared | MTLResourceCPUCacheModeWriteCombined]);
|
||||
memcpy([buf contents], data, bytes);
|
||||
[enc copyFromBuffer:buf
|
||||
sourceOffset:0
|
||||
sourceBytesPerRow:data_stride
|
||||
sourceBytesPerImage:bytes
|
||||
sourceSize:MTLSizeMake(width, height, 1)
|
||||
toTexture:texture
|
||||
destinationSlice:0
|
||||
destinationLevel:0
|
||||
destinationOrigin:MTLOriginMake(0, 0, 0)];
|
||||
[enc endEncoding];
|
||||
[cmdbuf commit];
|
||||
}
|
||||
|
||||
static bool s_capture_next = false;
|
||||
|
||||
GSDevice::PresentResult GSDeviceMTL::BeginPresent(bool frame_skip)
|
||||
{ @autoreleasepool {
|
||||
GSDeviceMTL* dev = static_cast<GSDeviceMTL*>(g_gs_device.get());
|
||||
if (dev && m_capture_start_frame && dev->FrameNo() == m_capture_start_frame)
|
||||
s_capture_next = true;
|
||||
if (frame_skip || m_window_info.type == WindowInfo::Type::Surfaceless || !g_gs_device)
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
id<MTLCommandBuffer> buf = dev->GetRenderCmdBuf();
|
||||
m_current_drawable = MRCRetain([m_layer nextDrawable]);
|
||||
dev->EndRenderPass();
|
||||
if (!m_current_drawable)
|
||||
{
|
||||
[buf pushDebugGroup:@"Present Skipped"];
|
||||
[buf popDebugGroup];
|
||||
dev->FlushEncoders();
|
||||
ImGui::EndFrame();
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
[m_pass_desc colorAttachments][0].texture = [m_current_drawable texture];
|
||||
id<MTLRenderCommandEncoder> enc = [buf renderCommandEncoderWithDescriptor:m_pass_desc];
|
||||
[enc setLabel:@"Present"];
|
||||
dev->m_current_render.encoder = MRCRetain(enc);
|
||||
return PresentResult::OK;
|
||||
}}
|
||||
|
||||
void GSDeviceMTL::EndPresent()
|
||||
{ @autoreleasepool {
|
||||
GSDeviceMTL* dev = static_cast<GSDeviceMTL*>(g_gs_device.get());
|
||||
pxAssertDev(dev && dev->m_current_render.encoder && dev->m_current_render_cmdbuf, "BeginPresent cmdbuf was destroyed");
|
||||
ImGui::Render();
|
||||
dev->RenderImGui(ImGui::GetDrawData());
|
||||
dev->EndRenderPass();
|
||||
if (m_current_drawable)
|
||||
{
|
||||
const bool use_present_drawable = m_use_present_drawable == UsePresentDrawable::Always ||
|
||||
(m_use_present_drawable == UsePresentDrawable::IfVsync && m_vsync_mode != VsyncMode::Off);
|
||||
|
||||
if (use_present_drawable)
|
||||
[dev->m_current_render_cmdbuf presentDrawable:m_current_drawable];
|
||||
else
|
||||
[dev->m_current_render_cmdbuf addScheduledHandler:[drawable = std::move(m_current_drawable)](id<MTLCommandBuffer>){
|
||||
[drawable present];
|
||||
}];
|
||||
}
|
||||
dev->FlushEncoders();
|
||||
dev->FrameCompleted();
|
||||
m_current_drawable = nullptr;
|
||||
if (m_capture_start_frame)
|
||||
{
|
||||
if (@available(macOS 10.15, iOS 13, *))
|
||||
{
|
||||
static NSString* const path = @"/tmp/PCSX2MTLCapture.gputrace";
|
||||
static u32 frames;
|
||||
if (frames)
|
||||
{
|
||||
--frames;
|
||||
if (!frames)
|
||||
{
|
||||
[[MTLCaptureManager sharedCaptureManager] stopCapture];
|
||||
Console.WriteLn("Metal Trace Capture to /tmp/PCSX2MTLCapture.gputrace finished");
|
||||
[[NSWorkspace sharedWorkspace] selectFile:path
|
||||
inFileViewerRootedAtPath:@"/tmp/"];
|
||||
}
|
||||
}
|
||||
else if (s_capture_next)
|
||||
{
|
||||
s_capture_next = false;
|
||||
MTLCaptureManager* mgr = [MTLCaptureManager sharedCaptureManager];
|
||||
if ([mgr supportsDestination:MTLCaptureDestinationGPUTraceDocument])
|
||||
{
|
||||
MTLCaptureDescriptor* desc = [[MTLCaptureDescriptor new] autorelease];
|
||||
[desc setCaptureObject:m_dev.dev];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path])
|
||||
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
|
||||
[desc setOutputURL:[NSURL fileURLWithPath:path]];
|
||||
[desc setDestination:MTLCaptureDestinationGPUTraceDocument];
|
||||
NSError* err = nullptr;
|
||||
[mgr startCaptureWithDescriptor:desc error:&err];
|
||||
if (err)
|
||||
{
|
||||
Console.Error("Metal Trace Capture failed: %s", [[err localizedDescription] UTF8String]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLn("Metal Trace Capture to /tmp/PCSX2MTLCapture.gputrace started");
|
||||
frames = 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error("Metal Trace Capture Failed: MTLCaptureManager doesn't support GPU trace documents! (Did you forget to run with METAL_CAPTURE_ENABLED=1?)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
void GSDeviceMTL::SetVSync(VsyncMode mode)
|
||||
{
|
||||
[m_layer setDisplaySyncEnabled:mode != VsyncMode::Off];
|
||||
m_vsync_mode = mode;
|
||||
}
|
||||
|
||||
bool GSDeviceMTL::GetHostRefreshRate(float* refresh_rate)
|
||||
{
|
||||
OnMainThread([this, refresh_rate]
|
||||
{
|
||||
u32 did = [[[[[m_view window] screen] deviceDescription] valueForKey:@"NSScreenNumber"] unsignedIntValue];
|
||||
if (CGDisplayModeRef mode = CGDisplayCopyDisplayMode(did))
|
||||
{
|
||||
*refresh_rate = CGDisplayModeGetRefreshRate(mode);
|
||||
CGDisplayModeRelease(mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
*refresh_rate = 0;
|
||||
}
|
||||
});
|
||||
return *refresh_rate != 0;
|
||||
}
|
||||
|
||||
bool GSDeviceMTL::SetGPUTimingEnabled(bool enabled)
|
||||
{
|
||||
if (enabled == m_gpu_timing_enabled)
|
||||
return true;
|
||||
if (@available(macOS 10.15, iOS 10.3, *))
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_mtx);
|
||||
m_gpu_timing_enabled = enabled;
|
||||
m_accumulated_gpu_time = 0;
|
||||
m_last_gpu_time_end = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float GSDeviceMTL::GetAndResetAccumulatedGPUTime()
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_mtx);
|
||||
float time = m_accumulated_gpu_time * 1000;
|
||||
m_accumulated_gpu_time = 0;
|
||||
return time;
|
||||
}
|
||||
|
||||
void GSDeviceMTL::AccumulateCommandBufferTime(id<MTLCommandBuffer> buffer)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_mtx);
|
||||
if (!m_gpu_timing_enabled)
|
||||
return;
|
||||
// We do the check before enabling m_gpu_timing_enabled
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
||||
// It's unlikely, but command buffers can overlap or run out of order
|
||||
// This doesn't handle every case (fully out of order), but it should at least handle overlapping
|
||||
double begin = std::max(m_last_gpu_time_end, [buffer GPUStartTime]);
|
||||
double end = [buffer GPUEndTime];
|
||||
if (end > begin)
|
||||
{
|
||||
m_accumulated_gpu_time += end - begin;
|
||||
m_last_gpu_time_end = end;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
void GSDeviceMTL::ClearRenderTarget(GSTexture* t, const GSVector4& c)
|
||||
{
|
||||
if (!t) return;
|
||||
|
@ -1078,11 +1412,21 @@ void GSDeviceMTL::ClearStencil(GSTexture* t, uint8 c)
|
|||
static_cast<GSTextureMTL*>(t)->RequestStencilClear(c);
|
||||
}
|
||||
|
||||
void GSDeviceMTL::InvalidateRenderTarget(GSTexture* t)
|
||||
{
|
||||
// TODO: Implement me
|
||||
}
|
||||
|
||||
std::unique_ptr<GSDownloadTexture> GSDeviceMTL::CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format)
|
||||
{
|
||||
return GSDownloadTextureMTL::Create(this, width, height, format);
|
||||
}
|
||||
|
||||
void GSDeviceMTL::ClearSamplerCache()
|
||||
{
|
||||
// TODO: Implement me
|
||||
}
|
||||
|
||||
void GSDeviceMTL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY)
|
||||
{ @autoreleasepool {
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
@ -1231,7 +1575,7 @@ static_assert(offsetof(DisplayConstantBuffer, TimeAndPad.x) == offsetof(G
|
|||
|
||||
void GSDeviceMTL::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear)
|
||||
{ @autoreleasepool {
|
||||
GSVector2i ds = dTex ? dTex->GetSize() : GSVector2i(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight());
|
||||
GSVector2i ds = dTex ? dTex->GetSize() : GetWindowSize();
|
||||
DisplayConstantBuffer cb;
|
||||
cb.SetSource(sRect, sTex->GetSize());
|
||||
cb.SetTarget(dRect, ds);
|
||||
|
@ -1965,12 +2309,11 @@ void GSDeviceMTL::RenderImGui(ImDrawData* data)
|
|||
[enc setVertexBuffer:map.gpu_buffer offset:map.gpu_offset atIndex:GSMTLBufferIndexVertices];
|
||||
[enc setVertexBytes:&transform length:sizeof(transform) atIndex:GSMTLBufferIndexUniforms];
|
||||
|
||||
simd::uint4 last_scissor = simd::make_uint4(0, 0, g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight());
|
||||
simd::uint4 last_scissor = simd::make_uint4(0, 0, GetWindowWidth(), GetWindowHeight());
|
||||
simd::float2 fb_size = simd_float(last_scissor.zw);
|
||||
simd::float2 clip_off = ToSimd(data->DisplayPos); // (0,0) unless using multi-viewports
|
||||
simd::float2 clip_scale = ToSimd(data->FramebufferScale); // (1,1) unless using retina display which are often (2,2)
|
||||
ImTextureID last_tex = nullptr;
|
||||
bool last_tex_a8 = false;
|
||||
|
||||
for (int i = 0; i < data->CmdListsCount; i++)
|
||||
{
|
||||
|
@ -2003,15 +2346,6 @@ void GSDeviceMTL::RenderImGui(ImDrawData* data)
|
|||
{
|
||||
last_tex = tex;
|
||||
[enc setFragmentTexture:(__bridge id<MTLTexture>)tex atIndex:0];
|
||||
if (!m_dev.features.texture_swizzle)
|
||||
{
|
||||
bool a8 = [(__bridge id<MTLTexture>)tex pixelFormat] == MTLPixelFormatA8Unorm;
|
||||
if (last_tex_a8 != a8)
|
||||
{
|
||||
[enc setRenderPipelineState:a8 ? m_imgui_pipeline_a8 : m_imgui_pipeline];
|
||||
last_tex_a8 = a8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[enc setVertexBufferOffset:map.gpu_offset + vtx_off + cmd.VtxOffset * sizeof(ImDrawVert) atIndex:0];
|
||||
|
|
|
@ -14,15 +14,16 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Header with all metal stuff available for use with C++ (rather than Objective-C++)
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
#include "HostDisplay.h"
|
||||
|
||||
class GSDevice;
|
||||
GSDevice* MakeGSDeviceMTL();
|
||||
HostDisplay* MakeMetalHostDisplay();
|
||||
HostDisplay::AdapterAndModeList GetMetalAdapterAndModeList();
|
||||
std::vector<std::string> GetMetalAdapterList();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -362,12 +362,6 @@ fragment half4 ps_imgui(ImGuiShaderData data [[stage_in]], texture2d<half> textu
|
|||
return data.c * texture.sample(s, data.t);
|
||||
}
|
||||
|
||||
fragment half4 ps_imgui_a8(ImGuiShaderData data [[stage_in]], texture2d<half> texture [[texture(GSMTLTextureIndexNonHW)]])
|
||||
{
|
||||
constexpr sampler s(coord::normalized, filter::linear, address::clamp_to_edge);
|
||||
return data.c * half4(1, 1, 1, texture.sample(s, data.t).a);
|
||||
}
|
||||
|
||||
fragment float4 ps_shadeboost(float4 p [[position]], DirectReadTextureIn<float> tex, constant float3& cb [[buffer(GSMTLBufferIndexUniforms)]])
|
||||
{
|
||||
const float brt = cb.x;
|
||||
|
|
|
@ -14,16 +14,20 @@
|
|||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "GS/GSState.h"
|
||||
#include "GSDeviceOGL.h"
|
||||
#include "GLState.h"
|
||||
#include "GS/GSGL.h"
|
||||
#include "GS/GSUtil.h"
|
||||
#include "GS/Renderers/OpenGL/GSDeviceOGL.h"
|
||||
#include "GS/Renderers/OpenGL/GLState.h"
|
||||
#include "Host.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "ShaderCacheVersion.h"
|
||||
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "IconsFontAwesome5.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
@ -45,35 +49,7 @@ GSDeviceOGL::GSDeviceOGL() = default;
|
|||
|
||||
GSDeviceOGL::~GSDeviceOGL()
|
||||
{
|
||||
s_texture_upload_buffer.reset();
|
||||
if (m_vertex_array_object)
|
||||
glDeleteVertexArrays(1, &m_vertex_array_object);
|
||||
m_vertex_stream_buffer.reset();
|
||||
m_index_stream_buffer.reset();
|
||||
|
||||
// Clean m_convert
|
||||
delete m_convert.dss;
|
||||
delete m_convert.dss_write;
|
||||
|
||||
// Clean m_date
|
||||
delete m_date.dss;
|
||||
|
||||
// Clean various opengl allocation
|
||||
glDeleteFramebuffers(1, &m_fbo);
|
||||
glDeleteFramebuffers(1, &m_fbo_read);
|
||||
glDeleteFramebuffers(1, &m_fbo_write);
|
||||
|
||||
// Delete HW FX
|
||||
m_vertex_uniform_stream_buffer.reset();
|
||||
m_fragment_uniform_stream_buffer.reset();
|
||||
glDeleteSamplers(1, &m_palette_ss);
|
||||
|
||||
m_programs.clear();
|
||||
|
||||
glDeleteSamplers(std::size(m_ps_ss), m_ps_ss);
|
||||
|
||||
for (GSDepthStencilOGL* ds : m_om_dss)
|
||||
delete ds;
|
||||
pxAssert(!m_gl_context);
|
||||
}
|
||||
|
||||
GSTexture* GSDeviceOGL::CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format)
|
||||
|
@ -82,21 +58,60 @@ GSTexture* GSDeviceOGL::CreateSurface(GSTexture::Type type, int width, int heigh
|
|||
return new GSTextureOGL(type, width, height, levels, format);
|
||||
}
|
||||
|
||||
bool GSDeviceOGL::Create()
|
||||
RenderAPI GSDeviceOGL::GetRenderAPI() const
|
||||
{
|
||||
if (!GSDevice::Create())
|
||||
return m_gl_context->IsGLES() ? RenderAPI::OpenGLES : RenderAPI::OpenGL;
|
||||
}
|
||||
|
||||
bool GSDeviceOGL::HasSurface() const
|
||||
{
|
||||
return m_window_info.type != WindowInfo::Type::Surfaceless;
|
||||
}
|
||||
|
||||
void GSDeviceOGL::SetVSync(VsyncMode mode)
|
||||
{
|
||||
if (m_vsync_mode == mode || m_gl_context->GetWindowInfo().type == WindowInfo::Type::Surfaceless)
|
||||
return;
|
||||
|
||||
// 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);
|
||||
|
||||
if (mode != VsyncMode::Adaptive || !m_gl_context->SetSwapInterval(-1))
|
||||
m_gl_context->SetSwapInterval(static_cast<s32>(mode != VsyncMode::Off));
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
|
||||
m_vsync_mode = mode;
|
||||
}
|
||||
|
||||
bool GSDeviceOGL::Create(const WindowInfo& wi, VsyncMode vsync)
|
||||
{
|
||||
if (!GSDevice::Create(wi, vsync))
|
||||
return false;
|
||||
|
||||
const RenderAPI render_api = g_host_display->GetRenderAPI();
|
||||
if (render_api != RenderAPI::OpenGL && render_api != RenderAPI::OpenGLES)
|
||||
m_gl_context = GL::Context::Create(wi);
|
||||
if (!m_gl_context)
|
||||
{
|
||||
Console.Error("Failed to create any GL context");
|
||||
m_gl_context.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_gl_context->MakeCurrent())
|
||||
{
|
||||
Console.Error("Failed to make GL context current");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check openGL requirement as soon as possible so we can switch to another
|
||||
// renderer/device
|
||||
GLLoader::is_gles = (render_api == RenderAPI::OpenGLES);
|
||||
GLLoader::is_gles = m_gl_context->IsGLES();
|
||||
if (!GLLoader::check_gl_requirements())
|
||||
return false;
|
||||
|
||||
SetSwapInterval();
|
||||
|
||||
if (!GSConfig.DisableShaderCache)
|
||||
{
|
||||
if (!m_shader_cache.Open(false, EmuFolders::Cache, SHADER_CACHE_VERSION))
|
||||
|
@ -155,7 +170,7 @@ bool GSDeviceOGL::Create()
|
|||
{
|
||||
if (!GLLoader::is_gles)
|
||||
{
|
||||
glDebugMessageCallback((GLDEBUGPROC)DebugOutputToFile, NULL);
|
||||
glDebugMessageCallback(DebugMessageCallback, NULL);
|
||||
|
||||
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, true);
|
||||
// Useless info message on Nvidia driver
|
||||
|
@ -164,7 +179,7 @@ bool GSDeviceOGL::Create()
|
|||
}
|
||||
else if (GLAD_GL_KHR_debug)
|
||||
{
|
||||
glDebugMessageCallbackKHR((GLDEBUGPROC)DebugOutputToFile, NULL);
|
||||
glDebugMessageCallbackKHR(DebugMessageCallback, NULL);
|
||||
glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, true);
|
||||
}
|
||||
|
||||
|
@ -183,14 +198,15 @@ bool GSDeviceOGL::Create()
|
|||
GL_PUSH("GSDeviceOGL::Various");
|
||||
|
||||
glGenFramebuffers(1, &m_fbo);
|
||||
// Always write to the first buffer
|
||||
OMSetFBO(m_fbo);
|
||||
const GLenum target[1] = {GL_COLOR_ATTACHMENT0};
|
||||
glDrawBuffers(1, target);
|
||||
OMSetFBO(0);
|
||||
|
||||
glGenFramebuffers(1, &m_fbo_read);
|
||||
glGenFramebuffers(1, &m_fbo_write);
|
||||
|
||||
OMSetFBO(m_fbo);
|
||||
|
||||
// Always write to the first buffer
|
||||
static constexpr GLenum target[1] = {GL_COLOR_ATTACHMENT0};
|
||||
glDrawBuffers(1, target);
|
||||
|
||||
// Always read from the first buffer
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
|
@ -239,10 +255,6 @@ bool GSDeviceOGL::Create()
|
|||
glVertexAttribPointer(7, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GSVertex), (const GLvoid*)(28));
|
||||
}
|
||||
|
||||
// must be done after va is created
|
||||
GLState::Clear();
|
||||
RestoreAPIState();
|
||||
|
||||
// ****************************************************************
|
||||
// Pre Generate the different sampler object
|
||||
// ****************************************************************
|
||||
|
@ -395,24 +407,10 @@ bool GSDeviceOGL::Create()
|
|||
}
|
||||
|
||||
// ****************************************************************
|
||||
// Shade boost
|
||||
// Post processing
|
||||
// ****************************************************************
|
||||
{
|
||||
GL_PUSH("GSDeviceOGL::Shadeboost");
|
||||
|
||||
const auto shader = Host::ReadResourceFileToString("shaders/opengl/shadeboost.glsl");
|
||||
if (!shader.has_value())
|
||||
{
|
||||
Host::ReportErrorAsync("GS", "Failed to read shaders/opengl/shadeboost.glsl.");
|
||||
if (!CompileShadeBoostProgram() || !CompileFXAAProgram())
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string ps(GetShaderSource("ps_main", GL_FRAGMENT_SHADER, *shader));
|
||||
if (!m_shader_cache.GetProgram(&m_shadeboost.ps, m_convert.vs, {}, ps))
|
||||
return false;
|
||||
m_shadeboost.ps.RegisterUniform("params");
|
||||
m_shadeboost.ps.SetName("Shadeboost pipe");
|
||||
}
|
||||
|
||||
// Image load store and GLSL 420pack is core in GL4.2, no need to check.
|
||||
m_features.cas_sharpening = ((GLAD_GL_VERSION_4_2 && GLAD_GL_ARB_compute_shader) || GLAD_GL_ES_VERSION_3_2) && CreateCASPrograms();
|
||||
|
@ -491,6 +489,9 @@ bool GSDeviceOGL::Create()
|
|||
}
|
||||
}
|
||||
|
||||
if (!CreateImGuiProgram())
|
||||
return false;
|
||||
|
||||
// Basic to ensure structures are correctly packed
|
||||
static_assert(sizeof(VSSelector) == 1, "Wrong VSSelector size");
|
||||
static_assert(sizeof(PSSelector) == 12, "Wrong PSSelector size");
|
||||
|
@ -501,6 +502,20 @@ bool GSDeviceOGL::Create()
|
|||
return true;
|
||||
}
|
||||
|
||||
void GSDeviceOGL::Destroy()
|
||||
{
|
||||
GSDevice::Destroy();
|
||||
|
||||
if (m_gl_context)
|
||||
{
|
||||
DestroyTimestampQueries();
|
||||
DestroyResources();
|
||||
|
||||
m_gl_context->DoneCurrent();
|
||||
m_gl_context.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool GSDeviceOGL::CreateTextureFX()
|
||||
{
|
||||
GL_PUSH("GSDeviceOGL::CreateTextureFX");
|
||||
|
@ -529,78 +544,293 @@ bool GSDeviceOGL::CreateTextureFX()
|
|||
m_om_dss[key] = CreateDepthStencil(OMDepthStencilSelector(key));
|
||||
}
|
||||
|
||||
GL::Program::ResetLastProgram();
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSDeviceOGL::ResetAPIState()
|
||||
void GSDeviceOGL::SetSwapInterval()
|
||||
{
|
||||
if (GLState::point_size)
|
||||
glDisable(GL_PROGRAM_POINT_SIZE);
|
||||
if (GLState::line_width != 1.0f)
|
||||
glLineWidth(1.0f);
|
||||
|
||||
// clear out DSB
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
|
||||
glDisable(GL_BLEND);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
const int interval = ((m_vsync_mode == VsyncMode::Adaptive) ? -1 : ((m_vsync_mode == VsyncMode::On) ? 1 : 0));
|
||||
m_gl_context->SetSwapInterval(interval);
|
||||
}
|
||||
|
||||
void GSDeviceOGL::RestoreAPIState()
|
||||
void GSDeviceOGL::DestroyResources()
|
||||
{
|
||||
glBindVertexArray(m_vertex_array_object);
|
||||
m_shader_cache.Close();
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLState::fbo);
|
||||
if (m_palette_ss != 0)
|
||||
glDeleteSamplers(1, &m_palette_ss);
|
||||
|
||||
glViewportIndexedf(0, 0, 0, static_cast<float>(GLState::viewport.x), static_cast<float>(GLState::viewport.y));
|
||||
glScissorIndexed(0, GLState::scissor.x, GLState::scissor.y, GLState::scissor.width(), GLState::scissor.height());
|
||||
m_programs.clear();
|
||||
|
||||
glBlendEquationSeparate(GLState::eq_RGB, GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(GLState::f_sRGB, GLState::f_dRGB, GL_ONE, GL_ZERO);
|
||||
for (GSDepthStencilOGL* ds : m_om_dss)
|
||||
delete ds;
|
||||
|
||||
const float bf = static_cast<float>(GLState::bf) / 128.0f;
|
||||
glBlendColor(bf, bf, bf, bf);
|
||||
if (m_ps_ss[0] != 0)
|
||||
glDeleteSamplers(std::size(m_ps_ss), m_ps_ss);
|
||||
|
||||
if (GLState::blend)
|
||||
{
|
||||
glEnable(GL_BLEND);
|
||||
m_imgui.ps.Destroy();
|
||||
if (m_imgui.vao != 0)
|
||||
glDeleteVertexArrays(1, &m_imgui.vao);
|
||||
|
||||
m_cas.upscale_ps.Destroy();
|
||||
m_cas.sharpen_ps.Destroy();
|
||||
|
||||
m_shadeboost.ps.Destroy();
|
||||
|
||||
for (GL::Program& prog : m_date.primid_ps)
|
||||
prog.Destroy();
|
||||
delete m_date.dss;
|
||||
|
||||
m_fxaa.ps.Destroy();
|
||||
|
||||
for (GL::Program& prog : m_present)
|
||||
prog.Destroy();
|
||||
|
||||
for (GL::Program& prog : m_convert.ps)
|
||||
prog.Destroy();
|
||||
delete m_convert.dss;
|
||||
delete m_convert.dss_write;
|
||||
|
||||
for (GL::Program& prog : m_interlace.ps)
|
||||
prog.Destroy();
|
||||
|
||||
for (GL::Program& prog : m_merge_obj.ps)
|
||||
prog.Destroy();
|
||||
|
||||
m_fragment_uniform_stream_buffer.reset();
|
||||
m_vertex_uniform_stream_buffer.reset();
|
||||
|
||||
glBindVertexArray(0);
|
||||
if (m_vertex_array_object != 0)
|
||||
glDeleteVertexArrays(1, &m_vertex_array_object);
|
||||
|
||||
m_index_stream_buffer.reset();
|
||||
m_vertex_stream_buffer.reset();
|
||||
s_texture_upload_buffer.reset();
|
||||
|
||||
if (m_fbo != 0)
|
||||
glDeleteFramebuffers(1, &m_fbo);
|
||||
if (m_fbo_read != 0)
|
||||
glDeleteFramebuffers(1, &m_fbo_read);
|
||||
if (m_fbo_write != 0)
|
||||
glDeleteFramebuffers(1, &m_fbo_write);
|
||||
}
|
||||
|
||||
bool GSDeviceOGL::ChangeWindow(const WindowInfo& new_wi)
|
||||
{
|
||||
pxAssert(m_gl_context);
|
||||
|
||||
if (!m_gl_context->ChangeSurface(new_wi))
|
||||
{
|
||||
Console.Error("Failed to change surface");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_window_info = m_gl_context->GetWindowInfo();
|
||||
|
||||
if (new_wi.type != WindowInfo::Type::Surfaceless)
|
||||
{
|
||||
// reset vsync rate, since it (usually) gets lost
|
||||
if (m_vsync_mode != VsyncMode::Adaptive || !m_gl_context->SetSwapInterval(-1))
|
||||
m_gl_context->SetSwapInterval(static_cast<s32>(m_vsync_mode != VsyncMode::Off));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSDeviceOGL::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
||||
{
|
||||
m_window_info.surface_scale = new_window_scale;
|
||||
if (m_window_info.surface_width == static_cast<u32>(new_window_width) &&
|
||||
m_window_info.surface_height == static_cast<u32>(new_window_height))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_gl_context->ResizeSurface(static_cast<u32>(new_window_width), static_cast<u32>(new_window_height));
|
||||
m_window_info = m_gl_context->GetWindowInfo();
|
||||
}
|
||||
|
||||
bool GSDeviceOGL::SupportsExclusiveFullscreen() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GSDeviceOGL::IsExclusiveFullscreen()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GSDeviceOGL::SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void GSDeviceOGL::DestroySurface()
|
||||
{
|
||||
m_window_info = {};
|
||||
if (!m_gl_context->ChangeSurface(m_window_info))
|
||||
Console.Error("Failed to switch to surfaceless");
|
||||
}
|
||||
|
||||
std::string GSDeviceOGL::GetDriverInfo() const
|
||||
{
|
||||
const char* gl_vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
|
||||
const char* gl_renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
|
||||
const char* gl_version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
||||
return StringUtil::StdStringFromFormat(
|
||||
"%s Context:\n%s\n%s %s", m_gl_context->IsGLES() ? "OpenGL ES" : "OpenGL", gl_version, gl_vendor, gl_renderer);
|
||||
}
|
||||
|
||||
GSDevice::PresentResult GSDeviceOGL::BeginPresent(bool frame_skip)
|
||||
{
|
||||
if (frame_skip || m_window_info.type == WindowInfo::Type::Surfaceless)
|
||||
return PresentResult::FrameSkipped;
|
||||
|
||||
OMSetFBO(0);
|
||||
OMSetColorMaskState();
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
||||
const GSVector2i size = GetWindowSize();
|
||||
SetViewport(size);
|
||||
SetScissor(GSVector4i::loadh(size));
|
||||
|
||||
return PresentResult::OK;
|
||||
}
|
||||
|
||||
void GSDeviceOGL::EndPresent()
|
||||
{
|
||||
RenderImGui();
|
||||
|
||||
if (m_gpu_timing_enabled)
|
||||
PopTimestampQuery();
|
||||
|
||||
m_gl_context->SwapBuffers();
|
||||
|
||||
if (m_gpu_timing_enabled)
|
||||
KickTimestampQuery();
|
||||
}
|
||||
|
||||
void GSDeviceOGL::CreateTimestampQueries()
|
||||
{
|
||||
const bool gles = m_gl_context->IsGLES();
|
||||
const auto GenQueries = gles ? glGenQueriesEXT : glGenQueries;
|
||||
|
||||
GenQueries(static_cast<u32>(m_timestamp_queries.size()), m_timestamp_queries.data());
|
||||
KickTimestampQuery();
|
||||
}
|
||||
|
||||
void GSDeviceOGL::DestroyTimestampQueries()
|
||||
{
|
||||
if (m_timestamp_queries[0] == 0)
|
||||
return;
|
||||
|
||||
const bool gles = m_gl_context->IsGLES();
|
||||
const auto DeleteQueries = gles ? glDeleteQueriesEXT : glDeleteQueries;
|
||||
|
||||
if (m_timestamp_query_started)
|
||||
{
|
||||
const auto EndQuery = gles ? glEndQueryEXT : glEndQuery;
|
||||
EndQuery(GL_TIME_ELAPSED);
|
||||
}
|
||||
|
||||
DeleteQueries(static_cast<u32>(m_timestamp_queries.size()), m_timestamp_queries.data());
|
||||
m_timestamp_queries.fill(0);
|
||||
m_read_timestamp_query = 0;
|
||||
m_write_timestamp_query = 0;
|
||||
m_waiting_timestamp_queries = 0;
|
||||
m_timestamp_query_started = false;
|
||||
}
|
||||
|
||||
void GSDeviceOGL::PopTimestampQuery()
|
||||
{
|
||||
const bool gles = m_gl_context->IsGLES();
|
||||
|
||||
if (gles)
|
||||
{
|
||||
GLint disjoint = 0;
|
||||
glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint);
|
||||
if (disjoint)
|
||||
{
|
||||
DevCon.WriteLn("GPU timing disjoint, resetting.");
|
||||
if (m_timestamp_query_started)
|
||||
glEndQueryEXT(GL_TIME_ELAPSED);
|
||||
|
||||
m_read_timestamp_query = 0;
|
||||
m_write_timestamp_query = 0;
|
||||
m_waiting_timestamp_queries = 0;
|
||||
m_timestamp_query_started = false;
|
||||
}
|
||||
}
|
||||
|
||||
while (m_waiting_timestamp_queries > 0)
|
||||
{
|
||||
const auto GetQueryObjectiv = gles ? glGetQueryObjectivEXT : glGetQueryObjectiv;
|
||||
const auto GetQueryObjectui64v = gles ? glGetQueryObjectui64vEXT : glGetQueryObjectui64v;
|
||||
|
||||
GLint available = 0;
|
||||
GetQueryObjectiv(m_timestamp_queries[m_read_timestamp_query], GL_QUERY_RESULT_AVAILABLE, &available);
|
||||
|
||||
if (!available)
|
||||
break;
|
||||
|
||||
u64 result = 0;
|
||||
GetQueryObjectui64v(m_timestamp_queries[m_read_timestamp_query], GL_QUERY_RESULT, &result);
|
||||
m_accumulated_gpu_time += static_cast<float>(static_cast<double>(result) / 1000000.0);
|
||||
m_read_timestamp_query = (m_read_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
|
||||
m_waiting_timestamp_queries--;
|
||||
}
|
||||
|
||||
if (m_timestamp_query_started)
|
||||
{
|
||||
const auto EndQuery = gles ? glEndQueryEXT : glEndQuery;
|
||||
EndQuery(GL_TIME_ELAPSED);
|
||||
|
||||
m_write_timestamp_query = (m_write_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
|
||||
m_timestamp_query_started = false;
|
||||
m_waiting_timestamp_queries++;
|
||||
}
|
||||
}
|
||||
|
||||
void GSDeviceOGL::KickTimestampQuery()
|
||||
{
|
||||
if (m_timestamp_query_started || m_waiting_timestamp_queries == NUM_TIMESTAMP_QUERIES)
|
||||
return;
|
||||
|
||||
const bool gles = m_gl_context->IsGLES();
|
||||
const auto BeginQuery = gles ? glBeginQueryEXT : glBeginQuery;
|
||||
|
||||
BeginQuery(GL_TIME_ELAPSED, m_timestamp_queries[m_write_timestamp_query]);
|
||||
m_timestamp_query_started = true;
|
||||
}
|
||||
|
||||
bool GSDeviceOGL::SetGPUTimingEnabled(bool enabled)
|
||||
{
|
||||
if (m_gpu_timing_enabled == enabled)
|
||||
return true;
|
||||
|
||||
if (enabled && m_gl_context->IsGLES() && !GLAD_GL_EXT_disjoint_timer_query)
|
||||
return false;
|
||||
|
||||
m_gpu_timing_enabled = enabled;
|
||||
if (m_gpu_timing_enabled)
|
||||
CreateTimestampQueries();
|
||||
else
|
||||
{
|
||||
glDisable(GL_BLEND);
|
||||
DestroyTimestampQueries();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const OMColorMaskSelector msel{ GLState::wrgba };
|
||||
glColorMask(msel.wr, msel.wg, msel.wb, msel.wa);
|
||||
|
||||
GLState::depth ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GLState::depth_func);
|
||||
glDepthMask(GLState::depth_mask);
|
||||
|
||||
if (GLState::stencil)
|
||||
float GSDeviceOGL::GetAndResetAccumulatedGPUTime()
|
||||
{
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
}
|
||||
else
|
||||
{
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
glStencilFunc(GLState::stencil_func, 1, 1);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GLState::stencil_pass);
|
||||
|
||||
glBindSampler(0, GLState::ps_ss);
|
||||
|
||||
for (GLuint i = 0; i < sizeof(GLState::tex_unit) / sizeof(GLState::tex_unit[0]); i++)
|
||||
glBindTextureUnit(i, GLState::tex_unit[i]);
|
||||
|
||||
if (GLState::point_size)
|
||||
glEnable(GL_PROGRAM_POINT_SIZE);
|
||||
if (GLState::line_width != 1.0f)
|
||||
glLineWidth(GLState::line_width);
|
||||
|
||||
// Force UBOs to be reuploaded, we don't know what else was bound there.
|
||||
std::memset(&m_vs_cb_cache, 0xFF, sizeof(m_vs_cb_cache));
|
||||
std::memset(&m_ps_cb_cache, 0xFF, sizeof(m_ps_cb_cache));
|
||||
const float value = m_accumulated_gpu_time;
|
||||
m_accumulated_gpu_time = 0.0f;
|
||||
return value;
|
||||
}
|
||||
|
||||
void GSDeviceOGL::DrawPrimitive()
|
||||
|
@ -898,8 +1128,6 @@ std::string GSDeviceOGL::GenGlslHeader(const std::string_view& entry, GLenum typ
|
|||
|
||||
// Need GL version 420
|
||||
header += "#extension GL_ARB_shading_language_420pack: require\n";
|
||||
// Need GL version 410
|
||||
header += "#extension GL_ARB_separate_shader_objects: require\n";
|
||||
|
||||
if (m_features.framebuffer_fetch && GLAD_GL_EXT_shader_framebuffer_fetch)
|
||||
header += "#extension GL_EXT_shader_framebuffer_fetch : require\n";
|
||||
|
@ -1176,7 +1404,7 @@ void GSDeviceOGL::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
|
|||
{
|
||||
ASSERT(sTex);
|
||||
|
||||
const GSVector2i ds(dTex ? dTex->GetSize() : GSVector2i(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight()));
|
||||
const GSVector2i ds(dTex ? dTex->GetSize() : GSVector2i(GetWindowWidth(), GetWindowHeight()));
|
||||
DisplayConstantBuffer cb;
|
||||
cb.SetSource(sRect, sTex->GetSize());
|
||||
cb.SetTarget(dRect, ds);
|
||||
|
@ -1441,25 +1669,40 @@ void GSDeviceOGL::DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture
|
|||
StretchRect(sTex, sRect, dTex, dRect, m_interlace.ps[static_cast<int>(shader)], linear);
|
||||
}
|
||||
|
||||
void GSDeviceOGL::DoFXAA(GSTexture* sTex, GSTexture* dTex)
|
||||
{
|
||||
// Lazy compile
|
||||
if (!m_fxaa.ps.IsValid())
|
||||
bool GSDeviceOGL::CompileFXAAProgram()
|
||||
{
|
||||
// Needs ARB_gpu_shader5 for gather.
|
||||
if (!GLLoader::is_gles && !GLLoader::found_GL_ARB_gpu_shader5)
|
||||
return;
|
||||
{
|
||||
Console.Warning("FXAA is not supported with the current GPU");
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string_view fxaa_macro = "#define FXAA_GLSL_130 1\n";
|
||||
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/common/fxaa.fx");
|
||||
if (!shader.has_value())
|
||||
return;
|
||||
{
|
||||
Console.Error("Failed to read fxaa.fs");
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string ps(GetShaderSource("ps_main", GL_FRAGMENT_SHADER, shader->c_str(), fxaa_macro));
|
||||
if (!m_fxaa.ps.Compile(m_convert.vs, {}, ps) || !m_fxaa.ps.Link())
|
||||
return;
|
||||
std::optional<GL::Program> prog = m_shader_cache.GetProgram(m_convert.vs, {}, ps);
|
||||
if (!prog.has_value())
|
||||
{
|
||||
Console.Error("Failed to compile FXAA fragment shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_fxaa.ps = std::move(prog.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSDeviceOGL::DoFXAA(GSTexture* sTex, GSTexture* dTex)
|
||||
{
|
||||
if (!m_fxaa.ps.IsValid())
|
||||
return;
|
||||
|
||||
GL_PUSH("DoFxaa");
|
||||
|
||||
OMSetColorMaskState();
|
||||
|
@ -1472,6 +1715,23 @@ void GSDeviceOGL::DoFXAA(GSTexture* sTex, GSTexture* dTex)
|
|||
StretchRect(sTex, sRect, dTex, dRect, m_fxaa.ps, true);
|
||||
}
|
||||
|
||||
bool GSDeviceOGL::CompileShadeBoostProgram()
|
||||
{
|
||||
const auto shader = Host::ReadResourceFileToString("shaders/opengl/shadeboost.glsl");
|
||||
if (!shader.has_value())
|
||||
{
|
||||
Host::ReportErrorAsync("GS", "Failed to read shaders/opengl/shadeboost.glsl.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string ps(GetShaderSource("ps_main", GL_FRAGMENT_SHADER, *shader));
|
||||
if (!m_shader_cache.GetProgram(&m_shadeboost.ps, m_convert.vs, {}, ps))
|
||||
return false;
|
||||
m_shadeboost.ps.RegisterUniform("params");
|
||||
m_shadeboost.ps.SetName("Shadeboost pipe");
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSDeviceOGL::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4])
|
||||
{
|
||||
GL_PUSH("DoShadeBoost");
|
||||
|
@ -1651,6 +1911,140 @@ bool GSDeviceOGL::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, con
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSDeviceOGL::CreateImGuiProgram()
|
||||
{
|
||||
std::optional<std::string> glsl = Host::ReadResourceFileToString("shaders/opengl/imgui.glsl");
|
||||
if (!glsl.has_value())
|
||||
{
|
||||
Console.Error("Failed to read imgui.glsl");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<GL::Program> prog = m_shader_cache.GetProgram(
|
||||
GetShaderSource("vs_main", GL_VERTEX_SHADER, glsl.value()), {},
|
||||
GetShaderSource("ps_main", GL_FRAGMENT_SHADER, glsl.value()));
|
||||
if (!prog.has_value())
|
||||
{
|
||||
Console.Error("Failed to compile imgui shaders");
|
||||
return false;
|
||||
}
|
||||
|
||||
prog->SetName("ImGui Render");
|
||||
prog->RegisterUniform("ProjMtx");
|
||||
m_imgui.ps = std::move(prog.value());
|
||||
|
||||
// Need a different VAO because the layout doesn't match GS
|
||||
glGenVertexArrays(1, &m_imgui.vao);
|
||||
glBindVertexArray(m_imgui.vao);
|
||||
m_vertex_stream_buffer->Bind();
|
||||
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
|
||||
glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
|
||||
|
||||
glBindVertexArray(m_vertex_array_object);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSDeviceOGL::RenderImGui()
|
||||
{
|
||||
ImGui::Render();
|
||||
const ImDrawData* draw_data = ImGui::GetDrawData();
|
||||
if (draw_data->CmdListsCount == 0)
|
||||
return;
|
||||
|
||||
constexpr float L = 0.0f;
|
||||
const float R = static_cast<float>(m_window_info.surface_width);
|
||||
constexpr float T = 0.0f;
|
||||
const float B = static_cast<float>(m_window_info.surface_height);
|
||||
|
||||
// clang-format off
|
||||
const float ortho_projection[4][4] =
|
||||
{
|
||||
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, -1.0f, 0.0f },
|
||||
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
m_imgui.ps.Bind();
|
||||
m_imgui.ps.UniformMatrix4fv(0, &ortho_projection[0][0]);
|
||||
glBindVertexArray(m_imgui.vao);
|
||||
OMSetBlendState(true, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_FUNC_ADD);
|
||||
OMSetDepthStencilState(m_convert.dss);
|
||||
PSSetSamplerState(m_convert.ln);
|
||||
|
||||
// Need to flip the scissor due to lower-left on the window framebuffer
|
||||
GSVector4i last_scissor = GSVector4i::xffffffff();
|
||||
|
||||
// Render command lists
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
|
||||
// Different vertex format.
|
||||
u32 vertex_start;
|
||||
{
|
||||
const u32 size = static_cast<u32>(cmd_list->VtxBuffer.Size) * sizeof(ImDrawVert);
|
||||
auto res = m_vertex_stream_buffer->Map(sizeof(ImDrawVert), size);
|
||||
std::memcpy(res.pointer, cmd_list->VtxBuffer.Data, size);
|
||||
vertex_start = res.index_aligned;
|
||||
m_vertex_stream_buffer->Unmap(size);
|
||||
}
|
||||
|
||||
// Bit awkward, because this is using 16-bit indices, not 32-bit.
|
||||
u32 index_start;
|
||||
{
|
||||
static_assert(sizeof(ImDrawIdx) == sizeof(u16));
|
||||
|
||||
const u32 size = static_cast<u32>(cmd_list->IdxBuffer.Size) * sizeof(ImDrawIdx);
|
||||
auto res = m_index_stream_buffer->Map(sizeof(u16), size);
|
||||
index_start = res.index_aligned;
|
||||
std::memcpy(res.pointer, cmd_list->IdxBuffer.Data, size);
|
||||
m_index_stream_buffer->Unmap(size);
|
||||
m_index_stream_buffer->Bind();
|
||||
}
|
||||
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
pxAssert(!pcmd->UserCallback);
|
||||
|
||||
const GSVector4 clip = GSVector4::load<false>(&pcmd->ClipRect);
|
||||
if ((clip.zwzw() <= clip.xyxy()).mask() != 0)
|
||||
continue;
|
||||
|
||||
// Apply scissor/clipping rectangle (Y is inverted in OpenGL)
|
||||
const GSVector4i iclip = GSVector4i(clip);
|
||||
if (!last_scissor.eq(iclip))
|
||||
{
|
||||
glScissor(iclip.x, m_window_info.surface_height - iclip.w, iclip.width(), iclip.height());
|
||||
last_scissor = iclip;
|
||||
}
|
||||
|
||||
// Since we don't have the GSTexture...
|
||||
const GLuint texture_id = static_cast<GLuint>(reinterpret_cast<uintptr_t>(pcmd->GetTexID()));
|
||||
if (GLState::tex_unit[0] != texture_id)
|
||||
{
|
||||
GLState::tex_unit[0] = texture_id;
|
||||
glBindTextureUnit(0, texture_id);
|
||||
}
|
||||
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, GL_UNSIGNED_SHORT,
|
||||
(void*)(intptr_t)((pcmd->IdxOffset + index_start) * sizeof(ImDrawIdx)), pcmd->VtxOffset + vertex_start);
|
||||
}
|
||||
|
||||
g_perfmon.Put(GSPerfMon::DrawCalls, cmd_list->CmdBuffer.Size);
|
||||
}
|
||||
|
||||
glBindVertexArray(m_vertex_array_object);
|
||||
glScissor(GLState::scissor.x, GLState::scissor.y, GLState::scissor.width(), GLState::scissor.height());
|
||||
}
|
||||
|
||||
void GSDeviceOGL::OMAttachRt(GSTextureOGL* rt)
|
||||
{
|
||||
if (rt)
|
||||
|
@ -1780,21 +2174,29 @@ void GSDeviceOGL::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVecto
|
|||
else
|
||||
OMAttachDs();
|
||||
|
||||
const GSVector2i size = rt ? rt->GetSize() : ds ? ds->GetSize() : GLState::viewport;
|
||||
if (GLState::viewport != size)
|
||||
if (rt || ds)
|
||||
{
|
||||
GLState::viewport = size;
|
||||
// FIXME ViewportIndexedf or ViewportIndexedfv (GL4.1)
|
||||
glViewportIndexedf(0, 0, 0, GLfloat(size.x), GLfloat(size.y));
|
||||
const GSVector2i size = rt ? rt->GetSize() : ds->GetSize();
|
||||
SetViewport(size);
|
||||
SetScissor(scissor ? *scissor : GSVector4i::loadh(size));
|
||||
}
|
||||
}
|
||||
|
||||
const GSVector4i r = scissor ? *scissor : GSVector4i(size).zwxy();
|
||||
|
||||
if (!GLState::scissor.eq(r))
|
||||
void GSDeviceOGL::SetViewport(const GSVector2i& viewport)
|
||||
{
|
||||
GLState::scissor = r;
|
||||
// FIXME ScissorIndexedv (GL4.1)
|
||||
glScissorIndexed(0, r.x, r.y, r.width(), r.height());
|
||||
if (GLState::viewport != viewport)
|
||||
{
|
||||
GLState::viewport = viewport;
|
||||
glViewport(0, 0, viewport.x, viewport.y);
|
||||
}
|
||||
}
|
||||
|
||||
void GSDeviceOGL::SetScissor(const GSVector4i& scissor)
|
||||
{
|
||||
if (!GLState::scissor.eq(scissor))
|
||||
{
|
||||
GLState::scissor = scissor;
|
||||
glScissor(scissor.x, scissor.y, scissor.width(), scissor.height());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2174,7 +2576,7 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config, bool needs_barrier)
|
|||
}
|
||||
|
||||
// Note: used as a callback of DebugMessageCallback. Don't change the signature
|
||||
void GSDeviceOGL::DebugOutputToFile(GLenum gl_source, GLenum gl_type, GLuint id, GLenum gl_severity, GLsizei gl_length, const GLchar* gl_message, const void* userParam)
|
||||
void GSDeviceOGL::DebugMessageCallback(GLenum gl_source, GLenum gl_type, GLuint id, GLenum gl_severity, GLsizei gl_length, const GLchar* gl_message, const void* userParam)
|
||||
{
|
||||
std::string message(gl_message, gl_length >= 0 ? gl_length : strlen(gl_message));
|
||||
std::string type, severity, source;
|
||||
|
@ -2230,7 +2632,7 @@ GL::StreamBuffer* GSDeviceOGL::GetTextureUploadBuffer()
|
|||
void GSDeviceOGL::PushDebugGroup(const char* fmt, ...)
|
||||
{
|
||||
#ifdef ENABLE_OGL_DEBUG
|
||||
if (!glPushDebugGroup)
|
||||
if (!glPushDebugGroup || !GSConfig.UseDebugDevice)
|
||||
return;
|
||||
|
||||
std::va_list ap;
|
||||
|
@ -2245,7 +2647,7 @@ void GSDeviceOGL::PushDebugGroup(const char* fmt, ...)
|
|||
void GSDeviceOGL::PopDebugGroup()
|
||||
{
|
||||
#ifdef ENABLE_OGL_DEBUG
|
||||
if (!glPopDebugGroup)
|
||||
if (!glPopDebugGroup || !GSConfig.UseDebugDevice)
|
||||
return;
|
||||
|
||||
glPopDebugGroup();
|
||||
|
@ -2255,7 +2657,7 @@ void GSDeviceOGL::PopDebugGroup()
|
|||
void GSDeviceOGL::InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...)
|
||||
{
|
||||
#ifdef ENABLE_OGL_DEBUG
|
||||
if (!glDebugMessageInsert)
|
||||
if (!glDebugMessageInsert || !GSConfig.UseDebugDevice)
|
||||
return;
|
||||
|
||||
GLenum type, id, severity;
|
||||
|
|
|
@ -202,8 +202,9 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
std::string m_shader_tfx_vgs;
|
||||
std::string m_shader_tfx_fs;
|
||||
static constexpr u8 NUM_TIMESTAMP_QUERIES = 5;
|
||||
|
||||
std::unique_ptr<GL::Context> m_gl_context;
|
||||
|
||||
GLuint m_fbo = 0; // frame buffer container
|
||||
GLuint m_fbo_read = 0; // frame buffer container only for reading
|
||||
|
@ -262,6 +263,12 @@ private:
|
|||
GL::Program sharpen_ps;
|
||||
} m_cas;
|
||||
|
||||
struct
|
||||
{
|
||||
GL::Program ps;
|
||||
GLuint vao = 0;
|
||||
} m_imgui;
|
||||
|
||||
GLuint m_ps_ss[1 << 8];
|
||||
GSDepthStencilOGL* m_om_dss[1 << 5] = {};
|
||||
std::unordered_map<ProgramSelector, GL::Program, ProgramSelectorHash> m_programs;
|
||||
|
@ -269,21 +276,47 @@ private:
|
|||
|
||||
GLuint m_palette_ss = 0;
|
||||
|
||||
std::array<GLuint, NUM_TIMESTAMP_QUERIES> m_timestamp_queries = {};
|
||||
float m_accumulated_gpu_time = 0.0f;
|
||||
u8 m_read_timestamp_query = 0;
|
||||
u8 m_write_timestamp_query = 0;
|
||||
u8 m_waiting_timestamp_queries = 0;
|
||||
bool m_timestamp_query_started = false;
|
||||
bool m_gpu_timing_enabled = false;
|
||||
|
||||
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
|
||||
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
|
||||
|
||||
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) final;
|
||||
std::string m_shader_tfx_vgs;
|
||||
std::string m_shader_tfx_fs;
|
||||
|
||||
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) final;
|
||||
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) final;
|
||||
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
|
||||
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final;
|
||||
void SetSwapInterval();
|
||||
void DestroyResources();
|
||||
|
||||
void CreateTimestampQueries();
|
||||
void DestroyTimestampQueries();
|
||||
void PopTimestampQuery();
|
||||
void KickTimestampQuery();
|
||||
|
||||
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
|
||||
|
||||
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) override;
|
||||
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) override;
|
||||
|
||||
bool CompileFXAAProgram();
|
||||
void DoFXAA(GSTexture* sTex, GSTexture* dTex) override;
|
||||
|
||||
bool CompileShadeBoostProgram();
|
||||
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) override;
|
||||
|
||||
bool CreateCASPrograms();
|
||||
bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) final;
|
||||
bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) override;
|
||||
|
||||
void OMAttachRt(GSTextureOGL* rt = NULL);
|
||||
void OMAttachDs(GSTextureOGL* ds = NULL);
|
||||
bool CreateImGuiProgram();
|
||||
void RenderImGui();
|
||||
|
||||
void OMAttachRt(GSTextureOGL* rt = nullptr);
|
||||
void OMAttachDs(GSTextureOGL* ds = nullptr);
|
||||
void OMSetFBO(GLuint fbo);
|
||||
|
||||
void DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds);
|
||||
|
@ -295,53 +328,70 @@ public:
|
|||
__fi static GSDeviceOGL* GetInstance() { return static_cast<GSDeviceOGL*>(g_gs_device.get()); }
|
||||
|
||||
// Used by OpenGL, so the same calling convention is required.
|
||||
static void APIENTRY DebugOutputToFile(GLenum gl_source, GLenum gl_type, GLuint id, GLenum gl_severity, GLsizei gl_length, const GLchar* gl_message, const void* userParam);
|
||||
static void APIENTRY DebugMessageCallback(GLenum gl_source, GLenum gl_type, GLuint id, GLenum gl_severity, GLsizei gl_length, const GLchar* gl_message, const void* userParam);
|
||||
|
||||
static GL::StreamBuffer* GetTextureUploadBuffer();
|
||||
|
||||
__fi u32 GetFBORead() const { return m_fbo_read; }
|
||||
__fi u32 GetFBOWrite() const { return m_fbo_write; }
|
||||
|
||||
bool Create() override;
|
||||
RenderAPI GetRenderAPI() const override;
|
||||
bool HasSurface() const override;
|
||||
|
||||
void ResetAPIState() override;
|
||||
void RestoreAPIState() override;
|
||||
bool Create(const WindowInfo& wi, VsyncMode vsync) override;
|
||||
void Destroy() override;
|
||||
|
||||
bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
||||
bool SupportsExclusiveFullscreen() const override;
|
||||
bool IsExclusiveFullscreen() override;
|
||||
bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||
void DestroySurface() override;
|
||||
std::string GetDriverInfo() const override;
|
||||
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
void DrawPrimitive();
|
||||
void DrawIndexedPrimitive();
|
||||
void DrawIndexedPrimitive(int offset, int count);
|
||||
|
||||
void ClearRenderTarget(GSTexture* t, const GSVector4& c) final;
|
||||
void ClearRenderTarget(GSTexture* t, u32 c) final;
|
||||
void InvalidateRenderTarget(GSTexture* t) final;
|
||||
void ClearDepth(GSTexture* t) final;
|
||||
void ClearStencil(GSTexture* t, u8 c) final;
|
||||
void ClearRenderTarget(GSTexture* t, const GSVector4& c) override;
|
||||
void ClearRenderTarget(GSTexture* t, u32 c) override;
|
||||
void InvalidateRenderTarget(GSTexture* t) override;
|
||||
void ClearDepth(GSTexture* t) override;
|
||||
void ClearStencil(GSTexture* t, u8 c) override;
|
||||
|
||||
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) override;
|
||||
|
||||
GSTexture* InitPrimDateTexture(GSTexture* rt, const GSVector4i& area, bool datm);
|
||||
|
||||
void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) final;
|
||||
void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override;
|
||||
|
||||
void PushDebugGroup(const char* fmt, ...) final;
|
||||
void PopDebugGroup() final;
|
||||
void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) final;
|
||||
void PushDebugGroup(const char* fmt, ...) override;
|
||||
void PopDebugGroup() override;
|
||||
void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) override;
|
||||
|
||||
// BlitRect *does* mess with GL state, be sure to re-bind.
|
||||
void BlitRect(GSTexture* sTex, const GSVector4i& r, const GSVector2i& dsize, bool at_origin, bool linear);
|
||||
|
||||
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true) final;
|
||||
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true) override;
|
||||
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, const GL::Program& ps, bool linear = true);
|
||||
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha) final;
|
||||
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha) override;
|
||||
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, const GL::Program& ps, bool alpha_blend, OMColorMaskSelector cms, bool linear = true);
|
||||
void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear) final;
|
||||
void UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) final;
|
||||
void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) final;
|
||||
void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear) override;
|
||||
void UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) override;
|
||||
void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) override;
|
||||
|
||||
void DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader) final;
|
||||
void DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader) override;
|
||||
void DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, const GSVector2& ds);
|
||||
|
||||
void RenderHW(GSHWDrawConfig& config) final;
|
||||
void RenderHW(GSHWDrawConfig& config) override;
|
||||
void SendHWDraw(const GSHWDrawConfig& config, bool needs_barrier);
|
||||
|
||||
void SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vertices, bool datm);
|
||||
|
@ -353,14 +403,17 @@ public:
|
|||
void PSSetShaderResource(int i, GSTexture* sr);
|
||||
void PSSetShaderResources(GSTexture* sr0, GSTexture* sr1);
|
||||
void PSSetSamplerState(GLuint ss);
|
||||
void ClearSamplerCache() final;
|
||||
void ClearSamplerCache() override;
|
||||
|
||||
void OMSetDepthStencilState(GSDepthStencilOGL* dss);
|
||||
void OMSetBlendState(bool enable = false, GLenum src_factor = GL_ONE, GLenum dst_factor = GL_ZERO, GLenum op = GL_FUNC_ADD, bool is_constant = false, u8 constant = 0);
|
||||
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = NULL);
|
||||
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = nullptr);
|
||||
void OMSetColorMaskState(OMColorMaskSelector sel = OMColorMaskSelector());
|
||||
void OMUnbindTexture(GSTextureOGL* tex);
|
||||
|
||||
void SetViewport(const GSVector2i& viewport);
|
||||
void SetScissor(const GSVector4i& scissor);
|
||||
|
||||
bool CreateTextureFX();
|
||||
std::string GetShaderSource(const std::string_view& entry, GLenum type, const std::string_view& glsl_h_code,
|
||||
const std::string_view& macro_sel = std::string_view());
|
||||
|
|
|
@ -14,6 +14,15 @@
|
|||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "GS/GS.h"
|
||||
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
|
||||
#include "GS/GSGL.h"
|
||||
#include "GS/GSPerfMon.h"
|
||||
#include "GS/GSUtil.h"
|
||||
#include "Host.h"
|
||||
#include "ShaderCacheVersion.h"
|
||||
|
||||
#include "common/Vulkan/Builders.h"
|
||||
#include "common/Vulkan/Context.h"
|
||||
#include "common/Vulkan/ShaderCache.h"
|
||||
|
@ -22,13 +31,9 @@
|
|||
#include "common/Align.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/ScopedGuard.h"
|
||||
#include "GS.h"
|
||||
#include "GSDeviceVK.h"
|
||||
#include "GS/GSGL.h"
|
||||
#include "GS/GSPerfMon.h"
|
||||
#include "GS/GSUtil.h"
|
||||
#include "Host.h"
|
||||
#include "HostDisplay.h"
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <limits>
|
||||
|
||||
|
@ -55,6 +60,16 @@ static VkAttachmentLoadOp GetLoadOpForTexture(GSTextureVK* tex)
|
|||
// clang-format on
|
||||
}
|
||||
|
||||
static VkPresentModeKHR GetPreferredPresentModeForVsyncMode(VsyncMode mode)
|
||||
{
|
||||
if (mode == VsyncMode::On)
|
||||
return VK_PRESENT_MODE_FIFO_KHR;
|
||||
else if (mode == VsyncMode::Adaptive)
|
||||
return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
|
||||
else
|
||||
return VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
}
|
||||
|
||||
GSDeviceVK::GSDeviceVK()
|
||||
{
|
||||
#ifdef ENABLE_OGL_DEBUG
|
||||
|
@ -64,13 +79,90 @@ GSDeviceVK::GSDeviceVK()
|
|||
std::memset(&m_pipeline_selector, 0, sizeof(m_pipeline_selector));
|
||||
}
|
||||
|
||||
GSDeviceVK::~GSDeviceVK() {}
|
||||
|
||||
bool GSDeviceVK::Create()
|
||||
GSDeviceVK::~GSDeviceVK()
|
||||
{
|
||||
if (!GSDevice::Create() || !CheckFeatures())
|
||||
pxAssert(!g_vulkan_context);
|
||||
}
|
||||
|
||||
void GSDeviceVK::GetAdaptersAndFullscreenModes(
|
||||
std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes)
|
||||
{
|
||||
std::vector<Vulkan::SwapChain::FullscreenModeInfo> fsmodes;
|
||||
|
||||
if (g_vulkan_context)
|
||||
{
|
||||
if (adapters)
|
||||
*adapters = Vulkan::Context::EnumerateGPUNames(g_vulkan_context->GetVulkanInstance());
|
||||
|
||||
if (fullscreen_modes)
|
||||
{
|
||||
fsmodes = Vulkan::SwapChain::GetSurfaceFullscreenModes(
|
||||
g_vulkan_context->GetVulkanInstance(), g_vulkan_context->GetPhysicalDevice(), WindowInfo());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Vulkan::LoadVulkanLibrary())
|
||||
{
|
||||
ScopedGuard lib_guard([]() { Vulkan::UnloadVulkanLibrary(); });
|
||||
const VkInstance instance = Vulkan::Context::CreateVulkanInstance(nullptr, false, false);
|
||||
if (instance != VK_NULL_HANDLE)
|
||||
{
|
||||
if (Vulkan::LoadVulkanInstanceFunctions(instance))
|
||||
*adapters = Vulkan::Context::EnumerateGPUNames(instance);
|
||||
|
||||
vkDestroyInstance(instance, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fsmodes.empty())
|
||||
{
|
||||
fullscreen_modes->clear();
|
||||
fullscreen_modes->reserve(fsmodes.size());
|
||||
for (const Vulkan::SwapChain::FullscreenModeInfo& fmi : fsmodes)
|
||||
{
|
||||
fullscreen_modes->push_back(GetFullscreenModeString(fmi.width, fmi.height, fmi.refresh_rate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RenderAPI GSDeviceVK::GetRenderAPI() const
|
||||
{
|
||||
return RenderAPI::Vulkan;
|
||||
}
|
||||
|
||||
bool GSDeviceVK::HasSurface() const
|
||||
{
|
||||
return static_cast<bool>(m_swap_chain);
|
||||
}
|
||||
|
||||
bool GSDeviceVK::Create(const WindowInfo& wi, VsyncMode vsync)
|
||||
{
|
||||
if (!GSDevice::Create(wi, vsync))
|
||||
return false;
|
||||
|
||||
WindowInfo local_wi(wi);
|
||||
const bool debug_device = GSConfig.UseDebugDevice;
|
||||
if (!Vulkan::Context::Create(GSConfig.Adapter, &local_wi, &m_swap_chain, GetPreferredPresentModeForVsyncMode(vsync),
|
||||
!GSConfig.DisableThreadedPresentation, debug_device, debug_device))
|
||||
{
|
||||
Console.Error("Failed to create Vulkan context");
|
||||
return false;
|
||||
}
|
||||
|
||||
Vulkan::ShaderCache::Create(GSConfig.DisableShaderCache ? std::string_view() : std::string_view(EmuFolders::Cache),
|
||||
SHADER_CACHE_VERSION, GSConfig.UseDebugDevice);
|
||||
|
||||
// NOTE: This is assigned afterwards, because some platforms can modify the window info (e.g. Metal).
|
||||
m_window_info = m_swap_chain ? m_swap_chain->GetWindowInfo() : local_wi;
|
||||
|
||||
if (!CheckFeatures())
|
||||
{
|
||||
Console.Error("Your GPU does not support the required Vulkan features.");
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/vulkan/tfx.glsl");
|
||||
if (!shader.has_value())
|
||||
|
@ -119,31 +211,253 @@ bool GSDeviceVK::Create()
|
|||
|
||||
CompileCASPipelines();
|
||||
|
||||
if (!CompileImGuiPipeline())
|
||||
return false;
|
||||
|
||||
InitializeState();
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSDeviceVK::Destroy()
|
||||
{
|
||||
if (!g_vulkan_context)
|
||||
return;
|
||||
GSDevice::Destroy();
|
||||
|
||||
if (g_vulkan_context)
|
||||
{
|
||||
EndRenderPass();
|
||||
ExecuteCommandBuffer(true);
|
||||
DestroyResources();
|
||||
GSDevice::Destroy();
|
||||
|
||||
g_vulkan_context->WaitForGPUIdle();
|
||||
m_swap_chain.reset();
|
||||
|
||||
Vulkan::ShaderCache::Destroy();
|
||||
Vulkan::Context::Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void GSDeviceVK::ResetAPIState()
|
||||
bool GSDeviceVK::ChangeWindow(const WindowInfo& new_wi)
|
||||
{
|
||||
if (new_wi.type == WindowInfo::Type::Surfaceless)
|
||||
{
|
||||
ExecuteCommandBuffer(true);
|
||||
m_swap_chain.reset();
|
||||
m_window_info = new_wi;
|
||||
return true;
|
||||
}
|
||||
|
||||
// make sure previous frames are presented
|
||||
g_vulkan_context->WaitForGPUIdle();
|
||||
|
||||
// recreate surface in existing swap chain if it already exists
|
||||
if (m_swap_chain)
|
||||
{
|
||||
if (m_swap_chain->RecreateSurface(new_wi))
|
||||
{
|
||||
m_window_info = m_swap_chain->GetWindowInfo();
|
||||
return true;
|
||||
}
|
||||
|
||||
m_swap_chain.reset();
|
||||
}
|
||||
|
||||
WindowInfo wi_copy(new_wi);
|
||||
VkSurfaceKHR surface = Vulkan::SwapChain::CreateVulkanSurface(
|
||||
g_vulkan_context->GetVulkanInstance(), g_vulkan_context->GetPhysicalDevice(), &wi_copy);
|
||||
if (surface == VK_NULL_HANDLE)
|
||||
{
|
||||
Console.Error("Failed to create new surface for swap chain");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_swap_chain = Vulkan::SwapChain::Create(wi_copy, surface, GetPreferredPresentModeForVsyncMode(m_vsync_mode));
|
||||
if (!m_swap_chain)
|
||||
{
|
||||
Console.Error("Failed to create swap chain");
|
||||
Vulkan::SwapChain::DestroyVulkanSurface(g_vulkan_context->GetVulkanInstance(), &wi_copy, surface);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_window_info = m_swap_chain->GetWindowInfo();
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSDeviceVK::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
||||
{
|
||||
if (m_swap_chain->GetWidth() == static_cast<u32>(new_window_width) &&
|
||||
m_swap_chain->GetHeight() == static_cast<u32>(new_window_height))
|
||||
{
|
||||
// skip unnecessary resizes
|
||||
m_window_info.surface_scale = new_window_scale;
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure previous frames are presented
|
||||
g_vulkan_context->WaitForGPUIdle();
|
||||
|
||||
if (!m_swap_chain->ResizeSwapChain(new_window_width, new_window_height, new_window_scale))
|
||||
{
|
||||
// AcquireNextImage() will fail, and we'll recreate the surface.
|
||||
Console.Error("Failed to resize swap chain. Next present will fail.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_window_info = m_swap_chain->GetWindowInfo();
|
||||
}
|
||||
|
||||
bool GSDeviceVK::SupportsExclusiveFullscreen() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GSDeviceVK::IsExclusiveFullscreen()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GSDeviceVK::SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void GSDeviceVK::DestroySurface()
|
||||
{
|
||||
g_vulkan_context->WaitForGPUIdle();
|
||||
m_swap_chain.reset();
|
||||
}
|
||||
|
||||
std::string GSDeviceVK::GetDriverInfo() const
|
||||
{
|
||||
std::string ret;
|
||||
const u32 api_version = g_vulkan_context->GetDeviceProperties().apiVersion;
|
||||
const u32 driver_version = g_vulkan_context->GetDeviceProperties().driverVersion;
|
||||
if (g_vulkan_context->GetOptionalExtensions().vk_khr_driver_properties)
|
||||
{
|
||||
const VkPhysicalDeviceDriverProperties& props = g_vulkan_context->GetDeviceDriverProperties();
|
||||
ret = StringUtil::StdStringFromFormat(
|
||||
"Driver %u.%u.%u\nVulkan %u.%u.%u\nConformance Version %u.%u.%u.%u\n%s\n%s\n%s",
|
||||
VK_VERSION_MAJOR(driver_version), VK_VERSION_MINOR(driver_version), VK_VERSION_PATCH(driver_version),
|
||||
VK_API_VERSION_MAJOR(api_version), VK_API_VERSION_MINOR(api_version), VK_API_VERSION_PATCH(api_version),
|
||||
props.conformanceVersion.major, props.conformanceVersion.minor, props.conformanceVersion.subminor,
|
||||
props.conformanceVersion.patch, props.driverInfo, props.driverName,
|
||||
g_vulkan_context->GetDeviceProperties().deviceName);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = StringUtil::StdStringFromFormat("Driver %u.%u.%u\nVulkan %u.%u.%u\n%s", VK_VERSION_MAJOR(driver_version),
|
||||
VK_VERSION_MINOR(driver_version), VK_VERSION_PATCH(driver_version), VK_API_VERSION_MAJOR(api_version),
|
||||
VK_API_VERSION_MINOR(api_version), VK_API_VERSION_PATCH(api_version),
|
||||
g_vulkan_context->GetDeviceProperties().deviceName);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GSDeviceVK::SetVSync(VsyncMode mode)
|
||||
{
|
||||
if (!m_swap_chain || m_vsync_mode == mode)
|
||||
return;
|
||||
|
||||
// This swap chain should not be used by the current buffer, thus safe to destroy.
|
||||
g_vulkan_context->WaitForGPUIdle();
|
||||
m_swap_chain->SetVSync(GetPreferredPresentModeForVsyncMode(mode));
|
||||
m_vsync_mode = mode;
|
||||
}
|
||||
|
||||
GSDevice::PresentResult GSDeviceVK::BeginPresent(bool frame_skip)
|
||||
{
|
||||
EndRenderPass();
|
||||
|
||||
if (frame_skip || !m_swap_chain)
|
||||
return PresentResult::FrameSkipped;
|
||||
|
||||
// Previous frame needs to be presented before we can acquire the swap chain.
|
||||
g_vulkan_context->WaitForPresentComplete();
|
||||
|
||||
// Check if the device was lost.
|
||||
if (g_vulkan_context->CheckLastSubmitFail())
|
||||
return PresentResult::DeviceLost;
|
||||
|
||||
VkResult res = m_swap_chain->AcquireNextImage();
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
m_swap_chain->ReleaseCurrentImage();
|
||||
|
||||
if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
|
||||
{
|
||||
ResizeWindow(0, 0, m_window_info.surface_scale);
|
||||
res = m_swap_chain->AcquireNextImage();
|
||||
}
|
||||
else if (res == VK_ERROR_SURFACE_LOST_KHR)
|
||||
{
|
||||
Console.Warning("Surface lost, attempting to recreate");
|
||||
if (!m_swap_chain->RecreateSurface(m_window_info))
|
||||
{
|
||||
Console.Error("Failed to recreate surface after loss");
|
||||
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::None);
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
|
||||
void GSDeviceVK::RestoreAPIState()
|
||||
res = m_swap_chain->AcquireNextImage();
|
||||
}
|
||||
|
||||
// This can happen when multiple resize events happen in quick succession.
|
||||
// In this case, just wait until the next frame to try again.
|
||||
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
|
||||
{
|
||||
// Still submit the command buffer, otherwise we'll end up with several frames waiting.
|
||||
LOG_VULKAN_ERROR(res, "vkAcquireNextImageKHR() failed: ");
|
||||
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::None);
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
}
|
||||
|
||||
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
|
||||
|
||||
// Swap chain images start in undefined
|
||||
Vulkan::Texture& swap_chain_texture = m_swap_chain->GetCurrentTexture();
|
||||
swap_chain_texture.OverrideImageLayout(VK_IMAGE_LAYOUT_UNDEFINED);
|
||||
swap_chain_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
const VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
|
||||
const VkRenderPassBeginInfo rp = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr,
|
||||
m_swap_chain->GetClearRenderPass(), m_swap_chain->GetCurrentFramebuffer(),
|
||||
{{0, 0}, {swap_chain_texture.GetWidth(), swap_chain_texture.GetHeight()}}, 1u, &clear_value};
|
||||
vkCmdBeginRenderPass(g_vulkan_context->GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
const VkViewport vp{0.0f, 0.0f, static_cast<float>(swap_chain_texture.GetWidth()),
|
||||
static_cast<float>(swap_chain_texture.GetHeight()), 0.0f, 1.0f};
|
||||
const VkRect2D scissor{
|
||||
{0, 0}, {static_cast<u32>(swap_chain_texture.GetWidth()), static_cast<u32>(swap_chain_texture.GetHeight())}};
|
||||
vkCmdSetViewport(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &vp);
|
||||
vkCmdSetScissor(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &scissor);
|
||||
return PresentResult::OK;
|
||||
}
|
||||
|
||||
void GSDeviceVK::EndPresent()
|
||||
{
|
||||
RenderImGui();
|
||||
|
||||
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
|
||||
vkCmdEndRenderPass(g_vulkan_context->GetCurrentCommandBuffer());
|
||||
m_swap_chain->GetCurrentTexture().TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
|
||||
g_vulkan_context->SubmitCommandBuffer(m_swap_chain.get(), !m_swap_chain->IsPresentModeSynchronizing());
|
||||
g_vulkan_context->MoveToNextCommandBuffer();
|
||||
|
||||
InvalidateCachedState();
|
||||
}
|
||||
|
||||
bool GSDeviceVK::SetGPUTimingEnabled(bool enabled)
|
||||
{
|
||||
return g_vulkan_context->SetEnableGPUTiming(enabled);
|
||||
}
|
||||
|
||||
float GSDeviceVK::GetAndResetAccumulatedGPUTime()
|
||||
{
|
||||
return g_vulkan_context->GetAndResetAccumulatedGPUTime();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OGL_DEBUG
|
||||
static std::array<float, 3> Palette(float phase, const std::array<float, 3>& a, const std::array<float, 3>& b,
|
||||
const std::array<float, 3>& c, const std::array<float, 3>& d)
|
||||
|
@ -551,7 +865,7 @@ void GSDeviceVK::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
|||
{
|
||||
DisplayConstantBuffer cb;
|
||||
cb.SetSource(sRect, sTex->GetSize());
|
||||
cb.SetTarget(dRect, dTex ? dTex->GetSize() : GSVector2i(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight()));
|
||||
cb.SetTarget(dRect, dTex ? dTex->GetSize() : GSVector2i(GetWindowWidth(), GetWindowHeight()));
|
||||
cb.SetTime(shaderTime);
|
||||
SetUtilityPushConstants(&cb, sizeof(cb));
|
||||
|
||||
|
@ -728,7 +1042,7 @@ void GSDeviceVK::DoStretchRect(GSTextureVK* sTex, const GSVector4& sRect, GSText
|
|||
const bool is_present = (!dTex);
|
||||
const bool depth = (dTex && dTex->GetType() == GSTexture::Type::DepthStencil);
|
||||
const GSVector2i size(
|
||||
is_present ? GSVector2i(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight()) : dTex->GetSize());
|
||||
is_present ? GSVector2i(GetWindowWidth(), GetWindowHeight()) : dTex->GetSize());
|
||||
const GSVector4i dtex_rc(0, 0, size.x, size.y);
|
||||
const GSVector4i dst_rc(GSVector4i(dRect).rintersect(dtex_rc));
|
||||
|
||||
|
@ -1007,7 +1321,6 @@ void GSDeviceVK::IASetVertexBuffer(const void* vertex, size_t stride, size_t cou
|
|||
|
||||
m_vertex.start = m_vertex_stream_buffer.GetCurrentOffset() / stride;
|
||||
m_vertex.count = count;
|
||||
SetVertexBuffer(m_vertex_stream_buffer.GetBuffer(), 0);
|
||||
|
||||
GSVector4i::storent(m_vertex_stream_buffer.GetCurrentHostPointer(), vertex, count * stride);
|
||||
m_vertex_stream_buffer.CommitMemory(size);
|
||||
|
@ -1025,7 +1338,6 @@ void GSDeviceVK::IASetIndexBuffer(const void* index, size_t count)
|
|||
|
||||
m_index.start = m_index_stream_buffer.GetCurrentOffset() / sizeof(u32);
|
||||
m_index.count = count;
|
||||
SetIndexBuffer(m_index_stream_buffer.GetBuffer(), 0, VK_INDEX_TYPE_UINT32);
|
||||
|
||||
std::memcpy(m_index_stream_buffer.GetCurrentHostPointer(), index, size);
|
||||
m_index_stream_buffer.CommitMemory(size);
|
||||
|
@ -1242,6 +1554,9 @@ bool GSDeviceVK::CreateBuffers()
|
|||
return false;
|
||||
}
|
||||
|
||||
SetVertexBuffer(m_vertex_stream_buffer.GetBuffer(), 0);
|
||||
SetIndexBuffer(m_index_stream_buffer.GetBuffer(), 0, VK_INDEX_TYPE_UINT32);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1384,16 +1699,6 @@ bool GSDeviceVK::CreateRenderPasses()
|
|||
|
||||
bool GSDeviceVK::CompileConvertPipelines()
|
||||
{
|
||||
// we may not have a swap chain if running in headless mode.
|
||||
Vulkan::SwapChain* swapchain = static_cast<Vulkan::SwapChain*>(g_host_display->GetSurface());
|
||||
if (swapchain)
|
||||
{
|
||||
m_swap_chain_render_pass =
|
||||
g_vulkan_context->GetRenderPass(swapchain->GetSurfaceFormat().format, VK_FORMAT_UNDEFINED);
|
||||
if (!m_swap_chain_render_pass)
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/vulkan/convert.glsl");
|
||||
if (!shader)
|
||||
{
|
||||
|
@ -1583,11 +1888,10 @@ bool GSDeviceVK::CompileConvertPipelines()
|
|||
bool GSDeviceVK::CompilePresentPipelines()
|
||||
{
|
||||
// we may not have a swap chain if running in headless mode.
|
||||
Vulkan::SwapChain* swapchain = static_cast<Vulkan::SwapChain*>(g_host_display->GetSurface());
|
||||
m_swap_chain_render_pass = swapchain ?
|
||||
swapchain->GetClearRenderPass() :
|
||||
m_swap_chain_render_pass = m_swap_chain ?
|
||||
m_swap_chain->GetClearRenderPass() :
|
||||
g_vulkan_context->GetRenderPass(VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_UNDEFINED);
|
||||
if (!m_swap_chain_render_pass)
|
||||
if (m_swap_chain_render_pass == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/vulkan/present.glsl");
|
||||
|
@ -1880,6 +2184,155 @@ bool GSDeviceVK::CompileCASPipelines()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSDeviceVK::CompileImGuiPipeline()
|
||||
{
|
||||
const std::optional<std::string> glsl = Host::ReadResourceFileToString("shaders/vulkan/imgui.glsl");
|
||||
if (!glsl.has_value())
|
||||
{
|
||||
Console.Error("Failed to read imgui.glsl");
|
||||
return false;
|
||||
}
|
||||
|
||||
VkShaderModule vs = GetUtilityVertexShader(glsl.value(), "vs_main");
|
||||
if (vs == VK_NULL_HANDLE)
|
||||
{
|
||||
Console.Error("Failed to compile ImGui vertex shader");
|
||||
return false;
|
||||
}
|
||||
ScopedGuard vs_guard([&vs]() { Vulkan::Util::SafeDestroyShaderModule(vs); });
|
||||
|
||||
VkShaderModule ps = GetUtilityFragmentShader(glsl.value(), "ps_main");
|
||||
if (ps == VK_NULL_HANDLE)
|
||||
{
|
||||
Console.Error("Failed to compile ImGui pixel shader");
|
||||
return false;
|
||||
}
|
||||
ScopedGuard ps_guard([&ps]() { Vulkan::Util::SafeDestroyShaderModule(ps); });
|
||||
|
||||
Vulkan::GraphicsPipelineBuilder gpb;
|
||||
gpb.SetPipelineLayout(m_utility_pipeline_layout);
|
||||
gpb.SetRenderPass(m_swap_chain_render_pass, 0);
|
||||
gpb.AddVertexBuffer(0, sizeof(ImDrawVert), VK_VERTEX_INPUT_RATE_VERTEX);
|
||||
gpb.AddVertexAttribute(0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(ImDrawVert, pos));
|
||||
gpb.AddVertexAttribute(1, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(ImDrawVert, uv));
|
||||
gpb.AddVertexAttribute(2, 0, VK_FORMAT_R8G8B8A8_UNORM, offsetof(ImDrawVert, col));
|
||||
gpb.SetPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
|
||||
gpb.SetVertexShader(vs);
|
||||
gpb.SetFragmentShader(ps);
|
||||
gpb.SetNoCullRasterizationState();
|
||||
gpb.SetNoDepthTestState();
|
||||
gpb.SetBlendAttachment(0, true, VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, VK_BLEND_OP_ADD,
|
||||
VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD);
|
||||
gpb.SetDynamicViewportAndScissorState();
|
||||
gpb.AddDynamicState(VK_DYNAMIC_STATE_BLEND_CONSTANTS);
|
||||
|
||||
m_imgui_pipeline = gpb.Create(g_vulkan_context->GetDevice(), g_vulkan_shader_cache->GetPipelineCache(), false);
|
||||
if (!m_imgui_pipeline)
|
||||
{
|
||||
Console.Error("Failed to compile ImGui pipeline");
|
||||
return false;
|
||||
}
|
||||
|
||||
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_imgui_pipeline, "ImGui pipeline");
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSDeviceVK::RenderImGui()
|
||||
{
|
||||
ImGui::Render();
|
||||
const ImDrawData* draw_data = ImGui::GetDrawData();
|
||||
if (draw_data->CmdListsCount == 0)
|
||||
return;
|
||||
|
||||
const float uniforms[2][2] = {{
|
||||
2.0f / static_cast<float>(m_window_info.surface_width),
|
||||
2.0f / static_cast<float>(m_window_info.surface_height),
|
||||
},
|
||||
{
|
||||
-1.0f,
|
||||
-1.0f,
|
||||
}};
|
||||
|
||||
SetUtilityPushConstants(uniforms, sizeof(uniforms));
|
||||
SetPipeline(m_imgui_pipeline);
|
||||
|
||||
if (m_utility_sampler != m_linear_sampler)
|
||||
{
|
||||
m_utility_sampler = m_linear_sampler;
|
||||
m_dirty_flags |= DIRTY_FLAG_UTILITY_TEXTURE;
|
||||
}
|
||||
|
||||
// imgui uses 16-bit indices
|
||||
SetIndexBuffer(m_index_stream_buffer.GetBuffer(), 0, VK_INDEX_TYPE_UINT16);
|
||||
|
||||
// this is for presenting, we don't want to screw with the viewport/scissor set by display
|
||||
m_dirty_flags &= ~(DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR);
|
||||
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
|
||||
u32 vertex_offset;
|
||||
{
|
||||
const u32 size = sizeof(ImDrawVert) * static_cast<u32>(cmd_list->VtxBuffer.Size);
|
||||
if (!m_vertex_stream_buffer.ReserveMemory(size, sizeof(ImDrawVert)))
|
||||
{
|
||||
Console.Warning("Skipping ImGui draw because of no vertex buffer space");
|
||||
return;
|
||||
}
|
||||
|
||||
vertex_offset = m_vertex_stream_buffer.GetCurrentOffset() / sizeof(ImDrawVert);
|
||||
std::memcpy(m_vertex_stream_buffer.GetCurrentHostPointer(), cmd_list->VtxBuffer.Data, size);
|
||||
m_vertex_stream_buffer.CommitMemory(size);
|
||||
}
|
||||
|
||||
u32 index_offset;
|
||||
{
|
||||
const u32 size = sizeof(ImDrawIdx) * static_cast<u32>(cmd_list->IdxBuffer.Size);
|
||||
if (!m_index_stream_buffer.ReserveMemory(size, sizeof(ImDrawIdx)))
|
||||
{
|
||||
Console.Warning("Skipping ImGui draw because of no vertex buffer space");
|
||||
return;
|
||||
}
|
||||
|
||||
index_offset = m_index_stream_buffer.GetCurrentOffset() / sizeof(ImDrawIdx);
|
||||
std::memcpy(m_index_stream_buffer.GetCurrentHostPointer(), cmd_list->IdxBuffer.Data, size);
|
||||
m_index_stream_buffer.CommitMemory(size);
|
||||
}
|
||||
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
pxAssert(!pcmd->UserCallback);
|
||||
|
||||
const GSVector4 clip = GSVector4::load<false>(&pcmd->ClipRect);
|
||||
if ((clip.zwzw() <= clip.xyxy()).mask() != 0)
|
||||
continue;
|
||||
|
||||
SetScissor(GSVector4i(clip));
|
||||
|
||||
// Since we don't have the GSTexture...
|
||||
Vulkan::Texture* tex = static_cast<Vulkan::Texture*>(pcmd->GetTexID());
|
||||
if (m_utility_texture != tex)
|
||||
{
|
||||
m_utility_texture = tex;
|
||||
m_dirty_flags |= DIRTY_FLAG_UTILITY_TEXTURE;
|
||||
}
|
||||
|
||||
if (ApplyUtilityState())
|
||||
{
|
||||
vkCmdDrawIndexed(g_vulkan_context->GetCurrentCommandBuffer(), pcmd->ElemCount, 1,
|
||||
index_offset + pcmd->IdxOffset, vertex_offset + pcmd->VtxOffset, 0);
|
||||
}
|
||||
}
|
||||
|
||||
g_perfmon.Put(GSPerfMon::DrawCalls, cmd_list->CmdBuffer.Size);
|
||||
}
|
||||
|
||||
// normal draws use 32-bit indices
|
||||
SetIndexBuffer(m_index_stream_buffer.GetBuffer(), 0, VK_INDEX_TYPE_UINT32);
|
||||
}
|
||||
|
||||
bool GSDeviceVK::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants)
|
||||
{
|
||||
EndRenderPass();
|
||||
|
@ -1994,6 +2447,7 @@ void GSDeviceVK::DestroyResources()
|
|||
Vulkan::Util::SafeDestroyPipeline(it);
|
||||
Vulkan::Util::SafeDestroyPipelineLayout(m_cas_pipeline_layout);
|
||||
Vulkan::Util::SafeDestroyDescriptorSetLayout(m_cas_ds_layout);
|
||||
Vulkan::Util::SafeDestroyPipeline(m_imgui_pipeline);
|
||||
|
||||
for (auto& it : m_samplers)
|
||||
Vulkan::Util::SafeDestroySampler(it.second);
|
||||
|
|
|
@ -24,6 +24,11 @@
|
|||
#include <array>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class SwapChain;
|
||||
}
|
||||
|
||||
class GSDeviceVK final : public GSDevice
|
||||
{
|
||||
public:
|
||||
|
@ -104,6 +109,8 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
std::unique_ptr<Vulkan::SwapChain> m_swap_chain;
|
||||
|
||||
VkDescriptorSetLayout m_utility_ds_layout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_utility_pipeline_layout = VK_NULL_HANDLE;
|
||||
|
||||
|
@ -153,6 +160,7 @@ private:
|
|||
VkDescriptorSetLayout m_cas_ds_layout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_cas_pipeline_layout = VK_NULL_HANDLE;
|
||||
std::array<VkPipeline, NUM_CAS_PIPELINES> m_cas_pipelines = {};
|
||||
VkPipeline m_imgui_pipeline = VK_NULL_HANDLE;
|
||||
|
||||
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
|
||||
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
|
||||
|
@ -196,6 +204,9 @@ private:
|
|||
bool CompilePostProcessingPipelines();
|
||||
bool CompileCASPipelines();
|
||||
|
||||
bool CompileImGuiPipeline();
|
||||
void RenderImGui();
|
||||
|
||||
void DestroyResources();
|
||||
|
||||
public:
|
||||
|
@ -204,6 +215,8 @@ public:
|
|||
|
||||
__fi static GSDeviceVK* GetInstance() { return static_cast<GSDeviceVK*>(g_gs_device.get()); }
|
||||
|
||||
static void GetAdaptersAndFullscreenModes(std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes);
|
||||
|
||||
__fi VkRenderPass GetTFXRenderPass(bool rt, bool ds, bool hdr, DATE_RENDER_PASS date, bool fbl, bool dsp,
|
||||
VkAttachmentLoadOp rt_op, VkAttachmentLoadOp ds_op) const
|
||||
{
|
||||
|
@ -212,11 +225,27 @@ public:
|
|||
__fi VkSampler GetPointSampler() const { return m_point_sampler; }
|
||||
__fi VkSampler GetLinearSampler() const { return m_linear_sampler; }
|
||||
|
||||
bool Create() override;
|
||||
RenderAPI GetRenderAPI() const override;
|
||||
bool HasSurface() const override;
|
||||
|
||||
bool Create(const WindowInfo& wi, VsyncMode vsync) override;
|
||||
void Destroy() override;
|
||||
|
||||
void ResetAPIState() override;
|
||||
void RestoreAPIState() override;
|
||||
bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
||||
bool SupportsExclusiveFullscreen() const override;
|
||||
bool IsExclusiveFullscreen() override;
|
||||
bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||
void DestroySurface() override;
|
||||
std::string GetDriverInfo() const override;
|
||||
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
void PushDebugGroup(const char* fmt, ...) override;
|
||||
void PopDebugGroup() override;
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
enum class VsyncMode;
|
||||
|
||||
namespace Host
|
||||
{
|
||||
/// Typical durations for OSD messages.
|
||||
|
@ -87,10 +89,4 @@ namespace Host
|
|||
|
||||
/// Requests shut down of the current virtual machine.
|
||||
void RequestVMShutdown(bool allow_confirm, bool allow_save_state, bool default_save_state);
|
||||
|
||||
/// Returns true if the hosting application is currently fullscreen.
|
||||
bool IsFullscreen();
|
||||
|
||||
/// Alters fullscreen state of hosting application.
|
||||
void SetFullscreen(bool enabled);
|
||||
} // namespace Host
|
||||
|
|
|
@ -1,190 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "HostDisplay.h"
|
||||
#include "VMManager.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
std::unique_ptr<HostDisplay> g_host_display;
|
||||
|
||||
HostDisplayTexture::~HostDisplayTexture() = default;
|
||||
|
||||
HostDisplay::~HostDisplay() = default;
|
||||
|
||||
const char* HostDisplay::RenderAPIToString(RenderAPI api)
|
||||
{
|
||||
switch (api)
|
||||
{
|
||||
#define CASE(x) case RenderAPI::x: return #x
|
||||
CASE(None);
|
||||
CASE(D3D11);
|
||||
CASE(D3D12);
|
||||
CASE(Metal);
|
||||
CASE(Vulkan);
|
||||
CASE(OpenGL);
|
||||
CASE(OpenGLES);
|
||||
#undef CASE
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
bool HostDisplay::UsesLowerLeftOrigin() const
|
||||
{
|
||||
const RenderAPI api = GetRenderAPI();
|
||||
return (api == RenderAPI::OpenGL || api == RenderAPI::OpenGLES);
|
||||
}
|
||||
|
||||
bool HostDisplay::GetHostRefreshRate(float* refresh_rate)
|
||||
{
|
||||
if (m_window_info.surface_refresh_rate > 0.0f)
|
||||
{
|
||||
*refresh_rate = m_window_info.surface_refresh_rate;
|
||||
return true;
|
||||
}
|
||||
|
||||
return WindowInfo::QueryRefreshRateForWindow(m_window_info, refresh_rate);
|
||||
}
|
||||
|
||||
bool HostDisplay::SetGPUTimingEnabled(bool enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
float HostDisplay::GetAndResetAccumulatedGPUTime()
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
bool HostDisplay::ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate)
|
||||
{
|
||||
if (!mode.empty())
|
||||
{
|
||||
std::string_view::size_type sep1 = mode.find('x');
|
||||
if (sep1 != std::string_view::npos)
|
||||
{
|
||||
std::optional<u32> owidth = StringUtil::FromChars<u32>(mode.substr(0, sep1));
|
||||
sep1++;
|
||||
|
||||
while (sep1 < mode.length() && std::isspace(mode[sep1]))
|
||||
sep1++;
|
||||
|
||||
if (owidth.has_value() && sep1 < mode.length())
|
||||
{
|
||||
std::string_view::size_type sep2 = mode.find('@', sep1);
|
||||
if (sep2 != std::string_view::npos)
|
||||
{
|
||||
std::optional<u32> oheight = StringUtil::FromChars<u32>(mode.substr(sep1, sep2 - sep1));
|
||||
sep2++;
|
||||
|
||||
while (sep2 < mode.length() && std::isspace(mode[sep2]))
|
||||
sep2++;
|
||||
|
||||
if (oheight.has_value() && sep2 < mode.length())
|
||||
{
|
||||
std::optional<float> orefresh_rate = StringUtil::FromChars<float>(mode.substr(sep2));
|
||||
if (orefresh_rate.has_value())
|
||||
{
|
||||
*width = owidth.value();
|
||||
*height = oheight.value();
|
||||
*refresh_rate = orefresh_rate.value();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
*refresh_rate = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string HostDisplay::GetFullscreenModeString(u32 width, u32 height, float refresh_rate)
|
||||
{
|
||||
return StringUtil::StdStringFromFormat("%u x %u @ %f hz", width, height, refresh_rate);
|
||||
}
|
||||
|
||||
VsyncMode Host::GetEffectiveVSyncMode()
|
||||
{
|
||||
const bool has_vm = VMManager::GetState() != VMState::Shutdown;
|
||||
|
||||
// Force vsync off when not running at 100% speed.
|
||||
if (has_vm && EmuConfig.GS.LimitScalar != 1.0f)
|
||||
return VsyncMode::Off;
|
||||
|
||||
// Otherwise use the config setting.
|
||||
return EmuConfig.GS.VsyncEnable;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
#include "Frontend/OpenGLHostDisplay.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "Frontend/VulkanHostDisplay.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Frontend/D3D11HostDisplay.h"
|
||||
#include "Frontend/D3D12HostDisplay.h"
|
||||
#endif
|
||||
#include "GS/Renderers/Metal/GSMetalCPPAccessible.h"
|
||||
|
||||
std::unique_ptr<HostDisplay> HostDisplay::CreateForAPI(RenderAPI api)
|
||||
{
|
||||
switch (api)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
case RenderAPI::D3D11:
|
||||
return std::make_unique<D3D11HostDisplay>();
|
||||
case RenderAPI::D3D12:
|
||||
return std::make_unique<D3D12HostDisplay>();
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
case RenderAPI::Metal:
|
||||
return std::unique_ptr<HostDisplay>(MakeMetalHostDisplay());
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
case RenderAPI::OpenGL:
|
||||
case RenderAPI::OpenGLES:
|
||||
return std::make_unique<OpenGLHostDisplay>();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
case RenderAPI::Vulkan:
|
||||
return std::make_unique<VulkanHostDisplay>();
|
||||
#endif
|
||||
|
||||
default:
|
||||
Console.Error("Unknown render API %u", static_cast<unsigned>(api));
|
||||
return {};
|
||||
}
|
||||
}
|
|
@ -1,206 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/WindowInfo.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "Host.h"
|
||||
#include "Config.h"
|
||||
|
||||
enum class RenderAPI
|
||||
{
|
||||
None,
|
||||
D3D11,
|
||||
Metal,
|
||||
D3D12,
|
||||
Vulkan,
|
||||
OpenGL,
|
||||
OpenGLES
|
||||
};
|
||||
|
||||
/// 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 Alignment
|
||||
{
|
||||
LeftOrTop,
|
||||
Center,
|
||||
RightOrBottom
|
||||
};
|
||||
|
||||
enum class PresentResult
|
||||
{
|
||||
OK,
|
||||
FrameSkipped,
|
||||
DeviceLost
|
||||
};
|
||||
|
||||
struct AdapterAndModeList
|
||||
{
|
||||
std::vector<std::string> adapter_names;
|
||||
std::vector<std::string> fullscreen_modes;
|
||||
};
|
||||
|
||||
virtual ~HostDisplay();
|
||||
|
||||
/// Returns a string representing the specified API.
|
||||
static const char* RenderAPIToString(RenderAPI api);
|
||||
|
||||
/// Creates a display for the specified API.
|
||||
static std::unique_ptr<HostDisplay> CreateForAPI(RenderAPI api);
|
||||
|
||||
/// Parses a fullscreen mode into its components (width * height @ refresh hz)
|
||||
static bool ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate);
|
||||
|
||||
/// Converts a fullscreen mode to a string.
|
||||
static std::string GetFullscreenModeString(u32 width, u32 height, float refresh_rate);
|
||||
|
||||
__fi const WindowInfo& GetWindowInfo() const { return m_window_info; }
|
||||
__fi s32 GetWindowWidth() const { return static_cast<s32>(m_window_info.surface_width); }
|
||||
__fi s32 GetWindowHeight() const { return static_cast<s32>(m_window_info.surface_height); }
|
||||
__fi float GetWindowScale() const { return m_window_info.surface_scale; }
|
||||
__fi VsyncMode GetVsyncMode() const { return m_vsync_mode; }
|
||||
|
||||
/// Changes the alignment for this display (screen positioning).
|
||||
__fi Alignment GetDisplayAlignment() const { return m_display_alignment; }
|
||||
__fi void SetDisplayAlignment(Alignment alignment) { m_display_alignment = alignment; }
|
||||
|
||||
virtual RenderAPI GetRenderAPI() const = 0;
|
||||
virtual void* GetDevice() const = 0;
|
||||
virtual void* GetContext() const = 0;
|
||||
virtual void* GetSurface() const = 0;
|
||||
|
||||
virtual bool HasDevice() const = 0;
|
||||
virtual bool HasSurface() const = 0;
|
||||
|
||||
/// Creates the rendering/GPU device. This should be called on the thread which owns the window.
|
||||
virtual bool CreateDevice(const WindowInfo& wi, VsyncMode vsync) = 0;
|
||||
|
||||
/// Fully initializes the rendering device. This should be called on the GS thread.
|
||||
virtual bool SetupDevice() = 0;
|
||||
|
||||
/// Sets the device for the current thread. Only needed for OpenGL.
|
||||
virtual bool MakeCurrent() = 0;
|
||||
|
||||
/// Clears the device for the current thread. Only needed for OpenGL.
|
||||
virtual bool DoneCurrent() = 0;
|
||||
|
||||
/// Destroys the surface we're currently drawing to.
|
||||
virtual void DestroySurface() = 0;
|
||||
|
||||
/// Switches to a new window/surface.
|
||||
virtual bool ChangeWindow(const WindowInfo& wi) = 0;
|
||||
|
||||
/// Call when the window size changes externally to recreate any resources.
|
||||
virtual void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) = 0;
|
||||
|
||||
/// Returns true if exclusive fullscreen is supported.
|
||||
virtual bool SupportsFullscreen() const = 0;
|
||||
|
||||
/// Returns true if exclusive fullscreen is active.
|
||||
virtual bool IsFullscreen() = 0;
|
||||
|
||||
/// Attempts to switch to the specified mode in exclusive fullscreen.
|
||||
virtual bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) = 0;
|
||||
|
||||
virtual AdapterAndModeList GetAdapterAndModeList() = 0;
|
||||
virtual std::string GetDriverInfo() 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;
|
||||
|
||||
/// Returns false if the window was completely occluded. If frame_skip is set, the frame won't be
|
||||
/// displayed, but the GPU command queue will still be flushed.
|
||||
virtual PresentResult BeginPresent(bool frame_skip) = 0;
|
||||
|
||||
/// Presents the frame to the display, and renders OSD elements.
|
||||
virtual void EndPresent() = 0;
|
||||
|
||||
/// Changes vsync mode for this display.
|
||||
virtual void SetVSync(VsyncMode mode) = 0;
|
||||
|
||||
/// ImGui context management, usually called by derived classes.
|
||||
virtual bool CreateImGuiContext() = 0;
|
||||
virtual void DestroyImGuiContext() = 0;
|
||||
virtual bool UpdateImGuiFontTexture() = 0;
|
||||
|
||||
/// Returns the effective refresh rate of this display.
|
||||
virtual bool GetHostRefreshRate(float* refresh_rate);
|
||||
|
||||
/// Enables/disables GPU frame timing.
|
||||
virtual bool SetGPUTimingEnabled(bool enabled);
|
||||
|
||||
/// Returns the amount of GPU time utilized since the last time this method was called.
|
||||
virtual float GetAndResetAccumulatedGPUTime();
|
||||
|
||||
/// Returns true if it's an OpenGL-based renderer.
|
||||
bool UsesLowerLeftOrigin() const;
|
||||
|
||||
protected:
|
||||
WindowInfo m_window_info;
|
||||
Alignment m_display_alignment = Alignment::Center;
|
||||
VsyncMode m_vsync_mode = VsyncMode::Off;
|
||||
};
|
||||
|
||||
/// Returns a pointer to the current host display abstraction. Assumes AcquireHostDisplay() has been caled.
|
||||
extern std::unique_ptr<HostDisplay> g_host_display;
|
||||
|
||||
namespace Host
|
||||
{
|
||||
/// Creates the host display. This may create a new window. The API used depends on the current configuration.
|
||||
bool AcquireHostDisplay(RenderAPI api, bool clear_state_on_fail);
|
||||
|
||||
/// Destroys the host display. This may close the display window.
|
||||
void ReleaseHostDisplay(bool clear_state);
|
||||
|
||||
/// Returns the desired vsync mode, depending on the runtime environment.
|
||||
VsyncMode GetEffectiveVSyncMode();
|
||||
|
||||
/// Returns false if the window was completely occluded. If frame_skip is set, the frame won't be
|
||||
/// displayed, but the GPU command queue will still be flushed.
|
||||
HostDisplay::PresentResult BeginPresentFrame(bool frame_skip);
|
||||
|
||||
/// Presents the frame to the display, and renders OSD elements.
|
||||
void EndPresentFrame();
|
||||
|
||||
/// Called on the MTGS thread when a resize request is received.
|
||||
void ResizeHostDisplay(u32 new_window_width, u32 new_window_height, float new_window_scale);
|
||||
|
||||
/// Called on the MTGS thread when a request to update the display is received.
|
||||
/// This could be a fullscreen transition, for example.
|
||||
void UpdateHostDisplay();
|
||||
} // namespace Host
|
||||
|
|
@ -27,7 +27,6 @@
|
|||
#include "Elfheader.h"
|
||||
|
||||
#include "Host.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "IconsFontAwesome5.h"
|
||||
#include "VMManager.h"
|
||||
|
||||
|
@ -276,7 +275,7 @@ bool SysMtgsThread::TryOpenGS()
|
|||
if (!GSopen(EmuConfig.GS, EmuConfig.GS.Renderer, RingBuffer.Regs))
|
||||
return false;
|
||||
|
||||
GSsetGameCRC(ElfCRC);
|
||||
GSSetGameCRC(ElfCRC);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -511,7 +510,7 @@ void SysMtgsThread::MainLoop()
|
|||
break;
|
||||
|
||||
case GS_RINGTYPE_CRC:
|
||||
GSsetGameCRC(tag.data[0]);
|
||||
GSSetGameCRC(tag.data[0]);
|
||||
break;
|
||||
|
||||
case GS_RINGTYPE_INIT_AND_READ_FIFO:
|
||||
|
@ -915,7 +914,7 @@ void SysMtgsThread::ApplySettings()
|
|||
|
||||
RunOnGSThread([opts = EmuConfig.GS]() {
|
||||
GSUpdateConfig(opts);
|
||||
g_host_display->SetVSync(Host::GetEffectiveVSyncMode());
|
||||
GSSetVSyncMode(Host::GetEffectiveVSyncMode());
|
||||
});
|
||||
|
||||
// We need to synchronize the thread when changing any settings when the download mode
|
||||
|
@ -929,9 +928,7 @@ void SysMtgsThread::ResizeDisplayWindow(int width, int height, float scale)
|
|||
{
|
||||
pxAssertRel(IsOpen(), "MTGS is running");
|
||||
RunOnGSThread([width, height, scale]() {
|
||||
GSResetAPIState();
|
||||
Host::ResizeHostDisplay(width, height, scale);
|
||||
GSRestoreAPIState();
|
||||
GSResizeDisplayWindow(width, height, scale);
|
||||
|
||||
// If we're paused, re-present the current frame at the new window size.
|
||||
if (VMManager::GetState() == VMState::Paused)
|
||||
|
@ -943,9 +940,7 @@ void SysMtgsThread::UpdateDisplayWindow()
|
|||
{
|
||||
pxAssertRel(IsOpen(), "MTGS is running");
|
||||
RunOnGSThread([]() {
|
||||
GSResetAPIState();
|
||||
Host::UpdateHostDisplay();
|
||||
GSRestoreAPIState();
|
||||
GSUpdateDisplayWindow();
|
||||
|
||||
// If we're paused, re-present the current frame at the new window size.
|
||||
if (VMManager::GetState() == VMState::Paused)
|
||||
|
@ -959,7 +954,7 @@ void SysMtgsThread::SetVSyncMode(VsyncMode mode)
|
|||
|
||||
RunOnGSThread([mode]() {
|
||||
Console.WriteLn("Vsync is %s", mode == VsyncMode::Off ? "OFF" : (mode == VsyncMode::Adaptive ? "ADAPTIVE" : "ON"));
|
||||
g_host_display->SetVSync(mode);
|
||||
GSSetVSyncMode(mode);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ void PADshutdown()
|
|||
{
|
||||
}
|
||||
|
||||
s32 PADopen(const WindowInfo& wi)
|
||||
s32 PADopen()
|
||||
{
|
||||
g_key_status.Init();
|
||||
return 0;
|
||||
|
|
|
@ -30,7 +30,7 @@ enum class GenericInputBinding : u8;
|
|||
|
||||
s32 PADinit();
|
||||
void PADshutdown();
|
||||
s32 PADopen(const WindowInfo& wi);
|
||||
s32 PADopen();
|
||||
void PADclose();
|
||||
s32 PADsetSlot(u8 port, u8 slot);
|
||||
s32 PADfreeze(FreezeAction mode, freezeData* data);
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "common/StringUtil.h"
|
||||
#include "Config.h"
|
||||
#include "GS.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "CDVD/CDVDcommon.h"
|
||||
#include "MemoryCardFile.h"
|
||||
#include "USB/USB.h"
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "USB/qemu-usb/USBinternal.h"
|
||||
#include "USB/USB.h"
|
||||
#include "GS/GS.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "StateWrapper.h"
|
||||
#include "VMManager.h"
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
#include "GameDatabase.h"
|
||||
#include "GS.h"
|
||||
#include "GSDumpReplayer.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "Host.h"
|
||||
#include "HostSettings.h"
|
||||
#include "INISettingsInterface.h"
|
||||
#include "IopBios.h"
|
||||
|
@ -993,7 +993,7 @@ bool VMManager::Initialize(VMBootParameters boot_params)
|
|||
ScopedGuard close_spu2(&SPU2::Close);
|
||||
|
||||
Console.WriteLn("Opening PAD...");
|
||||
if (PADinit() != 0 || PADopen(g_host_display->GetWindowInfo()) != 0)
|
||||
if (PADinit() != 0 || PADopen() != 0)
|
||||
{
|
||||
Host::ReportErrorAsync("Startup Error", "Failed to initialize PAD.");
|
||||
return false;
|
||||
|
@ -1591,6 +1591,18 @@ void VMManager::SetPaused(bool paused)
|
|||
SetState(paused ? VMState::Paused : VMState::Running);
|
||||
}
|
||||
|
||||
VsyncMode Host::GetEffectiveVSyncMode()
|
||||
{
|
||||
const bool has_vm = VMManager::GetState() != VMState::Shutdown;
|
||||
|
||||
// Force vsync off when not running at 100% speed.
|
||||
if (has_vm && EmuConfig.GS.LimitScalar != 1.0f)
|
||||
return VsyncMode::Off;
|
||||
|
||||
// Otherwise use the config setting.
|
||||
return EmuConfig.GS.VsyncEnable;
|
||||
}
|
||||
|
||||
const std::string& VMManager::Internal::GetElfOverride()
|
||||
{
|
||||
return s_elf_override;
|
||||
|
|
|
@ -194,27 +194,19 @@
|
|||
<ClCompile Include="DEV9\Win32\tap-win32.cpp" />
|
||||
<ClCompile Include="Frontend\CommonHost.cpp" />
|
||||
<ClCompile Include="Frontend\CommonHotkeys.cpp" />
|
||||
<ClCompile Include="Frontend\D3D11HostDisplay.cpp" />
|
||||
<ClCompile Include="Frontend\D3D12HostDisplay.cpp" />
|
||||
<ClCompile Include="Frontend\DInputSource.cpp" />
|
||||
<ClCompile Include="Frontend\FullscreenUI.cpp" />
|
||||
<ClCompile Include="Frontend\GameList.cpp" />
|
||||
<ClCompile Include="Frontend\ImGuiFullscreen.cpp" />
|
||||
<ClCompile Include="Frontend\ImGuiManager.cpp" />
|
||||
<ClCompile Include="Frontend\ImGuiOverlays.cpp" />
|
||||
<ClCompile Include="Frontend\imgui_impl_dx11.cpp" />
|
||||
<ClCompile Include="Frontend\imgui_impl_dx12.cpp" />
|
||||
<ClCompile Include="Frontend\imgui_impl_opengl3.cpp" />
|
||||
<ClCompile Include="Frontend\imgui_impl_vulkan.cpp" />
|
||||
<ClCompile Include="INISettingsInterface.cpp" />
|
||||
<ClCompile Include="Frontend\InputManager.cpp" />
|
||||
<ClCompile Include="Frontend\InputSource.cpp" />
|
||||
<ClCompile Include="Frontend\LayeredSettingsInterface.cpp" />
|
||||
<ClCompile Include="Frontend\LogSink.cpp" />
|
||||
<ClCompile Include="Frontend\OpenGLHostDisplay.cpp" />
|
||||
<ClCompile Include="Frontend\Achievements.cpp" />
|
||||
<ClCompile Include="Frontend\SDLInputSource.cpp" />
|
||||
<ClCompile Include="Frontend\VulkanHostDisplay.cpp" />
|
||||
<ClCompile Include="Frontend\XInputSource.cpp" />
|
||||
<ClCompile Include="GameDatabase.cpp" />
|
||||
<ClCompile Include="Gif_Logger.cpp" />
|
||||
|
@ -228,7 +220,6 @@
|
|||
<ClCompile Include="GS\Renderers\Vulkan\GSDeviceVK.cpp" />
|
||||
<ClCompile Include="GS\Renderers\Vulkan\GSTextureVK.cpp" />
|
||||
<ClCompile Include="Host.cpp" />
|
||||
<ClCompile Include="HostDisplay.cpp" />
|
||||
<ClCompile Include="Frontend\HostSettings.cpp" />
|
||||
<ClCompile Include="IopGte.cpp" />
|
||||
<ClCompile Include="MemoryCardProtocol.cpp" />
|
||||
|
@ -543,28 +534,20 @@
|
|||
<ClInclude Include="DEV9\Win32\pcap_io_win32_funcs.h" />
|
||||
<ClInclude Include="DEV9\Win32\tap.h" />
|
||||
<ClInclude Include="Frontend\CommonHost.h" />
|
||||
<ClInclude Include="Frontend\D3D11HostDisplay.h" />
|
||||
<ClInclude Include="Frontend\D3D12HostDisplay.h" />
|
||||
<ClInclude Include="Frontend\DInputSource.h" />
|
||||
<ClInclude Include="Frontend\FullscreenUI.h" />
|
||||
<ClInclude Include="Frontend\GameList.h" />
|
||||
<ClInclude Include="Frontend\ImGuiFullscreen.h" />
|
||||
<ClInclude Include="Frontend\ImGuiManager.h" />
|
||||
<ClInclude Include="Frontend\ImGuiOverlays.h" />
|
||||
<ClInclude Include="Frontend\imgui_impl_dx11.h" />
|
||||
<ClInclude Include="Frontend\imgui_impl_dx12.h" />
|
||||
<ClInclude Include="Frontend\imgui_impl_opengl3.h" />
|
||||
<ClInclude Include="Frontend\imgui_impl_vulkan.h" />
|
||||
<ClInclude Include="GS\Renderers\HW\GSHwHack.h" />
|
||||
<ClInclude Include="INISettingsInterface.h" />
|
||||
<ClInclude Include="Frontend\InputManager.h" />
|
||||
<ClInclude Include="Frontend\InputSource.h" />
|
||||
<ClInclude Include="Frontend\LayeredSettingsInterface.h" />
|
||||
<ClInclude Include="Frontend\LogSink.h" />
|
||||
<ClInclude Include="Frontend\OpenGLHostDisplay.h" />
|
||||
<ClInclude Include="Frontend\Achievements.h" />
|
||||
<ClInclude Include="Frontend\SDLInputSource.h" />
|
||||
<ClInclude Include="Frontend\VulkanHostDisplay.h" />
|
||||
<ClInclude Include="Frontend\XInputSource.h" />
|
||||
<ClInclude Include="GameDatabase.h" />
|
||||
<ClInclude Include="Gif_Unit.h" />
|
||||
|
@ -576,7 +559,6 @@
|
|||
<ClInclude Include="GS\Renderers\Vulkan\GSDeviceVK.h" />
|
||||
<ClInclude Include="GS\Renderers\Vulkan\GSTextureVK.h" />
|
||||
<ClInclude Include="Host.h" />
|
||||
<ClInclude Include="HostDisplay.h" />
|
||||
<ClInclude Include="HostSettings.h" />
|
||||
<ClInclude Include="IopGte.h" />
|
||||
<ClInclude Include="IPU\mpeg2_vlc.h" />
|
||||
|
|
|
@ -1157,24 +1157,12 @@
|
|||
<ClCompile Include="SPU2\SndOut_Cubeb.cpp">
|
||||
<Filter>System\Ps2\SPU2</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HostDisplay.cpp">
|
||||
<Filter>Host</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Frontend\OpenGLHostDisplay.cpp">
|
||||
<Filter>Host</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Frontend\D3D11HostDisplay.cpp">
|
||||
<Filter>Host</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Frontend\ImGuiManager.cpp">
|
||||
<Filter>Host</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Host.cpp">
|
||||
<Filter>Host</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Frontend\VulkanHostDisplay.cpp">
|
||||
<Filter>Host</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GS\Renderers\Vulkan\GSTextureVK.cpp">
|
||||
<Filter>System\Ps2\GS\Renderers\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1235,21 +1223,6 @@
|
|||
<ClCompile Include="GS\Renderers\DX12\GSDevice12.cpp">
|
||||
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Frontend\D3D12HostDisplay.cpp">
|
||||
<Filter>Host</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Frontend\imgui_impl_dx11.cpp">
|
||||
<Filter>Host</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Frontend\imgui_impl_dx12.cpp">
|
||||
<Filter>Host</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Frontend\imgui_impl_opengl3.cpp">
|
||||
<Filter>Host</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Frontend\imgui_impl_vulkan.cpp">
|
||||
<Filter>Host</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Recording\InputRecording.cpp">
|
||||
<Filter>Tools\Input Recording</Filter>
|
||||
</ClCompile>
|
||||
|
@ -2074,21 +2047,9 @@
|
|||
<ClInclude Include="Host.h">
|
||||
<Filter>Host</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HostDisplay.h">
|
||||
<Filter>Host</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Frontend\OpenGLHostDisplay.h">
|
||||
<Filter>Host</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Frontend\D3D11HostDisplay.h">
|
||||
<Filter>Host</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Frontend\ImGuiManager.h">
|
||||
<Filter>Host</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Frontend\VulkanHostDisplay.h">
|
||||
<Filter>Host</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GS\Renderers\Vulkan\GSTextureVK.h">
|
||||
<Filter>System\Ps2\GS\Renderers\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
|
@ -2147,21 +2108,6 @@
|
|||
<ClInclude Include="GS\Renderers\DX12\GSDevice12.h">
|
||||
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Frontend\D3D12HostDisplay.h">
|
||||
<Filter>Host</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Frontend\imgui_impl_dx11.h">
|
||||
<Filter>Host</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Frontend\imgui_impl_dx12.h">
|
||||
<Filter>Host</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Frontend\imgui_impl_opengl3.h">
|
||||
<Filter>Host</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Frontend\imgui_impl_vulkan.h">
|
||||
<Filter>Host</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Recording\InputRecording.h">
|
||||
<Filter>Tools\Input Recording</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
#include "pcsx2/Frontend/ImGuiManager.h"
|
||||
#include "pcsx2/Frontend/InputManager.h"
|
||||
#include "pcsx2/GS.h"
|
||||
#include "pcsx2/GS/GS.h"
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/HostDisplay.h"
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/VMManager.h"
|
||||
|
||||
|
@ -105,29 +105,21 @@ void Host::SetRelativeMouseMode(bool enabled)
|
|||
{
|
||||
}
|
||||
|
||||
bool Host::AcquireHostDisplay(RenderAPI api, bool clear_state_on_fail)
|
||||
std::optional<WindowInfo> Host::AcquireRenderWindow(RenderAPI api)
|
||||
{
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void Host::ReleaseHostDisplay(bool clear_state)
|
||||
std::optional<WindowInfo> Host::UpdateRenderWindow()
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void Host::ReleaseRenderWindow()
|
||||
{
|
||||
}
|
||||
|
||||
HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
|
||||
{
|
||||
return HostDisplay::PresentResult::FrameSkipped;
|
||||
}
|
||||
|
||||
void Host::EndPresentFrame()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::ResizeHostDisplay(u32 new_window_width, u32 new_window_height, float new_window_scale)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::UpdateHostDisplay()
|
||||
void Host::BeginPresentFrame()
|
||||
{
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue