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()
|
D3D11::ShaderCache::~ShaderCache()
|
||||||
{
|
{
|
||||||
if (m_index_file)
|
Close();
|
||||||
std::fclose(m_index_file);
|
|
||||||
if (m_blob_file)
|
|
||||||
std::fclose(m_blob_file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D11::ShaderCache::CacheIndexKey::operator==(const CacheIndexKey& key) const
|
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;
|
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)
|
bool D3D11::ShaderCache::CreateNew(const std::string& index_filename, const std::string& blob_filename)
|
||||||
{
|
{
|
||||||
if (FileSystem::FileExists(index_filename.c_str()))
|
if (FileSystem::FileExists(index_filename.c_str()))
|
||||||
|
|
|
@ -38,6 +38,7 @@ namespace D3D11
|
||||||
bool UsingDebugShaders() const { return m_debug; }
|
bool UsingDebugShaders() const { return m_debug; }
|
||||||
|
|
||||||
bool Open(std::string_view base_path, D3D_FEATURE_LEVEL feature_level, u32 version, bool 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,
|
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");
|
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 CreateNew(const std::string& index_filename, const std::string& blob_filename);
|
||||||
bool ReadExisting(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,
|
wil::com_ptr_nothrow<ID3DBlob> CompileAndAddShaderBlob(const CacheIndexKey& key, const std::string_view& shader_code,
|
||||||
const D3D_SHADER_MACRO* macros, const char* entry_point);
|
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");
|
pxAssertRel(!g_d3d12_context, "No context exists");
|
||||||
|
|
||||||
if (!LoadD3D12Library())
|
if (!LoadD3D12Library())
|
||||||
|
{
|
||||||
|
Console.Error("Failed to load D3D12 library");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
g_d3d12_context.reset(new Context());
|
g_d3d12_context.reset(new Context());
|
||||||
if (!g_d3d12_context->CreateDevice(dxgi_factory, adapter, enable_debug_layer) ||
|
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.
|
// Create the actual device.
|
||||||
hr = s_d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
|
hr = s_d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Console.Error("Failed to create D3D12 device: %08X", hr);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// get adapter
|
// get adapter
|
||||||
const LUID luid(m_device->GetAdapterLuid());
|
const LUID luid(m_device->GetAdapterLuid());
|
||||||
|
|
|
@ -45,14 +45,7 @@ ShaderCache::ShaderCache() = default;
|
||||||
|
|
||||||
ShaderCache::~ShaderCache()
|
ShaderCache::~ShaderCache()
|
||||||
{
|
{
|
||||||
if (m_pipeline_index_file)
|
Close();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderCache::CacheIndexKey::operator==(const CacheIndexKey& key) const
|
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;
|
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()
|
void ShaderCache::InvalidatePipelineCache()
|
||||||
{
|
{
|
||||||
m_pipeline_index.clear();
|
m_pipeline_index.clear();
|
||||||
|
|
|
@ -52,6 +52,7 @@ namespace D3D12
|
||||||
__fi bool UsingDebugShaders() const { return m_debug; }
|
__fi bool UsingDebugShaders() const { return m_debug; }
|
||||||
|
|
||||||
bool Open(std::string_view base_path, D3D_FEATURE_LEVEL feature_level, u32 version, bool 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,
|
__fi ComPtr<ID3DBlob> GetVertexShader(std::string_view shader_code,
|
||||||
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main")
|
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,
|
bool ReadExisting(const std::string& index_filename, const std::string& blob_filename, std::FILE*& index_file,
|
||||||
std::FILE*& blob_file, CacheIndex& index);
|
std::FILE*& blob_file, CacheIndex& index);
|
||||||
void InvalidatePipelineCache();
|
void InvalidatePipelineCache();
|
||||||
void Close();
|
|
||||||
|
|
||||||
ComPtr<ID3DBlob> CompileAndAddShaderBlob(const CacheIndexKey& key, std::string_view shader_code,
|
ComPtr<ID3DBlob> CompileAndAddShaderBlob(const CacheIndexKey& key, std::string_view shader_code,
|
||||||
const D3D_SHADER_MACRO* macros, const char* entry_point);
|
const D3D_SHADER_MACRO* macros, const char* entry_point);
|
||||||
|
|
|
@ -485,151 +485,28 @@ namespace GL
|
||||||
glUniform4fv(location, 1, v);
|
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)
|
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)
|
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)
|
if (location >= 0)
|
||||||
glUniform3ui(location, x, y, z);
|
glUniformMatrix4fv(location, 1, GL_FALSE, v);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Program::BindUniformBlock(const char* name, u32 index)
|
void Program::BindUniformBlock(const char* name, u32 index)
|
||||||
|
|
|
@ -79,27 +79,9 @@ namespace GL
|
||||||
void Uniform3fv(int index, const float* v) const;
|
void Uniform3fv(int index, const float* v) const;
|
||||||
void Uniform4fv(int index, const float* v) const;
|
void Uniform4fv(int index, const float* v) const;
|
||||||
|
|
||||||
void Uniform1ui(const char* name, u32 x) const;
|
void UniformMatrix2fv(int index, const float* v);
|
||||||
void Uniform2ui(const char* name, u32 x, u32 y) const;
|
void UniformMatrix3fv(int index, const float* v);
|
||||||
void Uniform3ui(const char* name, u32 x, u32 y, u32 z) const;
|
void UniformMatrix4fv(int index, const float* v);
|
||||||
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 BindUniformBlock(const char* name, u32 index);
|
void BindUniformBlock(const char* name, u32 index);
|
||||||
|
|
||||||
|
|
|
@ -218,9 +218,17 @@ namespace GL
|
||||||
{
|
{
|
||||||
m_index.clear();
|
m_index.clear();
|
||||||
if (m_index_file)
|
if (m_index_file)
|
||||||
|
{
|
||||||
std::fclose(m_index_file);
|
std::fclose(m_index_file);
|
||||||
|
m_index_file = nullptr;
|
||||||
|
}
|
||||||
if (m_blob_file)
|
if (m_blob_file)
|
||||||
|
{
|
||||||
std::fclose(m_blob_file);
|
std::fclose(m_blob_file);
|
||||||
|
m_blob_file = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_base_path = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderCache::Recreate()
|
bool ShaderCache::Recreate()
|
||||||
|
|
|
@ -36,6 +36,7 @@ namespace GL
|
||||||
~ShaderCache();
|
~ShaderCache();
|
||||||
|
|
||||||
bool Open(bool is_gles, std::string_view base_path, u32 version);
|
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,
|
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 = {});
|
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 CreateNew(const std::string& index_filename, const std::string& blob_filename);
|
||||||
bool ReadExisting(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 Recreate();
|
||||||
|
|
||||||
bool WriteToBlobFile(const CacheIndexKey& key, const std::vector<u8>& prog_data, u32 prog_format);
|
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.h"
|
||||||
#include "pcsx2/GS/GS.h"
|
#include "pcsx2/GS/GS.h"
|
||||||
#include "pcsx2/GSDumpReplayer.h"
|
#include "pcsx2/GSDumpReplayer.h"
|
||||||
#include "pcsx2/HostDisplay.h"
|
#include "pcsx2/Host.h"
|
||||||
#include "pcsx2/HostSettings.h"
|
#include "pcsx2/HostSettings.h"
|
||||||
#include "pcsx2/INISettingsInterface.h"
|
#include "pcsx2/INISettingsInterface.h"
|
||||||
#include "pcsx2/PAD/Host/PAD.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());
|
return 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
return GSRunner::GetPlatformWindowInfo();
|
||||||
g_host_display.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
|
void Host::ReleaseRenderWindow()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::BeginPresentFrame()
|
||||||
{
|
{
|
||||||
if (s_loop_number == 0 && !s_output_prefix.empty())
|
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));
|
std::string dump_path(fmt::format("{}_frame{}.png", s_output_prefix, s_dump_frame_number));
|
||||||
GSQueueSnapshot(dump_path);
|
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)
|
void Host::RequestResizeHostDisplay(s32 width, s32 height)
|
||||||
|
|
|
@ -32,9 +32,9 @@
|
||||||
#include "pcsx2/CDVD/CDVDdiscReader.h"
|
#include "pcsx2/CDVD/CDVDdiscReader.h"
|
||||||
#include "pcsx2/Frontend/GameList.h"
|
#include "pcsx2/Frontend/GameList.h"
|
||||||
#include "pcsx2/Frontend/LogSink.h"
|
#include "pcsx2/Frontend/LogSink.h"
|
||||||
|
#include "pcsx2/GS.h"
|
||||||
#include "pcsx2/GS/GS.h"
|
#include "pcsx2/GS/GS.h"
|
||||||
#include "pcsx2/GSDumpReplayer.h"
|
#include "pcsx2/GSDumpReplayer.h"
|
||||||
#include "pcsx2/HostDisplay.h"
|
|
||||||
#include "pcsx2/HostSettings.h"
|
#include "pcsx2/HostSettings.h"
|
||||||
#include "pcsx2/PerformanceMetrics.h"
|
#include "pcsx2/PerformanceMetrics.h"
|
||||||
#include "pcsx2/Recording/InputRecording.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.actionStartFullscreenUI, &QAction::triggered, thread, &EmuThread::startFullscreenUI);
|
||||||
connect(m_ui.actionStartFullscreenUI2, &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::messageConfirmed, this, &MainWindow::confirmMessage, Qt::BlockingQueuedConnection);
|
||||||
connect(thread, &EmuThread::onCreateDisplayRequested, this, &MainWindow::createDisplay, Qt::BlockingQueuedConnection);
|
connect(thread, &EmuThread::onCreateDisplayRequested, this, &MainWindow::createDisplayWindow, Qt::BlockingQueuedConnection);
|
||||||
connect(thread, &EmuThread::onUpdateDisplayRequested, this, &MainWindow::updateDisplay, Qt::BlockingQueuedConnection);
|
connect(thread, &EmuThread::onUpdateDisplayRequested, this, &MainWindow::updateDisplayWindow, Qt::BlockingQueuedConnection);
|
||||||
connect(thread, &EmuThread::onDestroyDisplayRequested, this, &MainWindow::destroyDisplay, Qt::BlockingQueuedConnection);
|
connect(thread, &EmuThread::onDestroyDisplayRequested, this, &MainWindow::destroyDisplay, Qt::BlockingQueuedConnection);
|
||||||
connect(thread, &EmuThread::onResizeDisplayRequested, this, &MainWindow::displayResizeRequested);
|
connect(thread, &EmuThread::onResizeDisplayRequested, this, &MainWindow::displayResizeRequested);
|
||||||
connect(thread, &EmuThread::onRelativeMouseModeRequested, this, &MainWindow::relativeMouseModeRequested);
|
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)
|
void MainWindow::updateDisplayRelatedActions(bool has_surface, bool render_to_main, bool fullscreen)
|
||||||
{
|
{
|
||||||
// rendering to main, or switched to gamelist/grid
|
// 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.menuWindowSize->setEnabled(has_surface && !fullscreen);
|
||||||
m_ui.actionFullscreen->setEnabled(has_surface);
|
m_ui.actionFullscreen->setEnabled(has_surface);
|
||||||
|
|
||||||
|
@ -1279,10 +1279,10 @@ bool MainWindow::isShowingGameList() const
|
||||||
|
|
||||||
bool MainWindow::isRenderingFullscreen() const
|
bool MainWindow::isRenderingFullscreen() const
|
||||||
{
|
{
|
||||||
if (!g_host_display || !m_display_widget)
|
if (!GetMTGS().IsOpen() || !m_display_widget)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return getDisplayContainer()->isFullScreen() || g_host_display->IsFullscreen();
|
return getDisplayContainer()->isFullScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MainWindow::isRenderingToMain() const
|
bool MainWindow::isRenderingToMain() const
|
||||||
|
@ -2197,17 +2197,12 @@ bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr
|
||||||
|
|
||||||
#endif
|
#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)
|
createDisplayWidget(fullscreen, render_to_main);
|
||||||
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);
|
|
||||||
|
|
||||||
// we need the surface visible.. this might be able to be replaced with something else
|
// we need the surface visible.. this might be able to be replaced with something else
|
||||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
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"));
|
QMessageBox::critical(this, tr("Error"), tr("Failed to get window info from widget"));
|
||||||
destroyDisplayWidget(true);
|
destroyDisplayWidget(true);
|
||||||
return nullptr;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_emu_thread->connectDisplaySignals(m_display_widget);
|
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;
|
m_display_created = true;
|
||||||
|
|
||||||
if (is_exclusive_fullscreen)
|
|
||||||
setDisplayFullscreen(fullscreen_mode);
|
|
||||||
|
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
updateWindowState();
|
updateWindowState();
|
||||||
|
|
||||||
|
@ -2246,34 +2227,28 @@ DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main)
|
||||||
updateDisplayWidgetCursor();
|
updateDisplayWidgetCursor();
|
||||||
m_display_widget->setFocus();
|
m_display_widget->setFocus();
|
||||||
|
|
||||||
g_host_display->DoneCurrent();
|
return wi;
|
||||||
return m_display_widget;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
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);
|
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_fullscreen = isRenderingFullscreen();
|
||||||
const bool is_rendering_to_main = isRenderingToMain();
|
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);
|
const bool changing_surfaceless = (!m_display_widget != surfaceless);
|
||||||
if (fullscreen == is_fullscreen && is_rendering_to_main == render_to_main && !changing_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.
|
// 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.
|
// .. except on Wayland, where everything tends to break if you don't recreate.
|
||||||
const bool has_container = (m_display_container != nullptr);
|
const bool has_container = (m_display_container != nullptr);
|
||||||
const bool needs_container = DisplayContainer::isNeeded(fullscreen, render_to_main);
|
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 &&
|
if (!is_rendering_to_main && !render_to_main && has_container == needs_container && !needs_container && !changing_surfaceless)
|
||||||
!changing_surfaceless)
|
|
||||||
{
|
{
|
||||||
DevCon.WriteLn("Toggling to %s without recreating surface", (fullscreen ? "fullscreen" : "windowed"));
|
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
|
// since we don't destroy the display widget, we need to save it here
|
||||||
if (!is_fullscreen && !is_rendering_to_main)
|
if (!is_fullscreen && !is_rendering_to_main)
|
||||||
|
@ -2294,45 +2269,37 @@ DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main, b
|
||||||
updateWindowState();
|
updateWindowState();
|
||||||
|
|
||||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||||
return m_display_widget;
|
return m_display_widget->getWindowInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
g_host_display->DestroySurface();
|
|
||||||
|
|
||||||
destroyDisplayWidget(surfaceless);
|
destroyDisplayWidget(surfaceless);
|
||||||
|
|
||||||
// if we're going to surfaceless, we're done here
|
// if we're going to surfaceless, we're done here
|
||||||
if (surfaceless)
|
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();
|
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo();
|
||||||
if (!wi.has_value())
|
if (!wi.has_value())
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, tr("Error"), tr("Failed to get new window info from widget"));
|
QMessageBox::critical(this, tr("Error"), tr("Failed to get new window info from widget"));
|
||||||
destroyDisplayWidget(true);
|
destroyDisplayWidget(true);
|
||||||
return nullptr;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_emu_thread->connectDisplaySignals(m_display_widget);
|
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();
|
updateWindowTitle();
|
||||||
updateWindowState();
|
updateWindowState();
|
||||||
|
|
||||||
updateDisplayWidgetCursor();
|
updateDisplayWidgetCursor();
|
||||||
m_display_widget->setFocus();
|
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),
|
// 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.
|
// 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();
|
restoreDisplayWindowGeometryFromConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_exclusive_fullscreen)
|
container->showFullScreen();
|
||||||
container->showFullScreen();
|
|
||||||
else
|
|
||||||
container->showNormal();
|
|
||||||
}
|
}
|
||||||
else if (!render_to_main)
|
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()
|
SettingsDialog* MainWindow::getSettingsDialog()
|
||||||
{
|
{
|
||||||
if (!m_settings_dialog)
|
if (!m_settings_dialog)
|
||||||
|
|
|
@ -128,8 +128,8 @@ public Q_SLOTS:
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void onUpdateCheckComplete();
|
void onUpdateCheckComplete();
|
||||||
|
|
||||||
DisplayWidget* createDisplay(bool fullscreen, bool render_to_main);
|
std::optional<WindowInfo> createDisplayWindow(bool fullscreen, bool render_to_main);
|
||||||
DisplayWidget* updateDisplay(bool fullscreen, bool render_to_main, bool surfaceless);
|
std::optional<WindowInfo> updateDisplayWindow(bool fullscreen, bool render_to_main, bool surfaceless);
|
||||||
void displayResizeRequested(qint32 width, qint32 height);
|
void displayResizeRequested(qint32 width, qint32 height);
|
||||||
void relativeMouseModeRequested(bool enabled);
|
void relativeMouseModeRequested(bool enabled);
|
||||||
void destroyDisplay();
|
void destroyDisplay();
|
||||||
|
@ -233,10 +233,9 @@ private:
|
||||||
QWidget* getDisplayContainer() const;
|
QWidget* getDisplayContainer() const;
|
||||||
void saveDisplayWindowGeometryToConfig();
|
void saveDisplayWindowGeometryToConfig();
|
||||||
void restoreDisplayWindowGeometryFromConfig();
|
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 destroyDisplayWidget(bool show_game_list);
|
||||||
void updateDisplayWidgetCursor();
|
void updateDisplayWidgetCursor();
|
||||||
void setDisplayFullscreen(const std::string& fullscreen_mode);
|
|
||||||
|
|
||||||
SettingsDialog* getSettingsDialog();
|
SettingsDialog* getSettingsDialog();
|
||||||
void doSettings(const char* category = nullptr);
|
void doSettings(const char* category = nullptr);
|
||||||
|
|
|
@ -41,7 +41,6 @@
|
||||||
#include "pcsx2/GS.h"
|
#include "pcsx2/GS.h"
|
||||||
#include "pcsx2/GS/GS.h"
|
#include "pcsx2/GS/GS.h"
|
||||||
#include "pcsx2/GSDumpReplayer.h"
|
#include "pcsx2/GSDumpReplayer.h"
|
||||||
#include "pcsx2/HostDisplay.h"
|
|
||||||
#include "pcsx2/HostSettings.h"
|
#include "pcsx2/HostSettings.h"
|
||||||
#include "pcsx2/INISettingsInterface.h"
|
#include "pcsx2/INISettingsInterface.h"
|
||||||
#include "pcsx2/PAD/Host/PAD.h"
|
#include "pcsx2/PAD/Host/PAD.h"
|
||||||
|
@ -191,9 +190,11 @@ void EmuThread::startFullscreenUI(bool fullscreen)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (VMManager::HasValidVM())
|
if (VMManager::HasValidVM() || GetMTGS().IsOpen())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// this should just set the flag so it gets automatically started
|
||||||
|
ImGuiManager::InitializeFullscreenUI();
|
||||||
m_run_fullscreen_ui = true;
|
m_run_fullscreen_ui = true;
|
||||||
if (fullscreen)
|
if (fullscreen)
|
||||||
m_is_fullscreen = true;
|
m_is_fullscreen = true;
|
||||||
|
@ -216,13 +217,13 @@ void EmuThread::stopFullscreenUI()
|
||||||
QMetaObject::invokeMethod(this, &EmuThread::stopFullscreenUI, Qt::QueuedConnection);
|
QMetaObject::invokeMethod(this, &EmuThread::stopFullscreenUI, Qt::QueuedConnection);
|
||||||
|
|
||||||
// wait until the host display is gone
|
// wait until the host display is gone
|
||||||
while (g_host_display)
|
while (GetMTGS().IsOpen())
|
||||||
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
|
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!g_host_display)
|
if (!GetMTGS().IsOpen())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pxAssertRel(!VMManager::HasValidVM(), "VM is not valid at FSUI shutdown time");
|
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);
|
QMetaObject::invokeMethod(g_main_window, &MainWindow::checkForSettingChanges, Qt::QueuedConnection);
|
||||||
|
|
||||||
if (g_host_display)
|
if (GetMTGS().IsOpen())
|
||||||
{
|
{
|
||||||
const bool render_to_main = shouldRenderToMain();
|
const bool render_to_main = shouldRenderToMain();
|
||||||
if (!m_is_fullscreen && m_is_rendering_to_main != render_to_main)
|
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)
|
void EmuThread::onDisplayWindowResized(int width, int height, float scale)
|
||||||
{
|
{
|
||||||
if (!g_host_display)
|
if (!GetMTGS().IsOpen())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
GetMTGS().ResizeDisplayWindow(width, height, scale);
|
GetMTGS().ResizeDisplayWindow(width, height, scale);
|
||||||
|
@ -892,115 +893,41 @@ void EmuThread::endCapture()
|
||||||
GetMTGS().RunOnGSThread(&GSEndCapture);
|
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_rendering_to_main = shouldRenderToMain();
|
||||||
m_is_surfaceless = false;
|
m_is_surfaceless = false;
|
||||||
|
|
||||||
g_host_display = HostDisplay::CreateForAPI(api);
|
return emit onCreateDisplayRequested(m_is_fullscreen, m_is_rendering_to_main);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
connectDisplaySignals(widget);
|
|
||||||
|
|
||||||
if (!g_host_display->MakeCurrent())
|
|
||||||
{
|
|
||||||
Console.Error("Failed to make render context current");
|
|
||||||
releaseHostDisplay(clear_state_on_fail);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!g_host_display->SetupDevice() || !ImGuiManager::Initialize())
|
|
||||||
{
|
|
||||||
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)
|
std::optional<WindowInfo> EmuThread::updateRenderWindow()
|
||||||
{
|
{
|
||||||
ImGuiManager::Shutdown(clear_state);
|
return emit onUpdateDisplayRequested(m_is_fullscreen, !m_is_fullscreen && m_is_rendering_to_main, m_is_surfaceless);
|
||||||
|
}
|
||||||
|
|
||||||
g_host_display.reset();
|
void EmuThread::releaseRenderWindow()
|
||||||
|
{
|
||||||
emit onDestroyDisplayRequested();
|
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);
|
return g_emu_thread->releaseRenderWindow();
|
||||||
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 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)
|
void Host::RequestResizeHostDisplay(s32 width, s32 height)
|
||||||
|
@ -1008,12 +935,6 @@ void Host::RequestResizeHostDisplay(s32 width, s32 height)
|
||||||
g_emu_thread->onResizeDisplayRequested(width, height);
|
g_emu_thread->onResizeDisplayRequested(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::UpdateHostDisplay()
|
|
||||||
{
|
|
||||||
g_emu_thread->updateDisplay();
|
|
||||||
ImGuiManager::WindowResized();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Host::OnVMStarting()
|
void Host::OnVMStarting()
|
||||||
{
|
{
|
||||||
CommonHost::OnVMStarting();
|
CommonHost::OnVMStarting();
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "pcsx2/Host.h"
|
#include "pcsx2/Host.h"
|
||||||
#include "pcsx2/HostDisplay.h"
|
|
||||||
#include "pcsx2/HostSettings.h"
|
#include "pcsx2/HostSettings.h"
|
||||||
#include "pcsx2/Frontend/InputManager.h"
|
#include "pcsx2/Frontend/InputManager.h"
|
||||||
#include "pcsx2/VMManager.h"
|
#include "pcsx2/VMManager.h"
|
||||||
|
@ -70,10 +69,10 @@ public:
|
||||||
bool shouldRenderToMain() const;
|
bool shouldRenderToMain() const;
|
||||||
|
|
||||||
/// Called back from the GS thread when the display state changes (e.g. fullscreen, render to main).
|
/// 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 connectDisplaySignals(DisplayWidget* widget);
|
||||||
void releaseHostDisplay(bool clear_state);
|
void releaseRenderWindow();
|
||||||
void updateDisplay();
|
|
||||||
|
|
||||||
void startBackgroundControllerPollTimer();
|
void startBackgroundControllerPollTimer();
|
||||||
void stopBackgroundControllerPollTimer();
|
void stopBackgroundControllerPollTimer();
|
||||||
|
@ -118,8 +117,8 @@ public Q_SLOTS:
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
bool messageConfirmed(const QString& title, const QString& message);
|
bool messageConfirmed(const QString& title, const QString& message);
|
||||||
|
|
||||||
DisplayWidget* onCreateDisplayRequested(bool fullscreen, bool render_to_main);
|
std::optional<WindowInfo> onCreateDisplayRequested(bool fullscreen, bool render_to_main);
|
||||||
DisplayWidget* onUpdateDisplayRequested(bool fullscreen, bool render_to_main, bool surfaceless);
|
std::optional<WindowInfo> onUpdateDisplayRequested(bool fullscreen, bool render_to_main, bool surfaceless);
|
||||||
void onResizeDisplayRequested(qint32 width, qint32 height);
|
void onResizeDisplayRequested(qint32 width, qint32 height);
|
||||||
void onDestroyDisplayRequested();
|
void onDestroyDisplayRequested();
|
||||||
void onRelativeMouseModeRequested(bool enabled);
|
void onRelativeMouseModeRequested(bool enabled);
|
||||||
|
|
|
@ -26,18 +26,6 @@
|
||||||
#include "pcsx2/GS/GSCapture.h"
|
#include "pcsx2/GS/GSCapture.h"
|
||||||
#include "pcsx2/GS/GSUtil.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
|
struct RendererInfo
|
||||||
{
|
{
|
||||||
const char* name;
|
const char* name;
|
||||||
|
@ -965,37 +953,9 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
|
||||||
m_ui.disableFramebufferFetch->setDisabled(is_sw_dx);
|
m_ui.disableFramebufferFetch->setDisabled(is_sw_dx);
|
||||||
|
|
||||||
// populate adapters
|
// populate adapters
|
||||||
HostDisplay::AdapterAndModeList modes;
|
std::vector<std::string> adapters;
|
||||||
switch (type)
|
std::vector<std::string> fullscreen_modes;
|
||||||
{
|
GSGetAdaptersAndFullscreenModes(type, &adapters, &fullscreen_modes);
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill+select adapters
|
// fill+select adapters
|
||||||
{
|
{
|
||||||
|
@ -1003,7 +963,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
|
||||||
|
|
||||||
std::string current_adapter = Host::GetBaseStringSettingValue("EmuCore/GS", "Adapter", "");
|
std::string current_adapter = Host::GetBaseStringSettingValue("EmuCore/GS", "Adapter", "");
|
||||||
m_ui.adapter->clear();
|
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->addItem(tr("(Default)"));
|
||||||
m_ui.adapter->setCurrentIndex(0);
|
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));
|
m_ui.adapter->addItem(QString::fromStdString(adapter));
|
||||||
if (current_adapter == 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));
|
m_ui.fullscreenModes->addItem(QString::fromStdString(fs_mode));
|
||||||
if (current_mode == fs_mode)
|
if (current_mode == fs_mode)
|
||||||
|
|
|
@ -98,7 +98,6 @@ set(pcsx2Sources
|
||||||
Gif_Unit.cpp
|
Gif_Unit.cpp
|
||||||
GS.cpp
|
GS.cpp
|
||||||
Host.cpp
|
Host.cpp
|
||||||
HostDisplay.cpp
|
|
||||||
Hw.cpp
|
Hw.cpp
|
||||||
HwRead.cpp
|
HwRead.cpp
|
||||||
HwWrite.cpp
|
HwWrite.cpp
|
||||||
|
@ -180,7 +179,6 @@ set(pcsx2Headers
|
||||||
GS.h
|
GS.h
|
||||||
Hardware.h
|
Hardware.h
|
||||||
Host.h
|
Host.h
|
||||||
HostDisplay.h
|
|
||||||
Hw.h
|
Hw.h
|
||||||
IopBios.h
|
IopBios.h
|
||||||
IopCounters.h
|
IopCounters.h
|
||||||
|
@ -785,42 +783,7 @@ set(pcsx2FrontendHeaders
|
||||||
Frontend/ImGuiOverlays.h
|
Frontend/ImGuiOverlays.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if(USE_OPENGL)
|
if(APPLE)
|
||||||
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)
|
|
||||||
list(APPEND pcsx2GSSources
|
list(APPEND pcsx2GSSources
|
||||||
GS/Renderers/Metal/GSDeviceMTL.mm
|
GS/Renderers/Metal/GSDeviceMTL.mm
|
||||||
GS/Renderers/Metal/GSMTLDeviceInfo.mm
|
GS/Renderers/Metal/GSMTLDeviceInfo.mm
|
||||||
|
@ -834,12 +797,6 @@ elseif(APPLE)
|
||||||
GS/Renderers/Metal/GSMTLShaderCommon.h
|
GS/Renderers/Metal/GSMTLShaderCommon.h
|
||||||
GS/Renderers/Metal/GSTextureMTL.h
|
GS/Renderers/Metal/GSTextureMTL.h
|
||||||
)
|
)
|
||||||
list(APPEND pcsx2FrontendSources
|
|
||||||
Frontend/MetalHostDisplay.mm
|
|
||||||
)
|
|
||||||
list(APPEND pcsx2FrontendHeaders
|
|
||||||
Frontend/MetalHostDisplay.h
|
|
||||||
)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
list(APPEND pcsx2FrontendSources
|
list(APPEND pcsx2FrontendSources
|
||||||
|
|
|
@ -25,13 +25,13 @@
|
||||||
#include "IopCounters.h"
|
#include "IopCounters.h"
|
||||||
|
|
||||||
#include "GS.h"
|
#include "GS.h"
|
||||||
|
#include "GS/GS.h"
|
||||||
#include "VUmicro.h"
|
#include "VUmicro.h"
|
||||||
#include "PerformanceMetrics.h"
|
#include "PerformanceMetrics.h"
|
||||||
#include "Patch.h"
|
#include "Patch.h"
|
||||||
|
|
||||||
#include "ps2/HwInternal.h"
|
#include "ps2/HwInternal.h"
|
||||||
#include "Sio.h"
|
#include "Sio.h"
|
||||||
#include "HostDisplay.h"
|
|
||||||
#include "SPU2/spu2.h"
|
#include "SPU2/spu2.h"
|
||||||
#include "PAD/Host/PAD.h"
|
#include "PAD/Host/PAD.h"
|
||||||
#include "Recording/InputRecording.h"
|
#include "Recording/InputRecording.h"
|
||||||
|
@ -357,7 +357,7 @@ static double AdjustToHostRefreshRate(double vertical_frequency, double frame_li
|
||||||
}
|
}
|
||||||
|
|
||||||
float host_refresh_rate;
|
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.");
|
Console.Warning("Cannot sync to host refresh since the query failed.");
|
||||||
SPU2::SetDeviceSampleRateMultiplier(1.0);
|
SPU2::SetDeviceSampleRateMultiplier(1.0);
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
#include "GS.h"
|
#include "GS.h"
|
||||||
#include "GS/Renderers/HW/GSTextureReplacements.h"
|
#include "GS/Renderers/HW/GSTextureReplacements.h"
|
||||||
#include "Host.h"
|
#include "Host.h"
|
||||||
#include "HostDisplay.h"
|
|
||||||
#include "HostSettings.h"
|
#include "HostSettings.h"
|
||||||
#include "IconsFontAwesome5.h"
|
#include "IconsFontAwesome5.h"
|
||||||
#include "MemoryCardFile.h"
|
#include "MemoryCardFile.h"
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
#include "Frontend/InputManager.h"
|
#include "Frontend/InputManager.h"
|
||||||
#include "GS.h"
|
#include "GS.h"
|
||||||
#include "Host.h"
|
#include "Host.h"
|
||||||
#include "HostDisplay.h"
|
|
||||||
#include "IconsFontAwesome5.h"
|
#include "IconsFontAwesome5.h"
|
||||||
#include "Recording/InputRecording.h"
|
#include "Recording/InputRecording.h"
|
||||||
#include "SPU2/spu2.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/CDVDcommon.h"
|
||||||
#include "CDVD/CDVDdiscReader.h"
|
#include "CDVD/CDVDdiscReader.h"
|
||||||
#include "GS.h"
|
#include "GS.h"
|
||||||
|
#include "GS/Renderers/Common/GSDevice.h"
|
||||||
|
#include "GS/Renderers/Common/GSTexture.h"
|
||||||
#include "Host.h"
|
#include "Host.h"
|
||||||
#include "HostDisplay.h"
|
|
||||||
#include "HostSettings.h"
|
#include "HostSettings.h"
|
||||||
#include "INISettingsInterface.h"
|
#include "INISettingsInterface.h"
|
||||||
#include "MemoryCardFile.h"
|
#include "MemoryCardFile.h"
|
||||||
|
@ -196,6 +197,7 @@ namespace FullscreenUI
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// Utility
|
// Utility
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
static void ReleaseTexture(std::unique_ptr<GSTexture>& tex);
|
||||||
static std::string TimeToPrintableString(time_t t);
|
static std::string TimeToPrintableString(time_t t);
|
||||||
static void StartAsyncOp(std::function<void(::ProgressCallback*)> callback, std::string name);
|
static void StartAsyncOp(std::function<void(::ProgressCallback*)> callback, std::string name);
|
||||||
static void AsyncOpThreadEntryPoint(std::function<void(::ProgressCallback*)> callback, FullscreenUI::ProgressCallback* progress);
|
static void AsyncOpThreadEntryPoint(std::function<void(::ProgressCallback*)> callback, FullscreenUI::ProgressCallback* progress);
|
||||||
|
@ -244,12 +246,12 @@ namespace FullscreenUI
|
||||||
static bool LoadResources();
|
static bool LoadResources();
|
||||||
static void DestroyResources();
|
static void DestroyResources();
|
||||||
|
|
||||||
static std::shared_ptr<HostDisplayTexture> s_app_icon_texture;
|
static std::shared_ptr<GSTexture> s_app_icon_texture;
|
||||||
static std::array<std::shared_ptr<HostDisplayTexture>, static_cast<u32>(GameDatabaseSchema::Compatibility::Perfect)>
|
static std::array<std::shared_ptr<GSTexture>, static_cast<u32>(GameDatabaseSchema::Compatibility::Perfect)>
|
||||||
s_game_compatibility_textures;
|
s_game_compatibility_textures;
|
||||||
static std::shared_ptr<HostDisplayTexture> s_fallback_disc_texture;
|
static std::shared_ptr<GSTexture> s_fallback_disc_texture;
|
||||||
static std::shared_ptr<HostDisplayTexture> s_fallback_exe_texture;
|
static std::shared_ptr<GSTexture> s_fallback_exe_texture;
|
||||||
static std::vector<std::unique_ptr<HostDisplayTexture>> s_cleanup_textures;
|
static std::vector<std::unique_ptr<GSTexture>> s_cleanup_textures;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// Landing
|
// Landing
|
||||||
|
@ -397,7 +399,7 @@ namespace FullscreenUI
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string summary;
|
std::string summary;
|
||||||
std::string path;
|
std::string path;
|
||||||
std::unique_ptr<HostDisplayTexture> preview_texture;
|
std::unique_ptr<GSTexture> preview_texture;
|
||||||
time_t timestamp;
|
time_t timestamp;
|
||||||
s32 slot;
|
s32 slot;
|
||||||
};
|
};
|
||||||
|
@ -433,9 +435,9 @@ namespace FullscreenUI
|
||||||
static void DrawGameListSettingsPage(const ImVec2& heading_size);
|
static void DrawGameListSettingsPage(const ImVec2& heading_size);
|
||||||
static void SwitchToGameList();
|
static void SwitchToGameList();
|
||||||
static void PopulateGameListEntryList();
|
static void PopulateGameListEntryList();
|
||||||
static HostDisplayTexture* GetTextureForGameListEntryType(GameList::EntryType type);
|
static GSTexture* GetTextureForGameListEntryType(GameList::EntryType type);
|
||||||
static HostDisplayTexture* GetGameListCover(const GameList::Entry* entry);
|
static GSTexture* GetGameListCover(const GameList::Entry* entry);
|
||||||
static HostDisplayTexture* GetCoverForCurrentGame();
|
static GSTexture* GetCoverForCurrentGame();
|
||||||
|
|
||||||
// Lazily populated cover images.
|
// Lazily populated cover images.
|
||||||
static std::unordered_map<std::string, std::string> s_cover_image_map;
|
static std::unordered_map<std::string, std::string> s_cover_image_map;
|
||||||
|
@ -465,6 +467,12 @@ namespace FullscreenUI
|
||||||
// Utility
|
// Utility
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void FullscreenUI::ReleaseTexture(std::unique_ptr<GSTexture>& tex)
|
||||||
|
{
|
||||||
|
if (tex)
|
||||||
|
g_gs_device->Recycle(tex.release());
|
||||||
|
}
|
||||||
|
|
||||||
std::string FullscreenUI::TimeToPrintableString(time_t t)
|
std::string FullscreenUI::TimeToPrintableString(time_t t)
|
||||||
{
|
{
|
||||||
struct tm lt = {};
|
struct tm lt = {};
|
||||||
|
@ -765,8 +773,8 @@ void FullscreenUI::Render()
|
||||||
if (!s_initialized)
|
if (!s_initialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (std::unique_ptr<HostDisplayTexture>& tex : s_cleanup_textures)
|
for (std::unique_ptr<GSTexture>& tex : s_cleanup_textures)
|
||||||
tex.reset();
|
g_gs_device->Recycle(tex.release());
|
||||||
s_cleanup_textures.clear();
|
s_cleanup_textures.clear();
|
||||||
ImGuiFullscreen::UploadAsyncTextures();
|
ImGuiFullscreen::UploadAsyncTextures();
|
||||||
|
|
||||||
|
@ -883,7 +891,7 @@ void FullscreenUI::DestroyResources()
|
||||||
for (auto& tex : s_game_compatibility_textures)
|
for (auto& tex : s_game_compatibility_textures)
|
||||||
tex.reset();
|
tex.reset();
|
||||||
for (auto& tex : s_cleanup_textures)
|
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);
|
const float image_size = LayoutScale(380.f);
|
||||||
ImGui::SetCursorPos(
|
ImGui::SetCursorPos(
|
||||||
ImVec2((ImGui::GetWindowWidth() * 0.5f) - (image_size * 0.5f), (ImGui::GetWindowHeight() * 0.5f) - (image_size * 0.5f)));
|
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();
|
EndFullscreenColumnWindow();
|
||||||
|
|
||||||
|
@ -2360,10 +2368,7 @@ void FullscreenUI::SwitchToGameSettings(const GameList::Entry* entry)
|
||||||
|
|
||||||
void FullscreenUI::PopulateGraphicsAdapterList()
|
void FullscreenUI::PopulateGraphicsAdapterList()
|
||||||
{
|
{
|
||||||
HostDisplay::AdapterAndModeList ml(g_host_display->GetAdapterAndModeList());
|
GSGetAdaptersAndFullscreenModes(GSConfig.Renderer, &s_graphics_adapter_list_cache, &s_fullscreen_mode_list_cache);
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FullscreenUI::PopulateGameListDirectoryCache(SettingsInterface* si)
|
void FullscreenUI::PopulateGameListDirectoryCache(SettingsInterface* si)
|
||||||
|
@ -3179,7 +3184,8 @@ void FullscreenUI::DrawGraphicsSettingsPage()
|
||||||
static constexpr const char* s_round_sprite_options[] = {"Off (Default)", "Half", "Full"};
|
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.",
|
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",
|
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);
|
"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.",
|
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);
|
"EmuCore/GS", "SkipDuplicateFrames", false);
|
||||||
DrawToggleSetting(bsi, "Disable Threaded Presentation",
|
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 "
|
"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)
|
if (hw_fixes_visible)
|
||||||
{
|
{
|
||||||
DrawIntListSetting(bsi, "Hardware Download Mode", "Changes synchronization behavior for GS downloads.", "EmuCore/GS",
|
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",
|
DrawToggleSetting(bsi, ICON_FA_WIFI " SDL DualShock 4 / DualSense Enhanced Mode",
|
||||||
"Provides vibration and LED control support over Bluetooth.", "InputSources", "SDLControllerEnhancedMode", false,
|
"Provides vibration and LED control support over Bluetooth.", "InputSources", "SDLControllerEnhancedMode", false,
|
||||||
bsi->GetBoolValue("InputSources", "SDL", true), false);
|
bsi->GetBoolValue("InputSources", "SDL", true), false);
|
||||||
DrawToggleSetting(bsi, ICON_FA_COG " SDL Raw Input",
|
DrawToggleSetting(bsi, ICON_FA_COG " SDL Raw Input", "Allow SDL to use raw access to input devices.", "InputSources", "SDLRawInput",
|
||||||
"Allow SDL to use raw access to input devices.", "InputSources", "SDLRawInput", false,
|
false, bsi->GetBoolValue("InputSources", "SDL", true), false);
|
||||||
bsi->GetBoolValue("InputSources", "SDL", true), false);
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
DrawToggleSetting(bsi, ICON_FA_COG " Enable XInput Input Source",
|
DrawToggleSetting(bsi, ICON_FA_COG " Enable XInput Input Source",
|
||||||
|
@ -4317,13 +4323,13 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
HostDisplayTexture* const cover = GetCoverForCurrentGame();
|
GSTexture* const cover = GetCoverForCurrentGame();
|
||||||
const ImVec2 image_min(
|
const ImVec2 image_min(
|
||||||
display_size.x - LayoutScale(10.0f + image_width) - rp_height, display_size.y - LayoutScale(10.0f + image_height) - rp_height);
|
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 ImVec2 image_max(image_min.x + LayoutScale(image_width) + rp_height, image_min.y + LayoutScale(image_height) + rp_height);
|
||||||
const ImRect image_rect(CenterImage(
|
const ImRect image_rect(CenterImage(
|
||||||
ImRect(image_min, image_max), ImVec2(static_cast<float>(cover->GetWidth()), static_cast<float>(cover->GetHeight()))));
|
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
|
// current time / play time
|
||||||
|
@ -4549,10 +4555,14 @@ bool FullscreenUI::InitializeSaveStateListEntry(
|
||||||
std::vector<u32> screenshot_pixels;
|
std::vector<u32> screenshot_pixels;
|
||||||
if (SaveState_ReadScreenshot(li->path, &screenshot_width, &screenshot_height, &screenshot_pixels))
|
if (SaveState_ReadScreenshot(li->path, &screenshot_width, &screenshot_height, &screenshot_pixels))
|
||||||
{
|
{
|
||||||
li->preview_texture = g_host_display->CreateTexture(
|
li->preview_texture =
|
||||||
screenshot_width, screenshot_height, screenshot_pixels.data(), sizeof(u32) * screenshot_width, false);
|
std::unique_ptr<GSTexture>(g_gs_device->CreateTexture(screenshot_width, screenshot_height, 1, GSTexture::Format::Color));
|
||||||
if (!li->preview_texture)
|
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");
|
Console.Error("Failed to upload save state image to GPU");
|
||||||
|
ReleaseTexture(li->preview_texture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -4849,12 +4859,11 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading)
|
||||||
bb.Min += style.FramePadding;
|
bb.Min += style.FramePadding;
|
||||||
bb.Max -= style.FramePadding;
|
bb.Max -= style.FramePadding;
|
||||||
|
|
||||||
const HostDisplayTexture* const screenshot =
|
const GSTexture* const screenshot = entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get();
|
||||||
entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get();
|
|
||||||
const ImRect image_rect(CenterImage(ImRect(bb.Min, bb.Min + image_size),
|
const ImRect image_rect(CenterImage(ImRect(bb.Min, bb.Min + image_size),
|
||||||
ImVec2(static_cast<float>(screenshot->GetWidth()), static_cast<float>(screenshot->GetHeight()))));
|
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));
|
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);
|
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?",
|
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());
|
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_height = LayoutScale(250.0f);
|
||||||
const float image_width = image_height * (static_cast<float>(image->GetWidth()) / static_cast<float>(image->GetHeight()));
|
const float image_width = image_height * (static_cast<float>(image->GetWidth()) / static_cast<float>(image->GetHeight()));
|
||||||
const ImVec2 pos(ImGui::GetCursorScreenPos() +
|
const ImVec2 pos(ImGui::GetCursorScreenPos() +
|
||||||
ImVec2((ImGui::GetCurrentWindow()->WorkRect.GetWidth() - image_width) * 0.5f, LayoutScale(20.0f)));
|
ImVec2((ImGui::GetCurrentWindow()->WorkRect.GetWidth() - image_width) * 0.5f, LayoutScale(20.0f)));
|
||||||
const ImRect image_bb(pos, pos + ImVec2(image_width, image_height));
|
const ImRect image_bb(pos, pos + ImVec2(image_width, image_height));
|
||||||
ImGui::GetWindowDrawList()->AddImage(
|
ImGui::GetWindowDrawList()->AddImage(static_cast<ImTextureID>(entry.preview_texture ? entry.preview_texture->GetNativeHandle() :
|
||||||
static_cast<ImTextureID>(entry.preview_texture ? entry.preview_texture->GetHandle() : GetPlaceholderTexture()->GetHandle()),
|
GetPlaceholderTexture()->GetNativeHandle()),
|
||||||
image_bb.Min, image_bb.Max);
|
image_bb.Min, image_bb.Max);
|
||||||
|
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + image_height + LayoutScale(40.0f));
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + image_height + LayoutScale(40.0f));
|
||||||
|
@ -5226,7 +5235,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
||||||
if (!visible)
|
if (!visible)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
HostDisplayTexture* cover_texture = GetGameListCover(entry);
|
GSTexture* cover_texture = GetGameListCover(entry);
|
||||||
|
|
||||||
summary.clear();
|
summary.clear();
|
||||||
if (entry->serial.empty())
|
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),
|
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()))));
|
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));
|
ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
|
||||||
|
|
||||||
const float midpoint = bb.Min.y + g_large_font->FontSize + LayoutScale(4.0f);
|
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))
|
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);
|
selected_entry ? GetGameListCover(selected_entry) : GetTextureForGameListEntryType(GameList::EntryType::Count);
|
||||||
if (cover_texture)
|
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()))));
|
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::SetCursorPos(LayoutScale(ImVec2(128.0f, 20.0f)) + image_rect.Min);
|
||||||
ImGui::Image(selected_entry ? GetGameListCover(selected_entry)->GetHandle() :
|
ImGui::Image(selected_entry ? GetGameListCover(selected_entry)->GetNativeHandle() :
|
||||||
GetTextureForGameListEntryType(GameList::EntryType::Count)->GetHandle(),
|
GetTextureForGameListEntryType(GameList::EntryType::Count)->GetNativeHandle(),
|
||||||
image_rect.GetSize());
|
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)));
|
std::string flag_texture(fmt::format("icons/flags/{}.png", GameList::RegionToString(selected_entry->region)));
|
||||||
ImGui::TextUnformatted("Region: ");
|
ImGui::TextUnformatted("Region: ");
|
||||||
ImGui::SameLine();
|
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::SameLine();
|
||||||
ImGui::Text(" (%s)", GameList::RegionToString(selected_entry->region));
|
ImGui::Text(" (%s)", GameList::RegionToString(selected_entry->region));
|
||||||
}
|
}
|
||||||
|
@ -5347,7 +5356,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (selected_entry->compatibility_rating != GameDatabaseSchema::Compatibility::Unknown)
|
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));
|
LayoutScale(64.0f, 16.0f));
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
}
|
}
|
||||||
|
@ -5451,11 +5460,11 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size)
|
||||||
bb.Min += style.FramePadding;
|
bb.Min += style.FramePadding;
|
||||||
bb.Max -= 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),
|
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()))));
|
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));
|
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);
|
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();
|
QueueResetFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
HostDisplayTexture* FullscreenUI::GetGameListCover(const GameList::Entry* entry)
|
GSTexture* FullscreenUI::GetGameListCover(const GameList::Entry* entry)
|
||||||
{
|
{
|
||||||
// lookup and grab cover image
|
// lookup and grab cover image
|
||||||
auto cover_it = s_cover_image_map.find(entry->path);
|
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;
|
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);
|
return tex ? tex : GetTextureForGameListEntryType(entry->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
HostDisplayTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::EntryType type)
|
GSTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::EntryType type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
|
@ -5805,7 +5814,7 @@ HostDisplayTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::Entry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HostDisplayTexture* FullscreenUI::GetCoverForCurrentGame()
|
GSTexture* FullscreenUI::GetCoverForCurrentGame()
|
||||||
{
|
{
|
||||||
auto lock = GameList::GetLock();
|
auto lock = GameList::GetLock();
|
||||||
|
|
||||||
|
@ -6064,11 +6073,11 @@ void FullscreenUI::DrawAchievement(const Achievements::Achievement& cheevo)
|
||||||
const std::string& badge_path = Achievements::GetAchievementBadgePath(cheevo);
|
const std::string& badge_path = Achievements::GetAchievementBadgePath(cheevo);
|
||||||
if (!badge_path.empty())
|
if (!badge_path.empty())
|
||||||
{
|
{
|
||||||
HostDisplayTexture* badge = GetCachedTextureAsync(badge_path.c_str());
|
GSTexture* badge = GetCachedTextureAsync(badge_path.c_str());
|
||||||
if (badge)
|
if (badge)
|
||||||
{
|
{
|
||||||
ImGui::GetWindowDrawList()->AddImage(
|
ImGui::GetWindowDrawList()->AddImage(badge->GetNativeHandle(), bb.Min, bb.Min + image_size, ImVec2(0.0f, 0.0f),
|
||||||
badge->GetHandle(), bb.Min, bb.Min + image_size, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
|
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();
|
const std::string& icon_path = Achievements::GetGameIcon();
|
||||||
if (!icon_path.empty())
|
if (!icon_path.empty())
|
||||||
{
|
{
|
||||||
HostDisplayTexture* badge = GetCachedTexture(icon_path.c_str());
|
GSTexture* badge = GetCachedTexture(icon_path.c_str());
|
||||||
if (badge)
|
if (badge)
|
||||||
{
|
{
|
||||||
ImGui::GetWindowDrawList()->AddImage(
|
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())
|
if (badge_path.empty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
HostDisplayTexture* badge = GetCachedTextureAsync(badge_path.c_str());
|
GSTexture* badge = GetCachedTextureAsync(badge_path.c_str());
|
||||||
if (!badge)
|
if (!badge)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
ImDrawList* dl = ImGui::GetBackgroundDrawList();
|
ImDrawList* dl = ImGui::GetBackgroundDrawList();
|
||||||
dl->AddImage(badge->GetHandle(), position, position + image_size);
|
dl->AddImage(badge->GetNativeHandle(), position, position + image_size);
|
||||||
position.x -= x_advance;
|
position.x -= x_advance;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
@ -6367,12 +6376,12 @@ void FullscreenUI::DrawPrimedAchievementsList()
|
||||||
if (badge_path.empty())
|
if (badge_path.empty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
HostDisplayTexture* badge = GetCachedTextureAsync(badge_path.c_str());
|
GSTexture* badge = GetCachedTextureAsync(badge_path.c_str());
|
||||||
if (!badge)
|
if (!badge)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
ImDrawList* dl = ImGui::GetBackgroundDrawList();
|
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_title = achievement.title.c_str();
|
||||||
const char* achievement_tile_end = achievement_title + achievement.title.length();
|
const char* achievement_tile_end = achievement_title + achievement.title.length();
|
||||||
|
@ -6580,11 +6589,11 @@ void FullscreenUI::DrawLeaderboardsWindow()
|
||||||
const std::string& icon_path = Achievements::GetGameIcon();
|
const std::string& icon_path = Achievements::GetGameIcon();
|
||||||
if (!icon_path.empty())
|
if (!icon_path.empty())
|
||||||
{
|
{
|
||||||
HostDisplayTexture* badge = GetCachedTexture(icon_path.c_str());
|
GSTexture* badge = GetCachedTexture(icon_path.c_str());
|
||||||
if (badge)
|
if (badge)
|
||||||
{
|
{
|
||||||
ImGui::GetWindowDrawList()->AddImage(
|
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 <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class HostDisplayTexture;
|
|
||||||
|
|
||||||
struct Pcsx2Config;
|
struct Pcsx2Config;
|
||||||
|
|
||||||
namespace FullscreenUI
|
namespace FullscreenUI
|
||||||
|
|
|
@ -29,7 +29,9 @@
|
||||||
#include "common/Threading.h"
|
#include "common/Threading.h"
|
||||||
#include "common/Timer.h"
|
#include "common/Timer.h"
|
||||||
#include "fmt/core.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_internal.h"
|
||||||
#include "imgui_stdlib.h"
|
#include "imgui_stdlib.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
|
@ -43,7 +45,7 @@ namespace ImGuiFullscreen
|
||||||
using MessageDialogCallbackVariant = std::variant<InfoMessageDialogCallback, ConfirmMessageDialogCallback>;
|
using MessageDialogCallbackVariant = std::variant<InfoMessageDialogCallback, ConfirmMessageDialogCallback>;
|
||||||
|
|
||||||
static std::optional<Common::RGBA8Image> LoadTextureImage(const char* path);
|
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 TextureLoaderThread();
|
||||||
|
|
||||||
static void DrawFileSelector();
|
static void DrawFileSelector();
|
||||||
|
@ -89,8 +91,8 @@ namespace ImGuiFullscreen
|
||||||
static u32 s_close_button_state = 0;
|
static u32 s_close_button_state = 0;
|
||||||
static bool s_focus_reset_queued = false;
|
static bool s_focus_reset_queued = false;
|
||||||
|
|
||||||
static LRUCache<std::string, std::shared_ptr<HostDisplayTexture>> s_texture_cache(128, true);
|
static LRUCache<std::string, std::shared_ptr<GSTexture>> s_texture_cache(128, true);
|
||||||
static std::shared_ptr<HostDisplayTexture> s_placeholder_texture;
|
static std::shared_ptr<GSTexture> s_placeholder_texture;
|
||||||
static std::atomic_bool s_texture_load_thread_quit{false};
|
static std::atomic_bool s_texture_load_thread_quit{false};
|
||||||
static std::mutex s_texture_load_mutex;
|
static std::mutex s_texture_load_mutex;
|
||||||
static std::condition_variable s_texture_load_cv;
|
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;
|
return s_placeholder_texture;
|
||||||
}
|
}
|
||||||
|
@ -278,26 +280,32 @@ std::optional<Common::RGBA8Image> ImGuiFullscreen::LoadTextureImage(const char*
|
||||||
return image;
|
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 =
|
GSTexture* texture = g_gs_device->CreateTexture(image.GetWidth(), image.GetHeight(), 1, GSTexture::Format::Color);
|
||||||
g_host_display->CreateTexture(image.GetWidth(), image.GetHeight(), image.GetPixels(), image.GetByteStride());
|
|
||||||
if (!texture)
|
if (!texture)
|
||||||
{
|
{
|
||||||
Console.Error("failed to create %ux%u texture for resource", image.GetWidth(), image.GetHeight());
|
Console.Error("failed to create %ux%u texture for resource", image.GetWidth(), image.GetHeight());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {};
|
||||||
|
}
|
||||||
|
|
||||||
DevCon.WriteLn("Uploaded texture resource '%s' (%ux%u)", path, image.GetWidth(), image.GetHeight());
|
DevCon.WriteLn("Uploaded texture resource '%s' (%ux%u)", path, image.GetWidth(), image.GetHeight());
|
||||||
return std::shared_ptr<HostDisplayTexture>(std::move(texture));
|
return std::shared_ptr<GSTexture>(texture, [](GSTexture* tex) { g_gs_device->Recycle(tex); });
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<HostDisplayTexture> ImGuiFullscreen::LoadTexture(const char* path)
|
std::shared_ptr<GSTexture> ImGuiFullscreen::LoadTexture(const char* path)
|
||||||
{
|
{
|
||||||
std::optional<Common::RGBA8Image> image(LoadTextureImage(path));
|
std::optional<Common::RGBA8Image> image(LoadTextureImage(path));
|
||||||
if (image.has_value())
|
if (image.has_value())
|
||||||
{
|
{
|
||||||
std::shared_ptr<HostDisplayTexture> ret(UploadTexture(path, image.value()));
|
std::shared_ptr<GSTexture> ret(UploadTexture(path, image.value()));
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -305,21 +313,21 @@ std::shared_ptr<HostDisplayTexture> ImGuiFullscreen::LoadTexture(const char* pat
|
||||||
return s_placeholder_texture;
|
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)
|
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));
|
tex_ptr = s_texture_cache.Insert(name, std::move(tex));
|
||||||
}
|
}
|
||||||
|
|
||||||
return tex_ptr->get();
|
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)
|
if (!tex_ptr)
|
||||||
{
|
{
|
||||||
// insert the placeholder
|
// insert the placeholder
|
||||||
|
@ -348,7 +356,7 @@ void ImGuiFullscreen::UploadAsyncTextures()
|
||||||
s_texture_upload_queue.pop_front();
|
s_texture_upload_queue.pop_front();
|
||||||
lock.unlock();
|
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)
|
if (tex)
|
||||||
s_texture_cache.Insert(std::move(it.first), std::move(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);
|
const ImVec2 badge_max(badge_min.x + badge_size, badge_min.y + badge_size);
|
||||||
if (!notif.badge_path.empty())
|
if (!notif.badge_path.empty())
|
||||||
{
|
{
|
||||||
HostDisplayTexture* tex = GetCachedTexture(notif.badge_path.c_str());
|
GSTexture* tex = GetCachedTexture(notif.badge_path.c_str());
|
||||||
if (tex)
|
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);
|
const ImVec2 title_min(badge_max.x + horizontal_spacing, box_min.y + vertical_padding);
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class HostDisplayTexture;
|
class GSTexture;
|
||||||
|
|
||||||
namespace ImGuiFullscreen
|
namespace ImGuiFullscreen
|
||||||
{
|
{
|
||||||
|
@ -116,10 +116,10 @@ namespace ImGuiFullscreen
|
||||||
void Shutdown(bool clear_state);
|
void Shutdown(bool clear_state);
|
||||||
|
|
||||||
/// Texture cache.
|
/// Texture cache.
|
||||||
const std::shared_ptr<HostDisplayTexture>& GetPlaceholderTexture();
|
const std::shared_ptr<GSTexture>& GetPlaceholderTexture();
|
||||||
std::shared_ptr<HostDisplayTexture> LoadTexture(const char* path);
|
std::shared_ptr<GSTexture> LoadTexture(const char* path);
|
||||||
HostDisplayTexture* GetCachedTexture(const char* name);
|
GSTexture* GetCachedTexture(const char* name);
|
||||||
HostDisplayTexture* GetCachedTextureAsync(const char* name);
|
GSTexture* GetCachedTextureAsync(const char* name);
|
||||||
bool InvalidateCachedTexture(const std::string& path);
|
bool InvalidateCachedTexture(const std::string& path);
|
||||||
void UploadAsyncTextures();
|
void UploadAsyncTextures();
|
||||||
|
|
||||||
|
|
|
@ -37,8 +37,8 @@
|
||||||
#include "Frontend/InputManager.h"
|
#include "Frontend/InputManager.h"
|
||||||
#include "GS.h"
|
#include "GS.h"
|
||||||
#include "GS/GS.h"
|
#include "GS/GS.h"
|
||||||
|
#include "GS/Renderers/Common/GSDevice.h"
|
||||||
#include "Host.h"
|
#include "Host.h"
|
||||||
#include "HostDisplay.h"
|
|
||||||
#include "IconsFontAwesome5.h"
|
#include "IconsFontAwesome5.h"
|
||||||
#include "PerformanceMetrics.h"
|
#include "PerformanceMetrics.h"
|
||||||
#include "Recording/InputRecording.h"
|
#include "Recording/InputRecording.h"
|
||||||
|
@ -90,40 +90,32 @@ bool ImGuiManager::Initialize()
|
||||||
return false;
|
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();
|
ImGui::CreateContext();
|
||||||
|
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
io.IniFilename = nullptr;
|
io.IniFilename = nullptr;
|
||||||
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_HasGamepad;
|
||||||
io.BackendUsingLegacyKeyArrays = 0;
|
io.BackendUsingLegacyKeyArrays = 0;
|
||||||
io.BackendUsingLegacyNavInputArray = 0;
|
io.BackendUsingLegacyNavInputArray = 0;
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
|
||||||
|
|
||||||
io.DisplayFramebufferScale = ImVec2(1, 1); // We already scale things ourselves, this would double-apply scaling
|
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.x = static_cast<float>(g_gs_device->GetWindowWidth());
|
||||||
io.DisplaySize.y = static_cast<float>(g_host_display->GetWindowHeight());
|
io.DisplaySize.y = static_cast<float>(g_gs_device->GetWindowHeight());
|
||||||
|
|
||||||
SetKeyMap();
|
SetKeyMap();
|
||||||
SetStyle();
|
SetStyle();
|
||||||
|
|
||||||
const bool add_fullscreen_fonts = s_fullscreen_ui_was_initialized;
|
const bool add_fullscreen_fonts = s_fullscreen_ui_was_initialized;
|
||||||
pxAssertRel(!FullscreenUI::IsInitialized(), "Fullscreen UI is not initialized on ImGui init");
|
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");
|
Host::ReportErrorAsync("ImGuiManager", "Failed to create ImGui font text");
|
||||||
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();
|
|
||||||
ImGui::DestroyContext();
|
ImGui::DestroyContext();
|
||||||
UnloadFontData();
|
UnloadFontData();
|
||||||
return false;
|
return false;
|
||||||
|
@ -143,7 +135,7 @@ bool ImGuiManager::Initialize()
|
||||||
|
|
||||||
bool ImGuiManager::InitializeFullscreenUI()
|
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;
|
return s_fullscreen_ui_was_initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,8 +146,6 @@ void ImGuiManager::Shutdown(bool clear_state)
|
||||||
if (clear_state)
|
if (clear_state)
|
||||||
s_fullscreen_ui_was_initialized = false;
|
s_fullscreen_ui_was_initialized = false;
|
||||||
|
|
||||||
if (g_host_display)
|
|
||||||
g_host_display->DestroyImGuiContext();
|
|
||||||
if (ImGui::GetCurrentContext())
|
if (ImGui::GetCurrentContext())
|
||||||
ImGui::DestroyContext();
|
ImGui::DestroyContext();
|
||||||
|
|
||||||
|
@ -170,8 +160,8 @@ void ImGuiManager::Shutdown(bool clear_state)
|
||||||
|
|
||||||
void ImGuiManager::WindowResized()
|
void ImGuiManager::WindowResized()
|
||||||
{
|
{
|
||||||
const u32 new_width = g_host_display ? g_host_display->GetWindowWidth() : 0;
|
const u32 new_width = g_gs_device ? g_gs_device->GetWindowWidth() : 0;
|
||||||
const u32 new_height = g_host_display ? g_host_display->GetWindowHeight() : 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));
|
ImGui::GetIO().DisplaySize = ImVec2(static_cast<float>(new_width), static_cast<float>(new_height));
|
||||||
|
|
||||||
|
@ -184,7 +174,7 @@ void ImGuiManager::WindowResized()
|
||||||
|
|
||||||
void ImGuiManager::UpdateScale()
|
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);
|
const float scale = std::max(window_scale * (EmuConfig.GS.OsdScale / 100.0f), 0.5f);
|
||||||
|
|
||||||
if (scale == s_global_scale && (!HasFullscreenFonts() || !ImGuiFullscreen::UpdateLayoutScale()))
|
if (scale == s_global_scale && (!HasFullscreenFonts() || !ImGuiFullscreen::UpdateLayoutScale()))
|
||||||
|
@ -199,7 +189,7 @@ void ImGuiManager::UpdateScale()
|
||||||
if (!AddImGuiFonts(HasFullscreenFonts()))
|
if (!AddImGuiFonts(HasFullscreenFonts()))
|
||||||
pxFailRel("Failed to create ImGui font text");
|
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");
|
pxFailRel("Failed to recreate font texture after scale+resize");
|
||||||
|
|
||||||
NewFrame();
|
NewFrame();
|
||||||
|
@ -483,7 +473,7 @@ bool ImGuiManager::AddFullscreenFontsIfMissing()
|
||||||
AddImGuiFonts(false);
|
AddImGuiFonts(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_host_display->UpdateImGuiFontTexture();
|
g_gs_device->UpdateImGuiFontTexture();
|
||||||
NewFrame();
|
NewFrame();
|
||||||
|
|
||||||
return HasFullscreenFonts();
|
return HasFullscreenFonts();
|
||||||
|
|
|
@ -43,7 +43,6 @@
|
||||||
#include "GS/GSCapture.h"
|
#include "GS/GSCapture.h"
|
||||||
#include "GS/GSVector.h"
|
#include "GS/GSVector.h"
|
||||||
#include "Host.h"
|
#include "Host.h"
|
||||||
#include "HostDisplay.h"
|
|
||||||
#include "IconsFontAwesome5.h"
|
#include "IconsFontAwesome5.h"
|
||||||
#include "PerformanceMetrics.h"
|
#include "PerformanceMetrics.h"
|
||||||
#include "PAD/Host/PAD.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();
|
|
396
pcsx2/GS/GS.cpp
396
pcsx2/GS/GS.cpp
|
@ -32,8 +32,8 @@
|
||||||
#include "common/StringUtil.h"
|
#include "common/StringUtil.h"
|
||||||
#include "pcsx2/Config.h"
|
#include "pcsx2/Config.h"
|
||||||
#include "pcsx2/Counters.h"
|
#include "pcsx2/Counters.h"
|
||||||
|
#include "pcsx2/Frontend/ImGuiManager.h"
|
||||||
#include "pcsx2/Host.h"
|
#include "pcsx2/Host.h"
|
||||||
#include "pcsx2/HostDisplay.h"
|
|
||||||
#include "pcsx2/HostSettings.h"
|
#include "pcsx2/HostSettings.h"
|
||||||
#include "pcsx2/Frontend/FullscreenUI.h"
|
#include "pcsx2/Frontend/FullscreenUI.h"
|
||||||
#include "pcsx2/Frontend/InputManager.h"
|
#include "pcsx2/Frontend/InputManager.h"
|
||||||
|
@ -106,27 +106,6 @@ void GSshutdown()
|
||||||
GSJoinSnapshotThreads();
|
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)
|
static RenderAPI GetAPIForRenderer(GSRendererType renderer)
|
||||||
{
|
{
|
||||||
switch (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
|
#ifdef _WIN32
|
||||||
case RenderAPI::D3D11:
|
case RenderAPI::D3D11:
|
||||||
|
@ -188,49 +211,93 @@ static bool DoGSOpen(GSRendererType renderer, u8* basemem)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
default:
|
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;
|
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)
|
||||||
{
|
{
|
||||||
g_gs_device->Destroy();
|
okay = ImGuiManager::Initialize();
|
||||||
g_gs_device.reset();
|
if (!okay)
|
||||||
return false;
|
Console.Error("Failed to initialize ImGuiManager");
|
||||||
}
|
|
||||||
|
|
||||||
if (!g_gs_renderer)
|
|
||||||
{
|
|
||||||
if (renderer == GSRendererType::Null)
|
|
||||||
{
|
|
||||||
g_gs_renderer = std::make_unique<GSRendererNull>();
|
|
||||||
}
|
|
||||||
else if (renderer != GSRendererType::SW)
|
|
||||||
{
|
|
||||||
g_gs_renderer = std::make_unique<GSRendererHW>();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
g_gs_renderer = std::unique_ptr<GSRenderer>(MULTI_ISA_SELECT(makeGSRendererSW)(GSConfig.SWExtraThreads));
|
|
||||||
}
|
|
||||||
|
|
||||||
g_gs_renderer->SetRegsMem(basemem);
|
|
||||||
g_gs_renderer->ResetPCRTC();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.Warning("(DoGSOpen) Using existing renderer.");
|
Console.Error("Failed to create GS device");
|
||||||
}
|
}
|
||||||
|
|
||||||
GSConfig.OsdShowGPU = EmuConfig.GS.OsdShowGPU && g_host_display->SetGPUTimingEnabled(true);
|
if (!okay)
|
||||||
|
{
|
||||||
|
ImGuiManager::Shutdown(clear_state_on_fail);
|
||||||
|
g_gs_device->Destroy();
|
||||||
|
g_gs_device.reset();
|
||||||
|
Host::ReleaseRenderWindow();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
g_perfmon.Reset();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config)
|
static void CloseGSDevice(bool clear_state)
|
||||||
{
|
{
|
||||||
Console.WriteLn("Reopening GS with %s display", recreate_display ? "new" : "existing");
|
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)
|
||||||
|
{
|
||||||
|
g_gs_renderer = std::make_unique<GSRendererNull>();
|
||||||
|
}
|
||||||
|
else if (renderer != GSRendererType::SW)
|
||||||
|
{
|
||||||
|
g_gs_renderer = std::make_unique<GSRendererHW>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_gs_renderer = std::unique_ptr<GSRenderer>(MULTI_ISA_SELECT(makeGSRendererSW)(GSConfig.SWExtraThreads));
|
||||||
|
}
|
||||||
|
|
||||||
|
g_gs_renderer->SetRegsMem(basemem);
|
||||||
|
g_gs_renderer->ResetPCRTC();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CloseGSRenderer()
|
||||||
|
{
|
||||||
|
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)
|
if (recreate_renderer)
|
||||||
g_gs_renderer->Flush(GSState::GSFlushReason::GSREOPEN);
|
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)
|
if (GSConfig.UserHacks_ReadTCOnClose)
|
||||||
g_gs_renderer->ReadbackTextureCache();
|
g_gs_renderer->ReadbackTextureCache();
|
||||||
|
|
||||||
|
u8* basemem = g_gs_renderer->GetRegsMem();
|
||||||
|
const u32 gamecrc = g_gs_renderer->GetGameCRC();
|
||||||
|
|
||||||
freezeData fd = {};
|
freezeData fd = {};
|
||||||
std::unique_ptr<u8[]> fd_data;
|
std::unique_ptr<u8[]> fd_data;
|
||||||
if (recreate_renderer)
|
if (recreate_renderer)
|
||||||
|
@ -255,6 +325,8 @@ bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::
|
||||||
Console.Error("(GSreopen) Failed to freeze GS");
|
Console.Error("(GSreopen) Failed to freeze GS");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CloseGSRenderer();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -263,66 +335,30 @@ bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::
|
||||||
g_gs_renderer->PurgePool();
|
g_gs_renderer->PurgePool();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recreate_display)
|
if (recreate_device)
|
||||||
{
|
{
|
||||||
g_gs_device->ResetAPIState();
|
CloseGSDevice(false);
|
||||||
if (Host::BeginPresentFrame(true) == HostDisplay::PresentResult::OK)
|
|
||||||
Host::EndPresentFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
u8* basemem = g_gs_renderer->GetRegsMem();
|
if (!OpenGSDevice(GSConfig.Renderer, false) || (recreate_renderer && !OpenGSRenderer(GSConfig.Renderer, basemem)))
|
||||||
const u32 gamecrc = g_gs_renderer->GetGameCRC();
|
|
||||||
if (recreate_renderer)
|
|
||||||
{
|
|
||||||
GSTextureReplacements::Shutdown();
|
|
||||||
g_gs_renderer->Destroy();
|
|
||||||
g_gs_renderer.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
g_gs_device->Destroy();
|
|
||||||
g_gs_device.reset();
|
|
||||||
|
|
||||||
if (recreate_display)
|
|
||||||
{
|
|
||||||
Host::ReleaseHostDisplay(false);
|
|
||||||
if (!Host::AcquireHostDisplay(GetAPIForRenderer(GSConfig.Renderer), false))
|
|
||||||
{
|
{
|
||||||
Console.Error("(GSreopen) Failed to reacquire host display");
|
Host::AddKeyedOSDMessage(
|
||||||
|
"GSReopenFailed", "Failed to reopen, restoring old configuration.", Host::OSD_CRITICAL_ERROR_DURATION);
|
||||||
|
|
||||||
// try to get the old one back
|
CloseGSDevice(false);
|
||||||
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;
|
GSConfig = old_config;
|
||||||
}
|
if (!OpenGSDevice(GSConfig.Renderer, false) || (recreate_renderer && !OpenGSRenderer(GSConfig.Renderer, basemem)))
|
||||||
}
|
|
||||||
|
|
||||||
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)");
|
pxFailRel("Failed to reopen GS on old config");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Host::AddKeyedOSDMessage("GSReopenFailed","Failed to reopen, restoring old configuration.", Host::OSD_CRITICAL_ERROR_DURATION);
|
else if (recreate_renderer)
|
||||||
GSConfig = old_config;
|
{
|
||||||
if (!DoGSOpen(GSConfig.Renderer, basemem))
|
if (!OpenGSRenderer(GSConfig.Renderer, basemem))
|
||||||
{
|
{
|
||||||
pxFailRel("Failed to reopen GS on old config");
|
Console.Error("(GSreopen) Failed to create new renderer");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -349,21 +385,32 @@ bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* b
|
||||||
GSConfig = config;
|
GSConfig = config;
|
||||||
GSConfig.Renderer = renderer;
|
GSConfig.Renderer = renderer;
|
||||||
|
|
||||||
if (!Host::AcquireHostDisplay(GetAPIForRenderer(renderer), true))
|
bool res = OpenGSDevice(renderer, true);
|
||||||
|
if (res)
|
||||||
{
|
{
|
||||||
Console.Error("Failed to acquire host display");
|
res = OpenGSRenderer(renderer, basemem);
|
||||||
return false;
|
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 false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GSclose()
|
||||||
|
{
|
||||||
|
CloseGSRenderer();
|
||||||
|
CloseGSDevice(true);
|
||||||
|
}
|
||||||
|
|
||||||
void GSreset(bool hardware_reset)
|
void GSreset(bool hardware_reset)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -539,7 +586,7 @@ void GSPresentCurrentFrame()
|
||||||
|
|
||||||
void GSThrottlePresentation()
|
void GSThrottlePresentation()
|
||||||
{
|
{
|
||||||
if (g_host_display->GetVsyncMode() != VsyncMode::Off)
|
if (g_gs_device->GetVsyncMode() != VsyncMode::Off)
|
||||||
{
|
{
|
||||||
// Let vsync take care of throttling.
|
// Let vsync take care of throttling.
|
||||||
return;
|
return;
|
||||||
|
@ -547,7 +594,7 @@ void GSThrottlePresentation()
|
||||||
|
|
||||||
// Manually throttle presentation when vsync isn't enabled, so we don't try to render the
|
// 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.
|
// 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 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));
|
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);
|
Threading::SleepUntil(s_next_manual_present_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSsetGameCRC(u32 crc)
|
void GSSetGameCRC(u32 crc)
|
||||||
{
|
{
|
||||||
g_gs_renderer->SetGameCRC(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()
|
GSVideoMode GSgetDisplayMode()
|
||||||
{
|
{
|
||||||
GSRenderer* gs = g_gs_renderer.get();
|
GSRenderer* gs = g_gs_renderer.get();
|
||||||
|
@ -594,7 +726,7 @@ void GSgetInternalResolution(int* width, int* height)
|
||||||
void GSgetStats(std::string& info)
|
void GSgetStats(std::string& info)
|
||||||
{
|
{
|
||||||
GSPerfMon& pm = g_perfmon;
|
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)
|
if (GSConfig.Renderer == GSRendererType::SW)
|
||||||
{
|
{
|
||||||
const double fps = GetVerticalFrequency();
|
const double fps = GetVerticalFrequency();
|
||||||
|
@ -661,7 +793,7 @@ void GSgetTitleStats(std::string& info)
|
||||||
static constexpr const char* deinterlace_modes[] = {
|
static constexpr const char* deinterlace_modes[] = {
|
||||||
"Automatic", "None", "Weave tff", "Weave bff", "Bob tff", "Bob bff", "Blend tff", "Blend bff", "Adaptive tff", "Adaptive bff"};
|
"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* hw_sw_name = (GSConfig.Renderer == GSRendererType::Null) ? " Null" : (GSConfig.UseHardwareRenderer() ? " HW" : " SW");
|
||||||
const char* deinterlace_mode = deinterlace_modes[static_cast<int>(GSConfig.InterlaceMode)];
|
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.
|
// Handle OSD scale changes by pushing a window resize through.
|
||||||
if (new_config.OsdScale != old_config.OsdScale)
|
if (new_config.OsdScale != old_config.OsdScale)
|
||||||
{
|
ImGuiManager::WindowResized();
|
||||||
g_gs_device->ResetAPIState();
|
|
||||||
Host::ResizeHostDisplay(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight(), g_host_display->GetWindowScale());
|
|
||||||
g_gs_device->RestoreAPIState();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options which need a full teardown/recreate.
|
// Options which need a full teardown/recreate.
|
||||||
if (!GSConfig.RestartOptionsAreEqual(old_config))
|
if (!GSConfig.RestartOptionsAreEqual(old_config))
|
||||||
{
|
{
|
||||||
RenderAPI existing_api = g_host_display->GetRenderAPI();
|
if (!GSreopen(true, true, old_config))
|
||||||
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))
|
|
||||||
pxFailRel("Failed to do full GS reopen");
|
pxFailRel("Failed to do full GS reopen");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -771,7 +887,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
|
||||||
|
|
||||||
if (GSConfig.OsdShowGPU != old_config.OsdShowGPU)
|
if (GSConfig.OsdShowGPU != old_config.OsdShowGPU)
|
||||||
{
|
{
|
||||||
if (!g_host_display->SetGPUTimingEnabled(GSConfig.OsdShowGPU))
|
if (!g_gs_device->SetGPUTimingEnabled(GSConfig.OsdShowGPU))
|
||||||
GSConfig.OsdShowGPU = false;
|
GSConfig.OsdShowGPU = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -784,7 +900,7 @@ void GSSwitchRenderer(GSRendererType new_renderer)
|
||||||
if (!g_gs_renderer || GSConfig.Renderer == new_renderer)
|
if (!g_gs_renderer || GSConfig.Renderer == new_renderer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
RenderAPI existing_api = g_host_display->GetRenderAPI();
|
RenderAPI existing_api = g_gs_device->GetRenderAPI();
|
||||||
if (existing_api == RenderAPI::OpenGLES)
|
if (existing_api == RenderAPI::OpenGLES)
|
||||||
existing_api = RenderAPI::OpenGL;
|
existing_api = RenderAPI::OpenGL;
|
||||||
|
|
||||||
|
@ -796,22 +912,6 @@ void GSSwitchRenderer(GSRendererType new_renderer)
|
||||||
pxFailRel("Failed to reopen GS for renderer switch.");
|
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,
|
bool GSSaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,
|
||||||
u32* width, u32* height, std::vector<u32>* pixels)
|
u32* width, u32* height, std::vector<u32>* pixels)
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,8 +20,21 @@
|
||||||
#include "pcsx2/Config.h"
|
#include "pcsx2/Config.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
enum class RenderAPI
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
D3D11,
|
||||||
|
Metal,
|
||||||
|
D3D12,
|
||||||
|
Vulkan,
|
||||||
|
OpenGL,
|
||||||
|
OpenGLES
|
||||||
|
};
|
||||||
|
|
||||||
// ST_WRITE is defined in libc, avoid this
|
// ST_WRITE is defined in libc, avoid this
|
||||||
enum stateType
|
enum stateType
|
||||||
|
@ -42,6 +55,13 @@ enum class GSVideoMode : u8
|
||||||
HDTV_1080I
|
HDTV_1080I
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class GSDisplayAlignment
|
||||||
|
{
|
||||||
|
Center,
|
||||||
|
LeftOrTop,
|
||||||
|
RightOrBottom
|
||||||
|
};
|
||||||
|
|
||||||
extern Pcsx2Config::GSOptions GSConfig;
|
extern Pcsx2Config::GSOptions GSConfig;
|
||||||
|
|
||||||
class HostDisplay;
|
class HostDisplay;
|
||||||
|
@ -53,7 +73,7 @@ s16 GSLookupBeforeDrawFunctionId(const std::string_view& name);
|
||||||
int GSinit();
|
int GSinit();
|
||||||
void GSshutdown();
|
void GSshutdown();
|
||||||
bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* basemem);
|
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 GSreset(bool hardware_reset);
|
||||||
void GSclose();
|
void GSclose();
|
||||||
void GSgifSoftReset(u32 mask);
|
void GSgifSoftReset(u32 mask);
|
||||||
|
@ -74,8 +94,15 @@ bool GSBeginCapture(std::string filename);
|
||||||
void GSEndCapture();
|
void GSEndCapture();
|
||||||
void GSPresentCurrentFrame();
|
void GSPresentCurrentFrame();
|
||||||
void GSThrottlePresentation();
|
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();
|
GSVideoMode GSgetDisplayMode();
|
||||||
void GSgetInternalResolution(int* width, int* height);
|
void GSgetInternalResolution(int* width, int* height);
|
||||||
void GSgetStats(std::string& info);
|
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 GSUpdateConfig(const Pcsx2Config::GSOptions& new_config);
|
||||||
void GSSwitchRenderer(GSRendererType new_renderer);
|
void GSSwitchRenderer(GSRendererType new_renderer);
|
||||||
void GSResetAPIState();
|
|
||||||
void GSRestoreAPIState();
|
|
||||||
bool GSSaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,
|
bool GSSaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,
|
||||||
u32* width, u32* height, std::vector<u32>* pixels);
|
u32* width, u32* height, std::vector<u32>* pixels);
|
||||||
void GSJoinSnapshotThreads();
|
void GSJoinSnapshotThreads();
|
||||||
|
@ -100,3 +125,28 @@ struct GSError
|
||||||
struct GSRecoverableError : 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 "Host.h"
|
||||||
#include "common/StringUtil.h"
|
#include "common/StringUtil.h"
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
const char* shaderName(ShaderConvert value)
|
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);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDevice::Destroy()
|
void GSDevice::Destroy()
|
||||||
{
|
{
|
||||||
|
if (m_imgui_font)
|
||||||
|
{
|
||||||
|
Recycle(m_imgui_font);
|
||||||
|
m_imgui_font = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
ClearCurrent();
|
ClearCurrent();
|
||||||
PurgePool();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return WindowInfo::QueryRefreshRateForWindow(m_window_info, refresh_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDevice::RestoreAPIState()
|
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)
|
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;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<GSDownloadTexture> GSDevice::CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format)
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void GSDevice::Recycle(GSTexture* t)
|
void GSDevice::Recycle(GSTexture* t)
|
||||||
{
|
{
|
||||||
if (!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()
|
void GSDevice::AgePool()
|
||||||
{
|
{
|
||||||
m_frame++;
|
m_frame++;
|
||||||
|
@ -229,10 +338,6 @@ void GSDevice::PurgePool()
|
||||||
m_pool_memory_usage = 0;
|
m_pool_memory_usage = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDevice::ClearSamplerCache()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
GSTexture* GSDevice::CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear)
|
GSTexture* GSDevice::CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear)
|
||||||
{
|
{
|
||||||
return FetchSurface(GSTexture::Type::RenderTarget, w, h, 1, format, clear, true);
|
return FetchSurface(GSTexture::Type::RenderTarget, w, h, 1, format, clear, true);
|
||||||
|
|
|
@ -17,15 +17,14 @@
|
||||||
|
|
||||||
#include "common/HashCombine.h"
|
#include "common/HashCombine.h"
|
||||||
#include "common/WindowInfo.h"
|
#include "common/WindowInfo.h"
|
||||||
#include "GSFastList.h"
|
#include "GS/GS.h"
|
||||||
#include "GSTexture.h"
|
#include "GS/Renderers/Common/GSFastList.h"
|
||||||
#include "GSVertex.h"
|
#include "GS/Renderers/Common/GSTexture.h"
|
||||||
|
#include "GS/Renderers/Common/GSVertex.h"
|
||||||
#include "GS/GSAlignedClass.h"
|
#include "GS/GSAlignedClass.h"
|
||||||
#include "GS/GSExtra.h"
|
#include "GS/GSExtra.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
class HostDisplay;
|
|
||||||
|
|
||||||
enum class ShaderConvert
|
enum class ShaderConvert
|
||||||
{
|
{
|
||||||
COPY = 0,
|
COPY = 0,
|
||||||
|
@ -694,6 +693,22 @@ struct alignas(16) GSHWDrawConfig
|
||||||
class GSDevice : public GSAlignedClass<32>
|
class GSDevice : public GSAlignedClass<32>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum class PresentResult
|
||||||
|
{
|
||||||
|
OK,
|
||||||
|
FrameSkipped,
|
||||||
|
DeviceLost
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class DebugMessageCategory
|
||||||
|
{
|
||||||
|
Cache,
|
||||||
|
Reg,
|
||||||
|
Debug,
|
||||||
|
Message,
|
||||||
|
Performance
|
||||||
|
};
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
struct FeatureSupport
|
struct FeatureSupport
|
||||||
{
|
{
|
||||||
|
@ -747,6 +762,7 @@ public:
|
||||||
private:
|
private:
|
||||||
FastList<GSTexture*> m_pool;
|
FastList<GSTexture*> m_pool;
|
||||||
u64 m_pool_memory_usage = 0;
|
u64 m_pool_memory_usage = 0;
|
||||||
|
|
||||||
static const std::array<HWBlend, 3*3*3*3> m_blendMap;
|
static const std::array<HWBlend, 3*3*3*3> m_blendMap;
|
||||||
static const std::array<u8, 16> m_replaceDualSrcBlendMap;
|
static const std::array<u8, 16> m_replaceDualSrcBlendMap;
|
||||||
|
|
||||||
|
@ -756,6 +772,11 @@ protected:
|
||||||
static constexpr u32 MAX_POOLED_TEXTURES = 300;
|
static constexpr u32 MAX_POOLED_TEXTURES = 300;
|
||||||
static constexpr u32 NUM_CAS_CONSTANTS = 12; // 8 plus src offset x/y, 16 byte alignment
|
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_merge = nullptr;
|
||||||
GSTexture* m_weavebob = nullptr;
|
GSTexture* m_weavebob = nullptr;
|
||||||
GSTexture* m_blend = 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 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 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 DoFXAA(GSTexture* sTex, GSTexture* dTex) = 0;
|
||||||
virtual void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) {}
|
virtual void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) = 0;
|
||||||
|
|
||||||
/// Resolves CAS shader includes for the specified source.
|
/// Resolves CAS shader includes for the specified source.
|
||||||
static bool GetCASShaderSource(std::string* source);
|
static bool GetCASShaderSource(std::string* source);
|
||||||
|
@ -794,58 +815,111 @@ public:
|
||||||
GSDevice();
|
GSDevice();
|
||||||
virtual ~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 unsigned int GetFrameNumber() const { return m_frame; }
|
||||||
__fi u64 GetPoolMemoryUsage() const { return m_pool_memory_usage; }
|
__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);
|
void Recycle(GSTexture* t);
|
||||||
|
|
||||||
enum
|
/// Returns true if it's an OpenGL-based renderer.
|
||||||
{
|
bool UsesLowerLeftOrigin() const;
|
||||||
Windowed,
|
|
||||||
Fullscreen,
|
|
||||||
DontCare
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class DebugMessageCategory
|
/// Recreates the font, call when the window scaling changes.
|
||||||
{
|
bool UpdateImGuiFontTexture();
|
||||||
Cache,
|
|
||||||
Reg,
|
|
||||||
Debug,
|
|
||||||
Message,
|
|
||||||
Performance
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual bool Create();
|
virtual bool Create(const WindowInfo& wi, VsyncMode vsync);
|
||||||
virtual void Destroy();
|
virtual void Destroy();
|
||||||
|
|
||||||
virtual void ResetAPIState();
|
/// Returns the graphics API used by this device.
|
||||||
virtual void RestoreAPIState();
|
virtual RenderAPI GetRenderAPI() const = 0;
|
||||||
|
|
||||||
virtual void ClearRenderTarget(GSTexture* t, const GSVector4& c) {}
|
/// Returns true if we have a window we're rendering into.
|
||||||
virtual void ClearRenderTarget(GSTexture* t, u32 c) {}
|
virtual bool HasSurface() const = 0;
|
||||||
virtual void InvalidateRenderTarget(GSTexture* t) {}
|
|
||||||
virtual void ClearDepth(GSTexture* t) {}
|
|
||||||
virtual void ClearStencil(GSTexture* t, u8 c) {}
|
|
||||||
|
|
||||||
virtual void PushDebugGroup(const char* fmt, ...) {}
|
/// Destroys the surface we're currently drawing to.
|
||||||
virtual void PopDebugGroup() {}
|
virtual void DestroySurface() = 0;
|
||||||
virtual void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) {}
|
|
||||||
|
/// 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* CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear = true);
|
||||||
GSTexture* CreateDepthStencil(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* CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse = false);
|
||||||
GSTexture::Format GetDefaultTextureFormat(GSTexture::Type type);
|
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 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) {}
|
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) {}
|
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);
|
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.
|
/// 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.
|
/// 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.
|
/// 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);
|
static void SortMultiStretchRects(MultiStretchRect* rects, u32 num_rects);
|
||||||
|
|
||||||
/// Updates a GPU CLUT texture from a source texture.
|
/// 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.
|
/// 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 RenderHW(GSHWDrawConfig& config) = 0;
|
||||||
virtual void ConvertToIndexedTexture(GSTexture* sTex, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) {}
|
|
||||||
|
|
||||||
virtual void RenderHW(GSHWDrawConfig& config) {}
|
virtual void ClearSamplerCache() = 0;
|
||||||
|
|
||||||
__fi FeatureSupport Features() const { return m_features; }
|
|
||||||
__fi GSTexture* GetCurrent() const { return m_current; }
|
|
||||||
|
|
||||||
void ClearCurrent();
|
void ClearCurrent();
|
||||||
void Merge(GSTexture* sTex[3], GSVector4* sRect, GSVector4* dRect, const GSVector2i& fs, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c);
|
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 AgePool();
|
||||||
void PurgePool();
|
void PurgePool();
|
||||||
|
|
||||||
virtual void ClearSamplerCache();
|
|
||||||
|
|
||||||
__fi static constexpr bool IsDualSourceBlendFactor(u8 factor)
|
__fi static constexpr bool IsDualSourceBlendFactor(u8 factor)
|
||||||
{
|
{
|
||||||
return (factor == SRC1_ALPHA || factor == INV_SRC1_ALPHA || factor == SRC1_COLOR
|
return (factor == SRC1_ALPHA || factor == INV_SRC1_ALPHA || factor == SRC1_COLOR
|
||||||
|
|
|
@ -14,22 +14,27 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "PrecompiledHeader.h"
|
#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/GSCapture.h"
|
||||||
#include "GS/GSGL.h"
|
#include "GS/GSGL.h"
|
||||||
#include "GSDumpReplayer.h"
|
#include "GSDumpReplayer.h"
|
||||||
#include "Host.h"
|
#include "Host.h"
|
||||||
#include "HostDisplay.h"
|
|
||||||
#include "PerformanceMetrics.h"
|
#include "PerformanceMetrics.h"
|
||||||
#include "pcsx2/Config.h"
|
#include "pcsx2/Config.h"
|
||||||
#include "IconsFontAwesome5.h"
|
#include "IconsFontAwesome5.h"
|
||||||
#include "VMManager.h"
|
#include "VMManager.h"
|
||||||
|
|
||||||
#include "common/FileSystem.h"
|
#include "common/FileSystem.h"
|
||||||
#include "common/Image.h"
|
#include "common/Image.h"
|
||||||
#include "common/Path.h"
|
#include "common/Path.h"
|
||||||
#include "common/StringUtil.h"
|
#include "common/StringUtil.h"
|
||||||
#include "common/Timer.h"
|
#include "common/Timer.h"
|
||||||
|
|
||||||
#include "fmt/core.h"
|
#include "fmt/core.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <deque>
|
#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.
|
// Last time we reset the renderer due to a GPU crash, if any.
|
||||||
static Common::Timer::Value s_last_gpu_reset_time;
|
static Common::Timer::Value s_last_gpu_reset_time;
|
||||||
|
|
||||||
|
// Screen alignment
|
||||||
|
static GSDisplayAlignment s_display_alignment = GSDisplayAlignment::Center;
|
||||||
|
|
||||||
GSRenderer::GSRenderer()
|
GSRenderer::GSRenderer()
|
||||||
: m_shader_time_start(Common::Timer::GetCurrentValue())
|
: m_shader_time_start(Common::Timer::GetCurrentValue())
|
||||||
{
|
{
|
||||||
|
@ -230,9 +238,9 @@ bool GSRenderer::Merge(int field)
|
||||||
g_gs_device->FXAA();
|
g_gs_device->FXAA();
|
||||||
|
|
||||||
// Sharpens biinear at lower resolutions, almost nearest but with more uniform pixels.
|
// 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)
|
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))];
|
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_width = static_cast<float>(window_width);
|
||||||
const float f_height = static_cast<float>(window_height);
|
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()),
|
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()));
|
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_width *= static_cast<float>(fs.x) / src_rect.width();
|
||||||
t_height *= static_cast<float>(fs.y) / src_rect.height();
|
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)
|
switch (alignment)
|
||||||
{
|
{
|
||||||
case HostDisplay::Alignment::Center:
|
case GSDisplayAlignment::Center:
|
||||||
target_x = (f_width - target_width) * 0.5f;
|
target_x = (f_width - target_width) * 0.5f;
|
||||||
break;
|
break;
|
||||||
case HostDisplay::Alignment::RightOrBottom:
|
case GSDisplayAlignment::RightOrBottom:
|
||||||
target_x = (f_width - target_width);
|
target_x = (f_width - target_width);
|
||||||
break;
|
break;
|
||||||
case HostDisplay::Alignment::LeftOrTop:
|
case GSDisplayAlignment::LeftOrTop:
|
||||||
default:
|
default:
|
||||||
target_x = 0.0f;
|
target_x = 0.0f;
|
||||||
break;
|
break;
|
||||||
|
@ -368,13 +376,13 @@ static GSVector4 CalculateDrawDstRect(s32 window_width, s32 window_height, const
|
||||||
{
|
{
|
||||||
switch (alignment)
|
switch (alignment)
|
||||||
{
|
{
|
||||||
case HostDisplay::Alignment::Center:
|
case GSDisplayAlignment::Center:
|
||||||
target_y = (f_height - target_height) * 0.5f;
|
target_y = (f_height - target_height) * 0.5f;
|
||||||
break;
|
break;
|
||||||
case HostDisplay::Alignment::RightOrBottom:
|
case GSDisplayAlignment::RightOrBottom:
|
||||||
target_y = (f_height - target_height);
|
target_y = (f_height - target_height);
|
||||||
break;
|
break;
|
||||||
case HostDisplay::Alignment::LeftOrTop:
|
case GSDisplayAlignment::LeftOrTop:
|
||||||
default:
|
default:
|
||||||
target_y = 0.0f;
|
target_y = 0.0f;
|
||||||
break;
|
break;
|
||||||
|
@ -466,11 +474,21 @@ void GSJoinSnapshotThreads()
|
||||||
|
|
||||||
bool GSRenderer::BeginPresentFrame(bool frame_skip)
|
bool GSRenderer::BeginPresentFrame(bool frame_skip)
|
||||||
{
|
{
|
||||||
const HostDisplay::PresentResult result = Host::BeginPresentFrame(frame_skip);
|
Host::BeginPresentFrame();
|
||||||
if (result == HostDisplay::PresentResult::OK)
|
|
||||||
return true;
|
const GSDevice::PresentResult res = g_gs_device->BeginPresent(frame_skip);
|
||||||
else if (result == HostDisplay::PresentResult::FrameSkipped)
|
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;
|
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
|
// 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
|
// 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;
|
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)
|
void GSRenderer::VSync(u32 field, bool registers_written)
|
||||||
{
|
{
|
||||||
Flush(GSFlushReason::VSYNC);
|
Flush(GSFlushReason::VSYNC);
|
||||||
|
@ -542,10 +571,9 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
||||||
|
|
||||||
if (skip_frame)
|
if (skip_frame)
|
||||||
{
|
{
|
||||||
g_gs_device->ResetAPIState();
|
|
||||||
if (BeginPresentFrame(true))
|
if (BeginPresentFrame(true))
|
||||||
Host::EndPresentFrame();
|
EndPresentFrame();
|
||||||
g_gs_device->RestoreAPIState();
|
|
||||||
PerformanceMetrics::Update(registers_written, fb_sprite_frame, true);
|
PerformanceMetrics::Update(registers_written, fb_sprite_frame, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -564,8 +592,8 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
||||||
{
|
{
|
||||||
src_rect = CalculateDrawSrcRect(current);
|
src_rect = CalculateDrawSrcRect(current);
|
||||||
src_uv = GSVector4(src_rect) / GSVector4(current->GetSize()).xyxy();
|
src_uv = GSVector4(src_rect) / GSVector4(current->GetSize()).xyxy();
|
||||||
draw_rect = CalculateDrawDstRect(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight(),
|
draw_rect = CalculateDrawDstRect(g_gs_device->GetWindowWidth(), g_gs_device->GetWindowHeight(),
|
||||||
src_rect, current->GetSize(), g_host_display->GetDisplayAlignment(), g_host_display->UsesLowerLeftOrigin(),
|
src_rect, current->GetSize(), s_display_alignment, g_gs_device->UsesLowerLeftOrigin(),
|
||||||
GetVideoMode() == GSVideoMode::SDTV_480P || (GSConfig.PCRTCOverscan && GSConfig.PCRTCOffsets));
|
GetVideoMode() == GSVideoMode::SDTV_480P || (GSConfig.PCRTCOverscan && GSConfig.PCRTCOffsets));
|
||||||
s_last_draw_rect = draw_rect;
|
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
|
// sharpen only if the IR is higher than the display resolution
|
||||||
const bool sharpen_only = (GSConfig.CASMode == GSCASMode::SharpenOnly ||
|
const bool sharpen_only = (GSConfig.CASMode == GSCASMode::SharpenOnly ||
|
||||||
(current->GetWidth() > g_host_display->GetWindowWidth() &&
|
(current->GetWidth() > g_gs_device->GetWindowWidth() &&
|
||||||
current->GetHeight() > g_host_display->GetWindowHeight()));
|
current->GetHeight() > g_gs_device->GetWindowHeight()));
|
||||||
g_gs_device->CAS(current, src_rect, src_uv, draw_rect, sharpen_only);
|
g_gs_device->CAS(current, src_rect, src_uv, draw_rect, sharpen_only);
|
||||||
}
|
}
|
||||||
else if (!cas_log_once)
|
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 (BeginPresentFrame(false))
|
||||||
{
|
{
|
||||||
if (current && !blank_frame)
|
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);
|
s_tv_shader_indices[GSConfig.TVShader], shader_time, GSConfig.LinearPresent != GSPostBilinearMode::Off);
|
||||||
}
|
}
|
||||||
|
|
||||||
Host::EndPresentFrame();
|
EndPresentFrame();
|
||||||
|
|
||||||
if (GSConfig.OsdShowGPU)
|
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);
|
PerformanceMetrics::Update(registers_written, fb_sprite_frame, false);
|
||||||
|
|
||||||
// snapshot
|
// snapshot
|
||||||
|
@ -665,8 +692,8 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
||||||
const bool aspect_correct = (GSConfig.ScreenshotSize != GSScreenshotSize::InternalResolutionUncorrected);
|
const bool aspect_correct = (GSConfig.ScreenshotSize != GSScreenshotSize::InternalResolutionUncorrected);
|
||||||
|
|
||||||
if (g_gs_device->GetCurrent() && SaveSnapshotToMemory(
|
if (g_gs_device->GetCurrent() && SaveSnapshotToMemory(
|
||||||
internal_resolution ? 0 : g_host_display->GetWindowWidth(),
|
internal_resolution ? 0 : g_gs_device->GetWindowWidth(),
|
||||||
internal_resolution ? 0 : g_host_display->GetWindowHeight(),
|
internal_resolution ? 0 : g_gs_device->GetWindowHeight(),
|
||||||
aspect_correct, true,
|
aspect_correct, true,
|
||||||
&screenshot_width, &screenshot_height, &screenshot_pixels))
|
&screenshot_width, &screenshot_height, &screenshot_pixels))
|
||||||
{
|
{
|
||||||
|
@ -805,7 +832,6 @@ void GSRenderer::StopGSDump()
|
||||||
|
|
||||||
void GSRenderer::PresentCurrentFrame()
|
void GSRenderer::PresentCurrentFrame()
|
||||||
{
|
{
|
||||||
g_gs_device->ResetAPIState();
|
|
||||||
if (BeginPresentFrame(false))
|
if (BeginPresentFrame(false))
|
||||||
{
|
{
|
||||||
GSTexture* current = g_gs_device->GetCurrent();
|
GSTexture* current = g_gs_device->GetCurrent();
|
||||||
|
@ -813,8 +839,8 @@ void GSRenderer::PresentCurrentFrame()
|
||||||
{
|
{
|
||||||
const GSVector4i src_rect(CalculateDrawSrcRect(current));
|
const GSVector4i src_rect(CalculateDrawSrcRect(current));
|
||||||
const GSVector4 src_uv(GSVector4(src_rect) / GSVector4(current->GetSize()).xyxy());
|
const GSVector4 src_uv(GSVector4(src_rect) / GSVector4(current->GetSize()).xyxy());
|
||||||
const GSVector4 draw_rect(CalculateDrawDstRect(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight(),
|
const GSVector4 draw_rect(CalculateDrawDstRect(g_gs_device->GetWindowWidth(), g_gs_device->GetWindowHeight(),
|
||||||
src_rect, current->GetSize(), g_host_display->GetDisplayAlignment(), g_host_display->UsesLowerLeftOrigin(),
|
src_rect, current->GetSize(), s_display_alignment, g_gs_device->UsesLowerLeftOrigin(),
|
||||||
GetVideoMode() == GSVideoMode::SDTV_480P || (GSConfig.PCRTCOverscan && GSConfig.PCRTCOffsets)));
|
GetVideoMode() == GSVideoMode::SDTV_480P || (GSConfig.PCRTCOverscan && GSConfig.PCRTCOffsets)));
|
||||||
s_last_draw_rect = draw_rect;
|
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);
|
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)
|
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;
|
*display_y = rel_y / draw_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GSSetDisplayAlignment(GSDisplayAlignment alignment)
|
||||||
|
{
|
||||||
|
s_display_alignment = alignment;
|
||||||
|
}
|
||||||
|
|
||||||
bool GSRenderer::BeginCapture(std::string filename)
|
bool GSRenderer::BeginCapture(std::string filename)
|
||||||
{
|
{
|
||||||
const GSVector2i capture_resolution(GSConfig.VideoCaptureAutoResolution ?
|
const GSVector2i capture_resolution(GSConfig.VideoCaptureAutoResolution ?
|
||||||
|
@ -910,7 +940,7 @@ bool GSRenderer::SaveSnapshotToMemory(u32 window_width, u32 window_height, bool
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
draw_rect = CalculateDrawDstRect(window_width, window_height, src_rect, current->GetSize(),
|
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_width = static_cast<u32>(draw_rect.z - draw_rect.x);
|
||||||
const u32 draw_height = static_cast<u32>(draw_rect.w - draw_rect.y);
|
const u32 draw_height = static_cast<u32>(draw_rect.w - draw_rect.y);
|
||||||
|
|
|
@ -24,6 +24,7 @@ class GSRenderer : public GSState
|
||||||
private:
|
private:
|
||||||
bool Merge(int field);
|
bool Merge(int field);
|
||||||
bool BeginPresentFrame(bool frame_skip);
|
bool BeginPresentFrame(bool frame_skip);
|
||||||
|
void EndPresentFrame();
|
||||||
|
|
||||||
u64 m_shader_time_start = 0;
|
u64 m_shader_time_start = 0;
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "PrecompiledHeader.h"
|
#include "PrecompiledHeader.h"
|
||||||
|
#include "GS/Renderers/Common/GSDevice.h"
|
||||||
#include "GS/Renderers/DX11/D3D.h"
|
#include "GS/Renderers/DX11/D3D.h"
|
||||||
#include "GS/GSExtra.h"
|
#include "GS/GSExtra.h"
|
||||||
|
|
||||||
|
@ -80,6 +81,45 @@ std::vector<std::string> D3D::GetAdapterNames(IDXGIFactory5* factory)
|
||||||
return adapter_names;
|
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)
|
wil::com_ptr_nothrow<IDXGIAdapter1> D3D::GetAdapterByName(IDXGIFactory5* factory, const std::string_view& name)
|
||||||
{
|
{
|
||||||
if (name.empty())
|
if (name.empty())
|
||||||
|
|
|
@ -33,6 +33,9 @@ namespace D3D
|
||||||
// returns a list of all adapter names
|
// returns a list of all adapter names
|
||||||
std::vector<std::string> GetAdapterNames(IDXGIFactory5* factory);
|
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
|
// get an adapter based on name
|
||||||
wil::com_ptr_nothrow<IDXGIAdapter1> GetAdapterByName(IDXGIFactory5* factory, const std::string_view& 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 "common/D3D11/ShaderCache.h"
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <wil/com.h>
|
#include <wil/com.h>
|
||||||
#include <dxgi1_3.h>
|
#include <dxgi1_5.h>
|
||||||
#include <d3d11_1.h>
|
#include <d3d11_1.h>
|
||||||
|
|
||||||
struct GSVertexShader11
|
struct GSVertexShader11
|
||||||
|
@ -116,32 +116,47 @@ private:
|
||||||
MAX_SAMPLERS = 1,
|
MAX_SAMPLERS = 1,
|
||||||
VERTEX_BUFFER_SIZE = 32 * 1024 * 1024,
|
VERTEX_BUFFER_SIZE = 32 * 1024 * 1024,
|
||||||
INDEX_BUFFER_SIZE = 16 * 1024 * 1024,
|
INDEX_BUFFER_SIZE = 16 * 1024 * 1024,
|
||||||
|
NUM_TIMESTAMP_QUERIES = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
int m_d3d_texsize;
|
|
||||||
|
|
||||||
void SetFeatures();
|
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 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) final;
|
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) final;
|
void DoFXAA(GSTexture* sTex, GSTexture* dTex) override;
|
||||||
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final;
|
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) override;
|
||||||
|
|
||||||
bool CreateCASShaders();
|
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;
|
bool CreateImGuiResources();
|
||||||
wil::com_ptr_nothrow<ID3D11DeviceContext> m_ctx;
|
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<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_vb;
|
||||||
wil::com_ptr_nothrow<ID3D11Buffer> m_ib;
|
wil::com_ptr_nothrow<ID3D11Buffer> m_ib;
|
||||||
u32 m_vb_pos = 0; // bytes
|
u32 m_vb_pos = 0; // bytes
|
||||||
u32 m_ib_pos = 0; // indices/sizeof(u32)
|
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
|
struct
|
||||||
{
|
{
|
||||||
|
@ -166,6 +181,13 @@ private:
|
||||||
ID3D11DepthStencilView* dsv;
|
ID3D11DepthStencilView* dsv;
|
||||||
} m_state;
|
} 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
|
struct
|
||||||
{
|
{
|
||||||
|
@ -222,6 +244,15 @@ private:
|
||||||
wil::com_ptr_nothrow<ID3D11ComputeShader> cs_sharpen;
|
wil::com_ptr_nothrow<ID3D11ComputeShader> cs_sharpen;
|
||||||
} m_cas;
|
} 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...
|
// Shaders...
|
||||||
|
|
||||||
std::unordered_map<u32, GSVertexShader11> m_vs;
|
std::unordered_map<u32, GSVertexShader11> m_vs;
|
||||||
|
@ -237,8 +268,6 @@ private:
|
||||||
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
|
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
|
||||||
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
|
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
|
||||||
|
|
||||||
std::unique_ptr<GSTexture11> m_download_tex;
|
|
||||||
|
|
||||||
D3D11::ShaderCache m_shader_cache;
|
D3D11::ShaderCache m_shader_cache;
|
||||||
std::string m_tfx_source;
|
std::string m_tfx_source;
|
||||||
|
|
||||||
|
@ -247,13 +276,32 @@ public:
|
||||||
~GSDevice11() override;
|
~GSDevice11() override;
|
||||||
|
|
||||||
__fi static GSDevice11* GetInstance() { return static_cast<GSDevice11*>(g_gs_device.get()); }
|
__fi static GSDevice11* GetInstance() { return static_cast<GSDevice11*>(g_gs_device.get()); }
|
||||||
__fi ID3D11Device* GetD3DDevice() const { return m_dev.get(); }
|
__fi ID3D11Device1* GetD3DDevice() const { return m_dev.get(); }
|
||||||
__fi ID3D11DeviceContext* GetD3DContext() const { return m_ctx.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;
|
RenderAPI GetRenderAPI() const 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;
|
||||||
|
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 DrawPrimitive();
|
||||||
void DrawIndexedPrimitive();
|
void DrawIndexedPrimitive();
|
||||||
|
@ -261,6 +309,7 @@ public:
|
||||||
|
|
||||||
void ClearRenderTarget(GSTexture* t, const GSVector4& c) override;
|
void ClearRenderTarget(GSTexture* t, const GSVector4& c) override;
|
||||||
void ClearRenderTarget(GSTexture* t, u32 c) override;
|
void ClearRenderTarget(GSTexture* t, u32 c) override;
|
||||||
|
void InvalidateRenderTarget(GSTexture* t) override;
|
||||||
void ClearDepth(GSTexture* t) override;
|
void ClearDepth(GSTexture* t) override;
|
||||||
void ClearStencil(GSTexture* t, u8 c) override;
|
void ClearStencil(GSTexture* t, u8 c) override;
|
||||||
|
|
||||||
|
@ -268,6 +317,9 @@ public:
|
||||||
void PopDebugGroup() override;
|
void PopDebugGroup() override;
|
||||||
void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) 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 CloneTexture(GSTexture* src, GSTexture** dest, const GSVector4i& rect);
|
||||||
|
|
||||||
void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override;
|
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 IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY topology);
|
||||||
|
|
||||||
void VSSetShader(ID3D11VertexShader* vs, ID3D11Buffer* vs_cb);
|
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 PSSetShaderResources(GSTexture* sr0, GSTexture* sr1);
|
||||||
void PSSetShaderResource(int i, GSTexture* sr);
|
void PSSetShaderResource(int i, GSTexture* sr);
|
||||||
|
@ -306,7 +358,9 @@ public:
|
||||||
|
|
||||||
void OMSetDepthStencilState(ID3D11DepthStencilState* dss, u8 sref);
|
void OMSetDepthStencilState(ID3D11DepthStencilState* dss, u8 sref);
|
||||||
void OMSetBlendState(ID3D11BlendState* bs, float bf);
|
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();
|
bool CreateTextureFX();
|
||||||
void SetupVS(VSSelector sel, const GSHWDrawConfig::VSConstantBuffer* cb);
|
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 SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstantBuffer* cb, PSSamplerSelector ssel);
|
||||||
void SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, u8 afix);
|
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(); }
|
ID3D11Device1* operator->() { return m_dev.get(); }
|
||||||
operator ID3D11Device*() { return m_dev.get(); }
|
operator ID3D11Device1*() { return m_dev.get(); }
|
||||||
operator ID3D11DeviceContext*() { return m_ctx.get(); }
|
operator ID3D11DeviceContext1*() { return m_ctx.get(); }
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "PrecompiledHeader.h"
|
#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/Builders.h"
|
||||||
#include "common/D3D12/Context.h"
|
#include "common/D3D12/Context.h"
|
||||||
#include "common/D3D12/ShaderCache.h"
|
#include "common/D3D12/ShaderCache.h"
|
||||||
|
@ -21,15 +31,10 @@
|
||||||
#include "common/Align.h"
|
#include "common/Align.h"
|
||||||
#include "common/ScopedGuard.h"
|
#include "common/ScopedGuard.h"
|
||||||
#include "common/StringUtil.h"
|
#include "common/StringUtil.h"
|
||||||
|
|
||||||
#include "D3D12MemAlloc.h"
|
#include "D3D12MemAlloc.h"
|
||||||
#include "GS.h"
|
#include "imgui.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 <sstream>
|
#include <sstream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
|
@ -94,16 +99,52 @@ D3D_SHADER_MACRO* GSDevice12::ShaderMacro::GetPtr(void)
|
||||||
return (D3D_SHADER_MACRO*)mout.data();
|
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() {}
|
RenderAPI GSDevice12::GetRenderAPI() const
|
||||||
|
|
||||||
bool GSDevice12::Create()
|
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -157,6 +198,9 @@ bool GSDevice12::Create()
|
||||||
}
|
}
|
||||||
|
|
||||||
CompileCASPipelines();
|
CompileCASPipelines();
|
||||||
|
if (!CompileImGuiPipeline())
|
||||||
|
return false;
|
||||||
|
|
||||||
InitializeState();
|
InitializeState();
|
||||||
InitializeSamplers();
|
InitializeSamplers();
|
||||||
return true;
|
return true;
|
||||||
|
@ -164,25 +208,381 @@ bool GSDevice12::Create()
|
||||||
|
|
||||||
void GSDevice12::Destroy()
|
void GSDevice12::Destroy()
|
||||||
{
|
{
|
||||||
if (!g_d3d12_context)
|
GSDevice::Destroy();
|
||||||
|
|
||||||
|
if (g_d3d12_context)
|
||||||
|
{
|
||||||
|
EndRenderPass();
|
||||||
|
ExecuteCommandList(true);
|
||||||
|
DestroyResources();
|
||||||
|
|
||||||
|
g_d3d12_context->WaitForGPUIdle();
|
||||||
|
GSDevice12::DestroySurface();
|
||||||
|
g_d3d12_context->Destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return;
|
||||||
|
|
||||||
EndRenderPass();
|
|
||||||
ExecuteCommandList(true);
|
ExecuteCommandList(true);
|
||||||
DestroyResources();
|
|
||||||
GSDevice::Destroy();
|
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDevice12::ResetAPIState()
|
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();
|
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();
|
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, ...)
|
void GSDevice12::PushDebugGroup(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -453,7 +853,7 @@ void GSDevice12::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
||||||
{
|
{
|
||||||
DisplayConstantBuffer cb;
|
DisplayConstantBuffer cb;
|
||||||
cb.SetSource(sRect, sTex->GetSize());
|
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);
|
cb.SetTime(shaderTime);
|
||||||
SetUtilityRootSignature();
|
SetUtilityRootSignature();
|
||||||
SetUtilityPushConstants(&cb, sizeof(cb));
|
SetUtilityPushConstants(&cb, sizeof(cb));
|
||||||
|
@ -661,8 +1061,7 @@ void GSDevice12::DoStretchRect(GSTexture12* sTex, const GSVector4& sRect, GSText
|
||||||
|
|
||||||
const bool is_present = (!dTex);
|
const bool is_present = (!dTex);
|
||||||
const bool depth = (dTex && dTex->GetType() == GSTexture::Type::DepthStencil);
|
const bool depth = (dTex && dTex->GetType() == GSTexture::Type::DepthStencil);
|
||||||
const GSVector2i size(
|
const GSVector2i size(is_present ? GSVector2i(GetWindowWidth(), GetWindowHeight()) : dTex->GetSize());
|
||||||
is_present ? GSVector2i(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight()) : dTex->GetSize());
|
|
||||||
const GSVector4i dtex_rc(0, 0, size.x, size.y);
|
const GSVector4i dtex_rc(0, 0, size.x, size.y);
|
||||||
const GSVector4i dst_rc(GSVector4i(dRect).rintersect(dtex_rc));
|
const GSVector4i dst_rc(GSVector4i(dRect).rintersect(dtex_rc));
|
||||||
|
|
||||||
|
@ -918,6 +1317,163 @@ bool GSDevice12::CompileCASPipelines()
|
||||||
return true;
|
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)
|
bool GSDevice12::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants)
|
||||||
{
|
{
|
||||||
EndRenderPass();
|
EndRenderPass();
|
||||||
|
@ -1182,7 +1738,7 @@ bool GSDevice12::CreateRootSignatures()
|
||||||
// Convert Pipeline Layout
|
// Convert Pipeline Layout
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
rsb.SetInputAssemblerFlag();
|
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_SRV, 0, NUM_UTILITY_SAMPLERS, D3D12_SHADER_VISIBILITY_PIXEL);
|
||||||
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 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()))
|
if (!(m_utility_root_signature = rsb.Create()))
|
||||||
|
@ -1528,6 +2084,12 @@ void GSDevice12::DestroyResources()
|
||||||
{
|
{
|
||||||
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::Sleep);
|
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)
|
for (auto& it : m_tfx_pipelines)
|
||||||
g_d3d12_context->DeferObjectDestruction(it.second.get());
|
g_d3d12_context->DeferObjectDestruction(it.second.get());
|
||||||
m_tfx_pipelines.clear();
|
m_tfx_pipelines.clear();
|
||||||
|
@ -1544,6 +2106,7 @@ void GSDevice12::DestroyResources()
|
||||||
m_date_image_setup_pipelines = {};
|
m_date_image_setup_pipelines = {};
|
||||||
m_fxaa_pipeline.reset();
|
m_fxaa_pipeline.reset();
|
||||||
m_shadeboost_pipeline.reset();
|
m_shadeboost_pipeline.reset();
|
||||||
|
m_imgui_pipeline.reset();
|
||||||
|
|
||||||
m_linear_sampler_cpu.Clear();
|
m_linear_sampler_cpu.Clear();
|
||||||
m_point_sampler_cpu.Clear();
|
m_point_sampler_cpu.Clear();
|
||||||
|
@ -1563,6 +2126,8 @@ void GSDevice12::DestroyResources()
|
||||||
m_tfx_root_signature.reset();
|
m_tfx_root_signature.reset();
|
||||||
|
|
||||||
m_null_texture.Destroy(false);
|
m_null_texture.Destroy(false);
|
||||||
|
|
||||||
|
m_shader_cache.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
const ID3DBlob* GSDevice12::GetTFXVertexShader(GSHWDrawConfig::VSSelector sel)
|
const ID3DBlob* GSDevice12::GetTFXVertexShader(GSHWDrawConfig::VSSelector sel)
|
||||||
|
|
|
@ -138,6 +138,15 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
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_tfx_root_signature;
|
||||||
ComPtr<ID3D12RootSignature> m_utility_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]
|
std::array<std::array<ComPtr<ID3D12PipelineState>, 2>, 2> m_date_image_setup_pipelines{}; // [depth][datm]
|
||||||
ComPtr<ID3D12PipelineState> m_fxaa_pipeline;
|
ComPtr<ID3D12PipelineState> m_fxaa_pipeline;
|
||||||
ComPtr<ID3D12PipelineState> m_shadeboost_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_vertex_shaders;
|
||||||
std::unordered_map<u32, ComPtr<ID3DBlob>> m_tfx_geometry_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;
|
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;
|
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,
|
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE,
|
||||||
|
@ -215,6 +229,9 @@ private:
|
||||||
bool CompilePostProcessingPipelines();
|
bool CompilePostProcessingPipelines();
|
||||||
bool CompileCASPipelines();
|
bool CompileCASPipelines();
|
||||||
|
|
||||||
|
bool CompileImGuiPipeline();
|
||||||
|
void RenderImGui();
|
||||||
|
|
||||||
void DestroyResources();
|
void DestroyResources();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -223,11 +240,29 @@ public:
|
||||||
|
|
||||||
__fi static GSDevice12* GetInstance() { return static_cast<GSDevice12*>(g_gs_device.get()); }
|
__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 Destroy() override;
|
||||||
|
|
||||||
void ResetAPIState() override;
|
bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||||
void RestoreAPIState() 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 PushDebugGroup(const char* fmt, ...) override;
|
||||||
void PopDebugGroup() override;
|
void PopDebugGroup() override;
|
||||||
|
|
|
@ -143,6 +143,12 @@ class GSDeviceMTL final : public GSDevice
|
||||||
public:
|
public:
|
||||||
using DepthStencilSelector = GSHWDrawConfig::DepthStencilSelector;
|
using DepthStencilSelector = GSHWDrawConfig::DepthStencilSelector;
|
||||||
using SamplerSelector = GSHWDrawConfig::SamplerSelector;
|
using SamplerSelector = GSHWDrawConfig::SamplerSelector;
|
||||||
|
enum class UsePresentDrawable : u8
|
||||||
|
{
|
||||||
|
Never = 0,
|
||||||
|
Always = 1,
|
||||||
|
IfVsync = 2,
|
||||||
|
};
|
||||||
enum class LoadAction
|
enum class LoadAction
|
||||||
{
|
{
|
||||||
DontCare,
|
DontCare,
|
||||||
|
@ -219,7 +225,18 @@ public:
|
||||||
MRCOwned<id<MTLFence>> m_draw_sync_fence;
|
MRCOwned<id<MTLFence>> m_draw_sync_fence;
|
||||||
MRCOwned<MTLFunctionConstantValues*> m_fn_constants;
|
MRCOwned<MTLFunctionConstantValues*> m_fn_constants;
|
||||||
MRCOwned<MTLVertexDescriptor*> m_hw_vertex;
|
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
|
// Draw IDs are used to make sure we're not clobbering things
|
||||||
u64 m_current_draw = 1;
|
u64 m_current_draw = 1;
|
||||||
|
@ -361,15 +378,45 @@ public:
|
||||||
MRCOwned<id<MTLFunction>> LoadShader(NSString* name);
|
MRCOwned<id<MTLFunction>> LoadShader(NSString* name);
|
||||||
MRCOwned<id<MTLRenderPipelineState>> MakePipeline(MTLRenderPipelineDescriptor* desc, id<MTLFunction> vertex, id<MTLFunction> fragment, 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);
|
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, const GSVector4& c) override;
|
||||||
void ClearRenderTarget(GSTexture* t, u32 c) override;
|
void ClearRenderTarget(GSTexture* t, u32 c) override;
|
||||||
void ClearDepth(GSTexture* t) override;
|
void ClearDepth(GSTexture* t) override;
|
||||||
void ClearStencil(GSTexture* t, u8 c) 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;
|
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 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 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);
|
void DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds);
|
||||||
|
|
|
@ -14,14 +14,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "PrecompiledHeader.h"
|
#include "PrecompiledHeader.h"
|
||||||
#include "GSMetalCPPAccessible.h"
|
|
||||||
#include "GSDeviceMTL.h"
|
|
||||||
|
|
||||||
#include "Frontend/MetalHostDisplay.h"
|
#include "Host.h"
|
||||||
#include "GSTextureMTL.h"
|
#include "GS/Renderers/Metal/GSMetalCPPAccessible.h"
|
||||||
|
#include "GS/Renderers/Metal/GSDeviceMTL.h"
|
||||||
|
#include "GS/Renderers/Metal/GSTextureMTL.h"
|
||||||
#include "GS/GSPerfMon.h"
|
#include "GS/GSPerfMon.h"
|
||||||
#include "HostDisplay.h"
|
|
||||||
#include <imgui.h>
|
#include "imgui.h"
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
#include "GSMTLSharedHeader.h"
|
#include "GSMTLSharedHeader.h"
|
||||||
|
@ -36,6 +36,15 @@ GSDevice* MakeGSDeviceMTL()
|
||||||
return new GSDeviceMTL();
|
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)
|
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; });
|
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()
|
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)
|
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
|
// 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));
|
u64 newval = std::max(draw, m_last_finished_draw.load(std::memory_order_relaxed));
|
||||||
m_last_finished_draw.store(newval, std::memory_order_release);
|
m_last_finished_draw.store(newval, std::memory_order_release);
|
||||||
static_cast<MetalHostDisplay*>(g_host_display.get())->AccumulateCommandBufferTime(buffer);
|
AccumulateCommandBufferTime(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDeviceMTL::FlushEncoders()
|
void GSDeviceMTL::FlushEncoders()
|
||||||
|
@ -706,19 +712,100 @@ static void setFnConstantI(MTLFunctionConstantValues* fc, unsigned int value, GS
|
||||||
[fc setConstantValue:&value type:MTLDataTypeUInt atIndex:constant];
|
[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 {
|
{ @autoreleasepool {
|
||||||
if (!GSDevice::Create())
|
if (!GSDevice::Create(wi, vsync))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (g_host_display->GetRenderAPI() != RenderAPI::Metal)
|
NSString* ns_adapter_name = [NSString stringWithUTF8String:EmuConfig.GS.Adapter.c_str()];
|
||||||
return false;
|
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;
|
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.broken_point_sampler = [[m_dev.dev name] containsString:@"AMD"];
|
||||||
m_features.geometry_shader = false;
|
m_features.geometry_shader = false;
|
||||||
|
@ -1042,8 +1129,6 @@ bool GSDeviceMTL::Create()
|
||||||
pdesc.vertexDescriptor.layouts[0].stride = sizeof(ImDrawVert);
|
pdesc.vertexDescriptor.layouts[0].stride = sizeof(ImDrawVert);
|
||||||
pdesc.colorAttachments[0].pixelFormat = layer_px_fmt;
|
pdesc.colorAttachments[0].pixelFormat = layer_px_fmt;
|
||||||
m_imgui_pipeline = MakePipeline(pdesc, LoadShader(@"vs_imgui"), LoadShader(@"ps_imgui"), @"imgui");
|
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];
|
[initCommands commit];
|
||||||
}
|
}
|
||||||
|
@ -1054,6 +1139,255 @@ bool GSDeviceMTL::Create()
|
||||||
return true;
|
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)
|
void GSDeviceMTL::ClearRenderTarget(GSTexture* t, const GSVector4& c)
|
||||||
{
|
{
|
||||||
if (!t) return;
|
if (!t) return;
|
||||||
|
@ -1078,11 +1412,21 @@ void GSDeviceMTL::ClearStencil(GSTexture* t, uint8 c)
|
||||||
static_cast<GSTextureMTL*>(t)->RequestStencilClear(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)
|
std::unique_ptr<GSDownloadTexture> GSDeviceMTL::CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format)
|
||||||
{
|
{
|
||||||
return GSDownloadTextureMTL::Create(this, width, height, 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)
|
void GSDeviceMTL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY)
|
||||||
{ @autoreleasepool {
|
{ @autoreleasepool {
|
||||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
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)
|
void GSDeviceMTL::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear)
|
||||||
{ @autoreleasepool {
|
{ @autoreleasepool {
|
||||||
GSVector2i ds = dTex ? dTex->GetSize() : GSVector2i(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight());
|
GSVector2i ds = dTex ? dTex->GetSize() : GetWindowSize();
|
||||||
DisplayConstantBuffer cb;
|
DisplayConstantBuffer cb;
|
||||||
cb.SetSource(sRect, sTex->GetSize());
|
cb.SetSource(sRect, sTex->GetSize());
|
||||||
cb.SetTarget(dRect, ds);
|
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 setVertexBuffer:map.gpu_buffer offset:map.gpu_offset atIndex:GSMTLBufferIndexVertices];
|
||||||
[enc setVertexBytes:&transform length:sizeof(transform) atIndex:GSMTLBufferIndexUniforms];
|
[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 fb_size = simd_float(last_scissor.zw);
|
||||||
simd::float2 clip_off = ToSimd(data->DisplayPos); // (0,0) unless using multi-viewports
|
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)
|
simd::float2 clip_scale = ToSimd(data->FramebufferScale); // (1,1) unless using retina display which are often (2,2)
|
||||||
ImTextureID last_tex = nullptr;
|
ImTextureID last_tex = nullptr;
|
||||||
bool last_tex_a8 = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < data->CmdListsCount; i++)
|
for (int i = 0; i < data->CmdListsCount; i++)
|
||||||
{
|
{
|
||||||
|
@ -2003,15 +2346,6 @@ void GSDeviceMTL::RenderImGui(ImDrawData* data)
|
||||||
{
|
{
|
||||||
last_tex = tex;
|
last_tex = tex;
|
||||||
[enc setFragmentTexture:(__bridge id<MTLTexture>)tex atIndex:0];
|
[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];
|
[enc setVertexBufferOffset:map.gpu_offset + vtx_off + cmd.VtxOffset * sizeof(ImDrawVert) atIndex:0];
|
||||||
|
|
|
@ -14,15 +14,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
// Header with all metal stuff available for use with C++ (rather than Objective-C++)
|
// Header with all metal stuff available for use with C++ (rather than Objective-C++)
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
|
||||||
#include "HostDisplay.h"
|
|
||||||
|
|
||||||
class GSDevice;
|
class GSDevice;
|
||||||
GSDevice* MakeGSDeviceMTL();
|
GSDevice* MakeGSDeviceMTL();
|
||||||
HostDisplay* MakeMetalHostDisplay();
|
std::vector<std::string> GetMetalAdapterList();
|
||||||
HostDisplay::AdapterAndModeList GetMetalAdapterAndModeList();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -362,12 +362,6 @@ fragment half4 ps_imgui(ImGuiShaderData data [[stage_in]], texture2d<half> textu
|
||||||
return data.c * texture.sample(s, data.t);
|
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)]])
|
fragment float4 ps_shadeboost(float4 p [[position]], DirectReadTextureIn<float> tex, constant float3& cb [[buffer(GSMTLBufferIndexUniforms)]])
|
||||||
{
|
{
|
||||||
const float brt = cb.x;
|
const float brt = cb.x;
|
||||||
|
|
|
@ -14,16 +14,20 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "PrecompiledHeader.h"
|
#include "PrecompiledHeader.h"
|
||||||
#include "common/StringUtil.h"
|
|
||||||
#include "GS/GSState.h"
|
#include "GS/GSState.h"
|
||||||
#include "GSDeviceOGL.h"
|
|
||||||
#include "GLState.h"
|
|
||||||
#include "GS/GSGL.h"
|
#include "GS/GSGL.h"
|
||||||
#include "GS/GSUtil.h"
|
#include "GS/GSUtil.h"
|
||||||
|
#include "GS/Renderers/OpenGL/GSDeviceOGL.h"
|
||||||
|
#include "GS/Renderers/OpenGL/GLState.h"
|
||||||
#include "Host.h"
|
#include "Host.h"
|
||||||
#include "HostDisplay.h"
|
|
||||||
#include "ShaderCacheVersion.h"
|
#include "ShaderCacheVersion.h"
|
||||||
|
|
||||||
|
#include "common/StringUtil.h"
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
#include "IconsFontAwesome5.h"
|
#include "IconsFontAwesome5.h"
|
||||||
|
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
@ -45,35 +49,7 @@ GSDeviceOGL::GSDeviceOGL() = default;
|
||||||
|
|
||||||
GSDeviceOGL::~GSDeviceOGL()
|
GSDeviceOGL::~GSDeviceOGL()
|
||||||
{
|
{
|
||||||
s_texture_upload_buffer.reset();
|
pxAssert(!m_gl_context);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GSTexture* GSDeviceOGL::CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format)
|
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);
|
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;
|
return false;
|
||||||
|
|
||||||
const RenderAPI render_api = g_host_display->GetRenderAPI();
|
m_gl_context = GL::Context::Create(wi);
|
||||||
if (render_api != RenderAPI::OpenGL && render_api != RenderAPI::OpenGLES)
|
if (!m_gl_context)
|
||||||
|
{
|
||||||
|
Console.Error("Failed to create any GL context");
|
||||||
|
m_gl_context.reset();
|
||||||
return false;
|
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
|
// Check openGL requirement as soon as possible so we can switch to another
|
||||||
// renderer/device
|
// renderer/device
|
||||||
GLLoader::is_gles = (render_api == RenderAPI::OpenGLES);
|
GLLoader::is_gles = m_gl_context->IsGLES();
|
||||||
if (!GLLoader::check_gl_requirements())
|
if (!GLLoader::check_gl_requirements())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
SetSwapInterval();
|
||||||
|
|
||||||
if (!GSConfig.DisableShaderCache)
|
if (!GSConfig.DisableShaderCache)
|
||||||
{
|
{
|
||||||
if (!m_shader_cache.Open(false, EmuFolders::Cache, SHADER_CACHE_VERSION))
|
if (!m_shader_cache.Open(false, EmuFolders::Cache, SHADER_CACHE_VERSION))
|
||||||
|
@ -155,7 +170,7 @@ bool GSDeviceOGL::Create()
|
||||||
{
|
{
|
||||||
if (!GLLoader::is_gles)
|
if (!GLLoader::is_gles)
|
||||||
{
|
{
|
||||||
glDebugMessageCallback((GLDEBUGPROC)DebugOutputToFile, NULL);
|
glDebugMessageCallback(DebugMessageCallback, NULL);
|
||||||
|
|
||||||
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, true);
|
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, true);
|
||||||
// Useless info message on Nvidia driver
|
// Useless info message on Nvidia driver
|
||||||
|
@ -164,7 +179,7 @@ bool GSDeviceOGL::Create()
|
||||||
}
|
}
|
||||||
else if (GLAD_GL_KHR_debug)
|
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);
|
glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,14 +198,15 @@ bool GSDeviceOGL::Create()
|
||||||
GL_PUSH("GSDeviceOGL::Various");
|
GL_PUSH("GSDeviceOGL::Various");
|
||||||
|
|
||||||
glGenFramebuffers(1, &m_fbo);
|
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_read);
|
||||||
glGenFramebuffers(1, &m_fbo_write);
|
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
|
// Always read from the first buffer
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
|
||||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||||
|
@ -239,10 +255,6 @@ bool GSDeviceOGL::Create()
|
||||||
glVertexAttribPointer(7, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GSVertex), (const GLvoid*)(28));
|
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
|
// Pre Generate the different sampler object
|
||||||
// ****************************************************************
|
// ****************************************************************
|
||||||
|
@ -395,24 +407,10 @@ bool GSDeviceOGL::Create()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ****************************************************************
|
// ****************************************************************
|
||||||
// Shade boost
|
// Post processing
|
||||||
// ****************************************************************
|
// ****************************************************************
|
||||||
{
|
if (!CompileShadeBoostProgram() || !CompileFXAAProgram())
|
||||||
GL_PUSH("GSDeviceOGL::Shadeboost");
|
return false;
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Image load store and GLSL 420pack is core in GL4.2, no need to check.
|
// 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();
|
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
|
// Basic to ensure structures are correctly packed
|
||||||
static_assert(sizeof(VSSelector) == 1, "Wrong VSSelector size");
|
static_assert(sizeof(VSSelector) == 1, "Wrong VSSelector size");
|
||||||
static_assert(sizeof(PSSelector) == 12, "Wrong PSSelector size");
|
static_assert(sizeof(PSSelector) == 12, "Wrong PSSelector size");
|
||||||
|
@ -501,6 +502,20 @@ bool GSDeviceOGL::Create()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GSDeviceOGL::Destroy()
|
||||||
|
{
|
||||||
|
GSDevice::Destroy();
|
||||||
|
|
||||||
|
if (m_gl_context)
|
||||||
|
{
|
||||||
|
DestroyTimestampQueries();
|
||||||
|
DestroyResources();
|
||||||
|
|
||||||
|
m_gl_context->DoneCurrent();
|
||||||
|
m_gl_context.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool GSDeviceOGL::CreateTextureFX()
|
bool GSDeviceOGL::CreateTextureFX()
|
||||||
{
|
{
|
||||||
GL_PUSH("GSDeviceOGL::CreateTextureFX");
|
GL_PUSH("GSDeviceOGL::CreateTextureFX");
|
||||||
|
@ -529,78 +544,293 @@ bool GSDeviceOGL::CreateTextureFX()
|
||||||
m_om_dss[key] = CreateDepthStencil(OMDepthStencilSelector(key));
|
m_om_dss[key] = CreateDepthStencil(OMDepthStencilSelector(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GL::Program::ResetLastProgram();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDeviceOGL::ResetAPIState()
|
void GSDeviceOGL::SetSwapInterval()
|
||||||
{
|
{
|
||||||
if (GLState::point_size)
|
const int interval = ((m_vsync_mode == VsyncMode::Adaptive) ? -1 : ((m_vsync_mode == VsyncMode::On) ? 1 : 0));
|
||||||
glDisable(GL_PROGRAM_POINT_SIZE);
|
m_gl_context->SetSwapInterval(interval);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
m_programs.clear();
|
||||||
glScissorIndexed(0, GLState::scissor.x, GLState::scissor.y, GLState::scissor.width(), GLState::scissor.height());
|
|
||||||
|
|
||||||
glBlendEquationSeparate(GLState::eq_RGB, GL_FUNC_ADD);
|
for (GSDepthStencilOGL* ds : m_om_dss)
|
||||||
glBlendFuncSeparate(GLState::f_sRGB, GLState::f_dRGB, GL_ONE, GL_ZERO);
|
delete ds;
|
||||||
|
|
||||||
const float bf = static_cast<float>(GLState::bf) / 128.0f;
|
if (m_ps_ss[0] != 0)
|
||||||
glBlendColor(bf, bf, bf, bf);
|
glDeleteSamplers(std::size(m_ps_ss), m_ps_ss);
|
||||||
|
|
||||||
if (GLState::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))
|
||||||
{
|
{
|
||||||
glEnable(GL_BLEND);
|
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
|
else
|
||||||
{
|
DestroyTimestampQueries();
|
||||||
glDisable(GL_BLEND);
|
|
||||||
}
|
|
||||||
|
|
||||||
const OMColorMaskSelector msel{ GLState::wrgba };
|
return true;
|
||||||
glColorMask(msel.wr, msel.wg, msel.wb, msel.wa);
|
}
|
||||||
|
|
||||||
GLState::depth ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
|
float GSDeviceOGL::GetAndResetAccumulatedGPUTime()
|
||||||
glDepthFunc(GLState::depth_func);
|
{
|
||||||
glDepthMask(GLState::depth_mask);
|
const float value = m_accumulated_gpu_time;
|
||||||
|
m_accumulated_gpu_time = 0.0f;
|
||||||
if (GLState::stencil)
|
return value;
|
||||||
{
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDeviceOGL::DrawPrimitive()
|
void GSDeviceOGL::DrawPrimitive()
|
||||||
|
@ -898,8 +1128,6 @@ std::string GSDeviceOGL::GenGlslHeader(const std::string_view& entry, GLenum typ
|
||||||
|
|
||||||
// Need GL version 420
|
// Need GL version 420
|
||||||
header += "#extension GL_ARB_shading_language_420pack: require\n";
|
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)
|
if (m_features.framebuffer_fetch && GLAD_GL_EXT_shader_framebuffer_fetch)
|
||||||
header += "#extension GL_EXT_shader_framebuffer_fetch : require\n";
|
header += "#extension GL_EXT_shader_framebuffer_fetch : require\n";
|
||||||
|
@ -1176,7 +1404,7 @@ void GSDeviceOGL::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
|
||||||
{
|
{
|
||||||
ASSERT(sTex);
|
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;
|
DisplayConstantBuffer cb;
|
||||||
cb.SetSource(sRect, sTex->GetSize());
|
cb.SetSource(sRect, sTex->GetSize());
|
||||||
cb.SetTarget(dRect, ds);
|
cb.SetTarget(dRect, ds);
|
||||||
|
@ -1441,24 +1669,39 @@ void GSDeviceOGL::DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture
|
||||||
StretchRect(sTex, sRect, dTex, dRect, m_interlace.ps[static_cast<int>(shader)], linear);
|
StretchRect(sTex, sRect, dTex, dRect, m_interlace.ps[static_cast<int>(shader)], linear);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GSDeviceOGL::CompileFXAAProgram()
|
||||||
|
{
|
||||||
|
// Needs ARB_gpu_shader5 for gather.
|
||||||
|
if (!GLLoader::is_gles && !GLLoader::found_GL_ARB_gpu_shader5)
|
||||||
|
{
|
||||||
|
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())
|
||||||
|
{
|
||||||
|
Console.Error("Failed to read fxaa.fs");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string ps(GetShaderSource("ps_main", GL_FRAGMENT_SHADER, shader->c_str(), fxaa_macro));
|
||||||
|
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)
|
void GSDeviceOGL::DoFXAA(GSTexture* sTex, GSTexture* dTex)
|
||||||
{
|
{
|
||||||
// Lazy compile
|
|
||||||
if (!m_fxaa.ps.IsValid())
|
if (!m_fxaa.ps.IsValid())
|
||||||
{
|
return;
|
||||||
// Needs ARB_gpu_shader5 for gather.
|
|
||||||
if (!GLLoader::is_gles && !GLLoader::found_GL_ARB_gpu_shader5)
|
|
||||||
return;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
GL_PUSH("DoFxaa");
|
GL_PUSH("DoFxaa");
|
||||||
|
|
||||||
|
@ -1472,6 +1715,23 @@ void GSDeviceOGL::DoFXAA(GSTexture* sTex, GSTexture* dTex)
|
||||||
StretchRect(sTex, sRect, dTex, dRect, m_fxaa.ps, true);
|
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])
|
void GSDeviceOGL::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4])
|
||||||
{
|
{
|
||||||
GL_PUSH("DoShadeBoost");
|
GL_PUSH("DoShadeBoost");
|
||||||
|
@ -1651,6 +1911,140 @@ bool GSDeviceOGL::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, con
|
||||||
return true;
|
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)
|
void GSDeviceOGL::OMAttachRt(GSTextureOGL* rt)
|
||||||
{
|
{
|
||||||
if (rt)
|
if (rt)
|
||||||
|
@ -1780,21 +2174,29 @@ void GSDeviceOGL::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVecto
|
||||||
else
|
else
|
||||||
OMAttachDs();
|
OMAttachDs();
|
||||||
|
|
||||||
const GSVector2i size = rt ? rt->GetSize() : ds ? ds->GetSize() : GLState::viewport;
|
if (rt || ds)
|
||||||
if (GLState::viewport != size)
|
|
||||||
{
|
{
|
||||||
GLState::viewport = size;
|
const GSVector2i size = rt ? rt->GetSize() : ds->GetSize();
|
||||||
// FIXME ViewportIndexedf or ViewportIndexedfv (GL4.1)
|
SetViewport(size);
|
||||||
glViewportIndexedf(0, 0, 0, GLfloat(size.x), GLfloat(size.y));
|
SetScissor(scissor ? *scissor : GSVector4i::loadh(size));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const GSVector4i r = scissor ? *scissor : GSVector4i(size).zwxy();
|
void GSDeviceOGL::SetViewport(const GSVector2i& viewport)
|
||||||
|
{
|
||||||
if (!GLState::scissor.eq(r))
|
if (GLState::viewport != viewport)
|
||||||
{
|
{
|
||||||
GLState::scissor = r;
|
GLState::viewport = viewport;
|
||||||
// FIXME ScissorIndexedv (GL4.1)
|
glViewport(0, 0, viewport.x, viewport.y);
|
||||||
glScissorIndexed(0, r.x, r.y, r.width(), r.height());
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// 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 message(gl_message, gl_length >= 0 ? gl_length : strlen(gl_message));
|
||||||
std::string type, severity, source;
|
std::string type, severity, source;
|
||||||
|
@ -2230,7 +2632,7 @@ GL::StreamBuffer* GSDeviceOGL::GetTextureUploadBuffer()
|
||||||
void GSDeviceOGL::PushDebugGroup(const char* fmt, ...)
|
void GSDeviceOGL::PushDebugGroup(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_OGL_DEBUG
|
#ifdef ENABLE_OGL_DEBUG
|
||||||
if (!glPushDebugGroup)
|
if (!glPushDebugGroup || !GSConfig.UseDebugDevice)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::va_list ap;
|
std::va_list ap;
|
||||||
|
@ -2245,7 +2647,7 @@ void GSDeviceOGL::PushDebugGroup(const char* fmt, ...)
|
||||||
void GSDeviceOGL::PopDebugGroup()
|
void GSDeviceOGL::PopDebugGroup()
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_OGL_DEBUG
|
#ifdef ENABLE_OGL_DEBUG
|
||||||
if (!glPopDebugGroup)
|
if (!glPopDebugGroup || !GSConfig.UseDebugDevice)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
glPopDebugGroup();
|
glPopDebugGroup();
|
||||||
|
@ -2255,7 +2657,7 @@ void GSDeviceOGL::PopDebugGroup()
|
||||||
void GSDeviceOGL::InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...)
|
void GSDeviceOGL::InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_OGL_DEBUG
|
#ifdef ENABLE_OGL_DEBUG
|
||||||
if (!glDebugMessageInsert)
|
if (!glDebugMessageInsert || !GSConfig.UseDebugDevice)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
GLenum type, id, severity;
|
GLenum type, id, severity;
|
||||||
|
|
|
@ -202,8 +202,9 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_shader_tfx_vgs;
|
static constexpr u8 NUM_TIMESTAMP_QUERIES = 5;
|
||||||
std::string m_shader_tfx_fs;
|
|
||||||
|
std::unique_ptr<GL::Context> m_gl_context;
|
||||||
|
|
||||||
GLuint m_fbo = 0; // frame buffer container
|
GLuint m_fbo = 0; // frame buffer container
|
||||||
GLuint m_fbo_read = 0; // frame buffer container only for reading
|
GLuint m_fbo_read = 0; // frame buffer container only for reading
|
||||||
|
@ -262,6 +263,12 @@ private:
|
||||||
GL::Program sharpen_ps;
|
GL::Program sharpen_ps;
|
||||||
} m_cas;
|
} m_cas;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
GL::Program ps;
|
||||||
|
GLuint vao = 0;
|
||||||
|
} m_imgui;
|
||||||
|
|
||||||
GLuint m_ps_ss[1 << 8];
|
GLuint m_ps_ss[1 << 8];
|
||||||
GSDepthStencilOGL* m_om_dss[1 << 5] = {};
|
GSDepthStencilOGL* m_om_dss[1 << 5] = {};
|
||||||
std::unordered_map<ProgramSelector, GL::Program, ProgramSelectorHash> m_programs;
|
std::unordered_map<ProgramSelector, GL::Program, ProgramSelectorHash> m_programs;
|
||||||
|
@ -269,21 +276,47 @@ private:
|
||||||
|
|
||||||
GLuint m_palette_ss = 0;
|
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::VSConstantBuffer m_vs_cb_cache;
|
||||||
GSHWDrawConfig::PSConstantBuffer m_ps_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 SetSwapInterval();
|
||||||
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) final;
|
void DestroyResources();
|
||||||
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
|
|
||||||
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final;
|
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 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);
|
bool CreateImGuiProgram();
|
||||||
void OMAttachDs(GSTextureOGL* ds = NULL);
|
void RenderImGui();
|
||||||
|
|
||||||
|
void OMAttachRt(GSTextureOGL* rt = nullptr);
|
||||||
|
void OMAttachDs(GSTextureOGL* ds = nullptr);
|
||||||
void OMSetFBO(GLuint fbo);
|
void OMSetFBO(GLuint fbo);
|
||||||
|
|
||||||
void DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds);
|
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()); }
|
__fi static GSDeviceOGL* GetInstance() { return static_cast<GSDeviceOGL*>(g_gs_device.get()); }
|
||||||
|
|
||||||
// Used by OpenGL, so the same calling convention is required.
|
// 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();
|
static GL::StreamBuffer* GetTextureUploadBuffer();
|
||||||
|
|
||||||
__fi u32 GetFBORead() const { return m_fbo_read; }
|
__fi u32 GetFBORead() const { return m_fbo_read; }
|
||||||
__fi u32 GetFBOWrite() const { return m_fbo_write; }
|
__fi u32 GetFBOWrite() const { return m_fbo_write; }
|
||||||
|
|
||||||
bool Create() override;
|
RenderAPI GetRenderAPI() const override;
|
||||||
|
bool HasSurface() const override;
|
||||||
|
|
||||||
void ResetAPIState() override;
|
bool Create(const WindowInfo& wi, VsyncMode vsync) override;
|
||||||
void RestoreAPIState() 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 DrawPrimitive();
|
||||||
void DrawIndexedPrimitive();
|
void DrawIndexedPrimitive();
|
||||||
void DrawIndexedPrimitive(int offset, int count);
|
void DrawIndexedPrimitive(int offset, int count);
|
||||||
|
|
||||||
void ClearRenderTarget(GSTexture* t, const GSVector4& c) final;
|
void ClearRenderTarget(GSTexture* t, const GSVector4& c) override;
|
||||||
void ClearRenderTarget(GSTexture* t, u32 c) final;
|
void ClearRenderTarget(GSTexture* t, u32 c) override;
|
||||||
void InvalidateRenderTarget(GSTexture* t) final;
|
void InvalidateRenderTarget(GSTexture* t) override;
|
||||||
void ClearDepth(GSTexture* t) final;
|
void ClearDepth(GSTexture* t) override;
|
||||||
void ClearStencil(GSTexture* t, u8 c) final;
|
void ClearStencil(GSTexture* t, u8 c) override;
|
||||||
|
|
||||||
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) override;
|
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) override;
|
||||||
|
|
||||||
GSTexture* InitPrimDateTexture(GSTexture* rt, const GSVector4i& area, bool datm);
|
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 PushDebugGroup(const char* fmt, ...) override;
|
||||||
void PopDebugGroup() final;
|
void PopDebugGroup() override;
|
||||||
void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) final;
|
void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) override;
|
||||||
|
|
||||||
// BlitRect *does* mess with GL state, be sure to re-bind.
|
// 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 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, 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 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 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) final;
|
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) final;
|
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 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 SendHWDraw(const GSHWDrawConfig& config, bool needs_barrier);
|
||||||
|
|
||||||
void SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vertices, bool datm);
|
void SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vertices, bool datm);
|
||||||
|
@ -353,14 +403,17 @@ public:
|
||||||
void PSSetShaderResource(int i, GSTexture* sr);
|
void PSSetShaderResource(int i, GSTexture* sr);
|
||||||
void PSSetShaderResources(GSTexture* sr0, GSTexture* sr1);
|
void PSSetShaderResources(GSTexture* sr0, GSTexture* sr1);
|
||||||
void PSSetSamplerState(GLuint ss);
|
void PSSetSamplerState(GLuint ss);
|
||||||
void ClearSamplerCache() final;
|
void ClearSamplerCache() override;
|
||||||
|
|
||||||
void OMSetDepthStencilState(GSDepthStencilOGL* dss);
|
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 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 OMSetColorMaskState(OMColorMaskSelector sel = OMColorMaskSelector());
|
||||||
void OMUnbindTexture(GSTextureOGL* tex);
|
void OMUnbindTexture(GSTextureOGL* tex);
|
||||||
|
|
||||||
|
void SetViewport(const GSVector2i& viewport);
|
||||||
|
void SetScissor(const GSVector4i& scissor);
|
||||||
|
|
||||||
bool CreateTextureFX();
|
bool CreateTextureFX();
|
||||||
std::string GetShaderSource(const std::string_view& entry, GLenum type, const std::string_view& glsl_h_code,
|
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());
|
const std::string_view& macro_sel = std::string_view());
|
||||||
|
|
|
@ -14,6 +14,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "PrecompiledHeader.h"
|
#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/Builders.h"
|
||||||
#include "common/Vulkan/Context.h"
|
#include "common/Vulkan/Context.h"
|
||||||
#include "common/Vulkan/ShaderCache.h"
|
#include "common/Vulkan/ShaderCache.h"
|
||||||
|
@ -22,13 +31,9 @@
|
||||||
#include "common/Align.h"
|
#include "common/Align.h"
|
||||||
#include "common/Path.h"
|
#include "common/Path.h"
|
||||||
#include "common/ScopedGuard.h"
|
#include "common/ScopedGuard.h"
|
||||||
#include "GS.h"
|
|
||||||
#include "GSDeviceVK.h"
|
#include "imgui.h"
|
||||||
#include "GS/GSGL.h"
|
|
||||||
#include "GS/GSPerfMon.h"
|
|
||||||
#include "GS/GSUtil.h"
|
|
||||||
#include "Host.h"
|
|
||||||
#include "HostDisplay.h"
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
|
@ -55,6 +60,16 @@ static VkAttachmentLoadOp GetLoadOpForTexture(GSTextureVK* tex)
|
||||||
// clang-format on
|
// 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()
|
GSDeviceVK::GSDeviceVK()
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_OGL_DEBUG
|
#ifdef ENABLE_OGL_DEBUG
|
||||||
|
@ -64,13 +79,90 @@ GSDeviceVK::GSDeviceVK()
|
||||||
std::memset(&m_pipeline_selector, 0, sizeof(m_pipeline_selector));
|
std::memset(&m_pipeline_selector, 0, sizeof(m_pipeline_selector));
|
||||||
}
|
}
|
||||||
|
|
||||||
GSDeviceVK::~GSDeviceVK() {}
|
GSDeviceVK::~GSDeviceVK()
|
||||||
|
|
||||||
bool GSDeviceVK::Create()
|
|
||||||
{
|
{
|
||||||
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;
|
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");
|
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/vulkan/tfx.glsl");
|
||||||
if (!shader.has_value())
|
if (!shader.has_value())
|
||||||
|
@ -119,31 +211,253 @@ bool GSDeviceVK::Create()
|
||||||
|
|
||||||
CompileCASPipelines();
|
CompileCASPipelines();
|
||||||
|
|
||||||
|
if (!CompileImGuiPipeline())
|
||||||
|
return false;
|
||||||
|
|
||||||
InitializeState();
|
InitializeState();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDeviceVK::Destroy()
|
void GSDeviceVK::Destroy()
|
||||||
{
|
{
|
||||||
if (!g_vulkan_context)
|
GSDevice::Destroy();
|
||||||
|
|
||||||
|
if (g_vulkan_context)
|
||||||
|
{
|
||||||
|
EndRenderPass();
|
||||||
|
ExecuteCommandBuffer(true);
|
||||||
|
DestroyResources();
|
||||||
|
|
||||||
|
g_vulkan_context->WaitForGPUIdle();
|
||||||
|
m_swap_chain.reset();
|
||||||
|
|
||||||
|
Vulkan::ShaderCache::Destroy();
|
||||||
|
Vulkan::Context::Destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return;
|
||||||
|
|
||||||
EndRenderPass();
|
// This swap chain should not be used by the current buffer, thus safe to destroy.
|
||||||
ExecuteCommandBuffer(true);
|
g_vulkan_context->WaitForGPUIdle();
|
||||||
DestroyResources();
|
m_swap_chain->SetVSync(GetPreferredPresentModeForVsyncMode(mode));
|
||||||
GSDevice::Destroy();
|
m_vsync_mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDeviceVK::ResetAPIState()
|
GSDevice::PresentResult GSDeviceVK::BeginPresent(bool frame_skip)
|
||||||
{
|
{
|
||||||
EndRenderPass();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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::RestoreAPIState()
|
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();
|
InvalidateCachedState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GSDeviceVK::SetGPUTimingEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
return g_vulkan_context->SetEnableGPUTiming(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
float GSDeviceVK::GetAndResetAccumulatedGPUTime()
|
||||||
|
{
|
||||||
|
return g_vulkan_context->GetAndResetAccumulatedGPUTime();
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_OGL_DEBUG
|
#ifdef ENABLE_OGL_DEBUG
|
||||||
static std::array<float, 3> Palette(float phase, const std::array<float, 3>& a, const std::array<float, 3>& b,
|
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)
|
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;
|
DisplayConstantBuffer cb;
|
||||||
cb.SetSource(sRect, sTex->GetSize());
|
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);
|
cb.SetTime(shaderTime);
|
||||||
SetUtilityPushConstants(&cb, sizeof(cb));
|
SetUtilityPushConstants(&cb, sizeof(cb));
|
||||||
|
|
||||||
|
@ -728,7 +1042,7 @@ void GSDeviceVK::DoStretchRect(GSTextureVK* sTex, const GSVector4& sRect, GSText
|
||||||
const bool is_present = (!dTex);
|
const bool is_present = (!dTex);
|
||||||
const bool depth = (dTex && dTex->GetType() == GSTexture::Type::DepthStencil);
|
const bool depth = (dTex && dTex->GetType() == GSTexture::Type::DepthStencil);
|
||||||
const GSVector2i size(
|
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 dtex_rc(0, 0, size.x, size.y);
|
||||||
const GSVector4i dst_rc(GSVector4i(dRect).rintersect(dtex_rc));
|
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.start = m_vertex_stream_buffer.GetCurrentOffset() / stride;
|
||||||
m_vertex.count = count;
|
m_vertex.count = count;
|
||||||
SetVertexBuffer(m_vertex_stream_buffer.GetBuffer(), 0);
|
|
||||||
|
|
||||||
GSVector4i::storent(m_vertex_stream_buffer.GetCurrentHostPointer(), vertex, count * stride);
|
GSVector4i::storent(m_vertex_stream_buffer.GetCurrentHostPointer(), vertex, count * stride);
|
||||||
m_vertex_stream_buffer.CommitMemory(size);
|
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.start = m_index_stream_buffer.GetCurrentOffset() / sizeof(u32);
|
||||||
m_index.count = count;
|
m_index.count = count;
|
||||||
SetIndexBuffer(m_index_stream_buffer.GetBuffer(), 0, VK_INDEX_TYPE_UINT32);
|
|
||||||
|
|
||||||
std::memcpy(m_index_stream_buffer.GetCurrentHostPointer(), index, size);
|
std::memcpy(m_index_stream_buffer.GetCurrentHostPointer(), index, size);
|
||||||
m_index_stream_buffer.CommitMemory(size);
|
m_index_stream_buffer.CommitMemory(size);
|
||||||
|
@ -1242,6 +1554,9 @@ bool GSDeviceVK::CreateBuffers()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetVertexBuffer(m_vertex_stream_buffer.GetBuffer(), 0);
|
||||||
|
SetIndexBuffer(m_index_stream_buffer.GetBuffer(), 0, VK_INDEX_TYPE_UINT32);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1384,16 +1699,6 @@ bool GSDeviceVK::CreateRenderPasses()
|
||||||
|
|
||||||
bool GSDeviceVK::CompileConvertPipelines()
|
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");
|
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/vulkan/convert.glsl");
|
||||||
if (!shader)
|
if (!shader)
|
||||||
{
|
{
|
||||||
|
@ -1583,11 +1888,10 @@ bool GSDeviceVK::CompileConvertPipelines()
|
||||||
bool GSDeviceVK::CompilePresentPipelines()
|
bool GSDeviceVK::CompilePresentPipelines()
|
||||||
{
|
{
|
||||||
// we may not have a swap chain if running in headless mode.
|
// 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 = m_swap_chain ?
|
||||||
m_swap_chain_render_pass = swapchain ?
|
m_swap_chain->GetClearRenderPass() :
|
||||||
swapchain->GetClearRenderPass() :
|
g_vulkan_context->GetRenderPass(VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_UNDEFINED);
|
||||||
g_vulkan_context->GetRenderPass(VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_UNDEFINED);
|
if (m_swap_chain_render_pass == VK_NULL_HANDLE)
|
||||||
if (!m_swap_chain_render_pass)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/vulkan/present.glsl");
|
std::optional<std::string> shader = Host::ReadResourceFileToString("shaders/vulkan/present.glsl");
|
||||||
|
@ -1880,6 +2184,155 @@ bool GSDeviceVK::CompileCASPipelines()
|
||||||
return true;
|
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)
|
bool GSDeviceVK::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants)
|
||||||
{
|
{
|
||||||
EndRenderPass();
|
EndRenderPass();
|
||||||
|
@ -1994,6 +2447,7 @@ void GSDeviceVK::DestroyResources()
|
||||||
Vulkan::Util::SafeDestroyPipeline(it);
|
Vulkan::Util::SafeDestroyPipeline(it);
|
||||||
Vulkan::Util::SafeDestroyPipelineLayout(m_cas_pipeline_layout);
|
Vulkan::Util::SafeDestroyPipelineLayout(m_cas_pipeline_layout);
|
||||||
Vulkan::Util::SafeDestroyDescriptorSetLayout(m_cas_ds_layout);
|
Vulkan::Util::SafeDestroyDescriptorSetLayout(m_cas_ds_layout);
|
||||||
|
Vulkan::Util::SafeDestroyPipeline(m_imgui_pipeline);
|
||||||
|
|
||||||
for (auto& it : m_samplers)
|
for (auto& it : m_samplers)
|
||||||
Vulkan::Util::SafeDestroySampler(it.second);
|
Vulkan::Util::SafeDestroySampler(it.second);
|
||||||
|
|
|
@ -24,6 +24,11 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
class SwapChain;
|
||||||
|
}
|
||||||
|
|
||||||
class GSDeviceVK final : public GSDevice
|
class GSDeviceVK final : public GSDevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -104,6 +109,8 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::unique_ptr<Vulkan::SwapChain> m_swap_chain;
|
||||||
|
|
||||||
VkDescriptorSetLayout m_utility_ds_layout = VK_NULL_HANDLE;
|
VkDescriptorSetLayout m_utility_ds_layout = VK_NULL_HANDLE;
|
||||||
VkPipelineLayout m_utility_pipeline_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;
|
VkDescriptorSetLayout m_cas_ds_layout = VK_NULL_HANDLE;
|
||||||
VkPipelineLayout m_cas_pipeline_layout = VK_NULL_HANDLE;
|
VkPipelineLayout m_cas_pipeline_layout = VK_NULL_HANDLE;
|
||||||
std::array<VkPipeline, NUM_CAS_PIPELINES> m_cas_pipelines = {};
|
std::array<VkPipeline, NUM_CAS_PIPELINES> m_cas_pipelines = {};
|
||||||
|
VkPipeline m_imgui_pipeline = VK_NULL_HANDLE;
|
||||||
|
|
||||||
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
|
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
|
||||||
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
|
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
|
||||||
|
@ -196,6 +204,9 @@ private:
|
||||||
bool CompilePostProcessingPipelines();
|
bool CompilePostProcessingPipelines();
|
||||||
bool CompileCASPipelines();
|
bool CompileCASPipelines();
|
||||||
|
|
||||||
|
bool CompileImGuiPipeline();
|
||||||
|
void RenderImGui();
|
||||||
|
|
||||||
void DestroyResources();
|
void DestroyResources();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -204,6 +215,8 @@ public:
|
||||||
|
|
||||||
__fi static GSDeviceVK* GetInstance() { return static_cast<GSDeviceVK*>(g_gs_device.get()); }
|
__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,
|
__fi VkRenderPass GetTFXRenderPass(bool rt, bool ds, bool hdr, DATE_RENDER_PASS date, bool fbl, bool dsp,
|
||||||
VkAttachmentLoadOp rt_op, VkAttachmentLoadOp ds_op) const
|
VkAttachmentLoadOp rt_op, VkAttachmentLoadOp ds_op) const
|
||||||
{
|
{
|
||||||
|
@ -212,11 +225,27 @@ public:
|
||||||
__fi VkSampler GetPointSampler() const { return m_point_sampler; }
|
__fi VkSampler GetPointSampler() const { return m_point_sampler; }
|
||||||
__fi VkSampler GetLinearSampler() const { return m_linear_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 Destroy() override;
|
||||||
|
|
||||||
void ResetAPIState() override;
|
bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||||
void RestoreAPIState() 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 PushDebugGroup(const char* fmt, ...) override;
|
||||||
void PopDebugGroup() override;
|
void PopDebugGroup() override;
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
enum class VsyncMode;
|
||||||
|
|
||||||
namespace Host
|
namespace Host
|
||||||
{
|
{
|
||||||
/// Typical durations for OSD messages.
|
/// Typical durations for OSD messages.
|
||||||
|
@ -87,10 +89,4 @@ namespace Host
|
||||||
|
|
||||||
/// Requests shut down of the current virtual machine.
|
/// Requests shut down of the current virtual machine.
|
||||||
void RequestVMShutdown(bool allow_confirm, bool allow_save_state, bool default_save_state);
|
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
|
} // 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 "Elfheader.h"
|
||||||
|
|
||||||
#include "Host.h"
|
#include "Host.h"
|
||||||
#include "HostDisplay.h"
|
|
||||||
#include "IconsFontAwesome5.h"
|
#include "IconsFontAwesome5.h"
|
||||||
#include "VMManager.h"
|
#include "VMManager.h"
|
||||||
|
|
||||||
|
@ -276,7 +275,7 @@ bool SysMtgsThread::TryOpenGS()
|
||||||
if (!GSopen(EmuConfig.GS, EmuConfig.GS.Renderer, RingBuffer.Regs))
|
if (!GSopen(EmuConfig.GS, EmuConfig.GS.Renderer, RingBuffer.Regs))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
GSsetGameCRC(ElfCRC);
|
GSSetGameCRC(ElfCRC);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,7 +510,7 @@ void SysMtgsThread::MainLoop()
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GS_RINGTYPE_CRC:
|
case GS_RINGTYPE_CRC:
|
||||||
GSsetGameCRC(tag.data[0]);
|
GSSetGameCRC(tag.data[0]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GS_RINGTYPE_INIT_AND_READ_FIFO:
|
case GS_RINGTYPE_INIT_AND_READ_FIFO:
|
||||||
|
@ -915,7 +914,7 @@ void SysMtgsThread::ApplySettings()
|
||||||
|
|
||||||
RunOnGSThread([opts = EmuConfig.GS]() {
|
RunOnGSThread([opts = EmuConfig.GS]() {
|
||||||
GSUpdateConfig(opts);
|
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
|
// 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");
|
pxAssertRel(IsOpen(), "MTGS is running");
|
||||||
RunOnGSThread([width, height, scale]() {
|
RunOnGSThread([width, height, scale]() {
|
||||||
GSResetAPIState();
|
GSResizeDisplayWindow(width, height, scale);
|
||||||
Host::ResizeHostDisplay(width, height, scale);
|
|
||||||
GSRestoreAPIState();
|
|
||||||
|
|
||||||
// If we're paused, re-present the current frame at the new window size.
|
// If we're paused, re-present the current frame at the new window size.
|
||||||
if (VMManager::GetState() == VMState::Paused)
|
if (VMManager::GetState() == VMState::Paused)
|
||||||
|
@ -943,9 +940,7 @@ void SysMtgsThread::UpdateDisplayWindow()
|
||||||
{
|
{
|
||||||
pxAssertRel(IsOpen(), "MTGS is running");
|
pxAssertRel(IsOpen(), "MTGS is running");
|
||||||
RunOnGSThread([]() {
|
RunOnGSThread([]() {
|
||||||
GSResetAPIState();
|
GSUpdateDisplayWindow();
|
||||||
Host::UpdateHostDisplay();
|
|
||||||
GSRestoreAPIState();
|
|
||||||
|
|
||||||
// If we're paused, re-present the current frame at the new window size.
|
// If we're paused, re-present the current frame at the new window size.
|
||||||
if (VMManager::GetState() == VMState::Paused)
|
if (VMManager::GetState() == VMState::Paused)
|
||||||
|
@ -959,7 +954,7 @@ void SysMtgsThread::SetVSyncMode(VsyncMode mode)
|
||||||
|
|
||||||
RunOnGSThread([mode]() {
|
RunOnGSThread([mode]() {
|
||||||
Console.WriteLn("Vsync is %s", mode == VsyncMode::Off ? "OFF" : (mode == VsyncMode::Adaptive ? "ADAPTIVE" : "ON"));
|
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();
|
g_key_status.Init();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -30,7 +30,7 @@ enum class GenericInputBinding : u8;
|
||||||
|
|
||||||
s32 PADinit();
|
s32 PADinit();
|
||||||
void PADshutdown();
|
void PADshutdown();
|
||||||
s32 PADopen(const WindowInfo& wi);
|
s32 PADopen();
|
||||||
void PADclose();
|
void PADclose();
|
||||||
s32 PADsetSlot(u8 port, u8 slot);
|
s32 PADsetSlot(u8 port, u8 slot);
|
||||||
s32 PADfreeze(FreezeAction mode, freezeData* data);
|
s32 PADfreeze(FreezeAction mode, freezeData* data);
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
#include "common/StringUtil.h"
|
#include "common/StringUtil.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "GS.h"
|
#include "GS.h"
|
||||||
#include "HostDisplay.h"
|
|
||||||
#include "CDVD/CDVDcommon.h"
|
#include "CDVD/CDVDcommon.h"
|
||||||
#include "MemoryCardFile.h"
|
#include "MemoryCardFile.h"
|
||||||
#include "USB/USB.h"
|
#include "USB/USB.h"
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
#include "USB/qemu-usb/USBinternal.h"
|
#include "USB/qemu-usb/USBinternal.h"
|
||||||
#include "USB/USB.h"
|
#include "USB/USB.h"
|
||||||
#include "GS/GS.h"
|
#include "GS/GS.h"
|
||||||
#include "HostDisplay.h"
|
|
||||||
#include "StateWrapper.h"
|
#include "StateWrapper.h"
|
||||||
#include "VMManager.h"
|
#include "VMManager.h"
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
#include "GameDatabase.h"
|
#include "GameDatabase.h"
|
||||||
#include "GS.h"
|
#include "GS.h"
|
||||||
#include "GSDumpReplayer.h"
|
#include "GSDumpReplayer.h"
|
||||||
#include "HostDisplay.h"
|
#include "Host.h"
|
||||||
#include "HostSettings.h"
|
#include "HostSettings.h"
|
||||||
#include "INISettingsInterface.h"
|
#include "INISettingsInterface.h"
|
||||||
#include "IopBios.h"
|
#include "IopBios.h"
|
||||||
|
@ -993,7 +993,7 @@ bool VMManager::Initialize(VMBootParameters boot_params)
|
||||||
ScopedGuard close_spu2(&SPU2::Close);
|
ScopedGuard close_spu2(&SPU2::Close);
|
||||||
|
|
||||||
Console.WriteLn("Opening PAD...");
|
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.");
|
Host::ReportErrorAsync("Startup Error", "Failed to initialize PAD.");
|
||||||
return false;
|
return false;
|
||||||
|
@ -1591,6 +1591,18 @@ void VMManager::SetPaused(bool paused)
|
||||||
SetState(paused ? VMState::Paused : VMState::Running);
|
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()
|
const std::string& VMManager::Internal::GetElfOverride()
|
||||||
{
|
{
|
||||||
return s_elf_override;
|
return s_elf_override;
|
||||||
|
|
|
@ -194,27 +194,19 @@
|
||||||
<ClCompile Include="DEV9\Win32\tap-win32.cpp" />
|
<ClCompile Include="DEV9\Win32\tap-win32.cpp" />
|
||||||
<ClCompile Include="Frontend\CommonHost.cpp" />
|
<ClCompile Include="Frontend\CommonHost.cpp" />
|
||||||
<ClCompile Include="Frontend\CommonHotkeys.cpp" />
|
<ClCompile Include="Frontend\CommonHotkeys.cpp" />
|
||||||
<ClCompile Include="Frontend\D3D11HostDisplay.cpp" />
|
|
||||||
<ClCompile Include="Frontend\D3D12HostDisplay.cpp" />
|
|
||||||
<ClCompile Include="Frontend\DInputSource.cpp" />
|
<ClCompile Include="Frontend\DInputSource.cpp" />
|
||||||
<ClCompile Include="Frontend\FullscreenUI.cpp" />
|
<ClCompile Include="Frontend\FullscreenUI.cpp" />
|
||||||
<ClCompile Include="Frontend\GameList.cpp" />
|
<ClCompile Include="Frontend\GameList.cpp" />
|
||||||
<ClCompile Include="Frontend\ImGuiFullscreen.cpp" />
|
<ClCompile Include="Frontend\ImGuiFullscreen.cpp" />
|
||||||
<ClCompile Include="Frontend\ImGuiManager.cpp" />
|
<ClCompile Include="Frontend\ImGuiManager.cpp" />
|
||||||
<ClCompile Include="Frontend\ImGuiOverlays.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="INISettingsInterface.cpp" />
|
||||||
<ClCompile Include="Frontend\InputManager.cpp" />
|
<ClCompile Include="Frontend\InputManager.cpp" />
|
||||||
<ClCompile Include="Frontend\InputSource.cpp" />
|
<ClCompile Include="Frontend\InputSource.cpp" />
|
||||||
<ClCompile Include="Frontend\LayeredSettingsInterface.cpp" />
|
<ClCompile Include="Frontend\LayeredSettingsInterface.cpp" />
|
||||||
<ClCompile Include="Frontend\LogSink.cpp" />
|
<ClCompile Include="Frontend\LogSink.cpp" />
|
||||||
<ClCompile Include="Frontend\OpenGLHostDisplay.cpp" />
|
|
||||||
<ClCompile Include="Frontend\Achievements.cpp" />
|
<ClCompile Include="Frontend\Achievements.cpp" />
|
||||||
<ClCompile Include="Frontend\SDLInputSource.cpp" />
|
<ClCompile Include="Frontend\SDLInputSource.cpp" />
|
||||||
<ClCompile Include="Frontend\VulkanHostDisplay.cpp" />
|
|
||||||
<ClCompile Include="Frontend\XInputSource.cpp" />
|
<ClCompile Include="Frontend\XInputSource.cpp" />
|
||||||
<ClCompile Include="GameDatabase.cpp" />
|
<ClCompile Include="GameDatabase.cpp" />
|
||||||
<ClCompile Include="Gif_Logger.cpp" />
|
<ClCompile Include="Gif_Logger.cpp" />
|
||||||
|
@ -228,7 +220,6 @@
|
||||||
<ClCompile Include="GS\Renderers\Vulkan\GSDeviceVK.cpp" />
|
<ClCompile Include="GS\Renderers\Vulkan\GSDeviceVK.cpp" />
|
||||||
<ClCompile Include="GS\Renderers\Vulkan\GSTextureVK.cpp" />
|
<ClCompile Include="GS\Renderers\Vulkan\GSTextureVK.cpp" />
|
||||||
<ClCompile Include="Host.cpp" />
|
<ClCompile Include="Host.cpp" />
|
||||||
<ClCompile Include="HostDisplay.cpp" />
|
|
||||||
<ClCompile Include="Frontend\HostSettings.cpp" />
|
<ClCompile Include="Frontend\HostSettings.cpp" />
|
||||||
<ClCompile Include="IopGte.cpp" />
|
<ClCompile Include="IopGte.cpp" />
|
||||||
<ClCompile Include="MemoryCardProtocol.cpp" />
|
<ClCompile Include="MemoryCardProtocol.cpp" />
|
||||||
|
@ -543,28 +534,20 @@
|
||||||
<ClInclude Include="DEV9\Win32\pcap_io_win32_funcs.h" />
|
<ClInclude Include="DEV9\Win32\pcap_io_win32_funcs.h" />
|
||||||
<ClInclude Include="DEV9\Win32\tap.h" />
|
<ClInclude Include="DEV9\Win32\tap.h" />
|
||||||
<ClInclude Include="Frontend\CommonHost.h" />
|
<ClInclude Include="Frontend\CommonHost.h" />
|
||||||
<ClInclude Include="Frontend\D3D11HostDisplay.h" />
|
|
||||||
<ClInclude Include="Frontend\D3D12HostDisplay.h" />
|
|
||||||
<ClInclude Include="Frontend\DInputSource.h" />
|
<ClInclude Include="Frontend\DInputSource.h" />
|
||||||
<ClInclude Include="Frontend\FullscreenUI.h" />
|
<ClInclude Include="Frontend\FullscreenUI.h" />
|
||||||
<ClInclude Include="Frontend\GameList.h" />
|
<ClInclude Include="Frontend\GameList.h" />
|
||||||
<ClInclude Include="Frontend\ImGuiFullscreen.h" />
|
<ClInclude Include="Frontend\ImGuiFullscreen.h" />
|
||||||
<ClInclude Include="Frontend\ImGuiManager.h" />
|
<ClInclude Include="Frontend\ImGuiManager.h" />
|
||||||
<ClInclude Include="Frontend\ImGuiOverlays.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="GS\Renderers\HW\GSHwHack.h" />
|
||||||
<ClInclude Include="INISettingsInterface.h" />
|
<ClInclude Include="INISettingsInterface.h" />
|
||||||
<ClInclude Include="Frontend\InputManager.h" />
|
<ClInclude Include="Frontend\InputManager.h" />
|
||||||
<ClInclude Include="Frontend\InputSource.h" />
|
<ClInclude Include="Frontend\InputSource.h" />
|
||||||
<ClInclude Include="Frontend\LayeredSettingsInterface.h" />
|
<ClInclude Include="Frontend\LayeredSettingsInterface.h" />
|
||||||
<ClInclude Include="Frontend\LogSink.h" />
|
<ClInclude Include="Frontend\LogSink.h" />
|
||||||
<ClInclude Include="Frontend\OpenGLHostDisplay.h" />
|
|
||||||
<ClInclude Include="Frontend\Achievements.h" />
|
<ClInclude Include="Frontend\Achievements.h" />
|
||||||
<ClInclude Include="Frontend\SDLInputSource.h" />
|
<ClInclude Include="Frontend\SDLInputSource.h" />
|
||||||
<ClInclude Include="Frontend\VulkanHostDisplay.h" />
|
|
||||||
<ClInclude Include="Frontend\XInputSource.h" />
|
<ClInclude Include="Frontend\XInputSource.h" />
|
||||||
<ClInclude Include="GameDatabase.h" />
|
<ClInclude Include="GameDatabase.h" />
|
||||||
<ClInclude Include="Gif_Unit.h" />
|
<ClInclude Include="Gif_Unit.h" />
|
||||||
|
@ -576,7 +559,6 @@
|
||||||
<ClInclude Include="GS\Renderers\Vulkan\GSDeviceVK.h" />
|
<ClInclude Include="GS\Renderers\Vulkan\GSDeviceVK.h" />
|
||||||
<ClInclude Include="GS\Renderers\Vulkan\GSTextureVK.h" />
|
<ClInclude Include="GS\Renderers\Vulkan\GSTextureVK.h" />
|
||||||
<ClInclude Include="Host.h" />
|
<ClInclude Include="Host.h" />
|
||||||
<ClInclude Include="HostDisplay.h" />
|
|
||||||
<ClInclude Include="HostSettings.h" />
|
<ClInclude Include="HostSettings.h" />
|
||||||
<ClInclude Include="IopGte.h" />
|
<ClInclude Include="IopGte.h" />
|
||||||
<ClInclude Include="IPU\mpeg2_vlc.h" />
|
<ClInclude Include="IPU\mpeg2_vlc.h" />
|
||||||
|
|
|
@ -1157,24 +1157,12 @@
|
||||||
<ClCompile Include="SPU2\SndOut_Cubeb.cpp">
|
<ClCompile Include="SPU2\SndOut_Cubeb.cpp">
|
||||||
<Filter>System\Ps2\SPU2</Filter>
|
<Filter>System\Ps2\SPU2</Filter>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="Frontend\ImGuiManager.cpp">
|
||||||
<Filter>Host</Filter>
|
<Filter>Host</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="Host.cpp">
|
<ClCompile Include="Host.cpp">
|
||||||
<Filter>Host</Filter>
|
<Filter>Host</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="Frontend\VulkanHostDisplay.cpp">
|
|
||||||
<Filter>Host</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="GS\Renderers\Vulkan\GSTextureVK.cpp">
|
<ClCompile Include="GS\Renderers\Vulkan\GSTextureVK.cpp">
|
||||||
<Filter>System\Ps2\GS\Renderers\Vulkan</Filter>
|
<Filter>System\Ps2\GS\Renderers\Vulkan</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -1235,21 +1223,6 @@
|
||||||
<ClCompile Include="GS\Renderers\DX12\GSDevice12.cpp">
|
<ClCompile Include="GS\Renderers\DX12\GSDevice12.cpp">
|
||||||
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
|
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="Recording\InputRecording.cpp">
|
||||||
<Filter>Tools\Input Recording</Filter>
|
<Filter>Tools\Input Recording</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -2074,21 +2047,9 @@
|
||||||
<ClInclude Include="Host.h">
|
<ClInclude Include="Host.h">
|
||||||
<Filter>Host</Filter>
|
<Filter>Host</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="Frontend\ImGuiManager.h">
|
||||||
<Filter>Host</Filter>
|
<Filter>Host</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="Frontend\VulkanHostDisplay.h">
|
|
||||||
<Filter>Host</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="GS\Renderers\Vulkan\GSTextureVK.h">
|
<ClInclude Include="GS\Renderers\Vulkan\GSTextureVK.h">
|
||||||
<Filter>System\Ps2\GS\Renderers\Vulkan</Filter>
|
<Filter>System\Ps2\GS\Renderers\Vulkan</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -2147,21 +2108,6 @@
|
||||||
<ClInclude Include="GS\Renderers\DX12\GSDevice12.h">
|
<ClInclude Include="GS\Renderers\DX12\GSDevice12.h">
|
||||||
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
|
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="Recording\InputRecording.h">
|
||||||
<Filter>Tools\Input Recording</Filter>
|
<Filter>Tools\Input Recording</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
#include "pcsx2/Frontend/ImGuiManager.h"
|
#include "pcsx2/Frontend/ImGuiManager.h"
|
||||||
#include "pcsx2/Frontend/InputManager.h"
|
#include "pcsx2/Frontend/InputManager.h"
|
||||||
#include "pcsx2/GS.h"
|
#include "pcsx2/GS.h"
|
||||||
|
#include "pcsx2/GS/GS.h"
|
||||||
#include "pcsx2/Host.h"
|
#include "pcsx2/Host.h"
|
||||||
#include "pcsx2/HostDisplay.h"
|
|
||||||
#include "pcsx2/HostSettings.h"
|
#include "pcsx2/HostSettings.h"
|
||||||
#include "pcsx2/VMManager.h"
|
#include "pcsx2/VMManager.h"
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ void Host::SetDefaultUISettings(SettingsInterface& si)
|
||||||
|
|
||||||
std::optional<std::vector<u8>> Host::ReadResourceFile(const char* filename)
|
std::optional<std::vector<u8>> Host::ReadResourceFile(const char* filename)
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> Host::ReadResourceFileToString(const char* filename)
|
std::optional<std::string> Host::ReadResourceFileToString(const char* filename)
|
||||||
|
@ -90,7 +90,7 @@ void Host::EndTextInput()
|
||||||
|
|
||||||
std::optional<WindowInfo> Host::GetTopLevelWindowInfo()
|
std::optional<WindowInfo> Host::GetTopLevelWindowInfo()
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::OnInputDeviceConnected(const std::string_view& identifier, const std::string_view& device_name)
|
void Host::OnInputDeviceConnected(const std::string_view& identifier, const std::string_view& device_name)
|
||||||
|
@ -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)
|
void Host::BeginPresentFrame()
|
||||||
{
|
|
||||||
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()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +219,7 @@ std::optional<std::string> InputManager::ConvertHostKeyboardCodeToString(u32 cod
|
||||||
|
|
||||||
SysMtgsThread& GetMTGS()
|
SysMtgsThread& GetMTGS()
|
||||||
{
|
{
|
||||||
throw std::exception();
|
throw std::exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
Loading…
Reference in New Issue