@@ -40,7 +40,11 @@ DXGI_FORMAT GetDXGIFormatForHostFormat(AbstractTextureFormat format)
case AbstractTextureFormat::BPTC:
return DXGI_FORMAT_BC7_UNORM;
case AbstractTextureFormat::RGBA8:
return DXGI_FORMAT_R8G8B8A8_UNORM;
case AbstractTextureFormat::BGRA8:
return DXGI_FORMAT_B8G8R8A8_UNORM;
default:
PanicAlert("Unhandled texture format.");
return DXGI_FORMAT_R8G8B8A8_UNORM;
}
}
@@ -80,7 +84,6 @@ DXTexture::DXTexture(const TextureConfig& tex_config) : AbstractTexture(tex_conf
DXTexture::~DXTexture()
{
m_texture->Release();
SAFE_RELEASE(m_staging_texture);
}

D3DTexture2D* DXTexture::GetRawTexIdentifier() const
@@ -93,97 +96,36 @@ void DXTexture::Bind(unsigned int stage)
D3D::stateman->SetTexture(stage, m_texture->GetSRV());
}

std::optional<AbstractTexture::RawTextureInfo> DXTexture::MapFullImpl()
void DXTexture::CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level)
{
CD3D11_TEXTURE2D_DESC staging_texture_desc(DXGI_FORMAT_R8G8B8A8_UNORM, m_config.width,
m_config.height, 1, 1, 0, D3D11_USAGE_STAGING,
D3D11_CPU_ACCESS_READ);

HRESULT hr = D3D::device->CreateTexture2D(&staging_texture_desc, nullptr, &m_staging_texture);
if (FAILED(hr))
{
WARN_LOG(VIDEO, "Failed to create texture dumping readback texture: %X", static_cast<u32>(hr));
return {};
}

// Copy the selected data to the staging texture
D3D::context->CopyResource(m_staging_texture, m_texture->GetTex());

// Map the staging texture to client memory, and encode it as a .png image.
D3D11_MAPPED_SUBRESOURCE map;
hr = D3D::context->Map(m_staging_texture, 0, D3D11_MAP_READ, 0, &map);
if (FAILED(hr))
{
WARN_LOG(VIDEO, "Failed to map texture dumping readback texture: %X", static_cast<u32>(hr));
return {};
}

return AbstractTexture::RawTextureInfo{reinterpret_cast<u8*>(map.pData), map.RowPitch,
m_config.width, m_config.height};
const DXTexture* srcentry = static_cast<const DXTexture*>(src);
_assert_(src_rect.GetWidth() == dst_rect.GetWidth() &&
src_rect.GetHeight() == dst_rect.GetHeight());

D3D11_BOX src_box;
src_box.left = src_rect.left;
src_box.top = src_rect.top;
src_box.right = src_rect.right;
src_box.bottom = src_rect.bottom;
src_box.front = 0;
src_box.back = 1;

D3D::context->CopySubresourceRegion(
m_texture->GetTex(), D3D11CalcSubresource(dst_level, dst_layer, m_config.levels),
dst_rect.left, dst_rect.top, 0, srcentry->m_texture->GetTex(),
D3D11CalcSubresource(src_level, src_layer, srcentry->m_config.levels), &src_box);
}

std::optional<AbstractTexture::RawTextureInfo> DXTexture::MapRegionImpl(u32 level, u32 x, u32 y,
u32 width, u32 height)
{
CD3D11_TEXTURE2D_DESC staging_texture_desc(DXGI_FORMAT_R8G8B8A8_UNORM, width, height, 1, 1, 0,
D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ);

HRESULT hr = D3D::device->CreateTexture2D(&staging_texture_desc, nullptr, &m_staging_texture);
if (FAILED(hr))
{
WARN_LOG(VIDEO, "Failed to create texture dumping readback texture: %X", static_cast<u32>(hr));
return {};
}

// Copy the selected data to the staging texture
CD3D11_BOX src_box(x, y, 0, width, height, 1);
D3D::context->CopySubresourceRegion(m_staging_texture, 0, 0, 0, 0, m_texture->GetTex(),
D3D11CalcSubresource(level, 0, m_config.levels), &src_box);

// Map the staging texture to client memory, and encode it as a .png image.
D3D11_MAPPED_SUBRESOURCE map;
hr = D3D::context->Map(m_staging_texture, 0, D3D11_MAP_READ, 0, &map);
if (FAILED(hr))
{
WARN_LOG(VIDEO, "Failed to map texture dumping readback texture: %X", static_cast<u32>(hr));
return {};
}

return AbstractTexture::RawTextureInfo{reinterpret_cast<u8*>(map.pData), map.RowPitch,
m_config.width, m_config.height};
}

void DXTexture::Unmap()
{
if (!m_staging_texture)
return;

D3D::context->Unmap(m_staging_texture, 0);
}

void DXTexture::CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect)
void DXTexture::ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect)
{
const DXTexture* srcentry = static_cast<const DXTexture*>(source);
if (srcrect.GetWidth() == dstrect.GetWidth() && srcrect.GetHeight() == dstrect.GetHeight())
{
D3D11_BOX srcbox;
srcbox.left = srcrect.left;
srcbox.top = srcrect.top;
srcbox.right = srcrect.right;
srcbox.bottom = srcrect.bottom;
srcbox.front = 0;
srcbox.back = srcentry->m_config.layers;

D3D::context->CopySubresourceRegion(m_texture->GetTex(), 0, dstrect.left, dstrect.top, 0,
srcentry->m_texture->GetTex(), 0, &srcbox);
return;
}
else if (!m_config.rendertarget)
{
return;
}
_assert_(m_config.rendertarget);

g_renderer->ResetAPIState(); // reset any game specific settings

const D3D11_VIEWPORT vp = CD3D11_VIEWPORT(float(dstrect.left), float(dstrect.top),
@@ -213,8 +155,140 @@ void DXTexture::CopyRectangleFromTexture(const AbstractTexture* source,
void DXTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
size_t buffer_size)
{
size_t src_pitch = CalculateHostTextureLevelPitch(m_config.format, row_length);
size_t src_pitch = CalculateStrideForFormat(m_config.format, row_length);
D3D::context->UpdateSubresource(m_texture->GetTex(), level, nullptr, buffer,
static_cast<UINT>(src_pitch), 0);
}

DXStagingTexture::DXStagingTexture(StagingTextureType type, const TextureConfig& config,
ID3D11Texture2D* tex)
: AbstractStagingTexture(type, config), m_tex(tex)
{
}

DXStagingTexture::~DXStagingTexture()
{
if (IsMapped())
DXStagingTexture::Unmap();
SAFE_RELEASE(m_tex);
}

std::unique_ptr<DXStagingTexture> DXStagingTexture::Create(StagingTextureType type,
const TextureConfig& config)
{
D3D11_USAGE usage;
UINT cpu_flags;
if (type == StagingTextureType::Readback)
{
usage = D3D11_USAGE_STAGING;
cpu_flags = D3D11_CPU_ACCESS_READ;
}
else if (type == StagingTextureType::Upload)
{
usage = D3D11_USAGE_DYNAMIC;
cpu_flags = D3D11_CPU_ACCESS_WRITE;
}
else
{
usage = D3D11_USAGE_STAGING;
cpu_flags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
}

CD3D11_TEXTURE2D_DESC desc(GetDXGIFormatForHostFormat(config.format), config.width, config.height,
1, 1, 0, usage, cpu_flags);

ID3D11Texture2D* texture;
HRESULT hr = D3D::device->CreateTexture2D(&desc, nullptr, &texture);
CHECK(SUCCEEDED(hr), "Create staging texture");
if (FAILED(hr))
return nullptr;

return std::unique_ptr<DXStagingTexture>(new DXStagingTexture(type, config, texture));
}

void DXStagingTexture::CopyFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect)
{
_assert_(m_type == StagingTextureType::Readback);
_assert_(src_rect.GetWidth() == dst_rect.GetWidth() &&
src_rect.GetHeight() == dst_rect.GetHeight());
_assert_(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= src->GetConfig().width &&
src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= src->GetConfig().height);
_assert_(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= m_config.width &&
dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= m_config.height);

if (IsMapped())
DXStagingTexture::Unmap();

CD3D11_BOX src_box(src_rect.left, src_rect.top, 0, src_rect.right, src_rect.bottom, 1);
D3D::context->CopySubresourceRegion(
m_tex, 0, static_cast<u32>(dst_rect.left), static_cast<u32>(dst_rect.top), 0,
static_cast<const DXTexture*>(src)->GetRawTexIdentifier()->GetTex(),
D3D11CalcSubresource(src_level, src_layer, src->GetConfig().levels), &src_box);

m_needs_flush = true;
}

void DXStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level)
{
_assert_(m_type == StagingTextureType::Upload);
_assert_(src_rect.GetWidth() == dst_rect.GetWidth() &&
src_rect.GetHeight() == dst_rect.GetHeight());
_assert_(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= m_config.width &&
src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= m_config.height);
_assert_(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= dst->GetConfig().width &&
dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= dst->GetConfig().height);

if (IsMapped())
DXStagingTexture::Unmap();

CD3D11_BOX src_box(src_rect.left, src_rect.top, 0, src_rect.right, src_rect.bottom, 1);
D3D::context->CopySubresourceRegion(
static_cast<const DXTexture*>(dst)->GetRawTexIdentifier()->GetTex(),
D3D11CalcSubresource(dst_level, dst_layer, dst->GetConfig().levels),
static_cast<u32>(dst_rect.left), static_cast<u32>(dst_rect.top), 0, m_tex, 0, &src_box);
}

bool DXStagingTexture::Map()
{
if (m_map_pointer)
return true;

D3D11_MAP map_type;
if (m_type == StagingTextureType::Readback)
map_type = D3D11_MAP_READ;
else if (m_type == StagingTextureType::Upload)
map_type = D3D11_MAP_WRITE;
else
map_type = D3D11_MAP_READ_WRITE;

D3D11_MAPPED_SUBRESOURCE sr;
HRESULT hr = D3D::context->Map(m_tex, 0, map_type, 0, &sr);
CHECK(SUCCEEDED(hr), "Map readback texture");
if (FAILED(hr))
return false;

m_map_pointer = reinterpret_cast<char*>(sr.pData);
m_map_stride = sr.RowPitch;
return true;
}

void DXStagingTexture::Unmap()
{
if (!m_map_pointer)
return;

D3D::context->Unmap(m_tex, 0);
m_map_pointer = nullptr;
}

void DXStagingTexture::Flush()
{
// Flushing is handled by the API.
m_needs_flush = false;
}

} // namespace DX11
@@ -6,6 +6,7 @@

#include "Common/CommonTypes.h"

#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"

class D3DTexture2D;
@@ -19,23 +20,47 @@ class DXTexture final : public AbstractTexture
~DXTexture();

void Bind(unsigned int stage) override;
void Unmap() override;

void CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level) override;
void ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
size_t buffer_size) override;

D3DTexture2D* GetRawTexIdentifier() const;

private:
std::optional<RawTextureInfo> MapFullImpl() override;
std::optional<RawTextureInfo> MapRegionImpl(u32 level, u32 x, u32 y, u32 width,
u32 height) override;

D3DTexture2D* m_texture;
ID3D11Texture2D* m_staging_texture = nullptr;
};

class DXStagingTexture final : public AbstractStagingTexture
{
public:
DXStagingTexture() = delete;
~DXStagingTexture();

void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect,
u32 src_layer, u32 src_level,
const MathUtil::Rectangle<int>& dst_rect) override;
void CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level) override;

bool Map() override;
void Unmap() override;
void Flush() override;

static std::unique_ptr<DXStagingTexture> Create(StagingTextureType type,
const TextureConfig& config);

private:
DXStagingTexture(StagingTextureType type, const TextureConfig& config, ID3D11Texture2D* tex);

ID3D11Texture2D* m_tex = nullptr;
};

} // namespace DX11
@@ -4,17 +4,21 @@

#include "VideoBackends/D3D/PSTextureEncoder.h"

#include "Common/Assert.h"
#include "Common/Logging/Log.h"
#include "Core/HW/Memmap.h"
#include "VideoBackends/D3D/D3DBase.h"
#include "VideoBackends/D3D/D3DShader.h"
#include "VideoBackends/D3D/D3DState.h"
#include "VideoBackends/D3D/D3DUtil.h"
#include "VideoBackends/D3D/DXTexture.h"
#include "VideoBackends/D3D/FramebufferManager.h"
#include "VideoBackends/D3D/Render.h"
#include "VideoBackends/D3D/TextureCache.h"
#include "VideoBackends/D3D/VertexShaderCache.h"

#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"
#include "VideoCommon/TextureConversionShader.h"
#include "VideoCommon/VideoCommon.h"

@@ -31,76 +35,41 @@ struct EFBEncodeParams
};

PSTextureEncoder::PSTextureEncoder()
: m_ready(false), m_out(nullptr), m_outRTV(nullptr), m_outStage(nullptr),
m_encodeParams(nullptr)
{
}

PSTextureEncoder::~PSTextureEncoder() = default;

void PSTextureEncoder::Init()
{
m_ready = false;

HRESULT hr;

// Create output texture RGBA format
// TODO: This Texture is overly large and parts of it are unused
// EFB2RAM copies use max (EFB_WIDTH * 4) by (EFB_HEIGHT / 4)
// XFB2RAM copies use max (EFB_WIDTH / 2) by (EFB_HEIGHT)
D3D11_TEXTURE2D_DESC t2dd = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_B8G8R8A8_UNORM, EFB_WIDTH * 4, 1024,
1, 1, D3D11_BIND_RENDER_TARGET);
hr = D3D::device->CreateTexture2D(&t2dd, nullptr, &m_out);
CHECK(SUCCEEDED(hr), "create efb encode output texture");
D3D::SetDebugObjectName(m_out, "efb encoder output texture");

// Create output render target view
D3D11_RENDER_TARGET_VIEW_DESC rtvd = CD3D11_RENDER_TARGET_VIEW_DESC(
m_out, D3D11_RTV_DIMENSION_TEXTURE2D, DXGI_FORMAT_B8G8R8A8_UNORM);
hr = D3D::device->CreateRenderTargetView(m_out, &rtvd, &m_outRTV);
CHECK(SUCCEEDED(hr), "create efb encode output render target view");
D3D::SetDebugObjectName(m_outRTV, "efb encoder output rtv");

// Create output staging buffer
t2dd.Usage = D3D11_USAGE_STAGING;
t2dd.BindFlags = 0;
t2dd.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
hr = D3D::device->CreateTexture2D(&t2dd, nullptr, &m_outStage);
CHECK(SUCCEEDED(hr), "create efb encode output staging buffer");
D3D::SetDebugObjectName(m_outStage, "efb encoder output staging buffer");
// TODO: Move this to a constant somewhere in common.
TextureConfig encoding_texture_config(EFB_WIDTH * 4, 1024, 1, 1, AbstractTextureFormat::BGRA8,
true);
m_encoding_render_texture = g_renderer->CreateTexture(encoding_texture_config);
m_encoding_readback_texture =
g_renderer->CreateStagingTexture(StagingTextureType::Readback, encoding_texture_config);
_assert_(m_encoding_render_texture && m_encoding_readback_texture);

// Create constant buffer for uploading data to shaders
D3D11_BUFFER_DESC bd = CD3D11_BUFFER_DESC(sizeof(EFBEncodeParams), D3D11_BIND_CONSTANT_BUFFER);
hr = D3D::device->CreateBuffer(&bd, nullptr, &m_encodeParams);
HRESULT hr = D3D::device->CreateBuffer(&bd, nullptr, &m_encode_params);
CHECK(SUCCEEDED(hr), "create efb encode params buffer");
D3D::SetDebugObjectName(m_encodeParams, "efb encoder params buffer");

m_ready = true;
D3D::SetDebugObjectName(m_encode_params, "efb encoder params buffer");
}

void PSTextureEncoder::Shutdown()
{
m_ready = false;

for (auto& it : m_encoding_shaders)
{
SAFE_RELEASE(it.second);
}
m_encoding_shaders.clear();

SAFE_RELEASE(m_encodeParams);
SAFE_RELEASE(m_outStage);
SAFE_RELEASE(m_outRTV);
SAFE_RELEASE(m_out);
SAFE_RELEASE(m_encode_params);
}

void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_width,
u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
const EFBRectangle& src_rect, bool scale_by_half)
{
if (!m_ready) // Make sure we initialized OK
return;

HRESULT hr;

// Resolve MSAA targets before copying.
// FIXME: Instead of resolving EFB, it would be better to pick out a
// single sample from each pixel. The game may break if it isn't
@@ -122,16 +91,19 @@ void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_w
constexpr EFBRectangle fullSrcRect(0, 0, EFB_WIDTH, EFB_HEIGHT);
TargetRectangle targetRect = g_renderer->ConvertEFBRectangle(fullSrcRect);

D3D::context->OMSetRenderTargets(1, &m_outRTV, nullptr);
D3D::context->OMSetRenderTargets(
1,
&static_cast<DXTexture*>(m_encoding_render_texture.get())->GetRawTexIdentifier()->GetRTV(),
nullptr);

EFBEncodeParams encode_params;
encode_params.SrcLeft = src_rect.left;
encode_params.SrcTop = src_rect.top;
encode_params.DestWidth = native_width;
encode_params.ScaleFactor = scale_by_half ? 2 : 1;
encode_params.y_scale = params.y_scale;
D3D::context->UpdateSubresource(m_encodeParams, 0, nullptr, &encode_params, 0, 0);
D3D::stateman->SetPixelConstants(m_encodeParams);
D3D::context->UpdateSubresource(m_encode_params, 0, nullptr, &encode_params, 0, 0);
D3D::stateman->SetPixelConstants(m_encode_params);

// We also linear filtering for both box filtering and downsampling higher resolutions to 1x
// TODO: This only produces perfect downsampling for 2x IR, other resolutions will need more
@@ -148,24 +120,15 @@ void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_w
VertexShaderCache::GetSimpleInputLayout());

// Copy to staging buffer
D3D11_BOX srcBox = CD3D11_BOX(0, 0, 0, words_per_row, num_blocks_y, 1);
D3D::context->CopySubresourceRegion(m_outStage, 0, 0, 0, 0, m_out, 0, &srcBox);

// Transfer staging buffer to GameCube/Wii RAM
D3D11_MAPPED_SUBRESOURCE map = {0};
hr = D3D::context->Map(m_outStage, 0, D3D11_MAP_READ, 0, &map);
CHECK(SUCCEEDED(hr), "map staging buffer (0x%x)", hr);

u8* src = (u8*)map.pData;
u32 readStride = std::min(bytes_per_row, map.RowPitch);
for (unsigned int y = 0; y < num_blocks_y; ++y)
MathUtil::Rectangle<int> copy_rect(0, 0, words_per_row, num_blocks_y);
m_encoding_readback_texture->CopyFromTexture(m_encoding_render_texture.get(), copy_rect, 0, 0,
copy_rect);
m_encoding_readback_texture->Flush();
if (m_encoding_readback_texture->Map())
{
memcpy(dst, src, readStride);
dst += memory_stride;
src += map.RowPitch;
m_encoding_readback_texture->ReadTexels(copy_rect, dst, memory_stride);
m_encoding_readback_texture->Unmap();
}

D3D::context->Unmap(m_outStage, 0);
}

// Restore API
@@ -5,11 +5,15 @@
#pragma once

#include <map>
#include <memory>

#include "Common/CommonTypes.h"
#include "VideoCommon/TextureConversionShader.h"
#include "VideoCommon/VideoCommon.h"

class AbstractTexture;
class AbstractStagingTexture;

struct ID3D11Texture2D;
struct ID3D11RenderTargetView;
struct ID3D11Buffer;
@@ -29,6 +33,7 @@ class PSTextureEncoder final
{
public:
PSTextureEncoder();
~PSTextureEncoder();

void Init();
void Shutdown();
@@ -39,12 +44,9 @@ class PSTextureEncoder final
private:
ID3D11PixelShader* GetEncodingPixelShader(const EFBCopyParams& params);

bool m_ready;

ID3D11Texture2D* m_out;
ID3D11RenderTargetView* m_outRTV;
ID3D11Texture2D* m_outStage;
ID3D11Buffer* m_encodeParams;
ID3D11Buffer* m_encode_params = nullptr;
std::unique_ptr<AbstractTexture> m_encoding_render_texture;
std::unique_ptr<AbstractStagingTexture> m_encoding_readback_texture;
std::map<EFBCopyParams, ID3D11PixelShader*> m_encoding_shaders;
};
}

Large diffs are not rendered by default.

@@ -4,7 +4,9 @@

#pragma once

#include <array>
#include <string>
#include "VideoBackends/D3D/D3DState.h"
#include "VideoCommon/RenderBase.h"

enum class EFBAccessType;
@@ -19,6 +21,11 @@ class Renderer : public ::Renderer
Renderer();
~Renderer() override;

StateCache& GetStateCache() { return m_state_cache; }
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;
std::unique_ptr<AbstractStagingTexture>
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override;

void SetBlendingState(const BlendingState& state) override;
void SetScissorRect(const EFBRectangle& rc) override;
void SetRasterizationState(const RasterizationState& state) override;
@@ -56,7 +63,35 @@ class Renderer : public ::Renderer
bool CheckForResize();

private:
struct GXPipelineState
{
std::array<SamplerState, 8> samplers;
BlendingState blend;
DepthState zmode;
RasterizationState raster;
};

void SetupDeviceObjects();
void TeardownDeviceObjects();
void Create3DVisionTexture(int width, int height);

void BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D* src_texture,
u32 src_width, u32 src_height, float Gamma);

StateCache m_state_cache;
GXPipelineState m_gx_state;

std::array<ID3D11BlendState*, 4> m_clear_blend_states{};
std::array<ID3D11DepthStencilState*, 3> m_clear_depth_states{};
ID3D11BlendState* m_reset_blend_state = nullptr;
ID3D11DepthStencilState* m_reset_depth_state = nullptr;
ID3D11RasterizerState* m_reset_rast_state = nullptr;

ID3D11Texture2D* m_screenshot_texture = nullptr;
D3DTexture2D* m_3d_vision_texture = nullptr;

u32 m_last_multisamples = 1;
bool m_last_stereo_mode = false;
bool m_last_fullscreen_mode = false;
};
}
@@ -33,11 +33,6 @@ static const size_t MAX_COPY_BUFFERS = 32;
static ID3D11Buffer* s_efbcopycbuf[MAX_COPY_BUFFERS] = {0};
static std::unique_ptr<PSTextureEncoder> g_encoder;

std::unique_ptr<AbstractTexture> TextureCache::CreateTexture(const TextureConfig& config)
{
return std::make_unique<DXTexture>(config);
}

void TextureCache::CopyEFB(u8* dst, const EFBCopyParams& params, u32 native_width,
u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
const EFBRectangle& src_rect, bool scale_by_half)
@@ -19,8 +19,6 @@ class TextureCache : public TextureCacheBase
~TextureCache();

private:
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;

u64 EncodeToRamFromTexture(u32 address, void* source_texture, u32 SourceW, u32 SourceH,
bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf,
const EFBRectangle& source)
@@ -181,6 +181,8 @@ void VideoBackend::Shutdown()
g_texture_cache.reset();
g_renderer.reset();

D3D::Close();

ShutdownShared();
}

@@ -14,9 +14,15 @@ void NullTexture::Bind(unsigned int stage)
{
}

void NullTexture::CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect)
void NullTexture::CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level)
{
}
void NullTexture::ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect)
{
}

@@ -25,4 +31,43 @@ void NullTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u
{
}

NullStagingTexture::NullStagingTexture(StagingTextureType type, const TextureConfig& config)
: AbstractStagingTexture(type, config)
{
m_texture_buf.resize(m_texel_size * config.width * config.height);
m_map_pointer = reinterpret_cast<char*>(m_texture_buf.data());
m_map_stride = m_texel_size * config.width;
}

NullStagingTexture::~NullStagingTexture() = default;

void NullStagingTexture::CopyFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect)
{
m_needs_flush = true;
}

void NullStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect,
AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level)
{
m_needs_flush = true;
}

bool NullStagingTexture::Map()
{
return true;
}

void NullStagingTexture::Unmap()
{
}

void NullStagingTexture::Flush()
{
m_needs_flush = false;
}

} // namespace Null
@@ -4,8 +4,11 @@

#pragma once

#include <vector>

#include "Common/CommonTypes.h"

#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"

namespace Null
@@ -18,11 +21,36 @@ class NullTexture final : public AbstractTexture

void Bind(unsigned int stage) override;

void CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level) override;
void ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
size_t buffer_size) override;
};

class NullStagingTexture final : public AbstractStagingTexture
{
public:
explicit NullStagingTexture(StagingTextureType type, const TextureConfig& config);
~NullStagingTexture();

void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect,
u32 src_layer, u32 src_level,
const MathUtil::Rectangle<int>& dst_rect) override;
void CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level) override;

bool Map() override;
void Unmap() override;
void Flush() override;

private:
std::vector<u8> m_texture_buf;
};

} // namespace Null
@@ -4,6 +4,7 @@

#include "Common/Logging/Log.h"

#include "VideoBackends/Null/NullTexture.h"
#include "VideoBackends/Null/Render.h"

#include "VideoCommon/VideoConfig.h"
@@ -21,6 +22,17 @@ Renderer::~Renderer()
UpdateActiveConfig();
}

std::unique_ptr<AbstractTexture> Renderer::CreateTexture(const TextureConfig& config)
{
return std::make_unique<NullTexture>(config);
}

std::unique_ptr<AbstractStagingTexture> Renderer::CreateStagingTexture(StagingTextureType type,
const TextureConfig& config)
{
return std::make_unique<NullStagingTexture>(type, config);
}

void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
{
NOTICE_LOG(VIDEO, "RenderText: %s", text.c_str());
@@ -14,6 +14,10 @@ class Renderer : public ::Renderer
Renderer();
~Renderer() override;

std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;
std::unique_ptr<AbstractStagingTexture>
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override;

void RenderText(const std::string& pstr, int left, int top, u32 color) override;
u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override { return 0; }
void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override {}
@@ -35,12 +35,6 @@ class TextureCache : public TextureCacheBase
bool scale_by_half, unsigned int cbuf_id, const float* colmat) override
{
}

private:
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override
{
return std::make_unique<NullTexture>(config);
}
};

} // Null name space

Large diffs are not rendered by default.

@@ -8,6 +8,7 @@

#include "Common/GL/GLUtil.h"

#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"

namespace OGL
@@ -20,9 +21,13 @@ class OGLTexture final : public AbstractTexture

void Bind(unsigned int stage) override;

void CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level) override;
void ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
size_t buffer_size) override;

@@ -33,14 +38,39 @@ class OGLTexture final : public AbstractTexture
static void SetStage();

private:
std::optional<RawTextureInfo> MapFullImpl() override;
std::optional<RawTextureInfo> MapRegionImpl(u32 level, u32 x, u32 y, u32 width,
u32 height) override;
void MapRegionSlow(u32 level, u32 x, u32 y, u32 width, u32 height);

GLuint m_texId;
GLuint m_framebuffer = 0;
std::vector<u8> m_staging_data;
};

class OGLStagingTexture final : public AbstractStagingTexture
{
public:
OGLStagingTexture() = delete;
~OGLStagingTexture();

void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect,
u32 src_layer, u32 src_level,
const MathUtil::Rectangle<int>& dst_rect) override;
void CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level) override;

bool Map() override;
void Unmap() override;
void Flush() override;

static std::unique_ptr<OGLStagingTexture> Create(StagingTextureType type,
const TextureConfig& config);

private:
OGLStagingTexture(StagingTextureType type, const TextureConfig& config, GLenum target,
GLuint buffer_name, size_t buffer_size, char* map_ptr, size_t map_stride);

private:
GLenum m_target;
GLuint m_buffer_name;
size_t m_buffer_size;
GLsync m_fence = 0;
};

} // namespace OGL
@@ -458,7 +458,7 @@ Renderer::Renderer()
GLExtensions::Supports("GL_EXT_copy_image") ||
GLExtensions::Supports("GL_OES_copy_image")) &&
!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_COPYIMAGE);
g_ogl_config.bSupportTextureSubImage = GLExtensions::Supports("ARB_get_texture_sub_image");
g_ogl_config.bSupportsTextureSubImage = GLExtensions::Supports("ARB_get_texture_sub_image");

// Desktop OpenGL supports the binding layout if it supports 420pack
// OpenGL ES 3.1 supports it implicitly without an extension
@@ -623,6 +623,8 @@ Renderer::Renderer()

// Compute shaders are core in GL4.3.
g_Config.backend_info.bSupportsComputeShaders = true;
if (GLExtensions::Version() >= 450)
g_ogl_config.bSupportsTextureSubImage = true;
}
else
{
@@ -814,6 +816,17 @@ void Renderer::Init()
OpenGL_CreateAttributelessVAO();
}

std::unique_ptr<AbstractTexture> Renderer::CreateTexture(const TextureConfig& config)
{
return std::make_unique<OGLTexture>(config);
}

std::unique_ptr<AbstractStagingTexture> Renderer::CreateStagingTexture(StagingTextureType type,
const TextureConfig& config)
{
return OGLStagingTexture::Create(type, config);
}

void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
{
u32 backbuffer_width = std::max(GLInterface->GetBackBufferWidth(), 1u);
@@ -58,7 +58,7 @@ struct VideoConfig
bool bSupportsImageLoadStore;
bool bSupportsAniso;
bool bSupportsBitfield;
bool bSupportTextureSubImage;
bool bSupportsTextureSubImage;

const char* gl_vendor;
const char* gl_renderer;
@@ -77,6 +77,10 @@ class Renderer : public ::Renderer
void Init();
void Shutdown();

std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;
std::unique_ptr<AbstractStagingTexture>
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override;

void SetBlendingState(const BlendingState& state) override;
void SetScissorRect(const EFBRectangle& rc) override;
void SetRasterizationState(const RasterizationState& state) override;
@@ -33,11 +33,6 @@ namespace OGL
{
//#define TIME_TEXTURE_DECODING 1

std::unique_ptr<AbstractTexture> TextureCache::CreateTexture(const TextureConfig& config)
{
return std::make_unique<OGLTexture>(config);
}

void TextureCache::CopyEFB(u8* dst, const EFBCopyParams& params, u32 native_width,
u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
const EFBRectangle& src_rect, bool scale_by_half)
@@ -59,7 +59,6 @@ class TextureCache : public TextureCacheBase
bool valid = false;
};

std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;
void ConvertTexture(TCacheEntry* destination, TCacheEntry* source, const void* palette,
TLUTFormat format) override;

@@ -98,7 +97,4 @@ class TextureCache : public TextureCacheBase
std::map<std::pair<u32, u32>, TextureDecodingProgramInfo> m_texture_decoding_program_info;
std::array<GLuint, TextureConversionShader::BUFFER_FORMAT_COUNT> m_texture_decoding_buffer_views;
};

bool SaveTexture(const std::string& filename, u32 textarget, u32 tex, int virtual_width,
int virtual_height, unsigned int level);
}
@@ -34,9 +34,8 @@ namespace TextureConverter
{
using OGL::TextureCache;

static GLuint s_texConvFrameBuffer[2] = {0, 0};
static GLuint s_srcTexture = 0; // for decoding from RAM
static GLuint s_dstTexture = 0; // for encoding to RAM
std::unique_ptr<AbstractTexture> s_encoding_render_texture;
std::unique_ptr<AbstractStagingTexture> s_encoding_readback_texture;

const int renderBufferWidth = EFB_WIDTH * 4;
const int renderBufferHeight = 1024;
@@ -49,8 +48,6 @@ struct EncodingProgram
};
static std::map<EFBCopyParams, EncodingProgram> s_encoding_programs;

static GLuint s_PBO = 0; // for readback with different strides

static EncodingProgram& GetOrCreateEncodingShader(const EFBCopyParams& params)
{
auto iter = s_encoding_programs.find(params);
@@ -87,52 +84,30 @@ static EncodingProgram& GetOrCreateEncodingShader(const EFBCopyParams& params)

void Init()
{
glGenFramebuffers(2, s_texConvFrameBuffer);

glActiveTexture(GL_TEXTURE9);
glGenTextures(1, &s_srcTexture);
glBindTexture(GL_TEXTURE_2D, s_srcTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);

glGenTextures(1, &s_dstTexture);
glBindTexture(GL_TEXTURE_2D, s_dstTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, renderBufferWidth, renderBufferHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);

FramebufferManager::SetFramebuffer(s_texConvFrameBuffer[0]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s_dstTexture, 0);
FramebufferManager::SetFramebuffer(0);

glGenBuffers(1, &s_PBO);
TextureConfig config(renderBufferWidth, renderBufferHeight, 1, 1, AbstractTextureFormat::BGRA8,
true);
s_encoding_render_texture = g_renderer->CreateTexture(config);
s_encoding_readback_texture =
g_renderer->CreateStagingTexture(StagingTextureType::Readback, config);
}

void Shutdown()
{
glDeleteTextures(1, &s_srcTexture);
glDeleteTextures(1, &s_dstTexture);
glDeleteBuffers(1, &s_PBO);
glDeleteFramebuffers(2, s_texConvFrameBuffer);
s_encoding_readback_texture.reset();
s_encoding_render_texture.reset();

for (auto& program : s_encoding_programs)
program.second.program.Destroy();
s_encoding_programs.clear();

s_srcTexture = 0;
s_dstTexture = 0;
s_PBO = 0;
s_texConvFrameBuffer[0] = 0;
s_texConvFrameBuffer[1] = 0;
}

// dst_line_size, writeStride in bytes

static void EncodeToRamUsingShader(GLuint srcTexture, u8* destAddr, u32 dst_line_size,
u32 dstHeight, u32 writeStride, bool linearFilter, float y_scale)
{
// switch to texture converter frame buffer
// attach render buffer as color destination
FramebufferManager::SetFramebuffer(s_texConvFrameBuffer[0]);
FramebufferManager::SetFramebuffer(
static_cast<OGLTexture*>(s_encoding_render_texture.get())->GetFramebuffer());

OpenGL_BindAttributelessVAO();

@@ -153,33 +128,13 @@ static void EncodeToRamUsingShader(GLuint srcTexture, u8* destAddr, u32 dst_line

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

int dstSize = dst_line_size * dstHeight;

// When the dst_line_size and writeStride are the same, we could use glReadPixels directly to RAM.
// But instead we always copy the data via a PBO, because macOS inexplicably prefers this (most
// noticeably in the Super Mario Sunshine transition).
glBindBuffer(GL_PIXEL_PACK_BUFFER, s_PBO);
glBufferData(GL_PIXEL_PACK_BUFFER, dstSize, nullptr, GL_STREAM_READ);
glReadPixels(0, 0, (GLsizei)(dst_line_size / 4), (GLsizei)dstHeight, GL_BGRA, GL_UNSIGNED_BYTE,
nullptr);
u8* pbo = (u8*)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, dstSize, GL_MAP_READ_BIT);
MathUtil::Rectangle<int> copy_rect(0, 0, dst_line_size / 4, dstHeight);
s_encoding_readback_texture->CopyFromTexture(s_encoding_render_texture.get(), copy_rect, 0, 0,
copy_rect);
s_encoding_readback_texture->ReadTexels(copy_rect, destAddr, writeStride);

if (dst_line_size == writeStride)
{
memcpy(destAddr, pbo, dst_line_size * dstHeight);
}
else
{
for (size_t i = 0; i < dstHeight; ++i)
{
memcpy(destAddr, pbo, dst_line_size);
pbo += dst_line_size;
destAddr += writeStride;
}
}

glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
FramebufferManager::SetFramebuffer(0);
OGLTexture::SetStage();
}

void EncodeToRamFromTexture(u8* dest_ptr, const EFBCopyParams& params, u32 native_width,
@@ -14,6 +14,7 @@
#include "VideoBackends/Software/EfbCopy.h"
#include "VideoBackends/Software/EfbInterface.h"
#include "VideoBackends/Software/SWOGLWindow.h"
#include "VideoBackends/Software/SWTexture.h"

#include "VideoCommon/BoundingBox.h"
#include "VideoCommon/OnScreenDisplay.h"
@@ -34,6 +35,17 @@ void SWRenderer::Shutdown()
UpdateActiveConfig();
}

std::unique_ptr<AbstractTexture> SWRenderer::CreateTexture(const TextureConfig& config)
{
return std::make_unique<SW::SWTexture>(config);
}

std::unique_ptr<AbstractStagingTexture>
SWRenderer::CreateStagingTexture(StagingTextureType type, const TextureConfig& config)
{
return std::make_unique<SW::SWStagingTexture>(type, config);
}

void SWRenderer::RenderText(const std::string& pstr, int left, int top, u32 color)
{
SWOGLWindow::s_instance->PrintText(pstr, left, top, color);
@@ -16,6 +16,10 @@ class SWRenderer : public Renderer
static void Init();
static void Shutdown();

std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;
std::unique_ptr<AbstractStagingTexture>
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override;

void RenderText(const std::string& pstr, int left, int top, u32 color) override;
u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override;
void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override {}
@@ -5,6 +5,7 @@
#include "VideoBackends/Software/SWTexture.h"

#include <cstring>
#include "Common/Assert.h"

#include "VideoBackends/Software/CopyRegion.h"

@@ -21,7 +22,31 @@ struct Pixel
u8 a;
};
#pragma pack(pop)

void CopyTextureData(const TextureConfig& src_config, const u8* src_ptr, u32 src_x, u32 src_y,
u32 width, u32 height, const TextureConfig& dst_config, u8* dst_ptr, u32 dst_x,
u32 dst_y)
{
size_t texel_size = AbstractTexture::GetTexelSizeForFormat(src_config.format);
size_t src_stride = src_config.GetStride();
size_t src_offset =
static_cast<size_t>(src_y) * src_stride + static_cast<size_t>(src_x) * texel_size;
size_t dst_stride = dst_config.GetStride();
size_t dst_offset =
static_cast<size_t>(dst_y) * dst_stride + static_cast<size_t>(dst_x) * texel_size;
size_t copy_len = static_cast<size_t>(width) * texel_size;

src_ptr += src_offset;
dst_ptr += dst_offset;
for (u32 i = 0; i < height; i++)
{
std::memcpy(dst_ptr, src_ptr, copy_len);
src_ptr += src_stride;
dst_ptr += dst_stride;
}
}
}

SWTexture::SWTexture(const TextureConfig& tex_config) : AbstractTexture(tex_config)
{
m_data.resize(tex_config.width * tex_config.height * 4);
@@ -31,9 +56,19 @@ void SWTexture::Bind(unsigned int stage)
{
}

void SWTexture::CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect)
void SWTexture::CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level)
{
_assert_(src_level == 0 && src_layer == 0 && dst_layer == 0 && dst_level == 0);
CopyTextureData(src->GetConfig(), static_cast<const SWTexture*>(src)->m_data.data(),
src_rect.left, src_rect.top, src_rect.GetWidth(), src_rect.GetHeight(), m_config,
m_data.data(), dst_rect.left, dst_rect.top);
}
void SWTexture::ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect)
{
const SWTexture* software_source_texture = static_cast<const SWTexture*>(source);

@@ -72,10 +107,49 @@ u8* SWTexture::GetData()
return m_data.data();
}

std::optional<AbstractTexture::RawTextureInfo> SWTexture::MapFullImpl()
SWStagingTexture::SWStagingTexture(StagingTextureType type, const TextureConfig& config)
: AbstractStagingTexture(type, config)
{
m_data.resize(m_texel_size * config.width * config.height);
m_map_pointer = reinterpret_cast<char*>(m_data.data());
m_map_stride = m_texel_size * config.width;
}

SWStagingTexture::~SWStagingTexture() = default;

void SWStagingTexture::CopyFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect)
{
_assert_(src_level == 0 && src_layer == 0);
CopyTextureData(src->GetConfig(), static_cast<const SWTexture*>(src)->GetData(), src_rect.left,
src_rect.top, src_rect.GetWidth(), src_rect.GetHeight(), m_config, m_data.data(),
dst_rect.left, dst_rect.top);
m_needs_flush = true;
}

void SWStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level)
{
_assert_(dst_level == 0 && dst_layer == 0);
CopyTextureData(m_config, m_data.data(), src_rect.left, src_rect.top, src_rect.GetWidth(),
src_rect.GetHeight(), dst->GetConfig(), static_cast<SWTexture*>(dst)->GetData(),
dst_rect.left, dst_rect.top);
m_needs_flush = true;
}

bool SWStagingTexture::Map()
{
return true;
}

void SWStagingTexture::Unmap()
{
return AbstractTexture::RawTextureInfo{GetData(), m_config.width * 4, m_config.width,
m_config.height};
}

void SWStagingTexture::Flush()
{
m_needs_flush = false;
}
} // namespace SW
@@ -8,6 +8,7 @@

#include "Common/CommonTypes.h"

#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"

namespace SW
@@ -20,18 +21,41 @@ class SWTexture final : public AbstractTexture

void Bind(unsigned int stage) override;

void CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level) override;
void ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
size_t buffer_size) override;

const u8* GetData() const;
u8* GetData();

private:
std::optional<RawTextureInfo> MapFullImpl() override;
std::vector<u8> m_data;
};

class SWStagingTexture final : public AbstractStagingTexture
{
public:
explicit SWStagingTexture(StagingTextureType type, const TextureConfig& config);
~SWStagingTexture();

void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect,
u32 src_layer, u32 src_level,
const MathUtil::Rectangle<int>& dst_rect) override;
void CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level) override;

bool Map() override;
void Unmap() override;
void Flush() override;

private:
std::vector<u8> m_data;
};

@@ -25,11 +25,6 @@ class TextureCache : public TextureCacheBase
}

private:
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override
{
return std::make_unique<SWTexture>(config);
}

void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect,
bool scale_by_half, unsigned int cbuf_id, const float* colmat) override
{
@@ -11,7 +11,6 @@ set(SRCS
ShaderCompiler.cpp
StateTracker.cpp
StagingBuffer.cpp
StagingTexture2D.cpp
StreamBuffer.cpp
SwapChain.cpp
Texture2D.cpp
@@ -371,6 +371,12 @@ void CommandBufferManager::OnCommandBufferExecuted(size_t index)
FrameResources& resources = m_frame_resources[index];

// Fire fence tracking callbacks.
for (auto iter = m_fence_point_callbacks.begin(); iter != m_fence_point_callbacks.end();)
{
auto backup_iter = iter++;
backup_iter->second.second(resources.fence);
}

for (const auto& iter : m_fence_point_callbacks)
iter.second.second(resources.fence);

@@ -16,7 +16,6 @@

#include "VideoBackends/Vulkan/CommandBufferManager.h"
#include "VideoBackends/Vulkan/ObjectCache.h"
#include "VideoBackends/Vulkan/StagingTexture2D.h"
#include "VideoBackends/Vulkan/StateTracker.h"
#include "VideoBackends/Vulkan/StreamBuffer.h"
#include "VideoBackends/Vulkan/Texture2D.h"
@@ -698,7 +697,7 @@ u32 FramebufferManager::PeekEFBColor(u32 x, u32 y)
return 0;

u32 value;
m_color_readback_texture->ReadTexel(x, y, &value, sizeof(value));
m_color_readback_texture->ReadTexel(x, y, &value);
return value;
}

@@ -711,7 +710,6 @@ bool FramebufferManager::PopulateColorReadbackTexture()
// Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on.
VkRect2D src_region = {{0, 0}, {GetEFBWidth(), GetEFBHeight()}};
Texture2D* src_texture = m_efb_color_texture.get();
VkImageAspectFlags src_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
if (GetEFBSamples() > 1)
src_texture = ResolveEFBColorTexture(src_region);

@@ -750,9 +748,9 @@ bool FramebufferManager::PopulateColorReadbackTexture()
// Copy from EFB or copy texture to staging texture.
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
m_color_readback_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(),
src_texture->GetImage(), src_aspect, 0, 0, EFB_WIDTH,
EFB_HEIGHT, 0, 0);
static_cast<VKStagingTexture*>(m_color_readback_texture.get())
->CopyFromTexture(src_texture, m_color_readback_texture->GetConfig().GetRect(), 0, 0,
m_color_readback_texture->GetConfig().GetRect());

// Restore original layout if we used the EFB as a source.
if (src_texture == m_efb_color_texture.get())
@@ -762,12 +760,7 @@ bool FramebufferManager::PopulateColorReadbackTexture()
}

// Wait until the copy is complete.
Util::ExecuteCurrentCommandsAndRestoreState(false, true);

// Map to host memory.
if (!m_color_readback_texture->IsMapped() && !m_color_readback_texture->Map())
return false;

m_color_readback_texture->Flush();
m_color_readback_texture_valid = true;
return true;
}
@@ -778,7 +771,7 @@ float FramebufferManager::PeekEFBDepth(u32 x, u32 y)
return 0.0f;

float value;
m_depth_readback_texture->ReadTexel(x, y, &value, sizeof(value));
m_depth_readback_texture->ReadTexel(x, y, &value);
return value;
}

@@ -791,12 +784,10 @@ bool FramebufferManager::PopulateDepthReadbackTexture()
// Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on.
VkRect2D src_region = {{0, 0}, {GetEFBWidth(), GetEFBHeight()}};
Texture2D* src_texture = m_efb_depth_texture.get();
VkImageAspectFlags src_aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
if (GetEFBSamples() > 1)
{
// EFB depth resolves are written out as color textures
src_texture = ResolveEFBDepthTexture(src_region);
src_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
}
if (GetEFBWidth() != EFB_WIDTH || GetEFBHeight() != EFB_HEIGHT)
{
@@ -828,15 +819,14 @@ bool FramebufferManager::PopulateDepthReadbackTexture()

// Use this as a source texture now.
src_texture = m_depth_copy_texture.get();
src_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
}

// Copy from EFB or copy texture to staging texture.
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
m_depth_readback_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(),
src_texture->GetImage(), src_aspect, 0, 0, EFB_WIDTH,
EFB_HEIGHT, 0, 0);
static_cast<VKStagingTexture*>(m_depth_readback_texture.get())
->CopyFromTexture(src_texture, m_depth_readback_texture->GetConfig().GetRect(), 0, 0,
m_depth_readback_texture->GetConfig().GetRect());

// Restore original layout if we used the EFB as a source.
if (src_texture == m_efb_depth_texture.get())
@@ -846,12 +836,7 @@ bool FramebufferManager::PopulateDepthReadbackTexture()
}

// Wait until the copy is complete.
Util::ExecuteCurrentCommandsAndRestoreState(false, true);

// Map to host memory.
if (!m_depth_readback_texture->IsMapped() && !m_depth_readback_texture->Map())
return false;

m_depth_readback_texture->Flush();
m_depth_readback_texture_valid = true;
return true;
}
@@ -1011,32 +996,27 @@ bool FramebufferManager::CreateReadbackTextures()
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);

m_color_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH,
EFB_HEIGHT, EFB_COLOR_TEXTURE_FORMAT);
if (!m_color_copy_texture || !m_color_readback_texture)
{
ERROR_LOG(VIDEO, "Failed to create EFB color readback texture");
return false;
}

m_depth_copy_texture =
Texture2D::Create(EFB_WIDTH, EFB_HEIGHT, 1, 1, EFB_DEPTH_AS_COLOR_TEXTURE_FORMAT,
VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);

m_depth_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH,
EFB_HEIGHT, EFB_DEPTH_TEXTURE_FORMAT);
if (!m_depth_copy_texture || !m_depth_readback_texture)
if (!m_color_copy_texture || !m_depth_copy_texture)
{
ERROR_LOG(VIDEO, "Failed to create EFB depth readback texture");
ERROR_LOG(VIDEO, "Failed to create EFB copy textures");
return false;
}

// With Vulkan, we can leave these textures mapped and use invalidate/flush calls instead.
if (!m_color_readback_texture->Map() || !m_depth_readback_texture->Map())
TextureConfig readback_texture_config(EFB_WIDTH, EFB_HEIGHT, 1, 1, AbstractTextureFormat::RGBA8,
false);
m_color_readback_texture =
g_renderer->CreateStagingTexture(StagingTextureType::Mutable, readback_texture_config);
m_depth_readback_texture =
g_renderer->CreateStagingTexture(StagingTextureType::Mutable, readback_texture_config);
if (!m_color_readback_texture || !m_depth_readback_texture)
{
ERROR_LOG(VIDEO, "Failed to map EFB readback textures");
ERROR_LOG(VIDEO, "Failed to create EFB readback textures");
return false;
}

@@ -1113,7 +1093,7 @@ void FramebufferManager::PokeEFBColor(u32 x, u32 y, u32 color)

// Update the peek cache if it's valid, since we know the color of the pixel now.
if (m_color_readback_texture_valid)
m_color_readback_texture->WriteTexel(x, y, &color, sizeof(color));
m_color_readback_texture->WriteTexel(x, y, &color);
}

void FramebufferManager::PokeEFBDepth(u32 x, u32 y, float depth)
@@ -1126,7 +1106,7 @@ void FramebufferManager::PokeEFBDepth(u32 x, u32 y, float depth)

// Update the peek cache if it's valid, since we know the color of the pixel now.
if (m_depth_readback_texture_valid)
m_depth_readback_texture->WriteTexel(x, y, &depth, sizeof(depth));
m_depth_readback_texture->WriteTexel(x, y, &depth);
}

void FramebufferManager::CreatePokeVertices(std::vector<EFBPokeVertex>* destination_list, u32 x,
@@ -13,9 +13,10 @@
#include "VideoCommon/FramebufferManagerBase.h"
#include "VideoCommon/RenderState.h"

class AbstractStagingTexture;

namespace Vulkan
{
class StagingTexture2D;
class StateTracker;
class StreamBuffer;
class Texture2D;
@@ -138,8 +139,8 @@ class FramebufferManager : public FramebufferManagerBase
VkFramebuffer m_depth_copy_framebuffer = VK_NULL_HANDLE;

// CPU-side EFB readback texture
std::unique_ptr<StagingTexture2D> m_color_readback_texture;
std::unique_ptr<StagingTexture2D> m_depth_readback_texture;
std::unique_ptr<AbstractStagingTexture> m_color_readback_texture;
std::unique_ptr<AbstractStagingTexture> m_depth_readback_texture;
bool m_color_readback_texture_valid = false;
bool m_depth_readback_texture_valid = false;

@@ -159,6 +159,17 @@ void Renderer::DestroySemaphores()
}
}

std::unique_ptr<AbstractTexture> Renderer::CreateTexture(const TextureConfig& config)
{
return VKTexture::Create(config);
}

std::unique_ptr<AbstractStagingTexture> Renderer::CreateStagingTexture(StagingTextureType type,
const TextureConfig& config)
{
return VKStagingTexture::Create(type, config);
}

void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
{
u32 backbuffer_width = m_swap_chain->GetWidth();
@@ -32,6 +32,10 @@ class Renderer : public ::Renderer

static Renderer* GetInstance();

std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;
std::unique_ptr<AbstractStagingTexture>
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override;

SwapChain* GetSwapChain() const { return m_swap_chain.get(); }
BoundingBox* GetBoundingBox() const { return m_bounding_box.get(); }
bool Initialize();
@@ -59,11 +59,11 @@ class StagingBuffer
static std::unique_ptr<StagingBuffer> Create(STAGING_BUFFER_TYPE type, VkDeviceSize size,
VkBufferUsageFlags usage);

protected:
// Allocates the resources needed to create a staging buffer.
static bool AllocateBuffer(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsageFlags usage,
VkBuffer* out_buffer, VkDeviceMemory* out_memory, bool* out_coherent);

protected:
STAGING_BUFFER_TYPE m_type;
VkBuffer m_buffer;
VkDeviceMemory m_memory;

This file was deleted.

This file was deleted.

@@ -178,11 +178,6 @@ void TextureCache::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u
}
}

std::unique_ptr<AbstractTexture> TextureCache::CreateTexture(const TextureConfig& config)
{
return VKTexture::Create(config);
}

bool TextureCache::CreateRenderPasses()
{
static constexpr VkAttachmentDescription update_attachment = {
@@ -31,8 +31,6 @@ class TextureCache : public TextureCacheBase
bool CompileShaders() override;
void DeleteShaders() override;

std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;

void ConvertTexture(TCacheEntry* destination, TCacheEntry* source, const void* palette,
TLUTFormat format) override;

@@ -19,7 +19,6 @@
#include "VideoBackends/Vulkan/CommandBufferManager.h"
#include "VideoBackends/Vulkan/FramebufferManager.h"
#include "VideoBackends/Vulkan/ObjectCache.h"
#include "VideoBackends/Vulkan/StagingTexture2D.h"
#include "VideoBackends/Vulkan/StateTracker.h"
#include "VideoBackends/Vulkan/StreamBuffer.h"
#include "VideoBackends/Vulkan/Texture2D.h"
@@ -67,9 +66,6 @@ TextureConverter::~TextureConverter()
if (m_encoding_render_pass != VK_NULL_HANDLE)
vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_encoding_render_pass, nullptr);

if (m_encoding_render_framebuffer != VK_NULL_HANDLE)
vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_encoding_render_framebuffer, nullptr);

for (auto& it : m_encoding_shaders)
vkDestroyShaderModule(g_vulkan_context->GetDevice(), it.second, nullptr);

@@ -111,12 +107,6 @@ bool TextureConverter::Initialize()
return false;
}

if (!CreateEncodingDownloadTexture())
{
PanicAlert("Failed to create download texture");
return false;
}

if (!CreateDecodingTexture())
{
PanicAlert("Failed to create decoding texture");
@@ -245,8 +235,10 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p
// Can't do our own draw within a render pass.
StateTracker::GetInstance()->EndRenderPass();

m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
static_cast<VKTexture*>(m_encoding_render_texture.get())
->GetRawTexIdentifier()
->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);

UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT),
@@ -276,23 +268,15 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p
render_height);

VkRect2D render_region = {{0, 0}, {render_width, render_height}};
draw.BeginRenderPass(m_encoding_render_framebuffer, render_region);
draw.BeginRenderPass(static_cast<VKTexture*>(m_encoding_render_texture.get())->GetFramebuffer(),
render_region);
draw.DrawWithoutVertexBuffer(4);
draw.EndRenderPass();

// Transition the image before copying
m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
m_encoding_download_texture->CopyFromImage(
g_command_buffer_mgr->GetCurrentCommandBuffer(), m_encoding_render_texture->GetImage(),
VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, render_width, render_height, 0, 0);

// Block until the GPU has finished copying to the staging texture.
Util::ExecuteCurrentCommandsAndRestoreState(false, true);

// Copy from staging texture to the final destination, adjusting pitch if necessary.
m_encoding_download_texture->ReadTexels(0, 0, render_width, render_height, dest_ptr,
memory_stride);
MathUtil::Rectangle<int> copy_rect(0, 0, render_width, render_height);
m_encoding_readback_texture->CopyFromTexture(m_encoding_render_texture.get(), copy_rect, 0, 0,
copy_rect);
m_encoding_readback_texture->ReadTexels(copy_rect, dest_ptr, memory_stride);
}

void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u32 dst_stride,
@@ -304,8 +288,9 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u
// Borrow framebuffer from EFB2RAM encoder.
VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
m_encoding_render_texture->TransitionToLayout(command_buffer,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
static_cast<VKTexture*>(m_encoding_render_texture.get())
->GetRawTexIdentifier()
->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);

// Use fragment shader to convert RGBA to YUYV.
// Use linear sampler for downscaling. This texture is in BGRA order, so the data is already in
@@ -317,26 +302,20 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u
m_encoding_render_pass, g_shader_cache->GetPassthroughVertexShader(),
VK_NULL_HANDLE, m_rgb_to_yuyv_shader);
VkRect2D region = {{0, 0}, {output_width, dst_height}};
draw.BeginRenderPass(m_encoding_render_framebuffer, region);
draw.BeginRenderPass(static_cast<VKTexture*>(m_encoding_render_texture.get())->GetFramebuffer(),
region);
draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetLinearSampler());
draw.DrawQuad(0, 0, static_cast<int>(output_width), static_cast<int>(dst_height), src_rect.left,
src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(),
static_cast<int>(src_texture->GetWidth()),
static_cast<int>(src_texture->GetHeight()));
draw.EndRenderPass();

// Render pass transitions to TRANSFER_SRC.
m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);

// Copy from encoding texture to download buffer.
m_encoding_download_texture->CopyFromImage(command_buffer, m_encoding_render_texture->GetImage(),
VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, output_width,
dst_height, 0, 0);
Util::ExecuteCurrentCommandsAndRestoreState(false, true);

// Finally, copy to guest memory. This may have a different stride.
m_encoding_download_texture->ReadTexels(0, 0, output_width, dst_height, dst_ptr, dst_stride);
MathUtil::Rectangle<int> copy_rect(0, 0, output_width, dst_height);
m_encoding_readback_texture->CopyFromTexture(m_encoding_render_texture.get(), copy_rect, 0, 0,
copy_rect);
m_encoding_readback_texture->ReadTexels(copy_rect, dst_ptr, dst_stride);
}

void TextureConverter::DecodeYUYVTextureFromMemory(VKTexture* dst_texture, const void* src_ptr,
@@ -734,10 +713,10 @@ VkShaderModule TextureConverter::GetEncodingShader(const EFBCopyParams& params)
bool TextureConverter::CreateEncodingRenderPass()
{
VkAttachmentDescription attachments[] = {
{0, ENCODING_TEXTURE_FORMAT, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}};
{0, Util::GetVkFormatForHostTextureFormat(ENCODING_TEXTURE_FORMAT), VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}};

VkAttachmentReference color_attachment_references[] = {
{0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}};
@@ -769,43 +748,14 @@ bool TextureConverter::CreateEncodingRenderPass()

bool TextureConverter::CreateEncodingTexture()
{
m_encoding_render_texture = Texture2D::Create(
ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1, ENCODING_TEXTURE_FORMAT,
VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
if (!m_encoding_render_texture)
return false;
TextureConfig config(ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1,
ENCODING_TEXTURE_FORMAT, true);

VkImageView framebuffer_attachments[] = {m_encoding_render_texture->GetView()};
VkFramebufferCreateInfo framebuffer_info = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
nullptr,
0,
m_encoding_render_pass,
static_cast<u32>(ArraySize(framebuffer_attachments)),
framebuffer_attachments,
m_encoding_render_texture->GetWidth(),
m_encoding_render_texture->GetHeight(),
m_encoding_render_texture->GetLayers()};

VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr,
&m_encoding_render_framebuffer);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: ");
return false;
}

return true;
}

bool TextureConverter::CreateEncodingDownloadTexture()
{
m_encoding_download_texture =
StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, ENCODING_TEXTURE_WIDTH,
ENCODING_TEXTURE_HEIGHT, ENCODING_TEXTURE_FORMAT);
m_encoding_render_texture = g_renderer->CreateTexture(config);
m_encoding_readback_texture =
g_renderer->CreateStagingTexture(StagingTextureType::Readback, config);

return m_encoding_download_texture && m_encoding_download_texture->Map();
return m_encoding_render_texture && m_encoding_readback_texture;
}

bool TextureConverter::CreateDecodingTexture()
@@ -16,6 +16,9 @@
#include "VideoCommon/TextureDecoder.h"
#include "VideoCommon/VideoCommon.h"

class AbstractTexture;
class AbstractStagingTexture;

namespace Vulkan
{
class StagingTexture2D;
@@ -58,7 +61,7 @@ class TextureConverter
private:
static const u32 ENCODING_TEXTURE_WIDTH = EFB_WIDTH * 4;
static const u32 ENCODING_TEXTURE_HEIGHT = 1024;
static const VkFormat ENCODING_TEXTURE_FORMAT = VK_FORMAT_B8G8R8A8_UNORM;
static const AbstractTextureFormat ENCODING_TEXTURE_FORMAT = AbstractTextureFormat::BGRA8;
static const size_t NUM_PALETTE_CONVERSION_SHADERS = 3;

// Maximum size of a texture based on BP registers.
@@ -75,8 +78,6 @@ class TextureConverter

bool CreateEncodingRenderPass();
bool CreateEncodingTexture();
bool CreateEncodingDownloadTexture();

bool CreateDecodingTexture();

bool CompileYUYVConversionShaders();
@@ -106,10 +107,9 @@ class TextureConverter

// Texture encoding - RGBA8->GX format in memory
std::map<EFBCopyParams, VkShaderModule> m_encoding_shaders;
std::unique_ptr<AbstractTexture> m_encoding_render_texture;
std::unique_ptr<AbstractStagingTexture> m_encoding_readback_texture;
VkRenderPass m_encoding_render_pass = VK_NULL_HANDLE;
std::unique_ptr<Texture2D> m_encoding_render_texture;
VkFramebuffer m_encoding_render_framebuffer = VK_NULL_HANDLE;
std::unique_ptr<StagingTexture2D> m_encoding_download_texture;

// Texture decoding - GX format in memory->RGBA8
struct TextureDecodingPipeline
@@ -107,7 +107,13 @@ VkFormat GetVkFormatForHostTextureFormat(AbstractTextureFormat format)
return VK_FORMAT_BC7_UNORM_BLOCK;

case AbstractTextureFormat::RGBA8:
return VK_FORMAT_R8G8B8A8_UNORM;

case AbstractTextureFormat::BGRA8:
return VK_FORMAT_B8G8R8A8_UNORM;

default:
PanicAlert("Unhandled texture format.");
return VK_FORMAT_R8G8B8A8_UNORM;
}
}

Large diffs are not rendered by default.

@@ -7,10 +7,12 @@
#include <memory>
#include <vulkan/vulkan.h>

#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"

namespace Vulkan
{
class StagingBuffer;
class Texture2D;

class VKTexture final : public AbstractTexture
@@ -20,13 +22,16 @@ class VKTexture final : public AbstractTexture
~VKTexture();

void Bind(unsigned int stage) override;
void Unmap() override;

void CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void CopyRectangleFromTexture(Texture2D* source, const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect);
void CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level) override;
void ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& src_rect,
const MathUtil::Rectangle<int>& dst_rect);
void ScaleRectangleFromTexture(Texture2D* src_texture, const MathUtil::Rectangle<int>& src_rect,
const MathUtil::Rectangle<int>& dst_rect);
void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
size_t buffer_size) override;

@@ -39,21 +44,41 @@ class VKTexture final : public AbstractTexture
VKTexture(const TextureConfig& tex_config, std::unique_ptr<Texture2D> texture,
VkFramebuffer framebuffer);

// Copies the contents of a texture using vkCmdCopyImage
void CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect, Texture2D* src_texture,
const MathUtil::Rectangle<int>& src_rect);
std::unique_ptr<Texture2D> m_texture;
VkFramebuffer m_framebuffer;
};

// Copies (and optionally scales) the contents of a texture using a framgent shader.
void ScaleTextureRectangle(const MathUtil::Rectangle<int>& dst_rect, Texture2D* src_texture,
const MathUtil::Rectangle<int>& src_rect);
class VKStagingTexture final : public AbstractStagingTexture
{
public:
VKStagingTexture() = delete;
~VKStagingTexture();

std::optional<RawTextureInfo> MapFullImpl() override;
std::optional<RawTextureInfo> MapRegionImpl(u32 level, u32 x, u32 y, u32 width,
u32 height) override;
void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect,
u32 src_layer, u32 src_level,
const MathUtil::Rectangle<int>& dst_rect) override;
void CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level) override;

std::unique_ptr<Texture2D> m_texture;
std::unique_ptr<StagingTexture2D> m_staging_texture;
VkFramebuffer m_framebuffer;
bool Map() override;
void Unmap() override;
void Flush() override;

// This overload is provided for compatibility as we dropped StagingTexture2D.
// For now, FramebufferManager relies on them. But we can drop it once we move that to common.
void CopyFromTexture(Texture2D* src, const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect);

static std::unique_ptr<VKStagingTexture> Create(StagingTextureType type,
const TextureConfig& config);

private:
VKStagingTexture(StagingTextureType type, const TextureConfig& config,
std::unique_ptr<StagingBuffer> buffer);

std::unique_ptr<StagingBuffer> m_staging_buffer;
VkFence m_flush_fence = VK_NULL_HANDLE;
};

} // namespace Vulkan
@@ -46,7 +46,6 @@
<ClCompile Include="PerfQuery.cpp" />
<ClCompile Include="RasterFont.cpp" />
<ClCompile Include="StagingBuffer.cpp" />
<ClCompile Include="StagingTexture2D.cpp" />
<ClCompile Include="Util.cpp" />
<ClCompile Include="VertexFormat.cpp" />
<ClCompile Include="ObjectCache.cpp" />
@@ -72,7 +71,6 @@
<ClInclude Include="TextureConverter.h" />
<ClInclude Include="RasterFont.h" />
<ClInclude Include="StagingBuffer.h" />
<ClInclude Include="StagingTexture2D.h" />
<ClInclude Include="Util.h" />
<ClInclude Include="VertexFormat.h" />
<ClInclude Include="PerfQuery.h" />
@@ -0,0 +1,133 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include <algorithm>
#include <cstring>

#include "Common/Assert.h"
#include "Common/MsgHandler.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"

AbstractStagingTexture::AbstractStagingTexture(StagingTextureType type, const TextureConfig& c)
: m_type(type), m_config(c), m_texel_size(AbstractTexture::GetTexelSizeForFormat(c.format))
{
}

AbstractStagingTexture::~AbstractStagingTexture() = default;

void AbstractStagingTexture::CopyFromTexture(const AbstractTexture* src, u32 src_layer,
u32 src_level)
{
MathUtil::Rectangle<int> src_rect = src->GetConfig().GetMipRect(src_level);
MathUtil::Rectangle<int> dst_rect = m_config.GetRect();
CopyFromTexture(src, src_rect, src_layer, src_level, dst_rect);
}

void AbstractStagingTexture::CopyToTexture(AbstractTexture* dst, u32 dst_layer, u32 dst_level)
{
MathUtil::Rectangle<int> src_rect = m_config.GetRect();
MathUtil::Rectangle<int> dst_rect = dst->GetConfig().GetMipRect(dst_level);
CopyToTexture(src_rect, dst, dst_rect, dst_layer, dst_level);
}

void AbstractStagingTexture::ReadTexels(const MathUtil::Rectangle<int>& rect, void* out_ptr,
u32 out_stride)
{
_assert_(m_type != StagingTextureType::Upload);
if (!PrepareForAccess())
return;

_assert_(rect.left >= 0 && static_cast<u32>(rect.right) <= m_config.width && rect.top >= 0 &&
static_cast<u32>(rect.bottom) <= m_config.height);

// Offset pointer to point to start of region being copied out.
const char* current_ptr = m_map_pointer;
current_ptr += rect.top * m_map_stride;
current_ptr += rect.left * m_texel_size;

// Optimal path: same dimensions, same stride.
if (rect.left == 0 && static_cast<u32>(rect.right) == m_config.width &&
m_map_stride == out_stride)
{
std::memcpy(out_ptr, current_ptr, m_map_stride * rect.GetHeight());
return;
}

size_t copy_size = std::min(static_cast<size_t>(rect.GetWidth() * m_texel_size), m_map_stride);
int copy_height = rect.GetHeight();
char* dst_ptr = reinterpret_cast<char*>(out_ptr);
for (int row = 0; row < copy_height; row++)
{
std::memcpy(dst_ptr, current_ptr, copy_size);
current_ptr += m_map_stride;
dst_ptr += out_stride;
}
}

void AbstractStagingTexture::ReadTexel(u32 x, u32 y, void* out_ptr)
{
_assert_(m_type != StagingTextureType::Upload);
if (!PrepareForAccess())
return;

_assert_(x < m_config.width && y < m_config.height);
const char* src_ptr = m_map_pointer + y * m_map_stride + x * m_texel_size;
std::memcpy(out_ptr, src_ptr, m_texel_size);
}

void AbstractStagingTexture::WriteTexels(const MathUtil::Rectangle<int>& rect, const void* in_ptr,
u32 in_stride)
{
_assert_(m_type != StagingTextureType::Readback);
if (!PrepareForAccess())
return;

_assert_(rect.left >= 0 && static_cast<u32>(rect.right) <= m_config.width && rect.top >= 0 &&
static_cast<u32>(rect.bottom) <= m_config.height);

// Offset pointer to point to start of region being copied to.
char* current_ptr = m_map_pointer;
current_ptr += rect.top * m_map_stride;
current_ptr += rect.left * m_texel_size;

// Optimal path: same dimensions, same stride.
if (rect.left == 0 && static_cast<u32>(rect.right) == m_config.width && m_map_stride == in_stride)
{
std::memcpy(current_ptr, in_ptr, m_map_stride * rect.GetHeight());
return;
}

size_t copy_size = std::min(static_cast<size_t>(rect.GetWidth() * m_texel_size), m_map_stride);
int copy_height = rect.GetHeight();
const char* src_ptr = reinterpret_cast<const char*>(in_ptr);
for (int row = 0; row < copy_height; row++)
{
std::memcpy(current_ptr, src_ptr, copy_size);
current_ptr += m_map_stride;
src_ptr += in_stride;
}
}

void AbstractStagingTexture::WriteTexel(u32 x, u32 y, const void* in_ptr)
{
_assert_(m_type != StagingTextureType::Readback);
if (!PrepareForAccess())
return;

_assert_(x < m_config.width && y < m_config.height);
char* dest_ptr = m_map_pointer + y * m_map_stride + x * m_texel_size;
std::memcpy(dest_ptr, in_ptr, m_texel_size);
}

bool AbstractStagingTexture::PrepareForAccess()
{
if (m_needs_flush)
{
if (IsMapped())
Unmap();
Flush();
}
return IsMapped() || Map();
}
@@ -0,0 +1,86 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <cstddef>
#include <string>

#include "Common/CommonTypes.h"
#include "Common/MathUtil.h"
#include "VideoCommon/TextureConfig.h"

class AbstractTexture;

class AbstractStagingTexture
{
public:
explicit AbstractStagingTexture(StagingTextureType type, const TextureConfig& c);
virtual ~AbstractStagingTexture();

const TextureConfig& GetConfig() const { return m_config; }
StagingTextureType GetType() const { return m_type; }
size_t GetTexelSize() const { return m_texel_size; }
bool IsMapped() const { return m_map_pointer != nullptr; }
char* GetMappedPointer() const { return m_map_pointer; }
size_t GetMappedStride() const { return m_map_stride; }
// Copies from the GPU texture object to the staging texture, which can be mapped/read by the CPU.
// Both src_rect and dst_rect must be with within the bounds of the the specified textures.
virtual void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect,
u32 src_layer, u32 src_level,
const MathUtil::Rectangle<int>& dst_rect) = 0;

// Wrapper for copying a whole layer of a texture to a readback texture.
// Assumes that the level of src texture and this texture have the same dimensions.
void CopyFromTexture(const AbstractTexture* src, u32 src_layer = 0, u32 src_level = 0);

// Copies from this staging texture to a GPU texture.
// Both src_rect and dst_rect must be with within the bounds of the the specified textures.
virtual void CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level) = 0;

// Wrapper for copying a whole layer of a texture to a readback texture.
// Assumes that the level of src texture and this texture have the same dimensions.
void CopyToTexture(AbstractTexture* dst, u32 dst_layer = 0, u32 dst_level = 0);

// Maps the texture into the CPU address space, enabling it to read the contents.
// The Map call may not perform synchronization. If the contents of the staging texture
// has been updated by a CopyFromTexture call, you must call Flush() first.
// If persistent mapping is supported in the backend, this may be a no-op.
virtual bool Map() = 0;

// Unmaps the CPU-readable copy of the texture. May be a no-op on backends which
// support persistent-mapped buffers.
virtual void Unmap() = 0;

// Flushes pending writes from the CPU to the GPU, and reads from the GPU to the CPU.
// This may cause a command buffer flush depending on if one has occurred between the last
// call to CopyFromTexture()/CopyToTexture() and the Flush() call.
virtual void Flush() = 0;

// Reads the specified rectangle from the staging texture to out_ptr, with the specified stride
// (length in bytes of each row). CopyFromTexture must be called first. The contents of any
// texels outside of the rectangle used for CopyFromTexture is undefined.
void ReadTexels(const MathUtil::Rectangle<int>& rect, void* out_ptr, u32 out_stride);
void ReadTexel(u32 x, u32 y, void* out_ptr);

// Copies the texels from in_ptr to the staging texture, which can be read by the GPU, with the
// specified stride (length in bytes of each row). After updating the staging texture with all
// changes, call CopyToTexture() to update the GPU copy.
void WriteTexels(const MathUtil::Rectangle<int>& rect, const void* in_ptr, u32 in_stride);
void WriteTexel(u32 x, u32 y, const void* in_ptr);

protected:
bool PrepareForAccess();

const StagingTextureType m_type;
const TextureConfig m_config;
const size_t m_texel_size;

char* m_map_pointer = nullptr;
size_t m_map_stride = 0;

bool m_needs_flush = false;
};
@@ -5,9 +5,11 @@
#include <algorithm>

#include "Common/Assert.h"

#include "Common/MsgHandler.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"
#include "VideoCommon/ImageWrite.h"
#include "VideoCommon/RenderBase.h"

AbstractTexture::AbstractTexture(const TextureConfig& c) : m_config(c)
{
@@ -20,105 +22,85 @@ bool AbstractTexture::Save(const std::string& filename, unsigned int level)
// We can't dump compressed textures currently (it would mean drawing them to a RGBA8
// framebuffer, and saving that). TextureCache does not call Save for custom textures
// anyway, so this is fine for now.
_assert_(m_config.format == AbstractTextureFormat::RGBA8);
_assert_(!IsCompressedFormat(m_config.format));
_assert_(level < m_config.levels);

auto result = level == 0 ? Map() : Map(level);
// Determine dimensions of image we want to save.
u32 level_width = std::max(1u, m_config.width >> level);
u32 level_height = std::max(1u, m_config.height >> level);

// Use a temporary staging texture for the download. Certainly not optimal,
// but this is not a frequently-executed code path..
TextureConfig readback_texture_config(level_width, level_height, 1, 1,
AbstractTextureFormat::RGBA8, false);
auto readback_texture =
g_renderer->CreateStagingTexture(StagingTextureType::Readback, readback_texture_config);
if (!readback_texture)
return false;

if (!result.has_value())
{
// Copy to the readback texture's buffer.
readback_texture->CopyFromTexture(this, 0, level);
readback_texture->Flush();

// Map it so we can encode it to the file.
if (!readback_texture->Map())
return false;
}

auto raw_data = result.value();
return TextureToPng(raw_data.data, raw_data.stride, filename, raw_data.width, raw_data.height);
return TextureToPng(reinterpret_cast<const u8*>(readback_texture->GetMappedPointer()),
static_cast<int>(readback_texture->GetMappedStride()), filename, level_width,
level_height);
}

std::optional<AbstractTexture::RawTextureInfo> AbstractTexture::Map()
bool AbstractTexture::IsCompressedFormat(AbstractTextureFormat format)
{
if (m_currently_mapped)
switch (format)
{
Unmap();
m_currently_mapped = false;
}
auto result = MapFullImpl();
case AbstractTextureFormat::DXT1:
case AbstractTextureFormat::DXT3:
case AbstractTextureFormat::DXT5:
case AbstractTextureFormat::BPTC:
return true;

if (!result.has_value())
{
m_currently_mapped = false;
return {};
default:
return false;
}

m_currently_mapped = true;
return result;
}

std::optional<AbstractTexture::RawTextureInfo> AbstractTexture::Map(u32 level, u32 x, u32 y,
u32 width, u32 height)
size_t AbstractTexture::CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length)
{
_assert_(level < m_config.levels);

u32 max_level_width = std::max(m_config.width >> level, 1u);
u32 max_level_height = std::max(m_config.height >> level, 1u);

_assert_(width < max_level_width);
_assert_(height < max_level_height);

auto result = MapRegionImpl(level, x, y, width, height);

if (!result.has_value())
switch (format)
{
m_currently_mapped = false;
return {};
case AbstractTextureFormat::DXT1:
return static_cast<size_t>(std::max(1u, row_length / 4)) * 8;
case AbstractTextureFormat::DXT3:
case AbstractTextureFormat::DXT5:
case AbstractTextureFormat::BPTC:
return static_cast<size_t>(std::max(1u, row_length / 4)) * 16;
case AbstractTextureFormat::RGBA8:
case AbstractTextureFormat::BGRA8:
return static_cast<size_t>(row_length) * 4;
default:
PanicAlert("Unhandled texture format.");
return 0;
}

m_currently_mapped = true;
return result;
}

std::optional<AbstractTexture::RawTextureInfo> AbstractTexture::Map(u32 level)
{
_assert_(level < m_config.levels);

u32 level_width = std::max(m_config.width >> level, 1u);
u32 level_height = std::max(m_config.height >> level, 1u);

return Map(level, 0, 0, level_width, level_height);
}

void AbstractTexture::Unmap()
{
}

std::optional<AbstractTexture::RawTextureInfo> AbstractTexture::MapFullImpl()
{
return {};
}

std::optional<AbstractTexture::RawTextureInfo>
AbstractTexture::MapRegionImpl(u32 level, u32 x, u32 y, u32 width, u32 height)
{
return {};
}

bool AbstractTexture::IsCompressedHostTextureFormat(AbstractTextureFormat format)
{
// This will need to be changed if we add any other uncompressed formats.
return format != AbstractTextureFormat::RGBA8;
}

size_t AbstractTexture::CalculateHostTextureLevelPitch(AbstractTextureFormat format, u32 row_length)
size_t AbstractTexture::GetTexelSizeForFormat(AbstractTextureFormat format)
{
switch (format)
{
case AbstractTextureFormat::DXT1:
return static_cast<size_t>(std::max(1u, row_length / 4)) * 8;
return 8;
case AbstractTextureFormat::DXT3:
case AbstractTextureFormat::DXT5:
case AbstractTextureFormat::BPTC:
return static_cast<size_t>(std::max(1u, row_length / 4)) * 16;
return 16;
case AbstractTextureFormat::RGBA8:
case AbstractTextureFormat::BGRA8:
return 4;
default:
return static_cast<size_t>(row_length) * 4;
PanicAlert("Unhandled texture format.");
return 0;
}
}