HostDisplay: Common texture base class for all APIs

This commit is contained in:
Connor McLaughlin
2022-10-03 16:44:34 +10:00
parent 12d400b76a
commit a9038133c8
50 changed files with 1428 additions and 1566 deletions

View File

@ -412,9 +412,9 @@ void Host::DisplayLoadingScreen(const char* message, int progress_min /*= -1*/,
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoBackground))
{
HostDisplayTexture* tex = ImGuiFullscreen::GetCachedTexture("images/duck.png");
GPUTexture* tex = ImGuiFullscreen::GetCachedTexture("images/duck.png");
if (tex)
ImGui::Image(tex->GetHandle(), ImVec2(logo_width, logo_height));
ImGui::Image(tex, ImVec2(logo_width, logo_height));
}
ImGui::End();

View File

@ -21,69 +21,6 @@ Log_SetChannel(D3D11HostDisplay);
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
class D3D11HostDisplayTexture final : public HostDisplayTexture
{
public:
D3D11HostDisplayTexture(D3D11::Texture texture, HostDisplayPixelFormat format, bool dynamic)
: m_texture(std::move(texture)), m_format(format), m_dynamic(dynamic)
{
}
~D3D11HostDisplayTexture() override = default;
void* GetHandle() const override { return const_cast<D3D11::Texture*>(&m_texture); }
u32 GetWidth() const override { return m_texture.GetWidth(); }
u32 GetHeight() const override { return m_texture.GetHeight(); }
u32 GetLayers() const override { return 1; }
u32 GetLevels() const override { return m_texture.GetLevels(); }
u32 GetSamples() const override { return m_texture.GetSamples(); }
HostDisplayPixelFormat GetFormat() const override { return m_format; }
bool BeginUpdate(u32 width, u32 height, void** out_buffer, u32* out_pitch) override
{
if (!m_dynamic || m_texture.GetWidth() != width || m_texture.GetHeight() != height)
return false;
D3D11_MAPPED_SUBRESOURCE sr;
HRESULT hr = static_cast<ID3D11DeviceContext*>(g_host_display->GetRenderContext())
->Map(m_texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &sr);
if (FAILED(hr))
{
Log_ErrorPrintf("Map pixels texture failed: %08X", hr);
return false;
}
*out_buffer = sr.pData;
*out_pitch = sr.RowPitch;
return true;
}
void EndUpdate(u32 x, u32 y, u32 width, u32 height)
{
static_cast<ID3D11DeviceContext*>(g_host_display->GetRenderContext())->Unmap(m_texture, 0);
}
bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch) override
{
if (m_dynamic)
return HostDisplayTexture::Update(x, y, width, height, data, pitch);
const CD3D11_BOX dst_box(x, y, 0, x + width, y + height, 1);
static_cast<ID3D11DeviceContext*>(g_host_display->GetRenderContext())
->UpdateSubresource(m_texture, 0, &dst_box, data, pitch, pitch * height);
return true;
}
ALWAYS_INLINE ID3D11Texture2D* GetD3DTexture() const { return m_texture.GetD3DTexture(); }
ALWAYS_INLINE ID3D11ShaderResourceView* GetD3DSRV() const { return m_texture.GetD3DSRV(); }
ALWAYS_INLINE ID3D11ShaderResourceView* const* GetD3DSRVArray() const { return m_texture.GetD3DSRVArray(); }
ALWAYS_INLINE bool IsDynamic() const { return m_dynamic; }
private:
D3D11::Texture m_texture;
HostDisplayPixelFormat m_format;
bool m_dynamic;
};
D3D11HostDisplay::D3D11HostDisplay() = default;
D3D11HostDisplay::~D3D11HostDisplay()
@ -120,34 +57,61 @@ bool D3D11HostDisplay::HasRenderSurface() const
return static_cast<bool>(m_swap_chain);
}
static constexpr std::array<DXGI_FORMAT, static_cast<u32>(HostDisplayPixelFormat::Count)>
s_display_pixel_format_mapping = {{DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM}};
std::unique_ptr<HostDisplayTexture> D3D11HostDisplay::CreateTexture(u32 width, u32 height, u32 layers, u32 levels,
u32 samples, HostDisplayPixelFormat format,
const void* data, u32 data_stride,
bool dynamic /* = false */)
std::unique_ptr<GPUTexture> D3D11HostDisplay::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Format format, const void* data,
u32 data_stride, bool dynamic /* = false */)
{
if (layers != 1)
return {};
D3D11::Texture tex;
if (!tex.Create(m_device.Get(), width, height, layers, levels, samples,
s_display_pixel_format_mapping[static_cast<u32>(format)], D3D11_BIND_SHADER_RESOURCE, data,
data_stride, dynamic))
std::unique_ptr<D3D11::Texture> tex(std::make_unique<D3D11::Texture>());
if (!tex->Create(m_device.Get(), width, height, layers, levels, samples, format, D3D11_BIND_SHADER_RESOURCE, data,
data_stride, dynamic))
{
return {};
tex.reset();
}
return std::make_unique<D3D11HostDisplayTexture>(std::move(tex), format, dynamic);
return tex;
}
bool D3D11HostDisplay::DownloadTexture(const void* texture_handle, HostDisplayPixelFormat texture_format, u32 x, u32 y,
u32 width, u32 height, void* out_data, u32 out_data_stride)
bool D3D11HostDisplay::BeginTextureUpdate(GPUTexture* texture, u32 width, u32 height, void** out_buffer, u32* out_pitch)
{
const D3D11::Texture* tex = static_cast<const D3D11::Texture*>(texture_handle);
if (!CheckStagingBufferSize(width, height, tex->GetFormat()))
D3D11::Texture* tex = static_cast<D3D11::Texture*>(texture);
if (!tex->IsDynamic() || tex->GetWidth() != width || tex->GetHeight() != height)
return false;
D3D11_MAPPED_SUBRESOURCE sr;
HRESULT hr = m_context->Map(tex->GetD3DTexture(), 0, D3D11_MAP_WRITE_DISCARD, 0, &sr);
if (FAILED(hr))
{
Log_ErrorPrintf("Map pixels texture failed: %08X", hr);
return false;
}
*out_buffer = sr.pData;
*out_pitch = sr.RowPitch;
return true;
}
void D3D11HostDisplay::EndTextureUpdate(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height)
{
D3D11::Texture* tex = static_cast<D3D11::Texture*>(texture);
m_context->Unmap(tex->GetD3DTexture(), 0);
}
bool D3D11HostDisplay::UpdateTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch)
{
D3D11::Texture* tex = static_cast<D3D11::Texture*>(texture);
if (tex->IsDynamic())
return HostDisplay::UpdateTexture(texture, x, y, width, height, data, pitch);
const CD3D11_BOX dst_box(x, y, 0, x + width, y + height, 1);
m_context->UpdateSubresource(tex->GetD3DTexture(), 0, &dst_box, data, pitch, pitch * height);
return true;
}
bool D3D11HostDisplay::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data,
u32 out_data_stride)
{
const D3D11::Texture* tex = static_cast<const D3D11::Texture*>(texture);
if (!CheckStagingBufferSize(width, height, tex->GetDXGIFormat()))
return false;
const CD3D11_BOX box(static_cast<LONG>(x), static_cast<LONG>(y), 0, static_cast<LONG>(x + width),
@ -162,7 +126,7 @@ bool D3D11HostDisplay::DownloadTexture(const void* texture_handle, HostDisplayPi
return false;
}
const u32 copy_size = GetDisplayPixelFormatSize(texture_format) * width;
const u32 copy_size = tex->GetPixelSize() * width;
StringUtil::StrideMemCpy(out_data, out_data_stride, sr.pData, sr.RowPitch, copy_size, height);
m_context->Unmap(m_readback_staging_texture.Get(), 0);
return true;
@ -195,9 +159,9 @@ void D3D11HostDisplay::DestroyStagingBuffer()
m_readback_staging_texture_format = DXGI_FORMAT_UNKNOWN;
}
bool D3D11HostDisplay::SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const
bool D3D11HostDisplay::SupportsTextureFormat(GPUTexture::Format format) const
{
const DXGI_FORMAT dfmt = s_display_pixel_format_mapping[static_cast<u32>(format)];
const DXGI_FORMAT dfmt = D3D11::Texture::GetDXGIFormat(format);
if (dfmt == DXGI_FORMAT_UNKNOWN)
return false;
@ -767,13 +731,12 @@ bool D3D11HostDisplay::Render(bool skip_present)
}
bool D3D11HostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
HostDisplayPixelFormat* out_format)
GPUTexture::Format* out_format)
{
static constexpr DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM;
static constexpr HostDisplayPixelFormat hdformat = HostDisplayPixelFormat::RGBA8;
static constexpr GPUTexture::Format hdformat = GPUTexture::Format::RGBA8;
D3D11::Texture render_texture;
if (!render_texture.Create(m_device.Get(), width, height, 1, 1, 1, format, D3D11_BIND_RENDER_TARGET))
if (!render_texture.Create(m_device.Get(), width, height, 1, 1, 1, hdformat, D3D11_BIND_RENDER_TARGET))
return false;
static constexpr std::array<float, 4> clear_color = {};
@ -786,24 +749,24 @@ bool D3D11HostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>*
if (!m_post_processing_chain.IsEmpty())
{
ApplyPostProcessingChain(render_texture.GetD3DRTV(), left, top, draw_width, draw_height, m_display_texture_handle,
m_display_texture_width, m_display_texture_height, m_display_texture_view_x,
ApplyPostProcessingChain(render_texture.GetD3DRTV(), left, top, draw_width, draw_height,
static_cast<D3D11::Texture*>(m_display_texture), m_display_texture_view_x,
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
width, height);
}
else
{
RenderDisplay(left, top, draw_width, draw_height, m_display_texture_handle, m_display_texture_width,
m_display_texture_height, m_display_texture_view_x, m_display_texture_view_y,
m_display_texture_view_width, m_display_texture_view_height, IsUsingLinearFiltering());
RenderDisplay(left, top, draw_width, draw_height, static_cast<D3D11::Texture*>(m_display_texture),
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, IsUsingLinearFiltering());
}
}
m_context->OMSetRenderTargets(0, nullptr, nullptr);
const u32 stride = GetDisplayPixelFormatSize(hdformat) * width;
const u32 stride = GPUTexture::GetPixelSize(hdformat) * width;
out_pixels->resize(width * height);
if (!DownloadTexture(&render_texture, hdformat, 0, 0, width, height, out_pixels->data(), stride))
if (!DownloadTexture(&render_texture, 0, 0, width, height, out_pixels->data(), stride))
return false;
*out_stride = stride;
@ -826,36 +789,36 @@ void D3D11HostDisplay::RenderDisplay()
if (!m_post_processing_chain.IsEmpty())
{
ApplyPostProcessingChain(m_swap_chain_rtv.Get(), left, top, width, height, m_display_texture_handle,
m_display_texture_width, m_display_texture_height, m_display_texture_view_x,
ApplyPostProcessingChain(m_swap_chain_rtv.Get(), left, top, width, height,
static_cast<D3D11::Texture*>(m_display_texture), m_display_texture_view_x,
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
GetWindowWidth(), GetWindowHeight());
return;
}
RenderDisplay(left, top, width, height, m_display_texture_handle, m_display_texture_width, m_display_texture_height,
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, IsUsingLinearFiltering());
RenderDisplay(left, top, width, height, static_cast<D3D11::Texture*>(m_display_texture), m_display_texture_view_x,
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
IsUsingLinearFiltering());
}
void D3D11HostDisplay::RenderDisplay(s32 left, s32 top, s32 width, s32 height, void* texture_handle, u32 texture_width,
s32 texture_height, s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
void D3D11HostDisplay::RenderDisplay(s32 left, s32 top, s32 width, s32 height, D3D11::Texture* texture,
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
s32 texture_view_height, bool linear_filter)
{
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_context->VSSetShader(m_display_vertex_shader.Get(), nullptr, 0);
m_context->PSSetShader(m_display_pixel_shader.Get(), nullptr, 0);
m_context->PSSetShaderResources(0, 1, static_cast<D3D11::Texture*>(texture_handle)->GetD3DSRVArray());
m_context->PSSetShaderResources(0, 1, texture->GetD3DSRVArray());
m_context->PSSetSamplers(0, 1, linear_filter ? m_linear_sampler.GetAddressOf() : m_point_sampler.GetAddressOf());
const bool linear = IsUsingLinearFiltering();
const float position_adjust = linear ? 0.5f : 0.0f;
const float size_adjust = linear ? 1.0f : 0.0f;
const float uniforms[4] = {
(static_cast<float>(texture_view_x) + position_adjust) / static_cast<float>(texture_width),
(static_cast<float>(texture_view_y) + position_adjust) / static_cast<float>(texture_height),
(static_cast<float>(texture_view_width) - size_adjust) / static_cast<float>(texture_width),
(static_cast<float>(texture_view_height) - size_adjust) / static_cast<float>(texture_height)};
(static_cast<float>(texture_view_x) + position_adjust) / static_cast<float>(texture->GetWidth()),
(static_cast<float>(texture_view_y) + position_adjust) / static_cast<float>(texture->GetHeight()),
(static_cast<float>(texture_view_width) - size_adjust) / static_cast<float>(texture->GetWidth()),
(static_cast<float>(texture_view_height) - size_adjust) / static_cast<float>(texture->GetHeight())};
const auto map = m_display_uniform_buffer.Map(m_context.Get(), m_display_uniform_buffer.GetSize(), sizeof(uniforms));
std::memcpy(map.pointer, uniforms, sizeof(uniforms));
m_display_uniform_buffer.Unmap(m_context.Get(), sizeof(uniforms));
@ -880,13 +843,12 @@ void D3D11HostDisplay::RenderSoftwareCursor()
RenderSoftwareCursor(left, top, width, height, m_cursor_texture.get());
}
void D3D11HostDisplay::RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height,
HostDisplayTexture* texture_handle)
void D3D11HostDisplay::RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, GPUTexture* texture_handle)
{
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_context->VSSetShader(m_display_vertex_shader.Get(), nullptr, 0);
m_context->PSSetShader(m_display_alpha_pixel_shader.Get(), nullptr, 0);
m_context->PSSetShaderResources(0, 1, static_cast<D3D11HostDisplayTexture*>(texture_handle)->GetD3DSRVArray());
m_context->PSSetShaderResources(0, 1, static_cast<D3D11::Texture*>(texture_handle)->GetD3DSRVArray());
m_context->PSSetSamplers(0, 1, m_linear_sampler.GetAddressOf());
const float uniforms[4] = {0.0f, 0.0f, 1.0f, 1.0f};
@ -1052,7 +1014,7 @@ bool D3D11HostDisplay::CheckPostProcessingRenderTargets(u32 target_width, u32 ta
{
DebugAssert(!m_post_processing_stages.empty());
const DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM;
const GPUTexture::Format format = GPUTexture::Format::RGBA8;
const u32 bind_flags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
if (m_post_processing_input_texture.GetWidth() != target_width ||
@ -1080,29 +1042,26 @@ bool D3D11HostDisplay::CheckPostProcessingRenderTargets(u32 target_width, u32 ta
}
void D3D11HostDisplay::ApplyPostProcessingChain(ID3D11RenderTargetView* final_target, s32 final_left, s32 final_top,
s32 final_width, s32 final_height, void* texture_handle,
u32 texture_width, s32 texture_height, s32 texture_view_x,
s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
u32 target_width, u32 target_height)
s32 final_width, s32 final_height, D3D11::Texture* texture,
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
s32 texture_view_height, u32 target_width, u32 target_height)
{
static constexpr std::array<float, 4> clear_color = {0.0f, 0.0f, 0.0f, 1.0f};
if (!CheckPostProcessingRenderTargets(target_width, target_height))
{
RenderDisplay(final_left, final_top, final_width, final_height, texture_handle, texture_width, texture_height,
texture_view_x, texture_view_y, texture_view_width, texture_view_height, IsUsingLinearFiltering());
RenderDisplay(final_left, final_top, final_width, final_height, texture, texture_view_x, texture_view_y,
texture_view_width, texture_view_height, IsUsingLinearFiltering());
return;
}
// downsample/upsample - use same viewport for remainder
m_context->ClearRenderTargetView(m_post_processing_input_texture.GetD3DRTV(), clear_color.data());
m_context->OMSetRenderTargets(1, m_post_processing_input_texture.GetD3DRTVArray(), nullptr);
RenderDisplay(final_left, final_top, final_width, final_height, texture_handle, texture_width, texture_height,
texture_view_x, texture_view_y, texture_view_width, texture_view_height, IsUsingLinearFiltering());
RenderDisplay(final_left, final_top, final_width, final_height, texture, texture_view_x, texture_view_y,
texture_view_width, texture_view_height, IsUsingLinearFiltering());
texture_handle = &m_post_processing_input_texture;
texture_width = m_post_processing_input_texture.GetWidth();
texture_height = m_post_processing_input_texture.GetHeight();
texture = &m_post_processing_input_texture;
texture_view_x = final_left;
texture_view_y = final_top;
texture_view_width = final_width;
@ -1125,13 +1084,13 @@ void D3D11HostDisplay::ApplyPostProcessingChain(ID3D11RenderTargetView* final_ta
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_context->VSSetShader(pps.vertex_shader.Get(), nullptr, 0);
m_context->PSSetShader(pps.pixel_shader.Get(), nullptr, 0);
m_context->PSSetShaderResources(0, 1, static_cast<D3D11::Texture*>(texture_handle)->GetD3DSRVArray());
m_context->PSSetShaderResources(0, 1, texture->GetD3DSRVArray());
m_context->PSSetSamplers(0, 1, m_point_sampler.GetAddressOf());
const auto map =
m_display_uniform_buffer.Map(m_context.Get(), m_display_uniform_buffer.GetSize(), pps.uniforms_size);
m_post_processing_chain.GetShaderStage(i).FillUniformBuffer(
map.pointer, texture_width, texture_height, texture_view_x, texture_view_y, texture_view_width,
map.pointer, texture->GetWidth(), texture->GetHeight(), texture_view_x, texture_view_y, texture_view_width,
texture_view_height, GetWindowWidth(), GetWindowHeight(), 0.0f);
m_display_uniform_buffer.Unmap(m_context.Get(), pps.uniforms_size);
m_context->VSSetConstantBuffers(0, 1, m_display_uniform_buffer.GetD3DBufferArray());
@ -1140,7 +1099,7 @@ void D3D11HostDisplay::ApplyPostProcessingChain(ID3D11RenderTargetView* final_ta
m_context->Draw(3, 0);
if (i != final_stage)
texture_handle = &pps.output_texture;
texture = &pps.output_texture;
}
ID3D11ShaderResourceView* null_srv = nullptr;

View File

@ -47,12 +47,15 @@ public:
bool SetPostProcessingChain(const std::string_view& config) override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
HostDisplayPixelFormat format, const void* data, u32 data_stride,
bool dynamic = false) override;
bool DownloadTexture(const void* texture_handle, HostDisplayPixelFormat texture_format, u32 x, u32 y, u32 width,
u32 height, void* out_data, u32 out_data_stride) override;
bool SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const override;
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Format format, const void* data, u32 data_stride,
bool dynamic = false) override;
bool BeginTextureUpdate(GPUTexture* texture, u32 width, u32 height, void** out_buffer, u32* out_pitch) override;
void EndTextureUpdate(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height) override;
bool UpdateTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch) override;
bool DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data,
u32 out_data_stride) override;
bool SupportsTextureFormat(GPUTexture::Format format) const override;
bool GetHostRefreshRate(float* refresh_rate) override;
@ -63,7 +66,7 @@ public:
bool Render(bool skip_present) override;
bool RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
HostDisplayPixelFormat* out_format) override;
GPUTexture::Format* out_format) override;
static AdapterAndModeList StaticGetAdapterAndModeList();
@ -90,10 +93,9 @@ protected:
void RenderSoftwareCursor();
void RenderImGui();
void RenderDisplay(s32 left, s32 top, s32 width, s32 height, void* texture_handle, u32 texture_width,
s32 texture_height, s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
s32 texture_view_height, bool linear_filter);
void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, HostDisplayTexture* texture_handle);
void RenderDisplay(s32 left, s32 top, s32 width, s32 height, D3D11::Texture* texture, s32 texture_view_x,
s32 texture_view_y, s32 texture_view_width, s32 texture_view_height, bool linear_filter);
void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, GPUTexture* texture_handle);
struct PostProcessingStage
{
@ -105,9 +107,8 @@ protected:
bool CheckPostProcessingRenderTargets(u32 target_width, u32 target_height);
void ApplyPostProcessingChain(ID3D11RenderTargetView* final_target, s32 final_left, s32 final_top, s32 final_width,
s32 final_height, void* texture_handle, u32 texture_width, s32 texture_height,
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
u32 target_width, u32 target_height);
s32 final_height, D3D11::Texture* texture, s32 texture_view_x, s32 texture_view_y,
s32 texture_view_width, s32 texture_view_height, u32 target_width, u32 target_height);
bool CreateTimestampQueries();
void DestroyTimestampQueries();

View File

@ -15,51 +15,6 @@
#include <dxgi1_5.h>
Log_SetChannel(D3D12HostDisplay);
static constexpr std::array<DXGI_FORMAT, static_cast<u32>(HostDisplayPixelFormat::Count)>
s_display_pixel_format_mapping = {{DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM}};
class D3D12HostDisplayTexture final : public HostDisplayTexture
{
public:
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(); }
u32 GetLayers() const override { return 1; }
u32 GetLevels() const override { return 1; }
u32 GetSamples() const override { return m_texture.GetSamples(); }
HostDisplayPixelFormat GetFormat() const override
{
for (u32 i = 0; i < static_cast<u32>(s_display_pixel_format_mapping.size()); i++)
{
if (m_texture.GetFormat() == s_display_pixel_format_mapping[i])
return static_cast<HostDisplayPixelFormat>(i);
}
return HostDisplayPixelFormat::Count;
}
bool BeginUpdate(u32 width, u32 height, void** out_buffer, u32* out_pitch) override
{
return m_texture.BeginStreamUpdate(0, 0, width, height, out_buffer, out_pitch);
}
void EndUpdate(u32 x, u32 y, u32 width, u32 height) override
{
m_texture.EndStreamUpdate(x, y, width, height);
}
const D3D12::Texture& GetTexture() const { return m_texture; }
D3D12::Texture& GetTexture() { return m_texture; }
private:
D3D12::Texture m_texture;
};
D3D12HostDisplay::D3D12HostDisplay() = default;
D3D12HostDisplay::~D3D12HostDisplay()
@ -98,47 +53,62 @@ bool D3D12HostDisplay::HasRenderSurface() const
return static_cast<bool>(m_swap_chain);
}
std::unique_ptr<HostDisplayTexture> D3D12HostDisplay::CreateTexture(u32 width, u32 height, u32 layers, u32 levels,
u32 samples, HostDisplayPixelFormat format,
const void* data, u32 data_stride,
bool dynamic /* = false */)
std::unique_ptr<GPUTexture> D3D12HostDisplay::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Format format, const void* data,
u32 data_stride, bool dynamic /* = false */)
{
if (layers != 1)
const DXGI_FORMAT dformat = D3D12::Texture::GetDXGIFormat(format);
if (dformat == DXGI_FORMAT_UNKNOWN)
return {};
const DXGI_FORMAT dxgi_format = s_display_pixel_format_mapping[static_cast<u32>(format)];
D3D12::Texture tex;
if (!tex.Create(width, height, samples, dxgi_format, dxgi_format, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN,
D3D12_RESOURCE_FLAG_NONE))
std::unique_ptr<D3D12::Texture> tex(std::make_unique<D3D12::Texture>());
if (!tex->Create(width, height, layers, levels, samples, dformat, dformat, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN,
D3D12_RESOURCE_FLAG_NONE))
{
return {};
}
if (data && !tex.LoadData(0, 0, width, height, data, data_stride))
if (data && !tex->LoadData(0, 0, width, height, data, data_stride))
return {};
return std::make_unique<D3D12HostDisplayTexture>(std::move(tex));
return tex;
}
bool D3D12HostDisplay::DownloadTexture(const void* texture_handle, HostDisplayPixelFormat texture_format, u32 x, u32 y,
u32 width, u32 height, void* out_data, u32 out_data_stride)
bool D3D12HostDisplay::BeginTextureUpdate(GPUTexture* texture, u32 width, u32 height, void** out_buffer, u32* out_pitch)
{
const D3D12::Texture* texture = static_cast<const D3D12::Texture*>(texture_handle);
return static_cast<D3D12::Texture*>(texture)->BeginStreamUpdate(0, 0, width, height, out_buffer, out_pitch);
}
if (!m_readback_staging_texture.EnsureSize(width, height, texture->GetFormat(), false))
void D3D12HostDisplay::EndTextureUpdate(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height)
{
static_cast<D3D12::Texture*>(texture)->EndStreamUpdate(x, y, width, height);
}
bool D3D12HostDisplay::UpdateTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data,
u32 pitch)
{
return HostDisplay::UpdateTexture(texture, x, y, width, height, data, pitch);
}
bool D3D12HostDisplay::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data,
u32 out_data_stride)
{
const D3D12::Texture* tex = static_cast<const D3D12::Texture*>(texture);
if (!m_readback_staging_texture.EnsureSize(width, height, tex->GetDXGIFormat(), false))
return false;
const D3D12_RESOURCE_STATES old_state = texture->GetState();
texture->TransitionToState(D3D12_RESOURCE_STATE_COPY_SOURCE);
m_readback_staging_texture.CopyFromTexture(texture->GetResource(), 0, x, y, 0, 0, width, height);
texture->TransitionToState(old_state);
const D3D12_RESOURCE_STATES old_state = tex->GetState();
tex->TransitionToState(D3D12_RESOURCE_STATE_COPY_SOURCE);
m_readback_staging_texture.CopyFromTexture(tex->GetResource(), 0, x, y, 0, 0, width, height);
tex->TransitionToState(old_state);
return m_readback_staging_texture.ReadPixels(0, 0, width, height, out_data, out_data_stride);
}
bool D3D12HostDisplay::SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const
bool D3D12HostDisplay::SupportsTextureFormat(GPUTexture::Format format) const
{
const DXGI_FORMAT dfmt = s_display_pixel_format_mapping[static_cast<u32>(format)];
const DXGI_FORMAT dfmt = D3D12::Texture::GetDXGIFormat(format);
if (dfmt == DXGI_FORMAT_UNKNOWN)
return false;
@ -654,13 +624,13 @@ bool D3D12HostDisplay::Render(bool skip_present)
}
bool D3D12HostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
HostDisplayPixelFormat* out_format)
GPUTexture::Format* out_format)
{
static constexpr DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM;
static constexpr HostDisplayPixelFormat hdformat = HostDisplayPixelFormat::RGBA8;
static constexpr GPUTexture::Format hdformat = GPUTexture::Format::RGBA8;
D3D12::Texture render_texture;
if (!render_texture.Create(width, height, 1, format, DXGI_FORMAT_UNKNOWN, format, DXGI_FORMAT_UNKNOWN,
if (!render_texture.Create(width, height, 1, 1, 1, format, DXGI_FORMAT_UNKNOWN, format, DXGI_FORMAT_UNKNOWN,
D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) ||
!m_readback_staging_texture.EnsureSize(width, height, format, false))
{
@ -676,9 +646,9 @@ bool D3D12HostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>*
if (HasDisplayTexture())
{
const auto [left, top, draw_width, draw_height] = CalculateDrawRect(width, height, 0);
RenderDisplay(cmdlist, left, top, draw_width, draw_height, m_display_texture_handle, m_display_texture_width,
m_display_texture_height, m_display_texture_view_x, m_display_texture_view_y,
m_display_texture_view_width, m_display_texture_view_height, IsUsingLinearFiltering());
RenderDisplay(cmdlist, left, top, draw_width, draw_height, static_cast<D3D12::Texture*>(m_display_texture),
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, IsUsingLinearFiltering());
}
cmdlist->OMSetRenderTargets(0, nullptr, FALSE, nullptr);
@ -728,23 +698,22 @@ void D3D12HostDisplay::RenderDisplay(ID3D12GraphicsCommandList* cmdlist)
// return;
// }
RenderDisplay(cmdlist, left, top, width, height, m_display_texture_handle, m_display_texture_width,
m_display_texture_height, m_display_texture_view_x, m_display_texture_view_y,
m_display_texture_view_width, m_display_texture_view_height, IsUsingLinearFiltering());
RenderDisplay(cmdlist, left, top, width, height, static_cast<D3D12::Texture*>(m_display_texture),
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, IsUsingLinearFiltering());
}
void D3D12HostDisplay::RenderDisplay(ID3D12GraphicsCommandList* cmdlist, s32 left, s32 top, s32 width, s32 height,
void* texture_handle, u32 texture_width, s32 texture_height, s32 texture_view_x,
s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
bool linear_filter)
D3D12::Texture* texture, s32 texture_view_x, s32 texture_view_y,
s32 texture_view_width, s32 texture_view_height, bool linear_filter)
{
const float position_adjust = linear_filter ? 0.5f : 0.0f;
const float size_adjust = linear_filter ? 1.0f : 0.0f;
const float uniforms[4] = {
(static_cast<float>(texture_view_x) + position_adjust) / static_cast<float>(texture_width),
(static_cast<float>(texture_view_y) + position_adjust) / static_cast<float>(texture_height),
(static_cast<float>(texture_view_width) - size_adjust) / static_cast<float>(texture_width),
(static_cast<float>(texture_view_height) - size_adjust) / static_cast<float>(texture_height)};
(static_cast<float>(texture_view_x) + position_adjust) / static_cast<float>(texture->GetWidth()),
(static_cast<float>(texture_view_y) + position_adjust) / static_cast<float>(texture->GetHeight()),
(static_cast<float>(texture_view_width) - size_adjust) / static_cast<float>(texture->GetWidth()),
(static_cast<float>(texture_view_height) - size_adjust) / static_cast<float>(texture->GetHeight())};
if (!m_display_uniform_buffer.ReserveMemory(sizeof(uniforms), D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT))
Panic("Failed to reserve UBO space");
@ -755,7 +724,7 @@ void D3D12HostDisplay::RenderDisplay(ID3D12GraphicsCommandList* cmdlist, s32 lef
cmdlist->SetGraphicsRootSignature(m_display_root_signature.Get());
cmdlist->SetPipelineState(m_display_pipeline.Get());
cmdlist->SetGraphicsRootConstantBufferView(0, m_display_uniform_buffer.GetGPUPointer() + ubo_offset);
cmdlist->SetGraphicsRootDescriptorTable(1, reinterpret_cast<D3D12::Texture*>(texture_handle)->GetSRVDescriptor());
cmdlist->SetGraphicsRootDescriptorTable(1, texture->GetSRVDescriptor());
cmdlist->SetGraphicsRootDescriptorTable(2, linear_filter ? m_linear_sampler : m_point_sampler);
D3D12::SetViewportAndScissor(cmdlist, left, top, width, height);
@ -774,7 +743,7 @@ void D3D12HostDisplay::RenderSoftwareCursor(ID3D12GraphicsCommandList* cmdlist)
}
void D3D12HostDisplay::RenderSoftwareCursor(ID3D12GraphicsCommandList* cmdlist, s32 left, s32 top, s32 width,
s32 height, HostDisplayTexture* texture_handle)
s32 height, GPUTexture* texture_handle)
{
const float uniforms[4] = {0.0f, 0.0f, 1.0f, 1.0f};
if (!m_display_uniform_buffer.ReserveMemory(sizeof(uniforms), D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT))
@ -786,8 +755,7 @@ void D3D12HostDisplay::RenderSoftwareCursor(ID3D12GraphicsCommandList* cmdlist,
cmdlist->SetPipelineState(m_display_pipeline.Get());
cmdlist->SetGraphicsRootConstantBufferView(0, m_display_uniform_buffer.GetGPUPointer() + ubo_offset);
cmdlist->SetGraphicsRootDescriptorTable(
1, static_cast<D3D12HostDisplayTexture*>(texture_handle)->GetTexture().GetRTVOrDSVDescriptor());
cmdlist->SetGraphicsRootDescriptorTable(1, static_cast<D3D12::Texture*>(texture_handle)->GetRTVOrDSVDescriptor());
cmdlist->SetGraphicsRootDescriptorTable(2, m_linear_sampler);
D3D12::SetViewportAndScissor(cmdlist, left, top, width, height);

View File

@ -49,12 +49,15 @@ public:
bool SetPostProcessingChain(const std::string_view& config) override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
HostDisplayPixelFormat format, const void* data, u32 data_stride,
bool dynamic = false) override;
bool DownloadTexture(const void* texture_handle, HostDisplayPixelFormat texture_format, u32 x, u32 y, u32 width,
u32 height, void* out_data, u32 out_data_stride) override;
bool SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const override;
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Format format, const void* data, u32 data_stride,
bool dynamic = false) override;
bool BeginTextureUpdate(GPUTexture* texture, u32 width, u32 height, void** out_buffer, u32* out_pitch) override;
void EndTextureUpdate(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height) override;
bool UpdateTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch) override;
bool DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data,
u32 out_data_stride) override;
bool SupportsTextureFormat(GPUTexture::Format format) const override;
bool GetHostRefreshRate(float* refresh_rate) override;
@ -62,7 +65,7 @@ public:
bool Render(bool skip_present) override;
bool RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
HostDisplayPixelFormat* out_format) override;
GPUTexture::Format* out_format) override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
@ -93,11 +96,11 @@ protected:
void RenderSoftwareCursor(ID3D12GraphicsCommandList* cmdlist);
void RenderImGui(ID3D12GraphicsCommandList* cmdlist);
void RenderDisplay(ID3D12GraphicsCommandList* cmdlist, s32 left, s32 top, s32 width, s32 height, void* texture_handle,
u32 texture_width, s32 texture_height, s32 texture_view_x, s32 texture_view_y,
s32 texture_view_width, s32 texture_view_height, bool linear_filter);
void RenderDisplay(ID3D12GraphicsCommandList* cmdlist, s32 left, s32 top, s32 width, s32 height,
D3D12::Texture* texture, s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
s32 texture_view_height, bool linear_filter);
void RenderSoftwareCursor(ID3D12GraphicsCommandList* cmdlist, s32 left, s32 top, s32 width, s32 height,
HostDisplayTexture* texture_handle);
GPUTexture* texture_handle);
ComPtr<IDXGIFactory> m_dxgi_factory;
ComPtr<IDXGISwapChain> m_swap_chain;

View File

@ -218,14 +218,14 @@ static std::deque<AsyncOpEntry> s_async_ops;
static bool LoadResources();
static void DestroyResources();
static std::shared_ptr<HostDisplayTexture> s_app_icon_texture;
static std::array<std::shared_ptr<HostDisplayTexture>, static_cast<u32>(GameDatabase::CompatibilityRating::Count)>
static std::shared_ptr<GPUTexture> s_app_icon_texture;
static std::array<std::shared_ptr<GPUTexture>, static_cast<u32>(GameDatabase::CompatibilityRating::Count)>
s_game_compatibility_textures;
static std::shared_ptr<HostDisplayTexture> s_fallback_disc_texture;
static std::shared_ptr<HostDisplayTexture> s_fallback_exe_texture;
static std::shared_ptr<HostDisplayTexture> s_fallback_psf_texture;
static std::shared_ptr<HostDisplayTexture> s_fallback_playlist_texture;
static std::vector<std::unique_ptr<HostDisplayTexture>> s_cleanup_textures;
static std::shared_ptr<GPUTexture> s_fallback_disc_texture;
static std::shared_ptr<GPUTexture> s_fallback_exe_texture;
static std::shared_ptr<GPUTexture> s_fallback_psf_texture;
static std::shared_ptr<GPUTexture> s_fallback_playlist_texture;
static std::vector<std::unique_ptr<GPUTexture>> s_cleanup_textures;
//////////////////////////////////////////////////////////////////////////
// Landing
@ -375,7 +375,7 @@ struct SaveStateListEntry
std::string title;
std::string summary;
std::string path;
std::unique_ptr<HostDisplayTexture> preview_texture;
std::unique_ptr<GPUTexture> preview_texture;
time_t timestamp;
s32 slot;
bool global;
@ -415,9 +415,9 @@ static void HandleGameListOptions(const GameList::Entry* entry);
static void DrawGameListSettingsPage(const ImVec2& heading_size);
static void SwitchToGameList();
static void PopulateGameListEntryList();
static HostDisplayTexture* GetTextureForGameListEntryType(GameList::EntryType type);
static HostDisplayTexture* GetGameListCover(const GameList::Entry* entry);
static HostDisplayTexture* GetCoverForCurrentGame();
static GPUTexture* GetTextureForGameListEntryType(GameList::EntryType type);
static GPUTexture* GetGameListCover(const GameList::Entry* entry);
static GPUTexture* GetCoverForCurrentGame();
// Lazily populated cover images.
static std::unordered_map<std::string, std::string> s_cover_image_map;
@ -717,7 +717,7 @@ void FullscreenUI::Render()
if (!s_initialized)
return;
for (std::unique_ptr<HostDisplayTexture>& tex : s_cleanup_textures)
for (std::unique_ptr<GPUTexture>& tex : s_cleanup_textures)
tex.reset();
s_cleanup_textures.clear();
ImGuiFullscreen::UploadAsyncTextures();
@ -1077,7 +1077,7 @@ void FullscreenUI::DrawLandingWindow()
const float image_size = LayoutScale(380.f);
ImGui::SetCursorPos(ImVec2((ImGui::GetWindowWidth() * 0.5f) - (image_size * 0.5f),
(ImGui::GetWindowHeight() * 0.5f) - (image_size * 0.5f)));
ImGui::Image(s_app_icon_texture->GetHandle(), ImVec2(image_size, image_size));
ImGui::Image(s_app_icon_texture.get(), ImVec2(image_size, image_size));
}
EndFullscreenColumnWindow();
@ -3885,7 +3885,7 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
const ImVec2 image_min(display_size.x - LayoutScale(20.0f + 50.0f) - rp_height,
display_size.y - LayoutScale(20.0f + 50.0f) - rp_height);
const ImVec2 image_max(image_min.x + LayoutScale(50.0f) + rp_height, image_min.y + LayoutScale(50.0f) + rp_height);
dl->AddImage(GetCoverForCurrentGame()->GetHandle(), image_min, image_max);
dl->AddImage(GetCoverForCurrentGame(), image_min, image_max);
}
const ImVec2 window_size(LayoutScale(500.0f, LAYOUT_SCREEN_HEIGHT));
@ -4087,15 +4087,15 @@ void FullscreenUI::PopulateSaveStateScreenshot(SaveStateListEntry* li, const Ext
li->preview_texture.reset();
if (ssi && !ssi->screenshot_data.empty())
{
li->preview_texture = g_host_display->CreateTexture(ssi->screenshot_width, ssi->screenshot_height, 1, 1, 1,
HostDisplayPixelFormat::RGBA8, ssi->screenshot_data.data(),
sizeof(u32) * ssi->screenshot_width, false);
li->preview_texture =
g_host_display->CreateTexture(ssi->screenshot_width, ssi->screenshot_height, 1, 1, 1, GPUTexture::Format::RGBA8,
ssi->screenshot_data.data(), sizeof(u32) * ssi->screenshot_width, false);
}
else
{
li->preview_texture = g_host_display->CreateTexture(PLACEHOLDER_ICON_WIDTH, PLACEHOLDER_ICON_HEIGHT, 1, 1, 1,
HostDisplayPixelFormat::RGBA8, PLACEHOLDER_ICON_DATA,
sizeof(u32) * PLACEHOLDER_ICON_WIDTH, false);
li->preview_texture =
g_host_display->CreateTexture(PLACEHOLDER_ICON_WIDTH, PLACEHOLDER_ICON_HEIGHT, 1, 1, 1, GPUTexture::Format::RGBA8,
PLACEHOLDER_ICON_DATA, sizeof(u32) * PLACEHOLDER_ICON_WIDTH, false);
}
if (!li->preview_texture)
@ -4254,8 +4254,7 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading, bool fullscreen)
ImVec2 pos(bb.Min);
// use aspect ratio of screenshot to determine height
const HostDisplayTexture* image =
entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get();
const GPUTexture* image = entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get();
const float image_height =
max_image_width / (static_cast<float>(image->GetWidth()) / static_cast<float>(image->GetHeight()));
const float image_margin = (max_image_height - image_height) / 2.0f;
@ -4289,10 +4288,9 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading, bool fullscreen)
ImGui::PopFont();
ImGui::GetWindowDrawList()->AddImage(static_cast<ImTextureID>(entry.preview_texture ?
entry.preview_texture->GetHandle() :
GetPlaceholderTexture()->GetHandle()),
image_bb.Min, image_bb.Max);
ImGui::GetWindowDrawList()->AddImage(
static_cast<ImTextureID>(entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get()),
image_bb.Min, image_bb.Max);
if (pressed)
{
@ -4353,18 +4351,16 @@ void FullscreenUI::DrawResumeStateSelector()
ImGui::TextWrapped("A resume save state created at %s was found.\n\nDo you want to load this save and continue?",
TimeToPrintableString(entry.timestamp).c_str());
const HostDisplayTexture* image =
entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get();
const GPUTexture* image = entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get();
const float image_height = LayoutScale(250.0f);
const float image_width =
image_height * (static_cast<float>(image->GetWidth()) / static_cast<float>(image->GetHeight()));
const ImVec2 pos(ImGui::GetCursorScreenPos() +
ImVec2((ImGui::GetCurrentWindow()->WorkRect.GetWidth() - image_width) * 0.5f, LayoutScale(20.0f)));
const ImRect image_bb(pos, pos + ImVec2(image_width, image_height));
ImGui::GetWindowDrawList()->AddImage(static_cast<ImTextureID>(entry.preview_texture ?
entry.preview_texture->GetHandle() :
GetPlaceholderTexture()->GetHandle()),
image_bb.Min, image_bb.Max);
ImGui::GetWindowDrawList()->AddImage(
static_cast<ImTextureID>(entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get()),
image_bb.Min, image_bb.Max);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + image_height + LayoutScale(40.0f));
@ -4564,7 +4560,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
if (!visible)
continue;
HostDisplayTexture* cover_texture = GetGameListCover(entry);
GPUTexture* cover_texture = GetGameListCover(entry);
if (entry->serial.empty())
{
@ -4582,8 +4578,8 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
CenterImage(ImRect(bb.Min, bb.Min + image_size), ImVec2(static_cast<float>(cover_texture->GetWidth()),
static_cast<float>(cover_texture->GetHeight()))));
ImGui::GetWindowDrawList()->AddImage(cover_texture->GetHandle(), image_rect.Min, image_rect.Max,
ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
ImGui::GetWindowDrawList()->AddImage(cover_texture, image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f),
ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
const float midpoint = bb.Min.y + g_large_font->FontSize + LayoutScale(4.0f);
const float text_start_x = bb.Min.x + image_size.x + LayoutScale(15.0f);
@ -4623,7 +4619,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
if (BeginFullscreenColumnWindow(-530.0f, 0.0f, "game_list_info", UIPrimaryDarkColor))
{
const HostDisplayTexture* cover_texture =
const GPUTexture* cover_texture =
selected_entry ? GetGameListCover(selected_entry) : GetTextureForGameListEntryType(GameList::EntryType::Count);
if (cover_texture)
{
@ -4632,8 +4628,8 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
static_cast<float>(cover_texture->GetHeight()))));
ImGui::SetCursorPos(LayoutScale(ImVec2(90.0f, 50.0f)) + image_rect.Min);
ImGui::Image(selected_entry ? GetGameListCover(selected_entry)->GetHandle() :
GetTextureForGameListEntryType(GameList::EntryType::Count)->GetHandle(),
ImGui::Image(selected_entry ? GetGameListCover(selected_entry) :
GetTextureForGameListEntryType(GameList::EntryType::Count),
image_rect.GetSize());
}
@ -4683,7 +4679,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
fmt::format("fullscreenui/{}.png", Settings::GetDiscRegionName(selected_entry->region)));
ImGui::TextUnformatted("Region: ");
ImGui::SameLine();
ImGui::Image(GetCachedTextureAsync(flag_texture.c_str())->GetHandle(), LayoutScale(23.0f, 16.0f));
ImGui::Image(GetCachedTextureAsync(flag_texture.c_str()), LayoutScale(23.0f, 16.0f));
ImGui::SameLine();
ImGui::Text(" (%s)", Settings::GetDiscRegionDisplayName(selected_entry->region));
}
@ -4701,7 +4697,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
ImGui::SameLine();
if (selected_entry->compatibility != GameDatabase::CompatibilityRating::Unknown)
{
ImGui::Image(s_game_compatibility_textures[static_cast<u32>(selected_entry->compatibility)]->GetHandle(),
ImGui::Image(s_game_compatibility_textures[static_cast<u32>(selected_entry->compatibility)].get(),
LayoutScale(64.0f, 16.0f));
ImGui::SameLine();
}
@ -4801,13 +4797,13 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size)
bb.Min += style.FramePadding;
bb.Max -= style.FramePadding;
const HostDisplayTexture* const cover_texture = GetGameListCover(entry);
GPUTexture* const cover_texture = GetGameListCover(entry);
const ImRect image_rect(
CenterImage(ImRect(bb.Min, bb.Min + image_size), ImVec2(static_cast<float>(cover_texture->GetWidth()),
static_cast<float>(cover_texture->GetHeight()))));
ImGui::GetWindowDrawList()->AddImage(cover_texture->GetHandle(), image_rect.Min, image_rect.Max,
ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
ImGui::GetWindowDrawList()->AddImage(cover_texture, image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f),
ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
const ImRect title_bb(ImVec2(bb.Min.x, bb.Min.y + image_height + title_spacing), bb.Max);
const std::string_view title(
@ -5111,7 +5107,7 @@ void FullscreenUI::SwitchToGameList()
QueueResetFocus();
}
HostDisplayTexture* FullscreenUI::GetGameListCover(const GameList::Entry* entry)
GPUTexture* FullscreenUI::GetGameListCover(const GameList::Entry* entry)
{
// lookup and grab cover image
auto cover_it = s_cover_image_map.find(entry->path);
@ -5121,11 +5117,11 @@ HostDisplayTexture* FullscreenUI::GetGameListCover(const GameList::Entry* entry)
cover_it = s_cover_image_map.emplace(entry->path, std::move(cover_path)).first;
}
HostDisplayTexture* tex = (!cover_it->second.empty()) ? GetCachedTextureAsync(cover_it->second.c_str()) : nullptr;
GPUTexture* tex = (!cover_it->second.empty()) ? GetCachedTextureAsync(cover_it->second.c_str()) : nullptr;
return tex ? tex : GetTextureForGameListEntryType(entry->type);
}
HostDisplayTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::EntryType type)
GPUTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::EntryType type)
{
switch (type)
{
@ -5144,7 +5140,7 @@ HostDisplayTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::Entry
}
}
HostDisplayTexture* FullscreenUI::GetCoverForCurrentGame()
GPUTexture* FullscreenUI::GetCoverForCurrentGame()
{
auto lock = GameList::GetLock();
@ -5360,11 +5356,11 @@ void FullscreenUI::DrawAchievement(const Achievements::Achievement& cheevo)
const std::string& badge_path = Achievements::GetAchievementBadgePath(cheevo);
if (!badge_path.empty())
{
HostDisplayTexture* badge = GetCachedTextureAsync(badge_path.c_str());
GPUTexture* badge = GetCachedTextureAsync(badge_path.c_str());
if (badge)
{
ImGui::GetWindowDrawList()->AddImage(badge->GetHandle(), bb.Min, bb.Min + image_size, ImVec2(0.0f, 0.0f),
ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
ImGui::GetWindowDrawList()->AddImage(badge, bb.Min, bb.Min + image_size, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f),
IM_COL32(255, 255, 255, 255));
}
}
@ -5463,11 +5459,11 @@ void FullscreenUI::DrawAchievementsWindow()
const std::string& icon_path = Achievements::GetGameIcon();
if (!icon_path.empty())
{
HostDisplayTexture* badge = GetCachedTexture(icon_path.c_str());
GPUTexture* badge = GetCachedTexture(icon_path.c_str());
if (badge)
{
ImGui::GetWindowDrawList()->AddImage(badge->GetHandle(), icon_min, icon_max, ImVec2(0.0f, 0.0f),
ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
ImGui::GetWindowDrawList()->AddImage(badge, icon_min, icon_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f),
IM_COL32(255, 255, 255, 255));
}
}
@ -5602,12 +5598,12 @@ void FullscreenUI::DrawPrimedAchievements()
if (badge_path.empty())
return true;
HostDisplayTexture* badge = GetCachedTextureAsync(badge_path.c_str());
GPUTexture* badge = GetCachedTextureAsync(badge_path.c_str());
if (!badge)
return true;
ImDrawList* dl = ImGui::GetBackgroundDrawList();
dl->AddImage(badge->GetHandle(), position, position + image_size);
dl->AddImage(badge, position, position + image_size);
position.x -= x_advance;
return true;
});
@ -5786,11 +5782,11 @@ void FullscreenUI::DrawLeaderboardsWindow()
const std::string& icon_path = Achievements::GetGameIcon();
if (!icon_path.empty())
{
HostDisplayTexture* badge = GetCachedTexture(icon_path.c_str());
GPUTexture* badge = GetCachedTexture(icon_path.c_str());
if (badge)
{
ImGui::GetWindowDrawList()->AddImage(badge->GetHandle(), icon_min, icon_max, ImVec2(0.0f, 0.0f),
ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
ImGui::GetWindowDrawList()->AddImage(badge, icon_min, icon_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f),
IM_COL32(255, 255, 255, 255));
}
}

View File

@ -4,7 +4,7 @@
#include <memory>
#include <string>
class HostDisplayTexture;
class GPUTexture;
struct Settings;

View File

@ -32,7 +32,7 @@ namespace ImGuiFullscreen {
using MessageDialogCallbackVariant = std::variant<InfoMessageDialogCallback, ConfirmMessageDialogCallback>;
static std::optional<Common::RGBA8Image> LoadTextureImage(const char* path);
static std::shared_ptr<HostDisplayTexture> UploadTexture(const char* path, const Common::RGBA8Image& image);
static std::shared_ptr<GPUTexture> UploadTexture(const char* path, const Common::RGBA8Image& image);
static void TextureLoaderThread();
static void DrawFileSelector();
@ -78,8 +78,8 @@ static u32 s_menu_button_index = 0;
static u32 s_close_button_state = 0;
static bool s_focus_reset_queued = false;
static LRUCache<std::string, std::shared_ptr<HostDisplayTexture>> s_texture_cache(128, true);
static std::shared_ptr<HostDisplayTexture> s_placeholder_texture;
static LRUCache<std::string, std::shared_ptr<GPUTexture>> s_texture_cache(128, true);
static std::shared_ptr<GPUTexture> s_placeholder_texture;
static std::atomic_bool s_texture_load_thread_quit{false};
static std::mutex s_texture_load_mutex;
static std::condition_variable s_texture_load_cv;
@ -231,7 +231,7 @@ void ImGuiFullscreen::Shutdown()
s_file_selector_items.clear();
}
const std::shared_ptr<HostDisplayTexture>& ImGuiFullscreen::GetPlaceholderTexture()
const std::shared_ptr<GPUTexture>& ImGuiFullscreen::GetPlaceholderTexture()
{
return s_placeholder_texture;
}
@ -262,10 +262,10 @@ std::optional<Common::RGBA8Image> ImGuiFullscreen::LoadTextureImage(const char*
return image;
}
std::shared_ptr<HostDisplayTexture> ImGuiFullscreen::UploadTexture(const char* path, const Common::RGBA8Image& image)
std::shared_ptr<GPUTexture> ImGuiFullscreen::UploadTexture(const char* path, const Common::RGBA8Image& image)
{
std::unique_ptr<HostDisplayTexture> texture =
g_host_display->CreateTexture(image.GetWidth(), image.GetHeight(), 1, 1, 1, HostDisplayPixelFormat::RGBA8,
std::unique_ptr<GPUTexture> texture =
g_host_display->CreateTexture(image.GetWidth(), image.GetHeight(), 1, 1, 1, GPUTexture::Format::RGBA8,
image.GetPixels(), image.GetPitch());
if (!texture)
{
@ -274,16 +274,16 @@ std::shared_ptr<HostDisplayTexture> ImGuiFullscreen::UploadTexture(const char* p
}
Log_DevPrintf("Uploaded texture resource '%s' (%ux%u)", path, image.GetWidth(), image.GetHeight());
return std::shared_ptr<HostDisplayTexture>(std::move(texture));
return std::shared_ptr<GPUTexture>(std::move(texture));
}
std::shared_ptr<HostDisplayTexture> ImGuiFullscreen::LoadTexture(const std::string_view& path)
std::shared_ptr<GPUTexture> ImGuiFullscreen::LoadTexture(const std::string_view& path)
{
std::string path_str(path);
std::optional<Common::RGBA8Image> image(LoadTextureImage(path_str.c_str()));
if (image.has_value())
{
std::shared_ptr<HostDisplayTexture> ret(UploadTexture(path_str.c_str(), image.value()));
std::shared_ptr<GPUTexture> ret(UploadTexture(path_str.c_str(), image.value()));
if (ret)
return ret;
}
@ -291,21 +291,21 @@ std::shared_ptr<HostDisplayTexture> ImGuiFullscreen::LoadTexture(const std::stri
return s_placeholder_texture;
}
HostDisplayTexture* ImGuiFullscreen::GetCachedTexture(const std::string_view& name)
GPUTexture* ImGuiFullscreen::GetCachedTexture(const std::string_view& name)
{
std::shared_ptr<HostDisplayTexture>* tex_ptr = s_texture_cache.Lookup(name);
std::shared_ptr<GPUTexture>* tex_ptr = s_texture_cache.Lookup(name);
if (!tex_ptr)
{
std::shared_ptr<HostDisplayTexture> tex(LoadTexture(name));
std::shared_ptr<GPUTexture> tex(LoadTexture(name));
tex_ptr = s_texture_cache.Insert(std::string(name), std::move(tex));
}
return tex_ptr->get();
}
HostDisplayTexture* ImGuiFullscreen::GetCachedTextureAsync(const std::string_view& name)
GPUTexture* ImGuiFullscreen::GetCachedTextureAsync(const std::string_view& name)
{
std::shared_ptr<HostDisplayTexture>* tex_ptr = s_texture_cache.Lookup(name);
std::shared_ptr<GPUTexture>* tex_ptr = s_texture_cache.Lookup(name);
if (!tex_ptr)
{
// insert the placeholder
@ -334,7 +334,7 @@ void ImGuiFullscreen::UploadAsyncTextures()
s_texture_upload_queue.pop_front();
lock.unlock();
std::shared_ptr<HostDisplayTexture> tex = UploadTexture(it.first.c_str(), it.second);
std::shared_ptr<GPUTexture> tex = UploadTexture(it.first.c_str(), it.second);
if (tex)
s_texture_cache.Insert(std::move(it.first), std::move(tex));
@ -2389,9 +2389,9 @@ void ImGuiFullscreen::DrawNotifications(ImVec2& position, float spacing)
const ImVec2 badge_max(badge_min.x + badge_size, badge_min.y + badge_size);
if (!notif.badge_path.empty())
{
HostDisplayTexture* tex = GetCachedTexture(notif.badge_path.c_str());
GPUTexture* tex = GetCachedTexture(notif.badge_path.c_str());
if (tex)
dl->AddImage(static_cast<ImTextureID>(tex->GetHandle()), badge_min, badge_max);
dl->AddImage(tex, badge_min, badge_max);
}
const ImVec2 title_min(badge_max.x + horizontal_spacing, box_min.y + vertical_padding);

View File

@ -9,7 +9,7 @@
#include <string>
#include <vector>
class HostDisplayTexture;
class GPUTexture;
namespace ImGuiFullscreen {
#define HEX_TO_IMVEC4(hex, alpha) \
@ -121,10 +121,10 @@ bool UpdateLayoutScale();
void Shutdown();
/// Texture cache.
const std::shared_ptr<HostDisplayTexture>& GetPlaceholderTexture();
std::shared_ptr<HostDisplayTexture> LoadTexture(const std::string_view& path);
HostDisplayTexture* GetCachedTexture(const std::string_view& name);
HostDisplayTexture* GetCachedTextureAsync(const std::string_view& name);
const std::shared_ptr<GPUTexture>& GetPlaceholderTexture();
std::shared_ptr<GPUTexture> LoadTexture(const std::string_view& path);
GPUTexture* GetCachedTexture(const std::string_view& name);
GPUTexture* GetCachedTextureAsync(const std::string_view& name);
bool InvalidateCachedTexture(const std::string& path);
void UploadAsyncTextures();

View File

@ -251,7 +251,7 @@ bool ImGui_ImplDX11_CreateFontsTexture()
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
const u32 stride = sizeof(u32) * width;
if (!bd->FontTexture.Create(bd->pd3dDevice, width, height, 1, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, D3D11_BIND_SHADER_RESOURCE, pixels, stride))
if (!bd->FontTexture.Create(bd->pd3dDevice, width, height, 1, 1, 1, GPUTexture::Format::RGBA8, D3D11_BIND_SHADER_RESOURCE, pixels, stride))
return false;
// Store our identifier

View File

@ -266,7 +266,7 @@ bool ImGui_ImplDX12_CreateFontsTexture()
// Upload texture to graphics system
if (bd->FontTexture.GetWidth() != static_cast<u32>(width) || bd->FontTexture.GetHeight() != static_cast<u32>(height))
{
if (!bd->FontTexture.Create(width, height, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
if (!bd->FontTexture.Create(width, height, 1, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN,
D3D12_RESOURCE_FLAG_NONE))
{

View File

@ -110,20 +110,20 @@
// 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 GlVersion = 0; // 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.
GL::Texture FontTexture;
GLuint ShaderHandle;
GLint AttribLocationTex; // Uniforms location
GLint AttribLocationProjMtx;
GLuint AttribLocationVtxPos; // Vertex attributes location
GLuint AttribLocationVtxUV;
GLuint AttribLocationVtxColor;
unsigned int VboHandle, ElementsHandle, VaoHandle;
GLsizeiptr VertexBufferSize;
GLsizeiptr IndexBufferSize;
GLuint ShaderHandle = 0;
GLint AttribLocationTex = 0; // Uniforms location
GLint AttribLocationProjMtx = 0;
GLuint AttribLocationVtxPos = 0; // Vertex attributes location
GLuint AttribLocationVtxUV = 0;
GLuint AttribLocationVtxColor = 0;
unsigned int VboHandle = 0, ElementsHandle = 0, VaoHandle = 0;
GLsizeiptr VertexBufferSize = 0;
GLsizeiptr IndexBufferSize = 0;
ImGui_ImplOpenGL3_Data() { memset((void*)this, 0, sizeof(*this)); }
ImGui_ImplOpenGL3_Data() = default;
};
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
@ -316,7 +316,7 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture()
// 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)
bd->FontTexture.Create(width, height, 1, 1, 1, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
bd->FontTexture.Create(width, height, 1, 1, 1, GPUTexture::Format::RGBA8, pixels);
bd->FontTexture.SetLinearFilter(true);
// Store our identifier
@ -326,7 +326,6 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture()
void ImGui_ImplOpenGL3_DestroyFontsTexture()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
if (bd->FontTexture.IsValid())
bd->FontTexture.Destroy();

View File

@ -409,7 +409,7 @@ struct ListEntry
std::string game_code;
std::string title;
std::string formatted_timestamp;
std::unique_ptr<HostDisplayTexture> preview_texture;
std::unique_ptr<GPUTexture> preview_texture;
s32 slot;
bool global;
};
@ -564,13 +564,13 @@ void SaveStateSelectorUI::InitializeListEntry(ListEntry* li, ExtendedSaveStateIn
if (ssi && !ssi->screenshot_data.empty())
{
li->preview_texture = g_host_display->CreateTexture(ssi->screenshot_width, ssi->screenshot_height, 1, 1, 1,
HostDisplayPixelFormat::RGBA8, ssi->screenshot_data.data(),
GPUTexture::Format::RGBA8, ssi->screenshot_data.data(),
sizeof(u32) * ssi->screenshot_width, false);
}
else
{
li->preview_texture = g_host_display->CreateTexture(PLACEHOLDER_ICON_WIDTH, PLACEHOLDER_ICON_HEIGHT, 1, 1, 1,
HostDisplayPixelFormat::RGBA8, PLACEHOLDER_ICON_DATA,
GPUTexture::Format::RGBA8, PLACEHOLDER_ICON_DATA,
sizeof(u32) * PLACEHOLDER_ICON_WIDTH, false);
}
@ -591,7 +591,7 @@ void SaveStateSelectorUI::InitializePlaceholderListEntry(ListEntry* li, std::str
if (g_host_display)
{
li->preview_texture = g_host_display->CreateTexture(PLACEHOLDER_ICON_WIDTH, PLACEHOLDER_ICON_HEIGHT, 1, 1, 1,
HostDisplayPixelFormat::RGBA8, PLACEHOLDER_ICON_DATA,
GPUTexture::Format::RGBA8, PLACEHOLDER_ICON_DATA,
sizeof(u32) * PLACEHOLDER_ICON_WIDTH, false);
if (!li->preview_texture)
Log_ErrorPrintf("Failed to upload save state image to GPU");
@ -645,7 +645,7 @@ void SaveStateSelectorUI::Draw()
{
ImGui::SetCursorPosY(y_start + padding);
ImGui::SetCursorPosX(padding);
ImGui::Image(reinterpret_cast<ImTextureID>(entry.preview_texture->GetHandle()), image_size);
ImGui::Image(entry.preview_texture.get(), image_size);
}
ImGui::SetCursorPosY(y_start + padding);

View File

@ -16,36 +16,6 @@ enum : u32
TEXTURE_STREAM_BUFFER_SIZE = 16 * 1024 * 1024,
};
class OpenGLHostDisplayTexture final : public HostDisplayTexture
{
public:
OpenGLHostDisplayTexture(GL::Texture texture, HostDisplayPixelFormat format)
: m_texture(std::move(texture)), m_format(format)
{
}
~OpenGLHostDisplayTexture() = default;
void* GetHandle() const override { return const_cast<GL::Texture*>(&m_texture); }
u32 GetWidth() const override { return m_texture.GetWidth(); }
u32 GetHeight() const override { return m_texture.GetHeight(); }
u32 GetLayers() const override { return m_texture.GetLayers(); }
u32 GetLevels() const override { return m_texture.GetLevels(); }
u32 GetSamples() const override { return m_texture.GetSamples(); }
HostDisplayPixelFormat GetFormat() const override { return m_format; }
GLuint GetGLID() const { return m_texture.GetGLId(); }
bool BeginUpdate(u32 width, u32 height, void** out_buffer, u32* out_pitch) override;
void EndUpdate(u32 x, u32 y, u32 width, u32 height) override;
bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch) override;
private:
GL::Texture m_texture;
HostDisplayPixelFormat m_format;
u32 m_map_offset = 0;
};
OpenGLHostDisplay::OpenGLHostDisplay() = default;
OpenGLHostDisplay::~OpenGLHostDisplay()
@ -74,54 +44,134 @@ void* OpenGLHostDisplay::GetRenderContext() const
return m_gl_context.get();
}
static const std::tuple<GLenum, GLenum, GLenum>& GetPixelFormatMapping(bool is_gles, HostDisplayPixelFormat format)
std::unique_ptr<GPUTexture> OpenGLHostDisplay::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Format format, const void* data,
u32 data_stride, bool dynamic /* = false */)
{
static constexpr std::array<std::tuple<GLenum, GLenum, GLenum>, static_cast<u32>(HostDisplayPixelFormat::Count)>
mapping = {{
{}, // Unknown
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA8
{GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // BGRA8
{GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565
{GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV} // RGBA5551
}};
std::unique_ptr<GL::Texture> tex(std::make_unique<GL::Texture>());
if (!tex->Create(width, height, layers, levels, samples, format, data, data_stride))
tex.reset();
static constexpr std::array<std::tuple<GLenum, GLenum, GLenum>, static_cast<u32>(HostDisplayPixelFormat::Count)>
mapping_gles2 = {{
{}, // Unknown
{GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA8
{}, // BGRA8
{GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565
{} // RGBA5551
}};
return tex;
}
if (is_gles && !GLAD_GL_ES_VERSION_3_0)
return mapping_gles2[static_cast<u32>(format)];
bool OpenGLHostDisplay::BeginTextureUpdate(GPUTexture* texture, u32 width, u32 height, void** out_buffer,
u32* out_pitch)
{
const u32 pixel_size = texture->GetPixelSize();
const u32 stride = Common::AlignUpPow2(width * pixel_size, 4);
const u32 size_required = stride * height;
GL::StreamBuffer* buffer = UsePBOForUploads() ? GetTextureStreamBuffer() : nullptr;
if (buffer && size_required < buffer->GetSize())
{
auto map = buffer->Map(4096, size_required);
m_texture_stream_buffer_offset = map.buffer_offset;
*out_buffer = map.pointer;
*out_pitch = stride;
}
else
return mapping[static_cast<u32>(format)];
{
std::vector<u8>& repack_buffer = GetTextureRepackBuffer();
if (repack_buffer.size() < size_required)
repack_buffer.resize(size_required);
*out_buffer = repack_buffer.data();
*out_pitch = stride;
}
return true;
}
std::unique_ptr<HostDisplayTexture> OpenGLHostDisplay::CreateTexture(u32 width, u32 height, u32 layers, u32 levels,
u32 samples, HostDisplayPixelFormat format,
const void* data, u32 data_stride,
bool dynamic /* = false */)
void OpenGLHostDisplay::EndTextureUpdate(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height)
{
if (layers != 1 || levels != 1)
return {};
const u32 pixel_size = texture->GetPixelSize();
const u32 stride = Common::AlignUpPow2(width * pixel_size, 4);
const u32 size_required = stride * height;
GL::Texture* gl_texture = static_cast<GL::Texture*>(texture);
GL::StreamBuffer* buffer = UsePBOForUploads() ? GetTextureStreamBuffer() : nullptr;
const auto [gl_internal_format, gl_format, gl_type] = GetPixelFormatMapping(m_gl_context->IsGLES(), format);
const auto [gl_internal_format, gl_format, gl_type] = GL::Texture::GetPixelFormatMapping(gl_texture->GetFormat());
const bool whole_texture = (!gl_texture->UseTextureStorage() && x == 0 && y == 0 && width == gl_texture->GetWidth() &&
height == gl_texture->GetHeight());
// TODO: Set pack width
Assert(!data || data_stride == (width * sizeof(u32)));
gl_texture->Bind();
if (buffer && size_required < buffer->GetSize())
{
buffer->Unmap(size_required);
buffer->Bind();
GL::Texture tex;
if (!tex.Create(width, height, layers, levels, samples, gl_internal_format, gl_format, gl_type, data, data_stride))
return {};
if (whole_texture)
{
glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, width, height, 0, gl_format, gl_type,
reinterpret_cast<void*>(static_cast<uintptr_t>(m_texture_stream_buffer_offset)));
}
else
{
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, gl_format, gl_type,
reinterpret_cast<void*>(static_cast<uintptr_t>(m_texture_stream_buffer_offset)));
}
return std::make_unique<OpenGLHostDisplayTexture>(std::move(tex), format);
buffer->Unbind();
}
else
{
std::vector<u8>& repack_buffer = GetTextureRepackBuffer();
if (whole_texture)
glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, width, height, 0, gl_format, gl_type, repack_buffer.data());
else
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, gl_format, gl_type, repack_buffer.data());
}
}
bool OpenGLHostDisplay::DownloadTexture(const void* texture_handle, HostDisplayPixelFormat texture_format, u32 x, u32 y,
u32 width, u32 height, void* out_data, u32 out_data_stride)
bool OpenGLHostDisplay::UpdateTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data,
u32 pitch)
{
GL::Texture* gl_texture = static_cast<GL::Texture*>(texture);
const auto [gl_internal_format, gl_format, gl_type] = GL::Texture::GetPixelFormatMapping(gl_texture->GetFormat());
const u32 pixel_size = gl_texture->GetPixelSize();
const bool is_packed_tightly = (pitch == (pixel_size * width));
const bool whole_texture = (!gl_texture->UseTextureStorage() && x == 0 && y == 0 && width == gl_texture->GetWidth() &&
height == gl_texture->GetHeight());
gl_texture->Bind();
// If we have GLES3, we can set row_length.
if (UseGLES3DrawPath() || is_packed_tightly)
{
if (!is_packed_tightly)
glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch / pixel_size);
if (whole_texture)
glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, width, height, 0, gl_format, gl_type, data);
else
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, gl_format, gl_type, data);
if (!is_packed_tightly)
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
else
{
// Otherwise, we need to repack the image.
std::vector<u8>& repack_buffer = GetTextureRepackBuffer();
const u32 packed_pitch = width * pixel_size;
const u32 repack_size = packed_pitch * height;
if (repack_buffer.size() < repack_size)
repack_buffer.resize(repack_size);
StringUtil::StrideMemCpy(repack_buffer.data(), packed_pitch, data, pitch, packed_pitch, height);
if (whole_texture)
glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, width, height, 0, gl_format, gl_type, repack_buffer.data());
else
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, gl_format, gl_type, repack_buffer.data());
}
return true;
}
bool OpenGLHostDisplay::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data,
u32 out_data_stride)
{
GLint alignment;
if (out_data_stride & 1)
@ -137,14 +187,13 @@ bool OpenGLHostDisplay::DownloadTexture(const void* texture_handle, HostDisplayP
if (!m_use_gles2_draw_path)
{
glGetIntegerv(GL_PACK_ROW_LENGTH, &old_row_length);
glPixelStorei(GL_PACK_ROW_LENGTH, out_data_stride / GetDisplayPixelFormatSize(texture_format));
glPixelStorei(GL_PACK_ROW_LENGTH, out_data_stride / texture->GetPixelSize());
}
const GL::Texture* texture = static_cast<const GL::Texture*>(texture_handle);
const auto [gl_internal_format, gl_format, gl_type] = GetPixelFormatMapping(m_gl_context->IsGLES(), texture_format);
const auto [gl_internal_format, gl_format, gl_type] = GL::Texture::GetPixelFormatMapping(texture->GetFormat());
GL::Texture::GetTextureSubImage(texture->GetGLId(), 0, x, y, 0, width, height, 1, gl_format, gl_type,
height * out_data_stride, out_data);
GL::Texture::GetTextureSubImage(static_cast<const GL::Texture*>(texture)->GetGLId(), 0, x, y, 0, width, height, 1,
gl_format, gl_type, height * out_data_stride, out_data);
glPixelStorei(GL_PACK_ALIGNMENT, old_alignment);
if (!m_use_gles2_draw_path)
@ -152,9 +201,9 @@ bool OpenGLHostDisplay::DownloadTexture(const void* texture_handle, HostDisplayP
return true;
}
bool OpenGLHostDisplay::SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const
bool OpenGLHostDisplay::SupportsTextureFormat(GPUTexture::Format format) const
{
const auto [gl_internal_format, gl_format, gl_type] = GetPixelFormatMapping(m_gl_context->IsGLES(), format);
const auto [gl_internal_format, gl_format, gl_type] = GL::Texture::GetPixelFormatMapping(format);
return (gl_internal_format != static_cast<GLenum>(0));
}
@ -594,11 +643,10 @@ bool OpenGLHostDisplay::Render(bool skip_present)
}
bool OpenGLHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
HostDisplayPixelFormat* out_format)
GPUTexture::Format* out_format)
{
GL::Texture texture;
if (!texture.Create(width, height, 1, 1, 1, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, nullptr) ||
!texture.CreateFramebuffer())
if (!texture.Create(width, height, 1, 1, 1, GPUTexture::Format::RGBA8, nullptr, 0) || !texture.CreateFramebuffer())
{
return false;
}
@ -615,22 +663,21 @@ bool OpenGLHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>
if (!m_post_processing_chain.IsEmpty())
{
ApplyPostProcessingChain(texture.GetGLFramebufferID(), left, height - top - draw_height, draw_width, draw_height,
m_display_texture_handle, m_display_texture_width, m_display_texture_height,
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, width, height);
static_cast<GL::Texture*>(m_display_texture), m_display_texture_view_x,
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
width, height);
}
else
{
RenderDisplay(left, height - top - draw_height, draw_width, draw_height, m_display_texture_handle,
m_display_texture_width, m_display_texture_height, m_display_texture_view_x,
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
IsUsingLinearFiltering());
RenderDisplay(left, height - top - draw_height, draw_width, draw_height,
static_cast<GL::Texture*>(m_display_texture), m_display_texture_view_x, m_display_texture_view_y,
m_display_texture_view_width, m_display_texture_view_height, IsUsingLinearFiltering());
}
}
out_pixels->resize(width * height);
*out_stride = sizeof(u32) * width;
*out_format = HostDisplayPixelFormat::RGBA8;
*out_format = GPUTexture::Format::RGBA8;
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, out_pixels->data());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
@ -653,16 +700,16 @@ void OpenGLHostDisplay::RenderDisplay()
if (!m_post_processing_chain.IsEmpty())
{
ApplyPostProcessingChain(0, left, GetWindowHeight() - top - height, width, height, m_display_texture_handle,
m_display_texture_width, m_display_texture_height, m_display_texture_view_x,
ApplyPostProcessingChain(0, left, GetWindowHeight() - top - height, width, height,
static_cast<GL::Texture*>(m_display_texture), m_display_texture_view_x,
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
GetWindowWidth(), GetWindowHeight());
return;
}
RenderDisplay(left, GetWindowHeight() - top - height, width, height, m_display_texture_handle,
m_display_texture_width, m_display_texture_height, m_display_texture_view_x, m_display_texture_view_y,
m_display_texture_view_width, m_display_texture_view_height, IsUsingLinearFiltering());
RenderDisplay(left, GetWindowHeight() - top - height, width, height, static_cast<GL::Texture*>(m_display_texture),
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, IsUsingLinearFiltering());
}
static void DrawFullscreenQuadES2(s32 tex_view_x, s32 tex_view_y, s32 tex_view_width, s32 tex_view_height,
@ -689,12 +736,10 @@ static void DrawFullscreenQuadES2(s32 tex_view_x, s32 tex_view_y, s32 tex_view_w
glDisableVertexAttribArray(0);
}
void OpenGLHostDisplay::RenderDisplay(s32 left, s32 bottom, s32 width, s32 height, void* texture_handle,
u32 texture_width, s32 texture_height, s32 texture_view_x, s32 texture_view_y,
s32 texture_view_width, s32 texture_view_height, bool linear_filter)
void OpenGLHostDisplay::RenderDisplay(s32 left, s32 bottom, s32 width, s32 height, GL::Texture* texture,
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
s32 texture_view_height, bool linear_filter)
{
const GL::Texture* texture = static_cast<const GL::Texture*>(texture_handle);
glViewport(left, bottom, width, height);
glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
@ -711,10 +756,11 @@ void OpenGLHostDisplay::RenderDisplay(s32 left, s32 bottom, s32 width, s32 heigh
const float size_adjust = linear ? 1.0f : 0.0f;
const float flip_adjust = (texture_view_height < 0) ? -1.0f : 1.0f;
m_display_program.Uniform4f(
0, (static_cast<float>(texture_view_x) + position_adjust) / static_cast<float>(texture_width),
(static_cast<float>(texture_view_y) + (position_adjust * flip_adjust)) / static_cast<float>(texture_height),
(static_cast<float>(texture_view_width) - size_adjust) / static_cast<float>(texture_width),
(static_cast<float>(texture_view_height) - (size_adjust * flip_adjust)) / static_cast<float>(texture_height));
0, (static_cast<float>(texture_view_x) + position_adjust) / static_cast<float>(texture->GetWidth()),
(static_cast<float>(texture_view_y) + (position_adjust * flip_adjust)) / static_cast<float>(texture->GetHeight()),
(static_cast<float>(texture_view_width) - size_adjust) / static_cast<float>(texture->GetWidth()),
(static_cast<float>(texture_view_height) - (size_adjust * flip_adjust)) /
static_cast<float>(texture->GetHeight()));
glBindSampler(0, linear_filter ? m_display_linear_sampler : m_display_nearest_sampler);
glBindVertexArray(m_display_vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
@ -725,7 +771,7 @@ void OpenGLHostDisplay::RenderDisplay(s32 left, s32 bottom, s32 width, s32 heigh
texture->SetLinearFilter(linear_filter);
DrawFullscreenQuadES2(m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, m_display_texture_width, m_display_texture_height);
m_display_texture_view_height, texture->GetWidth(), texture->GetHeight());
}
}
@ -738,8 +784,7 @@ void OpenGLHostDisplay::RenderSoftwareCursor()
RenderSoftwareCursor(left, GetWindowHeight() - top - height, width, height, m_cursor_texture.get());
}
void OpenGLHostDisplay::RenderSoftwareCursor(s32 left, s32 bottom, s32 width, s32 height,
HostDisplayTexture* texture_handle)
void OpenGLHostDisplay::RenderSoftwareCursor(s32 left, s32 bottom, s32 width, s32 height, GPUTexture* texture_handle)
{
glViewport(left, bottom, width, height);
glEnable(GL_BLEND);
@ -749,7 +794,7 @@ void OpenGLHostDisplay::RenderSoftwareCursor(s32 left, s32 bottom, s32 width, s3
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
m_cursor_program.Bind();
glBindTexture(GL_TEXTURE_2D, static_cast<OpenGLHostDisplayTexture*>(texture_handle)->GetGLID());
static_cast<GL::Texture*>(texture_handle)->Bind();
if (!m_use_gles2_draw_path)
{
@ -761,8 +806,8 @@ void OpenGLHostDisplay::RenderSoftwareCursor(s32 left, s32 bottom, s32 width, s3
}
else
{
const s32 tex_width = static_cast<s32>(static_cast<OpenGLHostDisplayTexture*>(texture_handle)->GetWidth());
const s32 tex_height = static_cast<s32>(static_cast<OpenGLHostDisplayTexture*>(texture_handle)->GetHeight());
const s32 tex_width = static_cast<s32>(texture_handle->GetWidth());
const s32 tex_height = static_cast<s32>(texture_handle->GetHeight());
DrawFullscreenQuadES2(0, 0, tex_width, tex_height, tex_width, tex_height);
}
}
@ -842,8 +887,7 @@ bool OpenGLHostDisplay::CheckPostProcessingRenderTargets(u32 target_width, u32 t
if (m_post_processing_input_texture.GetWidth() != target_width ||
m_post_processing_input_texture.GetHeight() != target_height)
{
if (!m_post_processing_input_texture.Create(target_width, target_height, 1, 1, 1, GL_RGBA8, GL_RGBA,
GL_UNSIGNED_BYTE) ||
if (!m_post_processing_input_texture.Create(target_width, target_height, 1, 1, 1, GPUTexture::Format::RGBA8) ||
!m_post_processing_input_texture.CreateFramebuffer())
{
return false;
@ -856,7 +900,7 @@ bool OpenGLHostDisplay::CheckPostProcessingRenderTargets(u32 target_width, u32 t
PostProcessingStage& pps = m_post_processing_stages[i];
if (pps.output_texture.GetWidth() != target_width || pps.output_texture.GetHeight() != target_height)
{
if (!pps.output_texture.Create(target_width, target_height, 1, 1, 1, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE) ||
if (!pps.output_texture.Create(target_width, target_height, 1, 1, 1, GPUTexture::Format::RGBA8) ||
!pps.output_texture.CreateFramebuffer())
{
return false;
@ -868,29 +912,24 @@ bool OpenGLHostDisplay::CheckPostProcessingRenderTargets(u32 target_width, u32 t
}
void OpenGLHostDisplay::ApplyPostProcessingChain(GLuint final_target, s32 final_left, s32 final_top, s32 final_width,
s32 final_height, void* texture_handle, u32 texture_width,
s32 texture_height, s32 texture_view_x, s32 texture_view_y,
s32 texture_view_width, s32 texture_view_height, u32 target_width,
u32 target_height)
s32 final_height, GL::Texture* texture, s32 texture_view_x,
s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
u32 target_width, u32 target_height)
{
if (!CheckPostProcessingRenderTargets(target_width, target_height))
{
RenderDisplay(final_left, target_height - final_top - final_height, final_width, final_height, texture_handle,
texture_width, texture_height, texture_view_x, texture_view_y, texture_view_width,
texture_view_height, IsUsingLinearFiltering());
RenderDisplay(final_left, target_height - final_top - final_height, final_width, final_height, texture,
texture_view_x, texture_view_y, texture_view_width, texture_view_height, IsUsingLinearFiltering());
return;
}
// downsample/upsample - use same viewport for remainder
m_post_processing_input_texture.BindFramebuffer(GL_DRAW_FRAMEBUFFER);
glClear(GL_COLOR_BUFFER_BIT);
RenderDisplay(final_left, target_height - final_top - final_height, final_width, final_height, texture_handle,
texture_width, texture_height, texture_view_x, texture_view_y, texture_view_width, texture_view_height,
IsUsingLinearFiltering());
RenderDisplay(final_left, target_height - final_top - final_height, final_width, final_height, texture,
texture_view_x, texture_view_y, texture_view_width, texture_view_height, IsUsingLinearFiltering());
texture_handle = &m_post_processing_input_texture;
texture_width = m_post_processing_input_texture.GetWidth();
texture_height = m_post_processing_input_texture.GetHeight();
texture = &m_post_processing_input_texture;
texture_view_x = final_left;
texture_view_y = final_top;
texture_view_width = final_width;
@ -914,12 +953,12 @@ void OpenGLHostDisplay::ApplyPostProcessingChain(GLuint final_target, s32 final_
pps.program.Bind();
static_cast<const GL::Texture*>(texture_handle)->Bind();
static_cast<const GL::Texture*>(texture)->Bind();
glBindSampler(0, m_display_nearest_sampler);
const auto map_result = m_post_processing_ubo->Map(m_uniform_buffer_alignment, pps.uniforms_size);
m_post_processing_chain.GetShaderStage(i).FillUniformBuffer(
map_result.pointer, texture_width, texture_height, texture_view_x, texture_view_y, texture_view_width,
map_result.pointer, texture->GetWidth(), texture->GetHeight(), texture_view_x, texture_view_y, texture_view_width,
texture_view_height, GetWindowWidth(), GetWindowHeight(), 0.0f);
m_post_processing_ubo->Unmap(pps.uniforms_size);
glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_post_processing_ubo->GetGLBufferId(), map_result.buffer_offset,
@ -928,7 +967,7 @@ void OpenGLHostDisplay::ApplyPostProcessingChain(GLuint final_target, s32 final_
glDrawArrays(GL_TRIANGLES, 0, 3);
if (i != final_stage)
texture_handle = &pps.output_texture;
texture = &pps.output_texture;
}
glBindSampler(0, 0);
@ -955,7 +994,7 @@ void OpenGLHostDisplay::DestroyTimestampQueries()
if (m_timestamp_query_started)
{
const auto EndQuery = gles ? glEndQueryEXT : glEndQuery;
EndQuery(m_timestamp_queries[m_write_timestamp_query]);
EndQuery(GL_TIME_ELAPSED);
}
DeleteQueries(static_cast<u32>(m_timestamp_queries.size()), m_timestamp_queries.data());
@ -1062,121 +1101,3 @@ GL::StreamBuffer* OpenGLHostDisplay::GetTextureStreamBuffer()
m_texture_stream_buffer = GL::StreamBuffer::Create(GL_PIXEL_UNPACK_BUFFER, TEXTURE_STREAM_BUFFER_SIZE);
return m_texture_stream_buffer.get();
}
bool OpenGLHostDisplayTexture::BeginUpdate(u32 width, u32 height, void** out_buffer, u32* out_pitch)
{
const u32 pixel_size = HostDisplay::GetDisplayPixelFormatSize(m_format);
const u32 stride = Common::AlignUpPow2(width * pixel_size, 4);
const u32 size_required = stride * height;
OpenGLHostDisplay* display = static_cast<OpenGLHostDisplay*>(g_host_display.get());
GL::StreamBuffer* buffer = display->UsePBOForUploads() ? display->GetTextureStreamBuffer() : nullptr;
if (buffer && size_required < buffer->GetSize())
{
auto map = buffer->Map(4096, size_required);
m_map_offset = map.buffer_offset;
*out_buffer = map.pointer;
*out_pitch = stride;
}
else
{
std::vector<u8>& repack_buffer = display->GetTextureRepackBuffer();
if (repack_buffer.size() < size_required)
repack_buffer.resize(size_required);
*out_buffer = repack_buffer.data();
*out_pitch = stride;
}
return true;
}
void OpenGLHostDisplayTexture::EndUpdate(u32 x, u32 y, u32 width, u32 height)
{
const u32 pixel_size = HostDisplay::GetDisplayPixelFormatSize(m_format);
const u32 stride = Common::AlignUpPow2(width * pixel_size, 4);
const u32 size_required = stride * height;
OpenGLHostDisplay* display = static_cast<OpenGLHostDisplay*>(g_host_display.get());
GL::StreamBuffer* buffer = display->UsePBOForUploads() ? display->GetTextureStreamBuffer() : nullptr;
const auto [gl_internal_format, gl_format, gl_type] =
GetPixelFormatMapping(display->GetGLContext()->IsGLES(), m_format);
const bool whole_texture = (!m_texture.UseTextureStorage() && x == 0 && y == 0 && width == m_texture.GetWidth() &&
height == m_texture.GetHeight());
m_texture.Create(width, height, 1, 1, 1, gl_internal_format, gl_format, gl_type, nullptr, false, false);
m_texture.Bind();
if (buffer && size_required < buffer->GetSize())
{
buffer->Unmap(size_required);
buffer->Bind();
if (whole_texture)
{
glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, width, height, 0, gl_format, gl_type,
reinterpret_cast<void*>(static_cast<uintptr_t>(m_map_offset)));
}
else
{
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, gl_format, gl_type,
reinterpret_cast<void*>(static_cast<uintptr_t>(m_map_offset)));
}
buffer->Unbind();
}
else
{
std::vector<u8>& repack_buffer = display->GetTextureRepackBuffer();
if (whole_texture)
glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, width, height, 0, gl_format, gl_type, repack_buffer.data());
else
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, gl_format, gl_type, repack_buffer.data());
}
}
bool OpenGLHostDisplayTexture::Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch)
{
OpenGLHostDisplay* display = static_cast<OpenGLHostDisplay*>(g_host_display.get());
const auto [gl_internal_format, gl_format, gl_type] =
GetPixelFormatMapping(display->GetGLContext()->IsGLES(), m_format);
const u32 pixel_size = HostDisplay::GetDisplayPixelFormatSize(m_format);
const bool is_packed_tightly = (pitch == (pixel_size * width));
const bool whole_texture = (!m_texture.UseTextureStorage() && x == 0 && y == 0 && width == m_texture.GetWidth() &&
height == m_texture.GetHeight());
m_texture.Bind();
// If we have GLES3, we can set row_length.
if (!display->UseGLES3DrawPath() || is_packed_tightly)
{
if (!is_packed_tightly)
glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch / pixel_size);
if (whole_texture)
glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, width, height, 0, gl_format, gl_type, data);
else
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, gl_format, gl_type, data);
if (!is_packed_tightly)
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
else
{
// Otherwise, we need to repack the image.
std::vector<u8>& repack_buffer = display->GetTextureRepackBuffer();
const u32 packed_pitch = width * pixel_size;
const u32 repack_size = packed_pitch * height;
if (repack_buffer.size() < repack_size)
repack_buffer.resize(repack_size);
StringUtil::StrideMemCpy(repack_buffer.data(), packed_pitch, data, pitch, packed_pitch, height);
if (whole_texture)
glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, width, height, 0, gl_format, gl_type, repack_buffer.data());
else
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, gl_format, gl_type, repack_buffer.data());
}
return true;
}

View File

@ -40,18 +40,21 @@ public:
bool SetPostProcessingChain(const std::string_view& config) override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
HostDisplayPixelFormat format, const void* data, u32 data_stride,
bool dynamic = false) override;
bool DownloadTexture(const void* texture_handle, HostDisplayPixelFormat texture_format, u32 x, u32 y, u32 width,
u32 height, void* out_data, u32 out_data_stride) override;
bool SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const override;
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Format format, const void* data, u32 data_stride,
bool dynamic = false) override;
bool BeginTextureUpdate(GPUTexture* texture, u32 width, u32 height, void** out_buffer, u32* out_pitch) override;
void EndTextureUpdate(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height) override;
bool UpdateTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch) override;
bool DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data,
u32 out_data_stride) override;
bool SupportsTextureFormat(GPUTexture::Format format) const override;
void SetVSync(bool enabled) override;
bool Render(bool skip_present) override;
bool RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
HostDisplayPixelFormat* out_format) override;
GPUTexture::Format* out_format) override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
@ -80,10 +83,9 @@ protected:
void RenderImGui();
void RenderSoftwareCursor();
void RenderDisplay(s32 left, s32 bottom, s32 width, s32 height, void* texture_handle, u32 texture_width,
s32 texture_height, s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
s32 texture_view_height, bool linear_filter);
void RenderSoftwareCursor(s32 left, s32 bottom, s32 width, s32 height, HostDisplayTexture* texture_handle);
void RenderDisplay(s32 left, s32 bottom, s32 width, s32 height, GL::Texture* texture, s32 texture_view_x,
s32 texture_view_y, s32 texture_view_width, s32 texture_view_height, bool linear_filter);
void RenderSoftwareCursor(s32 left, s32 bottom, s32 width, s32 height, GPUTexture* texture_handle);
struct PostProcessingStage
{
@ -94,9 +96,8 @@ protected:
bool CheckPostProcessingRenderTargets(u32 target_width, u32 target_height);
void ApplyPostProcessingChain(GLuint final_target, s32 final_left, s32 final_top, s32 final_width, s32 final_height,
void* texture_handle, u32 texture_width, s32 texture_height, s32 texture_view_x,
s32 texture_view_y, s32 texture_view_width, s32 texture_view_height, u32 target_width,
u32 target_height);
GL::Texture* texture, s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
s32 texture_view_height, u32 target_width, u32 target_height);
void CreateTimestampQueries();
void DestroyTimestampQueries();
@ -114,6 +115,7 @@ protected:
std::unique_ptr<GL::StreamBuffer> m_texture_stream_buffer;
std::vector<u8> m_texture_repack_buffer;
u32 m_texture_stream_buffer_offset = 0;
FrontendCommon::PostProcessingChain m_post_processing_chain;
GL::Texture m_post_processing_input_texture;

View File

@ -18,43 +18,6 @@
#include <array>
Log_SetChannel(VulkanHostDisplay);
class VulkanHostDisplayTexture : public HostDisplayTexture
{
public:
VulkanHostDisplayTexture(Vulkan::Texture texture, HostDisplayPixelFormat format)
: m_texture(std::move(texture)), m_format(format)
{
}
~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(); }
u32 GetLayers() const override { return m_texture.GetLayers(); }
u32 GetLevels() const override { return m_texture.GetLevels(); }
u32 GetSamples() const override { return m_texture.GetSamples(); }
HostDisplayPixelFormat GetFormat() const override { return m_format; }
bool BeginUpdate(u32 width, u32 height, void** out_buffer, u32* out_pitch) override
{
return m_texture.BeginUpdate(width, height, out_buffer, out_pitch);
}
void EndUpdate(u32 x, u32 y, u32 width, u32 height) override { m_texture.EndUpdate(x, y, width, height, 0, 0); }
bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch) override
{
return m_texture.Update(x, y, width, height, 0, 0, data, pitch);
}
const Vulkan::Texture& GetTexture() const { return m_texture; }
Vulkan::Texture& GetTexture() { return m_texture; }
private:
Vulkan::Texture m_texture;
HostDisplayPixelFormat m_format;
};
VulkanHostDisplay::VulkanHostDisplay() = default;
VulkanHostDisplay::~VulkanHostDisplay()
@ -172,56 +135,65 @@ void VulkanHostDisplay::DestroyRenderSurface()
m_swap_chain.reset();
}
static constexpr std::array<VkFormat, static_cast<u32>(HostDisplayPixelFormat::Count)> s_display_pixel_format_mapping =
{{VK_FORMAT_UNDEFINED, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R5G6B5_UNORM_PACK16,
VK_FORMAT_A1R5G5B5_UNORM_PACK16}};
std::unique_ptr<HostDisplayTexture> VulkanHostDisplay::CreateTexture(u32 width, u32 height, u32 layers, u32 levels,
u32 samples, HostDisplayPixelFormat format,
const void* data, u32 data_stride,
bool dynamic /* = false */)
std::unique_ptr<GPUTexture> VulkanHostDisplay::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Format format, const void* data,
u32 data_stride, bool dynamic /* = false */)
{
const VkFormat vk_format = s_display_pixel_format_mapping[static_cast<u32>(format)];
const VkFormat vk_format = Vulkan::Texture::GetVkFormat(format);
if (vk_format == VK_FORMAT_UNDEFINED)
return {};
static constexpr VkImageUsageFlags usage =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
const Vulkan::Util::DebugScope debugScope(g_vulkan_context->GetCurrentCommandBuffer(),
"VulkanHostDisplay::CreateTexture");
Vulkan::Texture texture;
if (!texture.Create(width, height, levels, layers, vk_format, static_cast<VkSampleCountFlagBits>(samples),
(layers > 1) ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
usage))
std::unique_ptr<Vulkan::Texture> texture(std::make_unique<Vulkan::Texture>());
if (!texture->Create(width, height, levels, layers, vk_format, static_cast<VkSampleCountFlagBits>(samples),
(layers > 1) ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
usage))
{
return {};
}
texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
texture->TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
if (data)
{
texture.Update(0, 0, width, height, 0, 0, data, data_stride);
texture->Update(0, 0, width, height, 0, 0, data, data_stride);
}
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->GetCurrentCommandBuffer(), texture.GetImage(), texture.GetLayout(), &ccv, 1u,
&isr);
vkCmdClearColorImage(g_vulkan_context->GetCurrentCommandBuffer(), texture->GetImage(), texture->GetLayout(), &ccv,
1u, &isr);
}
texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
texture->TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
return std::make_unique<VulkanHostDisplayTexture>(std::move(texture), format);
return texture;
}
bool VulkanHostDisplay::SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const
bool VulkanHostDisplay::BeginTextureUpdate(GPUTexture* texture, u32 width, u32 height, void** out_buffer,
u32* out_pitch)
{
const VkFormat vk_format = s_display_pixel_format_mapping[static_cast<u32>(format)];
return static_cast<Vulkan::Texture*>(texture)->BeginUpdate(width, height, out_buffer, out_pitch);
}
void VulkanHostDisplay::EndTextureUpdate(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height)
{
static_cast<Vulkan::Texture*>(texture)->EndUpdate(x, y, width, height, 0, 0);
}
bool VulkanHostDisplay::UpdateTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data,
u32 pitch)
{
return static_cast<Vulkan::Texture*>(texture)->Update(x, y, width, height, 0, 0, data, pitch);
}
bool VulkanHostDisplay::SupportsTextureFormat(GPUTexture::Format format) const
{
const VkFormat vk_format = Vulkan::Texture::GetVkFormat(format);
if (vk_format == VK_FORMAT_UNDEFINED)
return false;
@ -310,12 +282,12 @@ void VulkanHostDisplay::DestroyStagingBuffer()
}
}
bool VulkanHostDisplay::DownloadTexture(const void* texture_handle, HostDisplayPixelFormat texture_format, u32 x, u32 y,
u32 width, u32 height, void* out_data, u32 out_data_stride)
bool VulkanHostDisplay::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data,
u32 out_data_stride)
{
Vulkan::Texture* texture = static_cast<Vulkan::Texture*>(const_cast<void*>(texture_handle));
Vulkan::Texture* tex = static_cast<Vulkan::Texture*>(texture);
const u32 pitch = texture->CalcUpdatePitch(width);
const u32 pitch = tex->CalcUpdatePitch(width);
const u32 size = pitch * height;
const u32 level = 0;
if (!CheckStagingBufferSize(size))
@ -328,16 +300,16 @@ bool VulkanHostDisplay::DownloadTexture(const void* texture_handle, HostDisplayP
const VkCommandBuffer cmdbuf = g_vulkan_context->GetCurrentCommandBuffer();
const Vulkan::Util::DebugScope debugScope(cmdbuf, "VulkanHostDisplay::DownloadTexture(%u,%u)", width, height);
VkImageLayout old_layout = texture->GetLayout();
VkImageLayout old_layout = tex->GetLayout();
if (old_layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
texture->TransitionSubresourcesToLayout(cmdbuf, level, 1, 0, 1, old_layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
tex->TransitionSubresourcesToLayout(cmdbuf, level, 1, 0, 1, old_layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
VkBufferImageCopy image_copy = {};
const VkImageAspectFlags aspect = Vulkan::Util::IsDepthFormat(static_cast<VkFormat>(texture->GetFormat())) ?
const VkImageAspectFlags aspect = Vulkan::Util::IsDepthFormat(static_cast<VkFormat>(tex->GetFormat())) ?
VK_IMAGE_ASPECT_DEPTH_BIT :
VK_IMAGE_ASPECT_COLOR_BIT;
image_copy.bufferOffset = 0;
image_copy.bufferRowLength = texture->CalcUpdateRowLength(pitch);
image_copy.bufferRowLength = tex->CalcUpdateRowLength(pitch);
image_copy.bufferImageHeight = 0;
image_copy.imageSubresource = {aspect, level, 0u, 1u};
image_copy.imageOffset = {static_cast<s32>(x), static_cast<s32>(y), 0};
@ -349,8 +321,8 @@ bool VulkanHostDisplay::DownloadTexture(const void* texture_handle, HostDisplayP
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
// do the copy
vkCmdCopyImageToBuffer(cmdbuf, texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_readback_staging_buffer,
1, &image_copy);
vkCmdCopyImageToBuffer(cmdbuf, tex->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_readback_staging_buffer, 1,
&image_copy);
// flush gpu cache
Vulkan::Util::BufferMemoryBarrier(cmdbuf, m_readback_staging_buffer, VK_ACCESS_TRANSFER_WRITE_BIT,
@ -359,7 +331,7 @@ bool VulkanHostDisplay::DownloadTexture(const void* texture_handle, HostDisplayP
if (old_layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
{
texture->TransitionSubresourcesToLayout(cmdbuf, level, 1, 0, 1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, old_layout);
tex->TransitionSubresourcesToLayout(cmdbuf, level, 1, 0, 1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, old_layout);
}
}
@ -674,7 +646,7 @@ bool VulkanHostDisplay::Render(bool skip_present)
}
bool VulkanHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
HostDisplayPixelFormat* out_format)
GPUTexture::Format* out_format)
{
// in theory we could do this without a swap chain, but postprocessing assumes it for now...
if (!m_swap_chain)
@ -685,26 +657,26 @@ bool VulkanHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>
{
case VK_FORMAT_R8G8B8A8_UNORM:
case VK_FORMAT_R8G8B8A8_SRGB:
*out_format = HostDisplayPixelFormat::RGBA8;
*out_format = GPUTexture::Format::RGBA8;
*out_stride = sizeof(u32) * width;
out_pixels->resize(width * height);
break;
case VK_FORMAT_B8G8R8A8_UNORM:
case VK_FORMAT_B8G8R8A8_SRGB:
*out_format = HostDisplayPixelFormat::BGRA8;
*out_format = GPUTexture::Format::BGRA8;
*out_stride = sizeof(u32) * width;
out_pixels->resize(width * height);
break;
case VK_FORMAT_A1R5G5B5_UNORM_PACK16:
*out_format = HostDisplayPixelFormat::RGBA5551;
*out_format = GPUTexture::Format::RGBA5551;
*out_stride = sizeof(u16) * width;
out_pixels->resize(((width * height) + 1) / 2);
break;
case VK_FORMAT_R5G6B5_UNORM_PACK16:
*out_format = HostDisplayPixelFormat::RGB565;
*out_format = GPUTexture::Format::RGB565;
*out_stride = sizeof(u16) * width;
out_pixels->resize(((width * height) + 1) / 2);
break;
@ -746,22 +718,22 @@ bool VulkanHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>
if (!m_post_processing_chain.IsEmpty())
{
ApplyPostProcessingChain(fb, left, top, draw_width, draw_height, m_display_texture_handle, m_display_texture_width,
m_display_texture_height, m_display_texture_view_x, m_display_texture_view_y,
m_display_texture_view_width, m_display_texture_view_height, width, height);
ApplyPostProcessingChain(fb, left, top, draw_width, draw_height, static_cast<Vulkan::Texture*>(m_display_texture),
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, width, height);
}
else
{
BeginSwapChainRenderPass(fb, width, height);
RenderDisplay(left, top, draw_width, draw_height, m_display_texture_handle, m_display_texture_width,
m_display_texture_height, m_display_texture_view_x, m_display_texture_view_y,
m_display_texture_view_width, m_display_texture_view_height, IsUsingLinearFiltering());
RenderDisplay(left, top, draw_width, draw_height, static_cast<Vulkan::Texture*>(m_display_texture),
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, IsUsingLinearFiltering());
}
vkCmdEndRenderPass(g_vulkan_context->GetCurrentCommandBuffer());
Vulkan::Util::EndDebugScope(g_vulkan_context->GetCurrentCommandBuffer());
tex.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
DownloadTexture(&tex, *out_format, 0, 0, width, height, out_pixels->data(), *out_stride);
DownloadTexture(&tex, 0, 0, width, height, out_pixels->data(), *out_stride);
// destroying these immediately should be safe since nothing's going to access them, and it's not part of the command
// stream
@ -800,27 +772,27 @@ void VulkanHostDisplay::RenderDisplay()
if (!m_post_processing_chain.IsEmpty())
{
ApplyPostProcessingChain(m_swap_chain->GetCurrentFramebuffer(), left, top, width, height, m_display_texture_handle,
m_display_texture_width, m_display_texture_height, m_display_texture_view_x,
ApplyPostProcessingChain(m_swap_chain->GetCurrentFramebuffer(), left, top, width, height,
static_cast<Vulkan::Texture*>(m_display_texture), m_display_texture_view_x,
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
m_swap_chain->GetWidth(), m_swap_chain->GetHeight());
return;
}
BeginSwapChainRenderPass(m_swap_chain->GetCurrentFramebuffer(), m_swap_chain->GetWidth(), m_swap_chain->GetHeight());
RenderDisplay(left, top, width, height, m_display_texture_handle, m_display_texture_width, m_display_texture_height,
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, IsUsingLinearFiltering());
RenderDisplay(left, top, width, height, static_cast<Vulkan::Texture*>(m_display_texture), m_display_texture_view_x,
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
IsUsingLinearFiltering());
}
void VulkanHostDisplay::RenderDisplay(s32 left, s32 top, s32 width, s32 height, void* texture_handle, u32 texture_width,
s32 texture_height, s32 texture_view_x, s32 texture_view_y,
s32 texture_view_width, s32 texture_view_height, bool linear_filter)
void VulkanHostDisplay::RenderDisplay(s32 left, s32 top, s32 width, s32 height, Vulkan::Texture* texture,
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
s32 texture_view_height, bool linear_filter)
{
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
const Vulkan::Util::DebugScope debugScope(
cmdbuffer, "VulkanHostDisplay::RenderDisplay: {%u,%u} %ux%u | %ux%u | {%u,%u} %ux%u", left, top, width, height,
texture_height, texture_width, texture_view_x, texture_view_y, texture_view_width, texture_view_height);
texture->GetWidth(), texture->GetHeight(), texture_view_x, texture_view_y, texture_view_width, texture_view_height);
VkDescriptorSet ds = g_vulkan_context->AllocateDescriptorSet(m_descriptor_set_layout);
if (ds == VK_NULL_HANDLE)
@ -830,19 +802,19 @@ void VulkanHostDisplay::RenderDisplay(s32 left, s32 top, s32 width, s32 height,
}
{
const Vulkan::Texture* vktex = static_cast<Vulkan::Texture*>(texture_handle);
Vulkan::DescriptorSetUpdateBuilder dsupdate;
dsupdate.AddCombinedImageSamplerDescriptorWrite(
ds, 0, vktex->GetView(), linear_filter ? m_linear_sampler : m_point_sampler, vktex->GetLayout());
ds, 0, texture->GetView(), linear_filter ? m_linear_sampler : m_point_sampler, texture->GetLayout());
dsupdate.Update(g_vulkan_context->GetDevice());
}
const float position_adjust = IsUsingLinearFiltering() ? 0.5f : 0.0f;
const float size_adjust = IsUsingLinearFiltering() ? 1.0f : 0.0f;
const PushConstants pc{(static_cast<float>(texture_view_x) + position_adjust) / static_cast<float>(texture_width),
(static_cast<float>(texture_view_y) + position_adjust) / static_cast<float>(texture_height),
(static_cast<float>(texture_view_width) - size_adjust) / static_cast<float>(texture_width),
(static_cast<float>(texture_view_height) - size_adjust) / static_cast<float>(texture_height)};
const PushConstants pc{
(static_cast<float>(texture_view_x) + position_adjust) / static_cast<float>(texture->GetWidth()),
(static_cast<float>(texture_view_y) + position_adjust) / static_cast<float>(texture->GetHeight()),
(static_cast<float>(texture_view_width) - size_adjust) / static_cast<float>(texture->GetWidth()),
(static_cast<float>(texture_view_height) - size_adjust) / static_cast<float>(texture->GetHeight())};
vkCmdBindPipeline(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_display_pipeline);
vkCmdPushConstants(cmdbuffer, m_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(pc), &pc);
@ -867,7 +839,7 @@ void VulkanHostDisplay::RenderSoftwareCursor()
RenderSoftwareCursor(left, top, width, height, m_cursor_texture.get());
}
void VulkanHostDisplay::RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, HostDisplayTexture* texture)
void VulkanHostDisplay::RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, GPUTexture* texture)
{
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
const Vulkan::Util::DebugScope debugScope(cmdbuffer, "VulkanHostDisplay::RenderSoftwareCursor: {%u,%u} %ux%u", left,
@ -882,8 +854,8 @@ void VulkanHostDisplay::RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 h
{
Vulkan::DescriptorSetUpdateBuilder dsupdate;
dsupdate.AddCombinedImageSamplerDescriptorWrite(
ds, 0, static_cast<VulkanHostDisplayTexture*>(texture)->GetTexture().GetView(), m_linear_sampler);
dsupdate.AddCombinedImageSamplerDescriptorWrite(ds, 0, static_cast<Vulkan::Texture*>(texture)->GetView(),
m_linear_sampler);
dsupdate.Update(g_vulkan_context->GetDevice());
}
@ -1118,10 +1090,9 @@ bool VulkanHostDisplay::CheckPostProcessingRenderTargets(u32 target_width, u32 t
}
void VulkanHostDisplay::ApplyPostProcessingChain(VkFramebuffer target_fb, s32 final_left, s32 final_top,
s32 final_width, s32 final_height, void* texture_handle,
u32 texture_width, s32 texture_height, s32 texture_view_x,
s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
u32 target_width, u32 target_height)
s32 final_width, s32 final_height, Vulkan::Texture* texture,
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
s32 texture_view_height, u32 target_width, u32 target_height)
{
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
const Vulkan::Util::DebugScope post_scope(cmdbuffer, "VulkanHostDisplay::ApplyPostProcessingChain");
@ -1129,23 +1100,21 @@ void VulkanHostDisplay::ApplyPostProcessingChain(VkFramebuffer target_fb, s32 fi
if (!CheckPostProcessingRenderTargets(target_width, target_height))
{
BeginSwapChainRenderPass(target_fb, target_width, target_height);
RenderDisplay(final_left, final_top, final_width, final_height, texture_handle, texture_width, texture_height,
texture_view_x, texture_view_y, texture_view_width, texture_view_height, IsUsingLinearFiltering());
RenderDisplay(final_left, final_top, final_width, final_height, texture, texture_view_x, texture_view_y,
texture_view_width, texture_view_height, IsUsingLinearFiltering());
return;
}
// downsample/upsample - use same viewport for remainder
m_post_processing_input_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
BeginSwapChainRenderPass(m_post_processing_input_framebuffer, target_width, target_height);
RenderDisplay(final_left, final_top, final_width, final_height, texture_handle, texture_width, texture_height,
texture_view_x, texture_view_y, texture_view_width, texture_view_height, IsUsingLinearFiltering());
RenderDisplay(final_left, final_top, final_width, final_height, texture, texture_view_x, texture_view_y,
texture_view_width, texture_view_height, IsUsingLinearFiltering());
vkCmdEndRenderPass(cmdbuffer);
Vulkan::Util::EndDebugScope(g_vulkan_context->GetCurrentCommandBuffer());
m_post_processing_input_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
texture_handle = &m_post_processing_input_texture;
texture_width = m_post_processing_input_texture.GetWidth();
texture_height = m_post_processing_input_texture.GetHeight();
texture = &m_post_processing_input_texture;
texture_view_x = final_left;
texture_view_y = final_top;
texture_view_width = final_width;
@ -1177,17 +1146,16 @@ void VulkanHostDisplay::ApplyPostProcessingChain(VkFramebuffer target_fb, s32 fi
return;
}
const Vulkan::Texture* vktex = static_cast<Vulkan::Texture*>(texture_handle);
Vulkan::DescriptorSetUpdateBuilder dsupdate;
dsupdate.AddCombinedImageSamplerDescriptorWrite(ds, 1, vktex->GetView(), m_point_sampler, vktex->GetLayout());
dsupdate.AddCombinedImageSamplerDescriptorWrite(ds, 1, texture->GetView(), m_point_sampler, texture->GetLayout());
if (use_push_constants)
{
u8 buffer[FrontendCommon::PostProcessingShader::PUSH_CONSTANT_SIZE_THRESHOLD];
Assert(pps.uniforms_size <= sizeof(buffer));
m_post_processing_chain.GetShaderStage(i).FillUniformBuffer(
buffer, texture_width, texture_height, texture_view_x, texture_view_y, texture_view_width, texture_view_height,
GetWindowWidth(), GetWindowHeight(), 0.0f);
buffer, texture->GetWidth(), texture->GetHeight(), texture_view_x, texture_view_y, texture_view_width,
texture_view_height, GetWindowWidth(), GetWindowHeight(), 0.0f);
vkCmdPushConstants(cmdbuffer, m_post_process_pipeline_layout,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, pps.uniforms_size, buffer);
@ -1206,8 +1174,8 @@ void VulkanHostDisplay::ApplyPostProcessingChain(VkFramebuffer target_fb, s32 fi
const u32 offset = m_post_processing_ubo.GetCurrentOffset();
m_post_processing_chain.GetShaderStage(i).FillUniformBuffer(
m_post_processing_ubo.GetCurrentHostPointer(), texture_width, texture_height, texture_view_x, texture_view_y,
texture_view_width, texture_view_height, GetWindowWidth(), GetWindowHeight(), 0.0f);
m_post_processing_ubo.GetCurrentHostPointer(), texture->GetWidth(), texture->GetHeight(), texture_view_x,
texture_view_y, texture_view_width, texture_view_height, GetWindowWidth(), GetWindowHeight(), 0.0f);
m_post_processing_ubo.CommitMemory(pps.uniforms_size);
dsupdate.AddBufferDescriptorWrite(ds, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
@ -1226,7 +1194,7 @@ void VulkanHostDisplay::ApplyPostProcessingChain(VkFramebuffer target_fb, s32 fi
vkCmdEndRenderPass(cmdbuffer);
Vulkan::Util::EndDebugScope(g_vulkan_context->GetCurrentCommandBuffer());
pps.output_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
texture_handle = &pps.output_texture;
texture = &pps.output_texture;
}
}
}

View File

@ -30,7 +30,7 @@ public:
bool threaded_presentation) override;
bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device,
bool threaded_presentation) override;
bool MakeRenderContextCurrent() override;
bool DoneRenderContextCurrent() override;
@ -44,18 +44,21 @@ public:
bool SetPostProcessingChain(const std::string_view& config) override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
HostDisplayPixelFormat format, const void* data, u32 data_stride,
bool dynamic = false) override;
bool DownloadTexture(const void* texture_handle, HostDisplayPixelFormat texture_format, u32 x, u32 y, u32 width,
u32 height, void* out_data, u32 out_data_stride) override;
bool SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const override;
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Format format, const void* data, u32 data_stride,
bool dynamic = false) override;
bool BeginTextureUpdate(GPUTexture* texture, u32 width, u32 height, void** out_buffer, u32* out_pitch) override;
void EndTextureUpdate(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height) override;
bool UpdateTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch) override;
bool DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data,
u32 out_data_stride) override;
bool SupportsTextureFormat(GPUTexture::Format format) const override;
void SetVSync(bool enabled) override;
bool Render(bool skip_present) override;
bool RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
HostDisplayPixelFormat* out_format) override;
GPUTexture::Format* out_format) override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
@ -85,9 +88,8 @@ protected:
bool CheckPostProcessingRenderTargets(u32 target_width, u32 target_height);
void ApplyPostProcessingChain(VkFramebuffer target_fb, s32 final_left, s32 final_top, s32 final_width,
s32 final_height, void* texture_handle, u32 texture_width, s32 texture_height,
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
u32 target_width, u32 target_height);
s32 final_height, Vulkan::Texture* texture, s32 texture_view_x, s32 texture_view_y,
s32 texture_view_width, s32 texture_view_height, u32 target_width, u32 target_height);
VkRenderPass GetRenderPassForDisplay() const;
@ -106,10 +108,9 @@ protected:
void RenderImGui();
void RenderSoftwareCursor();
void RenderDisplay(s32 left, s32 top, s32 width, s32 height, void* texture_handle, u32 texture_width,
s32 texture_height, s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
s32 texture_view_height, bool linear_filter);
void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, HostDisplayTexture* texture_handle);
void RenderDisplay(s32 left, s32 top, s32 width, s32 height, Vulkan::Texture* texture, s32 texture_view_x,
s32 texture_view_y, s32 texture_view_width, s32 texture_view_height, bool linear_filter);
void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, GPUTexture* texture_handle);
std::unique_ptr<Vulkan::SwapChain> m_swap_chain;