HostDisplay: Use streaming for sw renderer display

This commit is contained in:
Connor McLaughlin
2022-09-11 01:54:01 +10:00
parent c27026aed5
commit 0b3461338c
16 changed files with 495 additions and 541 deletions

View File

@ -23,7 +23,7 @@ Log_SetChannel(D3D11HostDisplay);
namespace FrontendCommon {
class D3D11HostDisplayTexture : public HostDisplayTexture
class D3D11HostDisplayTexture final : public HostDisplayTexture
{
public:
D3D11HostDisplayTexture(D3D11::Texture texture, HostDisplayPixelFormat format, bool dynamic)
@ -40,6 +40,41 @@ public:
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(); }
@ -107,43 +142,6 @@ std::unique_ptr<HostDisplayTexture> D3D11HostDisplay::CreateTexture(u32 width, u
return std::make_unique<D3D11HostDisplayTexture>(std::move(tex), format, dynamic);
}
void D3D11HostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
const void* texture_data, u32 texture_data_stride)
{
D3D11HostDisplayTexture* d3d11_texture = static_cast<D3D11HostDisplayTexture*>(texture);
if (!d3d11_texture->IsDynamic())
{
const CD3D11_BOX dst_box(x, y, 0, x + width, y + height, 1);
m_context->UpdateSubresource(d3d11_texture->GetD3DTexture(), 0, &dst_box, texture_data, texture_data_stride,
texture_data_stride * height);
}
else
{
D3D11_MAPPED_SUBRESOURCE sr;
HRESULT hr = m_context->Map(d3d11_texture->GetD3DTexture(), 0, D3D11_MAP_WRITE_DISCARD, 0, &sr);
if (FAILED(hr))
Panic("Failed to map dynamic host display texture");
char* dst_ptr = static_cast<char*>(sr.pData) + (y * sr.RowPitch) + (x * sizeof(u32));
const char* src_ptr = static_cast<const char*>(texture_data);
if (sr.RowPitch == texture_data_stride)
{
std::memcpy(dst_ptr, src_ptr, texture_data_stride * height);
}
else
{
for (u32 row = 0; row < height; row++)
{
std::memcpy(dst_ptr, src_ptr, width * sizeof(u32));
src_ptr += texture_data_stride;
dst_ptr += sr.RowPitch;
}
}
m_context->Unmap(d3d11_texture->GetD3DTexture(), 0);
}
}
bool D3D11HostDisplay::DownloadTexture(const void* texture_handle, HostDisplayPixelFormat texture_format, u32 x, u32 y,
u32 width, u32 height, void* out_data, u32 out_data_stride)
{
@ -182,43 +180,6 @@ bool D3D11HostDisplay::SupportsDisplayPixelFormat(HostDisplayPixelFormat format)
return (SUCCEEDED(m_device->CheckFormatSupport(dfmt, &support)) && ((support & required) == required));
}
bool D3D11HostDisplay::BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer,
u32* out_pitch)
{
ClearDisplayTexture();
const DXGI_FORMAT dxgi_format = s_display_pixel_format_mapping[static_cast<u32>(format)];
if (m_display_pixels_texture.GetWidth() < width || m_display_pixels_texture.GetHeight() < height ||
m_display_pixels_texture.GetFormat() != dxgi_format)
{
if (!m_display_pixels_texture.Create(m_device.Get(), width, height, 1, 1, dxgi_format, D3D11_BIND_SHADER_RESOURCE,
nullptr, 0, true))
{
return false;
}
}
D3D11_MAPPED_SUBRESOURCE sr;
HRESULT hr = m_context->Map(m_display_pixels_texture.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;
SetDisplayTexture(m_display_pixels_texture.GetD3DSRV(), format, m_display_pixels_texture.GetWidth(),
m_display_pixels_texture.GetHeight(), 0, 0, static_cast<u32>(width), static_cast<u32>(height));
return true;
}
void D3D11HostDisplay::EndSetDisplayPixels()
{
m_context->Unmap(m_display_pixels_texture.GetD3DTexture(), 0);
}
bool D3D11HostDisplay::GetHostRefreshRate(float* refresh_rate)
{
if (m_swap_chain && IsFullscreen())
@ -1235,7 +1196,8 @@ void D3D11HostDisplay::PopTimestampQuery()
D3D11_ASYNC_GETDATA_DONOTFLUSH);
if (start_hr == S_OK && end_hr == S_OK)
{
const float delta = static_cast<float>(static_cast<double>(end - start) / (static_cast<double>(disjoint.Frequency) / 1000.0));
const float delta =
static_cast<float>(static_cast<double>(end - start) / (static_cast<double>(disjoint.Frequency) / 1000.0));
m_accumulated_gpu_time += delta;
m_read_timestamp_query = (m_read_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
m_waiting_timestamp_queries--;

View File

@ -54,14 +54,9 @@ public:
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;
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data,
u32 texture_data_stride) 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;
bool BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer,
u32* out_pitch) override;
void EndSetDisplayPixels() override;
bool GetHostRefreshRate(float* refresh_rate) override;
@ -137,7 +132,6 @@ protected:
ComPtr<ID3D11SamplerState> m_point_sampler;
ComPtr<ID3D11SamplerState> m_linear_sampler;
D3D11::Texture m_display_pixels_texture;
D3D11::StreamBuffer m_display_uniform_buffer;
D3D11::AutoStagingTexture m_readback_staging_texture;

View File

@ -21,7 +21,7 @@ static constexpr std::array<DXGI_FORMAT, static_cast<u32>(HostDisplayPixelFormat
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 : public HostDisplayTexture
class D3D12HostDisplayTexture final : public HostDisplayTexture
{
public:
D3D12HostDisplayTexture(D3D12::Texture texture) : m_texture(std::move(texture)) {}
@ -45,6 +45,16 @@ public:
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; }
@ -107,13 +117,6 @@ std::unique_ptr<HostDisplayTexture> D3D12HostDisplay::CreateTexture(u32 width, u
return std::make_unique<D3D12HostDisplayTexture>(std::move(tex));
}
void D3D12HostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
const void* texture_data, u32 texture_data_stride)
{
static_cast<D3D12HostDisplayTexture*>(texture)->GetTexture().LoadData(x, y, width, height, texture_data,
texture_data_stride);
}
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)
{
@ -139,36 +142,6 @@ bool D3D12HostDisplay::SupportsDisplayPixelFormat(HostDisplayPixelFormat format)
return g_d3d12_context->SupportsTextureFormat(dfmt);
}
bool D3D12HostDisplay::BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer,
u32* out_pitch)
{
ClearDisplayTexture();
const DXGI_FORMAT dxgi_format = s_display_pixel_format_mapping[static_cast<u32>(format)];
if (m_display_pixels_texture.GetWidth() < width || m_display_pixels_texture.GetHeight() < height ||
m_display_pixels_texture.GetFormat() != dxgi_format)
{
if (!m_display_pixels_texture.Create(width, height, 1, dxgi_format, dxgi_format, DXGI_FORMAT_UNKNOWN,
DXGI_FORMAT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE))
{
return false;
}
}
if (!m_display_pixels_texture.BeginStreamUpdate(0, 0, width, height, out_buffer, out_pitch))
return false;
SetDisplayTexture(&m_display_pixels_texture, format, m_display_pixels_texture.GetWidth(),
m_display_pixels_texture.GetHeight(), 0, 0, static_cast<u32>(width), static_cast<u32>(height));
return true;
}
void D3D12HostDisplay::EndSetDisplayPixels()
{
m_display_pixels_texture.EndStreamUpdate(0, 0, static_cast<u32>(m_display_texture_view_width),
static_cast<u32>(m_display_texture_view_height));
}
bool D3D12HostDisplay::GetHostRefreshRate(float* refresh_rate)
{
if (m_swap_chain && IsFullscreen())
@ -772,10 +745,13 @@ void D3D12HostDisplay::RenderDisplay(ID3D12GraphicsCommandList* cmdlist, s32 lef
s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
bool linear_filter)
{
const float uniforms[4] = {static_cast<float>(texture_view_x) / static_cast<float>(texture_width),
static_cast<float>(texture_view_y) / static_cast<float>(texture_height),
(static_cast<float>(texture_view_width) - 0.5f) / static_cast<float>(texture_width),
(static_cast<float>(texture_view_height) - 0.5f) / static_cast<float>(texture_height)};
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)};
if (!m_display_uniform_buffer.ReserveMemory(sizeof(uniforms), D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT))
Panic("Failed to reserve UBO space");

View File

@ -55,14 +55,9 @@ public:
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;
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data,
u32 texture_data_stride) 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;
bool BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer,
u32* out_pitch) override;
void EndSetDisplayPixels() override;
bool GetHostRefreshRate(float* refresh_rate) override;

View File

@ -13,28 +13,35 @@ Log_SetChannel(OpenGLHostDisplay);
namespace FrontendCommon {
class OpenGLHostDisplayTexture : public HostDisplayTexture
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() override = default;
OpenGLHostDisplayTexture(GL::Texture texture, HostDisplayPixelFormat format);
~OpenGLHostDisplayTexture() override;
void* GetHandle() const override { return reinterpret_cast<void*>(static_cast<uintptr_t>(m_texture.GetGLId())); }
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 { return m_format; }
void* GetHandle() const override;
u32 GetWidth() const override;
u32 GetHeight() const override;
u32 GetLayers() const override;
u32 GetLevels() const override;
u32 GetSamples() const override;
HostDisplayPixelFormat GetFormat() const override;
GLuint GetGLID() const { return m_texture.GetGLId(); }
GLuint GetGLID() const;
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;
@ -105,43 +112,6 @@ std::unique_ptr<HostDisplayTexture> OpenGLHostDisplay::CreateTexture(u32 width,
return std::make_unique<OpenGLHostDisplayTexture>(std::move(tex), format);
}
void OpenGLHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
const void* texture_data, u32 texture_data_stride)
{
OpenGLHostDisplayTexture* tex = static_cast<OpenGLHostDisplayTexture*>(texture);
const auto [gl_internal_format, gl_format, gl_type] =
GetPixelFormatMapping(m_gl_context->IsGLES(), texture->GetFormat());
GLint alignment;
if (texture_data_stride & 1)
alignment = 1;
else if (texture_data_stride & 2)
alignment = 2;
else
alignment = 4;
GLint old_texture_binding = 0, old_alignment = 0, old_row_length = 0;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture_binding);
glBindTexture(GL_TEXTURE_2D, tex->GetGLID());
glGetIntegerv(GL_UNPACK_ALIGNMENT, &old_alignment);
glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
if (!m_use_gles2_draw_path)
{
glGetIntegerv(GL_UNPACK_ROW_LENGTH, &old_row_length);
glPixelStorei(GL_UNPACK_ROW_LENGTH, texture_data_stride / GetDisplayPixelFormatSize(texture->GetFormat()));
}
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, gl_format, gl_type, texture_data);
if (!m_use_gles2_draw_path)
glPixelStorei(GL_UNPACK_ROW_LENGTH, old_row_length);
glPixelStorei(GL_UNPACK_ALIGNMENT, old_alignment);
glBindTexture(GL_TEXTURE_2D, old_texture_binding);
}
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)
{
@ -174,152 +144,12 @@ bool OpenGLHostDisplay::DownloadTexture(const void* texture_handle, HostDisplayP
return true;
}
void OpenGLHostDisplay::BindDisplayPixelsTexture()
{
if (m_display_pixels_texture_id == 0)
{
const bool linear = IsUsingLinearFiltering();
glGenTextures(1, &m_display_pixels_texture_id);
glBindTexture(GL_TEXTURE_2D, m_display_pixels_texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
m_display_texture_is_linear_filtered = linear;
}
else
{
glBindTexture(GL_TEXTURE_2D, m_display_pixels_texture_id);
}
}
void OpenGLHostDisplay::UpdateDisplayPixelsTextureFilter()
{
const bool linear = IsUsingLinearFiltering();
if (linear == m_display_texture_is_linear_filtered)
return;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST);
m_display_texture_is_linear_filtered = linear;
}
bool OpenGLHostDisplay::SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const
{
const auto [gl_internal_format, gl_format, gl_type] = GetPixelFormatMapping(m_gl_context->IsGLES(), format);
return (gl_internal_format != static_cast<GLenum>(0));
}
bool OpenGLHostDisplay::BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer,
u32* out_pitch)
{
const u32 pixel_size = GetDisplayPixelFormatSize(format);
const u32 stride = Common::AlignUpPow2(width * pixel_size, 4);
const u32 size_required = stride * height * pixel_size;
if (m_use_pbo_for_pixels)
{
const u32 buffer_size = Common::AlignUpPow2(size_required * 2, 4 * 1024 * 1024);
if (!m_display_pixels_texture_pbo || m_display_pixels_texture_pbo->GetSize() < buffer_size)
{
m_display_pixels_texture_pbo.reset();
m_display_pixels_texture_pbo = GL::StreamBuffer::Create(GL_PIXEL_UNPACK_BUFFER, buffer_size);
if (!m_display_pixels_texture_pbo)
return false;
}
const auto map = m_display_pixels_texture_pbo->Map(GetDisplayPixelFormatSize(format), size_required);
m_display_texture_format = format;
m_display_pixels_texture_pbo_map_offset = map.buffer_offset;
m_display_pixels_texture_pbo_map_size = size_required;
*out_buffer = map.pointer;
*out_pitch = stride;
}
else
{
if (m_gles_pixels_repack_buffer.size() < size_required)
m_gles_pixels_repack_buffer.resize(size_required);
*out_buffer = m_gles_pixels_repack_buffer.data();
*out_pitch = stride;
}
BindDisplayPixelsTexture();
SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_pixels_texture_id)), format, width, height,
0, 0, width, height);
return true;
}
void OpenGLHostDisplay::EndSetDisplayPixels()
{
const u32 width = static_cast<u32>(m_display_texture_view_width);
const u32 height = static_cast<u32>(m_display_texture_view_height);
const auto [gl_internal_format, gl_format, gl_type] =
GetPixelFormatMapping(m_gl_context->IsGLES(), m_display_texture_format);
glBindTexture(GL_TEXTURE_2D, m_display_pixels_texture_id);
if (m_use_pbo_for_pixels)
{
m_display_pixels_texture_pbo->Unmap(m_display_pixels_texture_pbo_map_size);
m_display_pixels_texture_pbo->Bind();
glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, width, height, 0, gl_format, gl_type,
reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_pixels_texture_pbo_map_offset)));
m_display_pixels_texture_pbo->Unbind();
m_display_pixels_texture_pbo_map_offset = 0;
m_display_pixels_texture_pbo_map_size = 0;
}
else
{
// glTexImage2D should be quicker on Mali...
glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, width, height, 0, gl_format, gl_type,
m_gles_pixels_repack_buffer.data());
}
glBindTexture(GL_TEXTURE_2D, 0);
}
bool OpenGLHostDisplay::SetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, const void* buffer,
u32 pitch)
{
BindDisplayPixelsTexture();
const auto [gl_internal_format, gl_format, gl_type] = GetPixelFormatMapping(m_gl_context->IsGLES(), format);
const u32 pixel_size = GetDisplayPixelFormatSize(format);
const bool is_packed_tightly = (pitch == (pixel_size * width));
// If we have GLES3, we can set row_length.
if (!m_use_gles2_draw_path || is_packed_tightly)
{
if (!is_packed_tightly)
glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch / pixel_size);
glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, width, height, 0, gl_format, gl_type, buffer);
if (!is_packed_tightly)
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
else
{
// Otherwise, we need to repack the image.
const u32 packed_pitch = width * pixel_size;
const u32 repack_size = packed_pitch * height;
if (m_gles_pixels_repack_buffer.size() < repack_size)
m_gles_pixels_repack_buffer.resize(repack_size);
StringUtil::StrideMemCpy(m_gles_pixels_repack_buffer.data(), packed_pitch, buffer, pitch, packed_pitch, height);
glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, width, height, 0, gl_format, gl_type,
m_gles_pixels_repack_buffer.data());
}
glBindTexture(GL_TEXTURE_2D, 0);
SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_pixels_texture_id)), format, width, height,
0, 0, width, height);
return true;
}
void OpenGLHostDisplay::SetVSync(bool enabled)
{
if (m_gl_context->GetWindowInfo().type == WindowInfo::Type::Surfaceless)
@ -420,9 +250,9 @@ bool OpenGLHostDisplay::InitializeRenderDevice(std::string_view shader_cache_dir
m_use_pbo_for_pixels = !m_use_gles2_draw_path;
if (GetRenderAPI() == RenderAPI::OpenGLES)
{
// Adreno seems to corrupt textures through PBOs...
// Adreno seems to corrupt textures through PBOs... and Mali is slow.
const char* gl_vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
if (std::strstr(gl_vendor, "Qualcomm") || std::strstr(gl_vendor, "Broadcom"))
if (std::strstr(gl_vendor, "Qualcomm") || std::strstr(gl_vendor, "ARM") || std::strstr(gl_vendor, "Broadcom"))
m_use_pbo_for_pixels = false;
}
@ -713,12 +543,6 @@ void OpenGLHostDisplay::DestroyResources()
m_post_processing_ubo.reset();
m_post_processing_stages.clear();
if (m_display_pixels_texture_id != 0)
{
glDeleteTextures(1, &m_display_pixels_texture_id);
m_display_pixels_texture_id = 0;
}
if (m_display_vao != 0)
{
glDeleteVertexArrays(1, &m_display_vao);
@ -877,9 +701,10 @@ void OpenGLHostDisplay::RenderDisplay(s32 left, s32 bottom, s32 width, s32 heigh
glBindTexture(GL_TEXTURE_2D, static_cast<GLuint>(reinterpret_cast<uintptr_t>(texture_handle)));
m_display_program.Bind();
const bool linear = IsUsingLinearFiltering();
if (!m_use_gles2_draw_path)
{
const bool linear = IsUsingLinearFiltering();
const float position_adjust = linear ? 0.5f : 0.0f;
const float size_adjust = linear ? 1.0f : 0.0f;
const float flip_adjust = (texture_view_height < 0) ? -1.0f : 1.0f;
@ -895,8 +720,9 @@ void OpenGLHostDisplay::RenderDisplay(s32 left, s32 bottom, s32 width, s32 heigh
}
else
{
if (static_cast<GLuint>(reinterpret_cast<uintptr_t>(texture_handle)) == m_display_pixels_texture_id)
UpdateDisplayPixelsTextureFilter();
// TODO: This sucks.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST);
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);
@ -1207,8 +1033,11 @@ bool OpenGLHostDisplay::SetGPUTimingEnabled(bool enabled)
if (m_gpu_timing_enabled == enabled)
return true;
if (enabled && m_gl_context->IsGLES() && !GLAD_GL_EXT_disjoint_timer_query)
if (enabled && m_gl_context->IsGLES() &&
(!GLAD_GL_EXT_disjoint_timer_query || !glGetQueryObjectivEXT || !glGetQueryObjectui64vEXT))
{
return false;
}
m_gpu_timing_enabled = enabled;
if (m_gpu_timing_enabled)
@ -1226,4 +1055,178 @@ float OpenGLHostDisplay::GetAndResetAccumulatedGPUTime()
return value;
}
GL::StreamBuffer* OpenGLHostDisplay::GetTextureStreamBuffer()
{
if (m_use_gles2_draw_path || m_texture_stream_buffer)
return m_texture_stream_buffer.get();
m_texture_stream_buffer = GL::StreamBuffer::Create(GL_PIXEL_UNPACK_BUFFER, TEXTURE_STREAM_BUFFER_SIZE);
return m_texture_stream_buffer.get();
}
OpenGLHostDisplayTexture::OpenGLHostDisplayTexture(GL::Texture texture, HostDisplayPixelFormat format)
: m_texture(std::move(texture)), m_format(format)
{
}
OpenGLHostDisplayTexture::~OpenGLHostDisplayTexture() = default;
void* OpenGLHostDisplayTexture::GetHandle() const
{
return reinterpret_cast<void*>(static_cast<uintptr_t>(m_texture.GetGLId()));
}
u32 OpenGLHostDisplayTexture::GetWidth() const
{
return m_texture.GetWidth();
}
u32 OpenGLHostDisplayTexture::GetHeight() const
{
return m_texture.GetHeight();
}
u32 OpenGLHostDisplayTexture::GetLayers() const
{
return 1;
}
u32 OpenGLHostDisplayTexture::GetLevels() const
{
return 1;
}
u32 OpenGLHostDisplayTexture::GetSamples() const
{
return m_texture.GetSamples();
}
HostDisplayPixelFormat OpenGLHostDisplayTexture::GetFormat() const
{
return m_format;
}
GLuint OpenGLHostDisplayTexture::GetGLID() const
{
return m_texture.GetGLId();
}
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, 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;
}
} // namespace FrontendCommon

View File

@ -46,15 +46,9 @@ public:
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;
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data,
u32 texture_data_stride) 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;
bool BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer,
u32* out_pitch) override;
void EndSetDisplayPixels() override;
bool SetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, const void* buffer, u32 pitch) override;
void SetVSync(bool enabled) override;
@ -65,6 +59,13 @@ public:
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
ALWAYS_INLINE GL::Context* GetGLContext() const { return m_gl_context.get(); }
ALWAYS_INLINE bool UsePBOForUploads() const { return m_use_pbo_for_pixels; }
ALWAYS_INLINE bool UseGLES3DrawPath() const { return m_use_gles2_draw_path; }
ALWAYS_INLINE std::vector<u8>& GetTextureRepackBuffer() { return m_texture_repack_buffer; }
GL::StreamBuffer* GetTextureStreamBuffer();
protected:
static constexpr u8 NUM_TIMESTAMP_QUERIES = 3;
@ -78,9 +79,6 @@ protected:
void DestroyImGuiContext() override;
bool UpdateImGuiFontTexture() override;
void BindDisplayPixelsTexture();
void UpdateDisplayPixelsTextureFilter();
void RenderDisplay();
void RenderImGui();
void RenderSoftwareCursor();
@ -117,11 +115,8 @@ protected:
GLuint m_display_linear_sampler = 0;
GLuint m_uniform_buffer_alignment = 1;
GLuint m_display_pixels_texture_id = 0;
std::unique_ptr<GL::StreamBuffer> m_display_pixels_texture_pbo;
u32 m_display_pixels_texture_pbo_map_offset = 0;
u32 m_display_pixels_texture_pbo_map_size = 0;
std::vector<u8> m_gles_pixels_repack_buffer;
std::unique_ptr<GL::StreamBuffer> m_texture_stream_buffer;
std::vector<u8> m_texture_repack_buffer;
PostProcessingChain m_post_processing_chain;
GL::Texture m_post_processing_input_texture;
@ -135,7 +130,6 @@ protected:
u8 m_waiting_timestamp_queries = 0;
bool m_timestamp_query_started = false;
bool m_display_texture_is_linear_filtered = false;
bool m_use_gles2_draw_path = false;
bool m_use_pbo_for_pixels = false;
};

View File

@ -1,7 +1,9 @@
#include "vulkan_host_display.h"
#include "common/align.h"
#include "common/assert.h"
#include "common/log.h"
#include "common/scoped_guard.h"
#include "common/string_util.h"
#include "common/vulkan/builders.h"
#include "common/vulkan/context.h"
#include "common/vulkan/shader_cache.h"
@ -22,9 +24,8 @@ namespace FrontendCommon {
class VulkanHostDisplayTexture : public HostDisplayTexture
{
public:
VulkanHostDisplayTexture(Vulkan::Texture texture, Vulkan::StagingTexture staging_texture,
HostDisplayPixelFormat format)
: m_texture(std::move(texture)), m_staging_texture(std::move(staging_texture)), m_format(format)
VulkanHostDisplayTexture(Vulkan::Texture texture, HostDisplayPixelFormat format)
: m_texture(std::move(texture)), m_format(format)
{
}
~VulkanHostDisplayTexture() override = default;
@ -37,13 +38,51 @@ public:
u32 GetSamples() const override { return m_texture.GetSamples(); }
HostDisplayPixelFormat GetFormat() const override { return m_format; }
u32 CalcUpdatePitch(u32 width) const
{
return Common::AlignUp(width * HostDisplay::GetDisplayPixelFormatSize(m_format),
g_vulkan_context->GetBufferCopyRowPitchAlignment());
}
bool BeginUpdate(u32 width, u32 height, void** out_buffer, u32* out_pitch)
{
const u32 pitch = CalcUpdatePitch(width);
const u32 required_size = pitch * height;
Vulkan::StreamBuffer& buffer = g_vulkan_context->GetTextureUploadBuffer();
if (required_size > buffer.GetCurrentSize())
return false;
// TODO: allocate temporary buffer if this fails...
if (!buffer.ReserveMemory(required_size, g_vulkan_context->GetBufferCopyOffsetAlignment()))
{
g_vulkan_context->ExecuteCommandBuffer(false);
if (!buffer.ReserveMemory(required_size, g_vulkan_context->GetBufferCopyOffsetAlignment()))
return false;
}
*out_buffer = buffer.GetCurrentHostPointer();
*out_pitch = pitch;
return true;
}
void EndUpdate(u32 x, u32 y, u32 width, u32 height)
{
const u32 pitch = CalcUpdatePitch(width);
const u32 required_size = pitch * height;
Vulkan::StreamBuffer& buffer = g_vulkan_context->GetTextureUploadBuffer();
const u32 buffer_offset = buffer.GetCurrentOffset();
buffer.CommitMemory(required_size);
m_texture.UpdateFromBuffer(g_vulkan_context->GetCurrentCommandBuffer(), 0, 0, x, y, width, height,
buffer.GetBuffer(), buffer_offset);
}
const Vulkan::Texture& GetTexture() const { return m_texture; }
Vulkan::Texture& GetTexture() { return m_texture; }
Vulkan::StagingTexture& GetStagingTexture() { return m_staging_texture; }
private:
Vulkan::Texture m_texture;
Vulkan::StagingTexture m_staging_texture;
HostDisplayPixelFormat m_format;
};
@ -168,6 +207,9 @@ std::unique_ptr<HostDisplayTexture> VulkanHostDisplay::CreateTexture(u32 width,
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,
@ -176,24 +218,41 @@ std::unique_ptr<HostDisplayTexture> VulkanHostDisplay::CreateTexture(u32 width,
return {};
}
Vulkan::StagingTexture staging_texture;
if (data || dynamic)
{
if (!staging_texture.Create(dynamic ? Vulkan::StagingBuffer::Type::Mutable : Vulkan::StagingBuffer::Type::Upload,
vk_format, width, height))
{
return {};
}
}
const Vulkan::Util::DebugScope debugScope(g_vulkan_context->GetCurrentCommandBuffer(),
"VulkanHostDisplay::CreateTexture");
texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
if (data)
{
staging_texture.WriteTexels(0, 0, width, height, data, data_stride);
staging_texture.CopyToTexture(g_vulkan_context->GetCurrentCommandBuffer(), 0, 0, texture, 0, 0, 0, 0, width,
height);
const u32 row_size = width * GetDisplayPixelFormatSize(format);
const u32 data_upload_pitch = Common::AlignUp(row_size, g_vulkan_context->GetBufferCopyRowPitchAlignment());
const u32 data_size = data_upload_pitch * height;
Vulkan::StreamBuffer& buffer = g_vulkan_context->GetTextureUploadBuffer();
if (data_size < buffer.GetCurrentSize())
{
if (!buffer.ReserveMemory(data_size, g_vulkan_context->GetBufferCopyOffsetAlignment()))
{
g_vulkan_context->ExecuteCommandBuffer(false);
if (!buffer.ReserveMemory(data_size, g_vulkan_context->GetBufferCopyOffsetAlignment()))
goto use_staging;
}
StringUtil::StrideMemCpy(buffer.GetCurrentHostPointer(), data_upload_pitch, data, data_stride, row_size, height);
const u32 buffer_offset = buffer.GetCurrentOffset();
buffer.CommitMemory(data_size);
texture.UpdateFromBuffer(g_vulkan_context->GetCurrentCommandBuffer(), 0, 0, 0, 0, width, height,
buffer.GetBuffer(), buffer_offset);
}
else
{
use_staging:
Vulkan::StagingTexture staging_texture;
if (!staging_texture.Create(Vulkan::StagingBuffer::Type::Upload, vk_format, width, height))
return {};
staging_texture.WriteTexels(0, 0, width, height, data, data_stride);
staging_texture.CopyToTexture(g_vulkan_context->GetCurrentCommandBuffer(), 0, 0, texture, 0, 0, 0, 0, width,
height);
}
}
else
{
@ -206,40 +265,7 @@ std::unique_ptr<HostDisplayTexture> VulkanHostDisplay::CreateTexture(u32 width,
texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
// don't need to keep the staging texture around if we're not dynamic
if (!dynamic)
staging_texture.Destroy(true);
return std::make_unique<VulkanHostDisplayTexture>(std::move(texture), std::move(staging_texture), format);
}
void VulkanHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
const void* data, u32 data_stride)
{
VulkanHostDisplayTexture* vk_texture = static_cast<VulkanHostDisplayTexture*>(texture);
Vulkan::StagingTexture* staging_texture;
if (vk_texture->GetStagingTexture().IsValid())
{
staging_texture = &vk_texture->GetStagingTexture();
}
else
{
// TODO: This should use a stream buffer instead for speed.
if (m_upload_staging_texture.IsValid())
m_upload_staging_texture.Flush();
if ((m_upload_staging_texture.GetWidth() < width || m_upload_staging_texture.GetHeight() < height) &&
!m_upload_staging_texture.Create(Vulkan::StagingBuffer::Type::Upload, VK_FORMAT_R8G8B8A8_UNORM, width, height))
{
Panic("Failed to create upload staging texture");
}
staging_texture = &m_upload_staging_texture;
}
staging_texture->WriteTexels(0, 0, width, height, data, data_stride);
staging_texture->CopyToTexture(0, 0, vk_texture->GetTexture(), x, y, 0, 0, width, height);
return std::make_unique<VulkanHostDisplayTexture>(std::move(texture), format);
}
bool VulkanHostDisplay::DownloadTexture(const void* texture_handle, HostDisplayPixelFormat texture_format, u32 x, u32 y,
@ -271,43 +297,6 @@ bool VulkanHostDisplay::SupportsDisplayPixelFormat(HostDisplayPixelFormat format
return ((fp.optimalTilingFeatures & required) == required);
}
bool VulkanHostDisplay::BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer,
u32* out_pitch)
{
const VkFormat vk_format = s_display_pixel_format_mapping[static_cast<u32>(format)];
if (m_display_pixels_texture.GetWidth() < width || m_display_pixels_texture.GetHeight() < height ||
m_display_pixels_texture.GetFormat() != vk_format)
{
if (!m_display_pixels_texture.Create(width, height, 1, 1, vk_format, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT))
{
return false;
}
}
if ((m_upload_staging_texture.GetWidth() < width || m_upload_staging_texture.GetHeight() < height) &&
!m_upload_staging_texture.Create(Vulkan::StagingBuffer::Type::Upload, vk_format, width, height))
{
return false;
}
SetDisplayTexture(&m_display_pixels_texture, format, m_display_pixels_texture.GetWidth(),
m_display_pixels_texture.GetHeight(), 0, 0, width, height);
*out_buffer = m_upload_staging_texture.GetMappedPointer();
*out_pitch = m_upload_staging_texture.GetMappedStride();
return true;
}
void VulkanHostDisplay::EndSetDisplayPixels()
{
m_upload_staging_texture.CopyToTexture(0, 0, m_display_pixels_texture, 0, 0, 0, 0,
static_cast<u32>(m_display_texture_view_width),
static_cast<u32>(m_display_texture_view_height));
}
void VulkanHostDisplay::SetVSync(bool enabled)
{
if (!m_swap_chain)
@ -518,9 +507,7 @@ void VulkanHostDisplay::DestroyResources()
m_post_processing_ubo.Destroy(true);
m_post_processing_chain.ClearStages();
m_display_pixels_texture.Destroy(false);
m_readback_staging_texture.Destroy(false);
m_upload_staging_texture.Destroy(false);
Vulkan::Util::SafeDestroyPipeline(m_display_pipeline);
Vulkan::Util::SafeDestroyPipeline(m_cursor_pipeline);

View File

@ -51,15 +51,9 @@ public:
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;
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data,
u32 texture_data_stride) 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;
bool BeginSetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, void** out_buffer,
u32* out_pitch) override;
void EndSetDisplayPixels() override;
void SetVSync(bool enabled) override;
@ -128,8 +122,6 @@ protected:
VkSampler m_point_sampler = VK_NULL_HANDLE;
VkSampler m_linear_sampler = VK_NULL_HANDLE;
Vulkan::Texture m_display_pixels_texture;
Vulkan::StagingTexture m_upload_staging_texture;
Vulkan::StagingTexture m_readback_staging_texture;
VkDescriptorSetLayout m_post_process_descriptor_set_layout = VK_NULL_HANDLE;