Merge pull request #6193 from stenzek/readbacks
Abstract Staging Textures - VideoCommon interface for texture readbacks/uploads
This commit is contained in:
commit
cd68b3606c
|
@ -1870,6 +1870,9 @@ const GLFunc gl_function_array[] = {
|
|||
GLFUNC_REQUIRES(glDispatchCompute, "GL_ARB_compute_shader !VERSION_4_3 |VERSION_GLES_3_1"),
|
||||
GLFUNC_REQUIRES(glDispatchComputeIndirect,
|
||||
"GL_ARB_compute_shader !VERSION_4_3 |VERSION_GLES_3_1"),
|
||||
|
||||
// ARB_get_texture_sub_image
|
||||
GLFUNC_REQUIRES(glGetTextureSubImage, "GL_ARB_get_texture_sub_image !VERSION_4_5"),
|
||||
};
|
||||
|
||||
namespace GLExtensions
|
||||
|
|
|
@ -52,8 +52,6 @@ const D3D_FEATURE_LEVEL supported_feature_levels[NUM_SUPPORTED_FEATURE_LEVELS] =
|
|||
|
||||
unsigned int xres, yres;
|
||||
|
||||
bool bFrameInProgress = false;
|
||||
|
||||
HRESULT LoadDXGI()
|
||||
{
|
||||
if (dxgi_dll_ref++ > 0)
|
||||
|
@ -602,27 +600,6 @@ void Reset()
|
|||
SetDebugObjectName(backbuf->GetRTV(), "backbuffer render target view");
|
||||
}
|
||||
|
||||
bool BeginFrame()
|
||||
{
|
||||
if (bFrameInProgress)
|
||||
{
|
||||
PanicAlert("BeginFrame called although a frame is already in progress");
|
||||
return false;
|
||||
}
|
||||
bFrameInProgress = true;
|
||||
return (device != nullptr);
|
||||
}
|
||||
|
||||
void EndFrame()
|
||||
{
|
||||
if (!bFrameInProgress)
|
||||
{
|
||||
PanicAlert("EndFrame called although no frame is in progress");
|
||||
return;
|
||||
}
|
||||
bFrameInProgress = false;
|
||||
}
|
||||
|
||||
void Present()
|
||||
{
|
||||
UINT present_flags = 0;
|
||||
|
|
|
@ -63,8 +63,6 @@ extern HWND hWnd;
|
|||
extern bool bFrameInProgress;
|
||||
|
||||
void Reset();
|
||||
bool BeginFrame();
|
||||
void EndFrame();
|
||||
void Present();
|
||||
|
||||
unsigned int GetBackBufferWidth();
|
||||
|
|
|
@ -263,6 +263,21 @@ void StateManager::SetTextureByMask(u32 textureSlotMask, ID3D11ShaderResourceVie
|
|||
|
||||
} // namespace D3D
|
||||
|
||||
StateCache::~StateCache()
|
||||
{
|
||||
for (auto& it : m_depth)
|
||||
SAFE_RELEASE(it.second);
|
||||
|
||||
for (auto& it : m_raster)
|
||||
SAFE_RELEASE(it.second);
|
||||
|
||||
for (auto& it : m_blend)
|
||||
SAFE_RELEASE(it.second);
|
||||
|
||||
for (auto& it : m_sampler)
|
||||
SAFE_RELEASE(it.second);
|
||||
}
|
||||
|
||||
ID3D11SamplerState* StateCache::Get(SamplerState state)
|
||||
{
|
||||
auto it = m_sampler.find(state.hex);
|
||||
|
@ -471,33 +486,6 @@ ID3D11DepthStencilState* StateCache::Get(DepthState state)
|
|||
return res;
|
||||
}
|
||||
|
||||
void StateCache::Clear()
|
||||
{
|
||||
for (auto it : m_depth)
|
||||
{
|
||||
SAFE_RELEASE(it.second);
|
||||
}
|
||||
m_depth.clear();
|
||||
|
||||
for (auto it : m_raster)
|
||||
{
|
||||
SAFE_RELEASE(it.second);
|
||||
}
|
||||
m_raster.clear();
|
||||
|
||||
for (auto it : m_blend)
|
||||
{
|
||||
SAFE_RELEASE(it.second);
|
||||
}
|
||||
m_blend.clear();
|
||||
|
||||
for (auto it : m_sampler)
|
||||
{
|
||||
SAFE_RELEASE(it.second);
|
||||
}
|
||||
m_sampler.clear();
|
||||
}
|
||||
|
||||
D3D11_PRIMITIVE_TOPOLOGY StateCache::GetPrimitiveTopology(PrimitiveType primitive)
|
||||
{
|
||||
static constexpr std::array<D3D11_PRIMITIVE_TOPOLOGY, 4> primitives = {
|
||||
|
|
|
@ -23,6 +23,8 @@ namespace DX11
|
|||
class StateCache
|
||||
{
|
||||
public:
|
||||
~StateCache();
|
||||
|
||||
// Get existing or create new render state.
|
||||
// Returned objects is owned by the cache and does not need to be released.
|
||||
ID3D11SamplerState* Get(SamplerState state);
|
||||
|
@ -30,9 +32,6 @@ public:
|
|||
ID3D11RasterizerState* Get(RasterizationState state);
|
||||
ID3D11DepthStencilState* Get(DepthState state);
|
||||
|
||||
// Release all cached states and clear hash tables.
|
||||
void Clear();
|
||||
|
||||
// Convert RasterState primitive type to D3D11 primitive topology.
|
||||
static D3D11_PRIMITIVE_TOPOLOGY GetPrimitiveTopology(PrimitiveType primitive);
|
||||
|
||||
|
|
|
@ -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);
|
||||
const DXTexture* srcentry = static_cast<const DXTexture*>(src);
|
||||
_assert_(src_rect.GetWidth() == dst_rect.GetWidth() &&
|
||||
src_rect.GetHeight() == dst_rect.GetHeight());
|
||||
|
||||
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 {};
|
||||
}
|
||||
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;
|
||||
|
||||
// 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};
|
||||
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;
|
||||
_assert_(m_config.rendertarget);
|
||||
|
||||
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;
|
||||
}
|
||||
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 @@ public:
|
|||
~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,7 +91,10 @@ 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;
|
||||
|
@ -130,8 +102,8 @@ void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_w
|
|||
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 @@ public:
|
|||
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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -53,31 +53,42 @@ typedef struct _Nv_Stereo_Image_Header
|
|||
|
||||
#define NVSTEREO_IMAGE_SIGNATURE 0x4433564e
|
||||
|
||||
struct GXPipelineState
|
||||
Renderer::Renderer() : ::Renderer(D3D::GetBackBufferWidth(), D3D::GetBackBufferHeight())
|
||||
{
|
||||
std::array<SamplerState, 8> samplers;
|
||||
BlendingState blend;
|
||||
DepthState zmode;
|
||||
RasterizationState raster;
|
||||
};
|
||||
m_last_multisamples = g_ActiveConfig.iMultisamples;
|
||||
m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off;
|
||||
m_last_fullscreen_mode = D3D::GetFullscreenState();
|
||||
|
||||
static u32 s_last_multisamples = 1;
|
||||
static bool s_last_stereo_mode = false;
|
||||
static bool s_last_fullscreen_mode = false;
|
||||
g_framebuffer_manager = std::make_unique<FramebufferManager>(m_target_width, m_target_height);
|
||||
SetupDeviceObjects();
|
||||
|
||||
static std::array<ID3D11BlendState*, 4> s_clear_blend_states{};
|
||||
static std::array<ID3D11DepthStencilState*, 3> s_clear_depth_states{};
|
||||
static ID3D11BlendState* s_reset_blend_state = nullptr;
|
||||
static ID3D11DepthStencilState* s_reset_depth_state = nullptr;
|
||||
static ID3D11RasterizerState* s_reset_rast_state = nullptr;
|
||||
// Setup GX pipeline state
|
||||
for (auto& sampler : m_gx_state.samplers)
|
||||
sampler.hex = RenderState::GetPointSamplerState().hex;
|
||||
|
||||
static ID3D11Texture2D* s_screenshot_texture = nullptr;
|
||||
static D3DTexture2D* s_3d_vision_texture = nullptr;
|
||||
m_gx_state.zmode.testenable = false;
|
||||
m_gx_state.zmode.updateenable = false;
|
||||
m_gx_state.zmode.func = ZMode::NEVER;
|
||||
m_gx_state.raster.cullmode = GenMode::CULL_NONE;
|
||||
|
||||
static GXPipelineState s_gx_state;
|
||||
static StateCache s_gx_state_cache;
|
||||
// Clear EFB textures
|
||||
constexpr std::array<float, 4> clear_color{{0.f, 0.f, 0.f, 1.f}};
|
||||
D3D::context->ClearRenderTargetView(FramebufferManager::GetEFBColorTexture()->GetRTV(),
|
||||
clear_color.data());
|
||||
D3D::context->ClearDepthStencilView(FramebufferManager::GetEFBDepthTexture()->GetDSV(),
|
||||
D3D11_CLEAR_DEPTH, 0.f, 0);
|
||||
|
||||
static void SetupDeviceObjects()
|
||||
D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)m_target_width, (float)m_target_height);
|
||||
D3D::context->RSSetViewports(1, &vp);
|
||||
FramebufferManager::BindEFBRenderTarget();
|
||||
}
|
||||
|
||||
Renderer::~Renderer()
|
||||
{
|
||||
TeardownDeviceObjects();
|
||||
}
|
||||
|
||||
void Renderer::SetupDeviceObjects()
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
|
@ -88,22 +99,22 @@ static void SetupDeviceObjects()
|
|||
ddesc.StencilEnable = FALSE;
|
||||
ddesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
|
||||
ddesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
|
||||
hr = D3D::device->CreateDepthStencilState(&ddesc, &s_clear_depth_states[0]);
|
||||
hr = D3D::device->CreateDepthStencilState(&ddesc, &m_clear_depth_states[0]);
|
||||
CHECK(hr == S_OK, "Create depth state for Renderer::ClearScreen");
|
||||
ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
|
||||
ddesc.DepthEnable = TRUE;
|
||||
hr = D3D::device->CreateDepthStencilState(&ddesc, &s_clear_depth_states[1]);
|
||||
hr = D3D::device->CreateDepthStencilState(&ddesc, &m_clear_depth_states[1]);
|
||||
CHECK(hr == S_OK, "Create depth state for Renderer::ClearScreen");
|
||||
ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
|
||||
hr = D3D::device->CreateDepthStencilState(&ddesc, &s_clear_depth_states[2]);
|
||||
hr = D3D::device->CreateDepthStencilState(&ddesc, &m_clear_depth_states[2]);
|
||||
CHECK(hr == S_OK, "Create depth state for Renderer::ClearScreen");
|
||||
D3D::SetDebugObjectName(s_clear_depth_states[0],
|
||||
D3D::SetDebugObjectName(m_clear_depth_states[0],
|
||||
"depth state for Renderer::ClearScreen (depth buffer disabled)");
|
||||
D3D::SetDebugObjectName(
|
||||
s_clear_depth_states[1],
|
||||
m_clear_depth_states[1],
|
||||
"depth state for Renderer::ClearScreen (depth buffer enabled, writing enabled)");
|
||||
D3D::SetDebugObjectName(
|
||||
s_clear_depth_states[2],
|
||||
m_clear_depth_states[2],
|
||||
"depth state for Renderer::ClearScreen (depth buffer enabled, writing disabled)");
|
||||
|
||||
D3D11_BLEND_DESC blenddesc;
|
||||
|
@ -117,24 +128,24 @@ static void SetupDeviceObjects()
|
|||
blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
|
||||
blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
|
||||
blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
|
||||
hr = D3D::device->CreateBlendState(&blenddesc, &s_reset_blend_state);
|
||||
hr = D3D::device->CreateBlendState(&blenddesc, &m_reset_blend_state);
|
||||
CHECK(hr == S_OK, "Create blend state for Renderer::ResetAPIState");
|
||||
D3D::SetDebugObjectName(s_reset_blend_state, "blend state for Renderer::ResetAPIState");
|
||||
D3D::SetDebugObjectName(m_reset_blend_state, "blend state for Renderer::ResetAPIState");
|
||||
|
||||
s_clear_blend_states[0] = s_reset_blend_state;
|
||||
s_reset_blend_state->AddRef();
|
||||
m_clear_blend_states[0] = m_reset_blend_state;
|
||||
m_reset_blend_state->AddRef();
|
||||
|
||||
blenddesc.RenderTarget[0].RenderTargetWriteMask =
|
||||
D3D11_COLOR_WRITE_ENABLE_RED | D3D11_COLOR_WRITE_ENABLE_GREEN | D3D11_COLOR_WRITE_ENABLE_BLUE;
|
||||
hr = D3D::device->CreateBlendState(&blenddesc, &s_clear_blend_states[1]);
|
||||
hr = D3D::device->CreateBlendState(&blenddesc, &m_clear_blend_states[1]);
|
||||
CHECK(hr == S_OK, "Create blend state for Renderer::ClearScreen");
|
||||
|
||||
blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALPHA;
|
||||
hr = D3D::device->CreateBlendState(&blenddesc, &s_clear_blend_states[2]);
|
||||
hr = D3D::device->CreateBlendState(&blenddesc, &m_clear_blend_states[2]);
|
||||
CHECK(hr == S_OK, "Create blend state for Renderer::ClearScreen");
|
||||
|
||||
blenddesc.RenderTarget[0].RenderTargetWriteMask = 0;
|
||||
hr = D3D::device->CreateBlendState(&blenddesc, &s_clear_blend_states[3]);
|
||||
hr = D3D::device->CreateBlendState(&blenddesc, &m_clear_blend_states[3]);
|
||||
CHECK(hr == S_OK, "Create blend state for Renderer::ClearScreen");
|
||||
|
||||
ddesc.DepthEnable = FALSE;
|
||||
|
@ -143,41 +154,39 @@ static void SetupDeviceObjects()
|
|||
ddesc.StencilEnable = FALSE;
|
||||
ddesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
|
||||
ddesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
|
||||
hr = D3D::device->CreateDepthStencilState(&ddesc, &s_reset_depth_state);
|
||||
hr = D3D::device->CreateDepthStencilState(&ddesc, &m_reset_depth_state);
|
||||
CHECK(hr == S_OK, "Create depth state for Renderer::ResetAPIState");
|
||||
D3D::SetDebugObjectName(s_reset_depth_state, "depth stencil state for Renderer::ResetAPIState");
|
||||
D3D::SetDebugObjectName(m_reset_depth_state, "depth stencil state for Renderer::ResetAPIState");
|
||||
|
||||
D3D11_RASTERIZER_DESC rastdesc = CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID, D3D11_CULL_NONE, false,
|
||||
0, 0.f, 0.f, false, false, false, false);
|
||||
hr = D3D::device->CreateRasterizerState(&rastdesc, &s_reset_rast_state);
|
||||
hr = D3D::device->CreateRasterizerState(&rastdesc, &m_reset_rast_state);
|
||||
CHECK(hr == S_OK, "Create rasterizer state for Renderer::ResetAPIState");
|
||||
D3D::SetDebugObjectName(s_reset_rast_state, "rasterizer state for Renderer::ResetAPIState");
|
||||
D3D::SetDebugObjectName(m_reset_rast_state, "rasterizer state for Renderer::ResetAPIState");
|
||||
|
||||
s_screenshot_texture = nullptr;
|
||||
m_screenshot_texture = nullptr;
|
||||
}
|
||||
|
||||
// Kill off all device objects
|
||||
static void TeardownDeviceObjects()
|
||||
void Renderer::TeardownDeviceObjects()
|
||||
{
|
||||
g_framebuffer_manager.reset();
|
||||
|
||||
SAFE_RELEASE(s_clear_blend_states[0]);
|
||||
SAFE_RELEASE(s_clear_blend_states[1]);
|
||||
SAFE_RELEASE(s_clear_blend_states[2]);
|
||||
SAFE_RELEASE(s_clear_blend_states[3]);
|
||||
SAFE_RELEASE(s_clear_depth_states[0]);
|
||||
SAFE_RELEASE(s_clear_depth_states[1]);
|
||||
SAFE_RELEASE(s_clear_depth_states[2]);
|
||||
SAFE_RELEASE(s_reset_blend_state);
|
||||
SAFE_RELEASE(s_reset_depth_state);
|
||||
SAFE_RELEASE(s_reset_rast_state);
|
||||
SAFE_RELEASE(s_screenshot_texture);
|
||||
SAFE_RELEASE(s_3d_vision_texture);
|
||||
|
||||
s_gx_state_cache.Clear();
|
||||
SAFE_RELEASE(m_clear_blend_states[0]);
|
||||
SAFE_RELEASE(m_clear_blend_states[1]);
|
||||
SAFE_RELEASE(m_clear_blend_states[2]);
|
||||
SAFE_RELEASE(m_clear_blend_states[3]);
|
||||
SAFE_RELEASE(m_clear_depth_states[0]);
|
||||
SAFE_RELEASE(m_clear_depth_states[1]);
|
||||
SAFE_RELEASE(m_clear_depth_states[2]);
|
||||
SAFE_RELEASE(m_reset_blend_state);
|
||||
SAFE_RELEASE(m_reset_depth_state);
|
||||
SAFE_RELEASE(m_reset_rast_state);
|
||||
SAFE_RELEASE(m_screenshot_texture);
|
||||
SAFE_RELEASE(m_3d_vision_texture);
|
||||
}
|
||||
|
||||
static void Create3DVisionTexture(int width, int height)
|
||||
void Renderer::Create3DVisionTexture(int width, int height)
|
||||
{
|
||||
// Create a staging texture for 3D vision with signature information in the last row.
|
||||
// Nvidia 3D Vision supports full SBS, so there is no loss in resolution during this process.
|
||||
|
@ -197,48 +206,20 @@ static void Create3DVisionTexture(int width, int height)
|
|||
sys_data.SysMemPitch = pitch;
|
||||
sys_data.pSysMem = memory.get();
|
||||
|
||||
s_3d_vision_texture =
|
||||
m_3d_vision_texture =
|
||||
D3DTexture2D::Create(width * 2, height + 1, D3D11_BIND_RENDER_TARGET, D3D11_USAGE_DEFAULT,
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1, &sys_data);
|
||||
}
|
||||
|
||||
Renderer::Renderer() : ::Renderer(D3D::GetBackBufferWidth(), D3D::GetBackBufferHeight())
|
||||
std::unique_ptr<AbstractTexture> Renderer::CreateTexture(const TextureConfig& config)
|
||||
{
|
||||
s_last_multisamples = g_ActiveConfig.iMultisamples;
|
||||
s_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off;
|
||||
s_last_fullscreen_mode = D3D::GetFullscreenState();
|
||||
|
||||
g_framebuffer_manager = std::make_unique<FramebufferManager>(m_target_width, m_target_height);
|
||||
SetupDeviceObjects();
|
||||
|
||||
// Setup GX pipeline state
|
||||
for (auto& sampler : s_gx_state.samplers)
|
||||
sampler.hex = RenderState::GetPointSamplerState().hex;
|
||||
|
||||
s_gx_state.zmode.testenable = false;
|
||||
s_gx_state.zmode.updateenable = false;
|
||||
s_gx_state.zmode.func = ZMode::NEVER;
|
||||
s_gx_state.raster.cullmode = GenMode::CULL_NONE;
|
||||
|
||||
// Clear EFB textures
|
||||
constexpr std::array<float, 4> clear_color{{0.f, 0.f, 0.f, 1.f}};
|
||||
D3D::context->ClearRenderTargetView(FramebufferManager::GetEFBColorTexture()->GetRTV(),
|
||||
clear_color.data());
|
||||
D3D::context->ClearDepthStencilView(FramebufferManager::GetEFBDepthTexture()->GetDSV(),
|
||||
D3D11_CLEAR_DEPTH, 0.f, 0);
|
||||
|
||||
D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)m_target_width, (float)m_target_height);
|
||||
D3D::context->RSSetViewports(1, &vp);
|
||||
FramebufferManager::BindEFBRenderTarget();
|
||||
D3D::BeginFrame();
|
||||
return std::make_unique<DXTexture>(config);
|
||||
}
|
||||
|
||||
Renderer::~Renderer()
|
||||
std::unique_ptr<AbstractStagingTexture> Renderer::CreateStagingTexture(StagingTextureType type,
|
||||
const TextureConfig& config)
|
||||
{
|
||||
TeardownDeviceObjects();
|
||||
D3D::EndFrame();
|
||||
D3D::Present();
|
||||
D3D::Close();
|
||||
return DXStagingTexture::Create(type, config);
|
||||
}
|
||||
|
||||
void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
|
||||
|
@ -443,8 +424,8 @@ void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num
|
|||
}
|
||||
else // if (type == EFBAccessType::PokeZ)
|
||||
{
|
||||
D3D::stateman->PushBlendState(s_clear_blend_states[3]);
|
||||
D3D::stateman->PushDepthState(s_clear_depth_states[1]);
|
||||
D3D::stateman->PushBlendState(m_clear_blend_states[3]);
|
||||
D3D::stateman->PushDepthState(m_clear_depth_states[1]);
|
||||
|
||||
D3D11_VIEWPORT vp =
|
||||
CD3D11_VIEWPORT(0.0f, 0.0f, (float)GetTargetWidth(), (float)GetTargetHeight());
|
||||
|
@ -526,21 +507,21 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaE
|
|||
ResetAPIState();
|
||||
|
||||
if (colorEnable && alphaEnable)
|
||||
D3D::stateman->PushBlendState(s_clear_blend_states[0]);
|
||||
D3D::stateman->PushBlendState(m_clear_blend_states[0]);
|
||||
else if (colorEnable)
|
||||
D3D::stateman->PushBlendState(s_clear_blend_states[1]);
|
||||
D3D::stateman->PushBlendState(m_clear_blend_states[1]);
|
||||
else if (alphaEnable)
|
||||
D3D::stateman->PushBlendState(s_clear_blend_states[2]);
|
||||
D3D::stateman->PushBlendState(m_clear_blend_states[2]);
|
||||
else
|
||||
D3D::stateman->PushBlendState(s_clear_blend_states[3]);
|
||||
D3D::stateman->PushBlendState(m_clear_blend_states[3]);
|
||||
|
||||
// TODO: Should we enable Z testing here?
|
||||
// if (!bpmem.zmode.testenable) D3D::stateman->PushDepthState(s_clear_depth_states[0]);
|
||||
// else
|
||||
if (zEnable)
|
||||
D3D::stateman->PushDepthState(s_clear_depth_states[1]);
|
||||
D3D::stateman->PushDepthState(m_clear_depth_states[1]);
|
||||
else /*if (!zEnable)*/
|
||||
D3D::stateman->PushDepthState(s_clear_depth_states[2]);
|
||||
D3D::stateman->PushDepthState(m_clear_depth_states[2]);
|
||||
|
||||
// Update the view port for clearing the picture
|
||||
TargetRectangle targetRc = Renderer::ConvertEFBRectangle(rc);
|
||||
|
@ -600,7 +581,7 @@ void Renderer::ReinterpretPixelData(unsigned int convtype)
|
|||
|
||||
void Renderer::SetBlendingState(const BlendingState& state)
|
||||
{
|
||||
s_gx_state.blend.hex = state.hex;
|
||||
m_gx_state.blend.hex = state.hex;
|
||||
}
|
||||
|
||||
// This function has the final picture. We adjust the aspect ratio here.
|
||||
|
@ -633,7 +614,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
Renderer::DrawDebugText();
|
||||
|
||||
OSD::DrawMessages();
|
||||
D3D::EndFrame();
|
||||
|
||||
g_texture_cache->Cleanup(frameCount);
|
||||
|
||||
|
@ -646,33 +626,33 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
|
||||
const bool window_resized = CheckForResize();
|
||||
const bool fullscreen = D3D::GetFullscreenState();
|
||||
const bool fs_changed = s_last_fullscreen_mode != fullscreen;
|
||||
const bool fs_changed = m_last_fullscreen_mode != fullscreen;
|
||||
|
||||
// Flip/present backbuffer to frontbuffer here
|
||||
D3D::Present();
|
||||
|
||||
// Resize the back buffers NOW to avoid flickering
|
||||
if (CalculateTargetSize() || window_resized || fs_changed ||
|
||||
s_last_multisamples != g_ActiveConfig.iMultisamples ||
|
||||
s_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off))
|
||||
m_last_multisamples != g_ActiveConfig.iMultisamples ||
|
||||
m_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off))
|
||||
{
|
||||
s_last_multisamples = g_ActiveConfig.iMultisamples;
|
||||
s_last_fullscreen_mode = fullscreen;
|
||||
m_last_multisamples = g_ActiveConfig.iMultisamples;
|
||||
m_last_fullscreen_mode = fullscreen;
|
||||
PixelShaderCache::InvalidateMSAAShaders();
|
||||
|
||||
if (window_resized || fs_changed)
|
||||
{
|
||||
// TODO: Aren't we still holding a reference to the back buffer right now?
|
||||
D3D::Reset();
|
||||
SAFE_RELEASE(s_screenshot_texture);
|
||||
SAFE_RELEASE(s_3d_vision_texture);
|
||||
SAFE_RELEASE(m_screenshot_texture);
|
||||
SAFE_RELEASE(m_3d_vision_texture);
|
||||
m_backbuffer_width = D3D::GetBackBufferWidth();
|
||||
m_backbuffer_height = D3D::GetBackBufferHeight();
|
||||
}
|
||||
|
||||
UpdateDrawRectangle();
|
||||
|
||||
s_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off;
|
||||
m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off;
|
||||
|
||||
D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), nullptr);
|
||||
|
||||
|
@ -694,7 +674,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
|
||||
// begin next frame
|
||||
RestoreAPIState();
|
||||
D3D::BeginFrame();
|
||||
FramebufferManager::BindEFBRenderTarget();
|
||||
SetViewport();
|
||||
}
|
||||
|
@ -702,9 +681,9 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
// ALWAYS call RestoreAPIState for each ResetAPIState call you're doing
|
||||
void Renderer::ResetAPIState()
|
||||
{
|
||||
D3D::stateman->PushBlendState(s_reset_blend_state);
|
||||
D3D::stateman->PushDepthState(s_reset_depth_state);
|
||||
D3D::stateman->PushRasterizerState(s_reset_rast_state);
|
||||
D3D::stateman->PushBlendState(m_reset_blend_state);
|
||||
D3D::stateman->PushDepthState(m_reset_depth_state);
|
||||
D3D::stateman->PushRasterizerState(m_reset_rast_state);
|
||||
}
|
||||
|
||||
void Renderer::RestoreAPIState()
|
||||
|
@ -719,15 +698,15 @@ void Renderer::RestoreAPIState()
|
|||
|
||||
void Renderer::ApplyState()
|
||||
{
|
||||
D3D::stateman->PushBlendState(s_gx_state_cache.Get(s_gx_state.blend));
|
||||
D3D::stateman->PushDepthState(s_gx_state_cache.Get(s_gx_state.zmode));
|
||||
D3D::stateman->PushRasterizerState(s_gx_state_cache.Get(s_gx_state.raster));
|
||||
D3D::stateman->PushBlendState(m_state_cache.Get(m_gx_state.blend));
|
||||
D3D::stateman->PushDepthState(m_state_cache.Get(m_gx_state.zmode));
|
||||
D3D::stateman->PushRasterizerState(m_state_cache.Get(m_gx_state.raster));
|
||||
D3D::stateman->SetPrimitiveTopology(
|
||||
StateCache::GetPrimitiveTopology(s_gx_state.raster.primitive));
|
||||
FramebufferManager::SetIntegerEFBRenderTarget(s_gx_state.blend.logicopenable);
|
||||
StateCache::GetPrimitiveTopology(m_gx_state.raster.primitive));
|
||||
FramebufferManager::SetIntegerEFBRenderTarget(m_gx_state.blend.logicopenable);
|
||||
|
||||
for (u32 stage = 0; stage < static_cast<u32>(s_gx_state.samplers.size()); stage++)
|
||||
D3D::stateman->SetSampler(stage, s_gx_state_cache.Get(s_gx_state.samplers[stage]));
|
||||
for (u32 stage = 0; stage < static_cast<u32>(m_gx_state.samplers.size()); stage++)
|
||||
D3D::stateman->SetSampler(stage, m_state_cache.Get(m_gx_state.samplers[stage]));
|
||||
|
||||
ID3D11Buffer* vertexConstants = VertexShaderCache::GetConstantBuffer();
|
||||
|
||||
|
@ -746,17 +725,17 @@ void Renderer::RestoreState()
|
|||
|
||||
void Renderer::SetRasterizationState(const RasterizationState& state)
|
||||
{
|
||||
s_gx_state.raster.hex = state.hex;
|
||||
m_gx_state.raster.hex = state.hex;
|
||||
}
|
||||
|
||||
void Renderer::SetDepthState(const DepthState& state)
|
||||
{
|
||||
s_gx_state.zmode.hex = state.hex;
|
||||
m_gx_state.zmode.hex = state.hex;
|
||||
}
|
||||
|
||||
void Renderer::SetSamplerState(u32 index, const SamplerState& state)
|
||||
{
|
||||
s_gx_state.samplers[index].hex = state.hex;
|
||||
m_gx_state.samplers[index].hex = state.hex;
|
||||
}
|
||||
|
||||
void Renderer::SetInterlacingMode()
|
||||
|
@ -831,7 +810,7 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D
|
|||
}
|
||||
else if (g_ActiveConfig.stereo_mode == StereoMode::Nvidia3DVision)
|
||||
{
|
||||
if (!s_3d_vision_texture)
|
||||
if (!m_3d_vision_texture)
|
||||
Create3DVisionTexture(m_backbuffer_width, m_backbuffer_height);
|
||||
|
||||
D3D11_VIEWPORT leftVp = CD3D11_VIEWPORT((float)dst.left, (float)dst.top, (float)dst.GetWidth(),
|
||||
|
@ -840,7 +819,7 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D
|
|||
(float)dst.GetWidth(), (float)dst.GetHeight());
|
||||
|
||||
// Render to staging texture which is double the width of the backbuffer
|
||||
D3D::context->OMSetRenderTargets(1, &s_3d_vision_texture->GetRTV(), nullptr);
|
||||
D3D::context->OMSetRenderTargets(1, &m_3d_vision_texture->GetRTV(), nullptr);
|
||||
|
||||
D3D::context->RSSetViewports(1, &leftVp);
|
||||
D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height,
|
||||
|
@ -858,7 +837,7 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D
|
|||
// recognize the signature and automatically include the right eye frame.
|
||||
D3D11_BOX box = CD3D11_BOX(0, 0, 0, m_backbuffer_width, m_backbuffer_height, 1);
|
||||
D3D::context->CopySubresourceRegion(D3D::GetBackBuffer()->GetTex(), 0, 0, 0, 0,
|
||||
s_3d_vision_texture->GetTex(), 0, &box);
|
||||
m_3d_vision_texture->GetTex(), 0, &box);
|
||||
|
||||
// Restore render target to backbuffer
|
||||
D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), nullptr);
|
||||
|
|
|
@ -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 @@ public:
|
|||
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 @@ public:
|
|||
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 @@ public:
|
|||
~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 @@ public:
|
|||
|
||||
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 @@ public:
|
|||
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 @@ public:
|
|||
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
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include "VideoBackends/OGL/FramebufferManager.h"
|
||||
#include "VideoBackends/OGL/OGLTexture.h"
|
||||
#include "VideoBackends/OGL/Render.h"
|
||||
#include "VideoBackends/OGL/SamplerCache.h"
|
||||
#include "VideoBackends/OGL/TextureCache.h"
|
||||
|
||||
|
@ -36,7 +35,11 @@ GLenum GetGLInternalFormatForTextureFormat(AbstractTextureFormat format, bool st
|
|||
case AbstractTextureFormat::BPTC:
|
||||
return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
|
||||
case AbstractTextureFormat::RGBA8:
|
||||
return storage ? GL_RGBA8 : GL_RGBA;
|
||||
case AbstractTextureFormat::BGRA8:
|
||||
return storage ? GL_RGBA8 : GL_BGRA;
|
||||
default:
|
||||
PanicAlert("Unhandled texture format.");
|
||||
return storage ? GL_RGBA8 : GL_RGBA;
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +50,8 @@ GLenum GetGLFormatForTextureFormat(AbstractTextureFormat format)
|
|||
{
|
||||
case AbstractTextureFormat::RGBA8:
|
||||
return GL_RGBA;
|
||||
case AbstractTextureFormat::BGRA8:
|
||||
return GL_BGRA;
|
||||
// Compressed texture formats don't use this parameter.
|
||||
default:
|
||||
return GL_UNSIGNED_BYTE;
|
||||
|
@ -58,12 +63,22 @@ GLenum GetGLTypeForTextureFormat(AbstractTextureFormat format)
|
|||
switch (format)
|
||||
{
|
||||
case AbstractTextureFormat::RGBA8:
|
||||
case AbstractTextureFormat::BGRA8:
|
||||
return GL_UNSIGNED_BYTE;
|
||||
// Compressed texture formats don't use this parameter.
|
||||
default:
|
||||
return GL_UNSIGNED_BYTE;
|
||||
}
|
||||
}
|
||||
|
||||
bool UsePersistentStagingBuffers()
|
||||
{
|
||||
// We require ARB_buffer_storage to create the persistent mapped buffer,
|
||||
// ARB_shader_image_load_store for glMemoryBarrier, and ARB_sync to ensure
|
||||
// the GPU has finished the copy before reading the buffer from the CPU.
|
||||
return g_ogl_config.bSupportsGLBufferStorage && g_ogl_config.bSupportsImageLoadStore &&
|
||||
g_ogl_config.bSupportsGLSync;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_config)
|
||||
|
@ -85,7 +100,7 @@ OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_co
|
|||
if (m_config.rendertarget)
|
||||
{
|
||||
// We can't render to compressed formats.
|
||||
_assert_(!IsCompressedHostTextureFormat(m_config.format));
|
||||
_assert_(!IsCompressedFormat(m_config.format));
|
||||
|
||||
if (!g_ogl_config.bSupportsTextureStorage)
|
||||
{
|
||||
|
@ -100,6 +115,10 @@ OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_co
|
|||
FramebufferManager::SetFramebuffer(m_framebuffer);
|
||||
FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D_ARRAY, m_texId, 0);
|
||||
|
||||
// We broke the framebuffer binding here, and need to restore it, as the CreateTexture
|
||||
// method is in the base renderer class and can be called by VideoCommon.
|
||||
FramebufferManager::SetFramebuffer(0);
|
||||
}
|
||||
|
||||
SetStage();
|
||||
|
@ -148,89 +167,70 @@ void OGLTexture::Bind(unsigned int stage)
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<AbstractTexture::RawTextureInfo> OGLTexture::MapFullImpl()
|
||||
void OGLTexture::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)
|
||||
{
|
||||
if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL)
|
||||
return {};
|
||||
|
||||
m_staging_data.reserve(m_config.width * m_config.height * 4);
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId);
|
||||
glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_staging_data.data());
|
||||
OGLTexture::SetStage();
|
||||
return AbstractTexture::RawTextureInfo{reinterpret_cast<u8*>(m_staging_data.data()),
|
||||
m_config.width * 4, m_config.width, m_config.height};
|
||||
}
|
||||
|
||||
std::optional<AbstractTexture::RawTextureInfo> OGLTexture::MapRegionImpl(u32 level, u32 x, u32 y,
|
||||
u32 width, u32 height)
|
||||
{
|
||||
if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL)
|
||||
return {};
|
||||
m_staging_data.reserve(m_config.width * m_config.height * 4);
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId);
|
||||
if (g_ogl_config.bSupportTextureSubImage)
|
||||
const OGLTexture* srcentry = static_cast<const OGLTexture*>(src);
|
||||
_assert_(src_rect.GetWidth() == dst_rect.GetWidth() &&
|
||||
src_rect.GetHeight() == dst_rect.GetHeight());
|
||||
if (g_ogl_config.bSupportsCopySubImage)
|
||||
{
|
||||
glGetTextureSubImage(GL_TEXTURE_2D_ARRAY, level, GLint(x), GLint(y), 0, GLsizei(width),
|
||||
GLsizei(height), 0, GL_RGBA, GL_UNSIGNED_BYTE, GLsizei(width * height * 4),
|
||||
m_staging_data.data());
|
||||
glCopyImageSubData(srcentry->m_texId, GL_TEXTURE_2D_ARRAY, src_level, src_rect.left,
|
||||
src_rect.top, src_layer, m_texId, GL_TEXTURE_2D_ARRAY, dst_level,
|
||||
dst_rect.left, dst_rect.top, dst_layer, dst_rect.GetWidth(),
|
||||
dst_rect.GetHeight(), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
MapRegionSlow(level, x, y, width, height);
|
||||
// If it isn't a single leveled/layered texture, we need to update the framebuffer.
|
||||
bool update_src_framebuffer =
|
||||
srcentry->m_framebuffer == 0 || srcentry->m_config.layers != 0 || src_level != 0;
|
||||
bool update_dst_framebuffer = m_framebuffer == 0 || m_config.layers != 0 || dst_level != 0;
|
||||
if (!m_framebuffer)
|
||||
glGenFramebuffers(1, &m_framebuffer);
|
||||
if (!srcentry->m_framebuffer)
|
||||
glGenFramebuffers(1, &const_cast<OGLTexture*>(srcentry)->m_framebuffer);
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcentry->m_framebuffer);
|
||||
if (update_src_framebuffer)
|
||||
{
|
||||
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, srcentry->m_texId,
|
||||
src_level, src_layer);
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_framebuffer);
|
||||
if (update_dst_framebuffer)
|
||||
{
|
||||
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texId, dst_level,
|
||||
dst_layer);
|
||||
}
|
||||
|
||||
glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left,
|
||||
dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT,
|
||||
GL_NEAREST);
|
||||
|
||||
if (update_src_framebuffer)
|
||||
{
|
||||
FramebufferManager::FramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D_ARRAY, srcentry->m_texId, 0);
|
||||
}
|
||||
if (update_dst_framebuffer)
|
||||
{
|
||||
FramebufferManager::FramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D_ARRAY, m_texId, 0);
|
||||
}
|
||||
|
||||
FramebufferManager::SetFramebuffer(0);
|
||||
}
|
||||
OGLTexture::SetStage();
|
||||
return AbstractTexture::RawTextureInfo{m_staging_data.data(), width * 4, width, height};
|
||||
}
|
||||
|
||||
void OGLTexture::MapRegionSlow(u32 level, u32 x, u32 y, u32 width, u32 height)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId);
|
||||
glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_staging_data.data());
|
||||
|
||||
// Now copy the region out of the staging data
|
||||
|
||||
const u32 partial_stride = width * 4;
|
||||
|
||||
std::vector<u8> partial_data;
|
||||
partial_data.resize(partial_stride * height);
|
||||
|
||||
const u32 staging_stride = m_config.width * 4;
|
||||
const u32 x_offset = x * 4;
|
||||
|
||||
auto staging_location = m_staging_data.begin() + staging_stride * y;
|
||||
auto partial_location = partial_data.begin();
|
||||
|
||||
for (size_t i = 0; i < height; ++i)
|
||||
{
|
||||
auto starting_location = staging_location + x_offset;
|
||||
std::copy(starting_location, starting_location + partial_stride, partial_location);
|
||||
staging_location += staging_stride;
|
||||
partial_location += partial_stride;
|
||||
}
|
||||
|
||||
// Now swap the region back in for the staging data
|
||||
m_staging_data.swap(partial_data);
|
||||
}
|
||||
|
||||
void OGLTexture::CopyRectangleFromTexture(const AbstractTexture* source,
|
||||
const MathUtil::Rectangle<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect)
|
||||
void OGLTexture::ScaleRectangleFromTexture(const AbstractTexture* source,
|
||||
const MathUtil::Rectangle<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect)
|
||||
{
|
||||
const OGLTexture* srcentry = static_cast<const OGLTexture*>(source);
|
||||
if (srcrect.GetWidth() == dstrect.GetWidth() && srcrect.GetHeight() == dstrect.GetHeight() &&
|
||||
g_ogl_config.bSupportsCopySubImage)
|
||||
{
|
||||
glCopyImageSubData(srcentry->m_texId, GL_TEXTURE_2D_ARRAY, 0, srcrect.left, srcrect.top, 0,
|
||||
m_texId, GL_TEXTURE_2D_ARRAY, 0, dstrect.left, dstrect.top, 0,
|
||||
dstrect.GetWidth(), dstrect.GetHeight(), srcentry->m_config.layers);
|
||||
return;
|
||||
}
|
||||
else if (!m_framebuffer)
|
||||
if (!m_framebuffer)
|
||||
{
|
||||
glGenFramebuffers(1, &m_framebuffer);
|
||||
FramebufferManager::SetFramebuffer(m_framebuffer);
|
||||
|
@ -269,7 +269,7 @@ void OGLTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8
|
|||
glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length);
|
||||
|
||||
GLenum gl_internal_format = GetGLInternalFormatForTextureFormat(m_config.format, false);
|
||||
if (IsCompressedHostTextureFormat(m_config.format))
|
||||
if (IsCompressedFormat(m_config.format))
|
||||
{
|
||||
if (g_ogl_config.bSupportsTextureStorage)
|
||||
{
|
||||
|
@ -315,4 +315,262 @@ void OGLTexture::SetStage()
|
|||
glActiveTexture(GL_TEXTURE0 + s_ActiveTexture);
|
||||
}
|
||||
|
||||
OGLStagingTexture::OGLStagingTexture(StagingTextureType type, const TextureConfig& config,
|
||||
GLenum target, GLuint buffer_name, size_t buffer_size,
|
||||
char* map_ptr, size_t map_stride)
|
||||
: AbstractStagingTexture(type, config), m_target(target), m_buffer_name(buffer_name),
|
||||
m_buffer_size(buffer_size)
|
||||
{
|
||||
m_map_pointer = map_ptr;
|
||||
m_map_stride = map_stride;
|
||||
}
|
||||
|
||||
OGLStagingTexture::~OGLStagingTexture()
|
||||
{
|
||||
if (m_fence != 0)
|
||||
glDeleteSync(m_fence);
|
||||
if (m_map_pointer)
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_buffer_name);
|
||||
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
}
|
||||
if (m_buffer_name != 0)
|
||||
glDeleteBuffers(1, &m_buffer_name);
|
||||
}
|
||||
|
||||
std::unique_ptr<OGLStagingTexture> OGLStagingTexture::Create(StagingTextureType type,
|
||||
const TextureConfig& config)
|
||||
{
|
||||
size_t stride = config.GetStride();
|
||||
size_t buffer_size = stride * config.height;
|
||||
GLenum target =
|
||||
type == StagingTextureType::Readback ? GL_PIXEL_PACK_BUFFER : GL_PIXEL_UNPACK_BUFFER;
|
||||
GLuint buffer;
|
||||
glGenBuffers(1, &buffer);
|
||||
glBindBuffer(target, buffer);
|
||||
|
||||
// Prefer using buffer_storage where possible. This allows us to skip the map/unmap steps.
|
||||
char* buffer_ptr;
|
||||
if (UsePersistentStagingBuffers())
|
||||
{
|
||||
GLenum buffer_flags;
|
||||
GLenum map_flags;
|
||||
if (type == StagingTextureType::Readback)
|
||||
{
|
||||
buffer_flags = GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT;
|
||||
map_flags = GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT;
|
||||
}
|
||||
else if (type == StagingTextureType::Upload)
|
||||
{
|
||||
buffer_flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT;
|
||||
map_flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer_flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT;
|
||||
map_flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT;
|
||||
}
|
||||
|
||||
glBufferStorage(target, buffer_size, nullptr, buffer_flags);
|
||||
buffer_ptr =
|
||||
reinterpret_cast<char*>(glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, buffer_size, map_flags));
|
||||
_assert_(buffer_ptr != nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, fallback to mapping the buffer each time.
|
||||
glBufferData(target, buffer_size, nullptr,
|
||||
type == StagingTextureType::Readback ? GL_STREAM_READ : GL_STREAM_DRAW);
|
||||
buffer_ptr = nullptr;
|
||||
}
|
||||
glBindBuffer(target, 0);
|
||||
|
||||
return std::unique_ptr<OGLStagingTexture>(
|
||||
new OGLStagingTexture(type, config, target, buffer, buffer_size, buffer_ptr, stride));
|
||||
}
|
||||
|
||||
void OGLStagingTexture::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);
|
||||
|
||||
// Unmap the buffer before writing when not using persistent mappings.
|
||||
if (!UsePersistentStagingBuffers())
|
||||
OGLStagingTexture::Unmap();
|
||||
|
||||
// Copy from the texture object to the staging buffer.
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_buffer_name);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, m_config.width);
|
||||
|
||||
const OGLTexture* gltex = static_cast<const OGLTexture*>(src);
|
||||
size_t dst_offset = dst_rect.top * m_config.GetStride() + dst_rect.left * m_texel_size;
|
||||
|
||||
// If we don't have a FBO associated with this texture, we need to use a slow path.
|
||||
if (gltex->GetFramebuffer() != 0 && src_layer == 0 && src_level == 0)
|
||||
{
|
||||
// This texture has a framebuffer, so we can use glReadPixels().
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, gltex->GetFramebuffer());
|
||||
glReadPixels(src_rect.left, src_rect.top, src_rect.GetWidth(), src_rect.GetHeight(),
|
||||
GetGLFormatForTextureFormat(m_config.format),
|
||||
GetGLTypeForTextureFormat(m_config.format), reinterpret_cast<void*>(dst_offset));
|
||||
|
||||
// Reset both read/draw framebuffers.
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferManager::GetEFBFramebuffer());
|
||||
}
|
||||
else
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, gltex->GetRawTexIdentifier());
|
||||
if (g_ogl_config.bSupportsTextureSubImage)
|
||||
{
|
||||
glGetTextureSubImage(
|
||||
GL_TEXTURE_2D_ARRAY, src_level, src_rect.left, src_rect.top, src_layer,
|
||||
src_rect.GetWidth(), src_rect.GetHeight(), 1,
|
||||
GetGLFormatForTextureFormat(m_config.format), GetGLTypeForTextureFormat(m_config.format),
|
||||
static_cast<GLsizei>(m_buffer_size - dst_offset), reinterpret_cast<void*>(dst_offset));
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Investigate whether it's faster to use glReadPixels() with a framebuffer, since we're
|
||||
// copying the whole texture, which may waste bandwidth. So we're trading CPU work in creating
|
||||
// the framebuffer for GPU work in copying potentially redundant texels.
|
||||
glGetTexImage(GL_TEXTURE_2D_ARRAY, src_level, GetGLFormatForTextureFormat(m_config.format),
|
||||
GetGLTypeForTextureFormat(m_config.format), nullptr);
|
||||
}
|
||||
|
||||
OGLTexture::SetStage();
|
||||
}
|
||||
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
|
||||
// If we support buffer storage, create a fence for synchronization.
|
||||
if (UsePersistentStagingBuffers())
|
||||
{
|
||||
if (m_fence != 0)
|
||||
glDeleteSync(m_fence);
|
||||
|
||||
glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
|
||||
m_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
}
|
||||
|
||||
m_needs_flush = true;
|
||||
}
|
||||
|
||||
void OGLStagingTexture::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);
|
||||
|
||||
size_t src_offset = src_rect.top * m_config.GetStride() + src_rect.left * m_texel_size;
|
||||
size_t copy_size = src_rect.GetHeight() * m_config.GetStride();
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_buffer_name);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, m_config.width);
|
||||
|
||||
if (!UsePersistentStagingBuffers())
|
||||
{
|
||||
// Unmap the buffer before writing when not using persistent mappings.
|
||||
if (m_map_pointer)
|
||||
{
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
m_map_pointer = nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Since we're not using coherent mapping, we must flush the range explicitly.
|
||||
if (m_type == StagingTextureType::Upload)
|
||||
glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, src_offset, copy_size);
|
||||
glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
|
||||
}
|
||||
|
||||
// Copy from the staging buffer to the texture object.
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, static_cast<const OGLTexture*>(dst)->GetRawTexIdentifier());
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, dst_rect.left, dst_rect.top, dst_layer,
|
||||
dst_rect.GetWidth(), dst_rect.GetHeight(), 1,
|
||||
GetGLFormatForTextureFormat(m_config.format),
|
||||
GetGLTypeForTextureFormat(m_config.format), reinterpret_cast<void*>(src_offset));
|
||||
OGLTexture::SetStage();
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
// If we support buffer storage, create a fence for synchronization.
|
||||
if (UsePersistentStagingBuffers())
|
||||
{
|
||||
if (m_fence != 0)
|
||||
glDeleteSync(m_fence);
|
||||
|
||||
m_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
}
|
||||
|
||||
m_needs_flush = true;
|
||||
}
|
||||
|
||||
void OGLStagingTexture::Flush()
|
||||
{
|
||||
// No-op when not using buffer storage, as the transfers happen on Map().
|
||||
// m_fence will always be zero in this case.
|
||||
if (m_fence == 0)
|
||||
{
|
||||
m_needs_flush = false;
|
||||
return;
|
||||
}
|
||||
|
||||
glClientWaitSync(m_fence, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync(m_fence);
|
||||
m_fence = 0;
|
||||
m_needs_flush = false;
|
||||
}
|
||||
|
||||
bool OGLStagingTexture::Map()
|
||||
{
|
||||
if (m_map_pointer)
|
||||
return true;
|
||||
|
||||
// Slow path, map the texture, unmap it later.
|
||||
GLenum flags;
|
||||
if (m_type == StagingTextureType::Readback)
|
||||
flags = GL_MAP_READ_BIT;
|
||||
else if (m_type == StagingTextureType::Upload)
|
||||
flags = GL_MAP_WRITE_BIT;
|
||||
else
|
||||
flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT;
|
||||
glBindBuffer(m_target, m_buffer_name);
|
||||
m_map_pointer = reinterpret_cast<char*>(glMapBufferRange(m_target, 0, m_buffer_size, flags));
|
||||
if (!m_map_pointer)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OGLStagingTexture::Unmap()
|
||||
{
|
||||
// No-op with persistent mapped buffers.
|
||||
if (!m_map_pointer || UsePersistentStagingBuffers())
|
||||
return;
|
||||
|
||||
glBindBuffer(m_target, m_buffer_name);
|
||||
glUnmapBuffer(m_target);
|
||||
glBindBuffer(m_target, 0);
|
||||
m_map_pointer = nullptr;
|
||||
}
|
||||
|
||||
} // namespace OGL
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "Common/GL/GLUtil.h"
|
||||
|
||||
#include "VideoCommon/AbstractStagingTexture.h"
|
||||
#include "VideoCommon/AbstractTexture.h"
|
||||
|
||||
namespace OGL
|
||||
|
@ -20,9 +21,13 @@ public:
|
|||
|
||||
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 @@ public:
|
|||
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 @@ public:
|
|||
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 @@ private:
|
|||
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 @@ private:
|
|||
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,42 +84,21 @@ 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
|
||||
|
@ -130,9 +106,8 @@ void Shutdown()
|
|||
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;
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
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 @@ public:
|
|||
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)
|
||||
{
|
||||
return AbstractTexture::RawTextureInfo{GetData(), m_config.width * 4, m_config.width,
|
||||
m_config.height};
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
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,9 +21,13 @@ public:
|
|||
|
||||
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;
|
||||
|
||||
|
@ -30,8 +35,27 @@ public:
|
|||
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 @@ public:
|
|||
}
|
||||
|
||||
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 @@ private:
|
|||
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 @@ public:
|
|||
|
||||
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 @@ public:
|
|||
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;
|
||||
|
|
|
@ -1,164 +0,0 @@
|
|||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
|
||||
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
||||
#include "VideoBackends/Vulkan/StagingTexture2D.h"
|
||||
#include "VideoBackends/Vulkan/Util.h"
|
||||
#include "VideoBackends/Vulkan/VulkanContext.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
StagingTexture2D::StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory,
|
||||
VkDeviceSize size, bool coherent, u32 width, u32 height,
|
||||
VkFormat format, u32 stride)
|
||||
: StagingBuffer(type, buffer, memory, size, coherent), m_width(width), m_height(height),
|
||||
m_format(format), m_texel_size(Util::GetTexelSize(format)), m_row_stride(stride)
|
||||
{
|
||||
}
|
||||
|
||||
StagingTexture2D::~StagingTexture2D()
|
||||
{
|
||||
}
|
||||
|
||||
void StagingTexture2D::ReadTexel(u32 x, u32 y, void* data, size_t data_size) const
|
||||
{
|
||||
_assert_(data_size >= m_texel_size);
|
||||
|
||||
VkDeviceSize offset = y * m_row_stride + x * m_texel_size;
|
||||
VkDeviceSize map_offset = offset - m_map_offset;
|
||||
_assert_(offset >= m_map_offset && (map_offset + m_texel_size) <= (m_map_offset + m_map_size));
|
||||
|
||||
const char* ptr = m_map_pointer + map_offset;
|
||||
memcpy(data, ptr, data_size);
|
||||
}
|
||||
|
||||
void StagingTexture2D::WriteTexel(u32 x, u32 y, const void* data, size_t data_size)
|
||||
{
|
||||
_assert_(data_size >= m_texel_size);
|
||||
|
||||
VkDeviceSize offset = y * m_row_stride + x * m_texel_size;
|
||||
VkDeviceSize map_offset = offset - m_map_offset;
|
||||
_assert_(offset >= m_map_offset && (map_offset + m_texel_size) <= (m_map_offset + m_map_size));
|
||||
|
||||
char* ptr = m_map_pointer + map_offset;
|
||||
memcpy(ptr, data, data_size);
|
||||
}
|
||||
|
||||
void StagingTexture2D::ReadTexels(u32 x, u32 y, u32 width, u32 height, void* data,
|
||||
u32 data_stride) const
|
||||
{
|
||||
const char* src_ptr = GetRowPointer(y);
|
||||
|
||||
// Optimal path: same dimensions, same stride.
|
||||
_assert_((x + width) <= m_width && (y + height) <= m_height);
|
||||
if (x == 0 && width == m_width && m_row_stride == data_stride)
|
||||
{
|
||||
memcpy(data, src_ptr, m_row_stride * height);
|
||||
return;
|
||||
}
|
||||
|
||||
u32 copy_size = std::min(width * m_texel_size, data_stride);
|
||||
char* dst_ptr = reinterpret_cast<char*>(data);
|
||||
for (u32 row = 0; row < height; row++)
|
||||
{
|
||||
memcpy(dst_ptr, src_ptr + (x * m_texel_size), copy_size);
|
||||
src_ptr += m_row_stride;
|
||||
dst_ptr += data_stride;
|
||||
}
|
||||
}
|
||||
|
||||
void StagingTexture2D::WriteTexels(u32 x, u32 y, u32 width, u32 height, const void* data,
|
||||
u32 data_stride)
|
||||
{
|
||||
char* dst_ptr = GetRowPointer(y);
|
||||
|
||||
// Optimal path: same dimensions, same stride.
|
||||
_assert_((x + width) <= m_width && (y + height) <= m_height);
|
||||
if (x == 0 && width == m_width && m_row_stride == data_stride)
|
||||
{
|
||||
memcpy(dst_ptr, data, m_row_stride * height);
|
||||
return;
|
||||
}
|
||||
|
||||
u32 copy_size = std::min(width * m_texel_size, data_stride);
|
||||
const char* src_ptr = reinterpret_cast<const char*>(data);
|
||||
for (u32 row = 0; row < height; row++)
|
||||
{
|
||||
memcpy(dst_ptr + (x * m_texel_size), src_ptr, copy_size);
|
||||
dst_ptr += m_row_stride;
|
||||
src_ptr += data_stride;
|
||||
}
|
||||
}
|
||||
|
||||
void StagingTexture2D::CopyFromImage(VkCommandBuffer command_buffer, VkImage image,
|
||||
VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width,
|
||||
u32 height, u32 level, u32 layer)
|
||||
{
|
||||
// Issue the image->buffer copy.
|
||||
VkBufferImageCopy image_copy = {
|
||||
y * m_row_stride + x * m_texel_size, // VkDeviceSize bufferOffset
|
||||
m_width, // uint32_t bufferRowLength
|
||||
0, // uint32_t bufferImageHeight
|
||||
{src_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource
|
||||
{static_cast<s32>(x), static_cast<s32>(y), 0}, // VkOffset3D imageOffset
|
||||
{width, height, 1} // VkExtent3D imageExtent
|
||||
};
|
||||
vkCmdCopyImageToBuffer(command_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_buffer, 1,
|
||||
&image_copy);
|
||||
|
||||
// Flush CPU and GPU caches if not coherent mapping.
|
||||
VkDeviceSize buffer_flush_offset = y * m_row_stride;
|
||||
VkDeviceSize buffer_flush_size = height * m_row_stride;
|
||||
FlushGPUCache(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
buffer_flush_offset, buffer_flush_size);
|
||||
InvalidateCPUCache(buffer_flush_offset, buffer_flush_size);
|
||||
}
|
||||
|
||||
void StagingTexture2D::CopyToImage(VkCommandBuffer command_buffer, VkImage image,
|
||||
VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width,
|
||||
u32 height, u32 level, u32 layer)
|
||||
{
|
||||
// Flush CPU and GPU caches if not coherent mapping.
|
||||
VkDeviceSize buffer_flush_offset = y * m_row_stride;
|
||||
VkDeviceSize buffer_flush_size = height * m_row_stride;
|
||||
FlushCPUCache(buffer_flush_offset, buffer_flush_size);
|
||||
InvalidateGPUCache(command_buffer, VK_ACCESS_HOST_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
buffer_flush_offset, buffer_flush_size);
|
||||
|
||||
// Issue the buffer->image copy.
|
||||
VkBufferImageCopy image_copy = {
|
||||
y * m_row_stride + x * m_texel_size, // VkDeviceSize bufferOffset
|
||||
m_width, // uint32_t bufferRowLength
|
||||
0, // uint32_t bufferImageHeight
|
||||
{dst_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource
|
||||
{static_cast<s32>(x), static_cast<s32>(y), 0}, // VkOffset3D imageOffset
|
||||
{width, height, 1} // VkExtent3D imageExtent
|
||||
};
|
||||
vkCmdCopyBufferToImage(command_buffer, m_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
|
||||
&image_copy);
|
||||
}
|
||||
|
||||
std::unique_ptr<StagingTexture2D> StagingTexture2D::Create(STAGING_BUFFER_TYPE type, u32 width,
|
||||
u32 height, VkFormat format)
|
||||
{
|
||||
// Assume tight packing.
|
||||
u32 stride = Util::GetTexelSize(format) * width;
|
||||
u32 size = stride * height;
|
||||
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
|
||||
VkBuffer buffer;
|
||||
VkDeviceMemory memory;
|
||||
bool coherent;
|
||||
if (!AllocateBuffer(type, size, usage, &buffer, &memory, &coherent))
|
||||
return nullptr;
|
||||
|
||||
return std::make_unique<StagingTexture2D>(type, buffer, memory, size, coherent, width, height,
|
||||
format, stride);
|
||||
}
|
||||
} // namespace Vulkan
|
|
@ -1,58 +0,0 @@
|
|||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "VideoBackends/Vulkan/Constants.h"
|
||||
#include "VideoBackends/Vulkan/StagingBuffer.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class StagingTexture2D final : public StagingBuffer
|
||||
{
|
||||
public:
|
||||
StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory,
|
||||
VkDeviceSize size, bool coherent, u32 width, u32 height, VkFormat format,
|
||||
u32 stride);
|
||||
~StagingTexture2D();
|
||||
|
||||
u32 GetWidth() const { return m_width; }
|
||||
u32 GetHeight() const { return m_height; }
|
||||
VkFormat GetFormat() const { return m_format; }
|
||||
u32 GetRowStride() const { return m_row_stride; }
|
||||
u32 GetTexelSize() const { return m_texel_size; }
|
||||
// Requires Map() to be called first.
|
||||
const char* GetRowPointer(u32 row) const { return m_map_pointer + row * m_row_stride; }
|
||||
char* GetRowPointer(u32 row) { return m_map_pointer + row * m_row_stride; }
|
||||
void ReadTexel(u32 x, u32 y, void* data, size_t data_size) const;
|
||||
void WriteTexel(u32 x, u32 y, const void* data, size_t data_size);
|
||||
void ReadTexels(u32 x, u32 y, u32 width, u32 height, void* data, u32 data_stride) const;
|
||||
void WriteTexels(u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride);
|
||||
|
||||
// Assumes that image is in TRANSFER_SRC layout.
|
||||
// Results are not ready until command_buffer has been executed.
|
||||
void CopyFromImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags src_aspect,
|
||||
u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer);
|
||||
|
||||
// Assumes that image is in TRANSFER_DST layout.
|
||||
// Buffer is not safe for re-use until after command_buffer has been executed.
|
||||
void CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect,
|
||||
u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer);
|
||||
|
||||
// Creates the optimal format of image copy.
|
||||
static std::unique_ptr<StagingTexture2D> Create(STAGING_BUFFER_TYPE type, u32 width, u32 height,
|
||||
VkFormat format);
|
||||
|
||||
protected:
|
||||
u32 m_width;
|
||||
u32 m_height;
|
||||
VkFormat m_format;
|
||||
u32 m_texel_size;
|
||||
u32 m_row_stride;
|
||||
};
|
||||
}
|
|
@ -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 @@ public:
|
|||
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,7 +302,8 @@ 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(),
|
||||
|
@ -325,18 +311,11 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u
|
|||
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()};
|
||||
m_encoding_render_texture = g_renderer->CreateTexture(config);
|
||||
m_encoding_readback_texture =
|
||||
g_renderer->CreateStagingTexture(StagingTextureType::Readback, config);
|
||||
|
||||
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);
|
||||
|
||||
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 @@ public:
|
|||
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 @@ private:
|
|||
|
||||
bool CreateEncodingRenderPass();
|
||||
bool CreateEncodingTexture();
|
||||
bool CreateEncodingDownloadTexture();
|
||||
|
||||
bool CreateDecodingTexture();
|
||||
|
||||
bool CompileYUYVConversionShaders();
|
||||
|
@ -106,10 +107,9 @@ private:
|
|||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
||||
#include "VideoBackends/Vulkan/FramebufferManager.h"
|
||||
#include "VideoBackends/Vulkan/StagingTexture2D.h"
|
||||
#include "VideoBackends/Vulkan/StagingBuffer.h"
|
||||
#include "VideoBackends/Vulkan/StateTracker.h"
|
||||
#include "VideoBackends/Vulkan/Texture2D.h"
|
||||
#include "VideoBackends/Vulkan/Util.h"
|
||||
|
@ -113,60 +113,13 @@ void VKTexture::Bind(unsigned int stage)
|
|||
StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView());
|
||||
}
|
||||
|
||||
std::optional<AbstractTexture::RawTextureInfo> VKTexture::MapFullImpl()
|
||||
void VKTexture::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)
|
||||
{
|
||||
// No support for optimization of full copy
|
||||
return MapRegionImpl(0, 0, 0, m_config.width, m_config.height);
|
||||
}
|
||||
Texture2D* src_texture = static_cast<const VKTexture*>(src)->GetRawTexIdentifier();
|
||||
|
||||
std::optional<AbstractTexture::RawTextureInfo> VKTexture::MapRegionImpl(u32 level, u32 x, u32 y,
|
||||
u32 width, u32 height)
|
||||
{
|
||||
m_staging_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, width, height,
|
||||
TEXTURECACHE_TEXTURE_FORMAT);
|
||||
|
||||
// Transition image to transfer source, and invalidate the current state,
|
||||
// since we'll be executing the command buffer.
|
||||
m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
StateTracker::GetInstance()->EndRenderPass();
|
||||
|
||||
// Copy to download buffer.
|
||||
m_staging_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
m_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, x, y, width,
|
||||
height, level, 0);
|
||||
|
||||
// Restore original state of texture.
|
||||
m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
// Block until the GPU has finished copying to the staging texture.
|
||||
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
|
||||
|
||||
// Map the staging texture so we can copy the contents out.
|
||||
if (!m_staging_texture->Map())
|
||||
{
|
||||
PanicAlert("Failed to map staging texture");
|
||||
return {};
|
||||
}
|
||||
|
||||
return AbstractTexture::RawTextureInfo{reinterpret_cast<u8*>(m_staging_texture->GetMapPointer()),
|
||||
static_cast<u32>(m_staging_texture->GetRowStride()), width,
|
||||
height};
|
||||
}
|
||||
|
||||
void VKTexture::Unmap()
|
||||
{
|
||||
if (!m_staging_texture)
|
||||
return;
|
||||
|
||||
m_staging_texture->Unmap();
|
||||
}
|
||||
|
||||
void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
|
||||
Texture2D* src_texture,
|
||||
const MathUtil::Rectangle<int>& src_rect)
|
||||
{
|
||||
_assert_msg_(VIDEO, static_cast<u32>(src_rect.GetWidth()) <= src_texture->GetWidth() &&
|
||||
static_cast<u32>(src_rect.GetHeight()) <= src_texture->GetHeight(),
|
||||
"Source rect is too large for CopyRectangleFromTexture");
|
||||
|
@ -176,15 +129,11 @@ void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
|
|||
"Dest rect is too large for CopyRectangleFromTexture");
|
||||
|
||||
VkImageCopy image_copy = {
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0,
|
||||
src_texture->GetLayers()}, // VkImageSubresourceLayers srcSubresource
|
||||
{src_rect.left, src_rect.top, 0}, // VkOffset3D srcOffset
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, // VkImageSubresourceLayers dstSubresource
|
||||
m_config.layers},
|
||||
{dst_rect.left, dst_rect.top, 0}, // VkOffset3D dstOffset
|
||||
{static_cast<uint32_t>(src_rect.GetWidth()), static_cast<uint32_t>(src_rect.GetHeight()),
|
||||
1} // VkExtent3D extent
|
||||
};
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, src_level, src_layer, src_texture->GetLayers()},
|
||||
{src_rect.left, src_rect.top, 0},
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, dst_level, dst_layer, m_config.layers},
|
||||
{dst_rect.left, dst_rect.top, 0},
|
||||
{static_cast<uint32_t>(src_rect.GetWidth()), static_cast<uint32_t>(src_rect.GetHeight()), 1}};
|
||||
|
||||
// Must be called outside of a render pass.
|
||||
StateTracker::GetInstance()->EndRenderPass();
|
||||
|
@ -197,12 +146,20 @@ void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
|
|||
vkCmdCopyImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), src_texture->GetImage(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_texture->GetImage(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
|
||||
|
||||
// Ensure both textures remain in the SHADER_READ_ONLY layout so they can be bound.
|
||||
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
||||
|
||||
void VKTexture::ScaleTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
|
||||
Texture2D* src_texture,
|
||||
const MathUtil::Rectangle<int>& src_rect)
|
||||
void VKTexture::ScaleRectangleFromTexture(const AbstractTexture* source,
|
||||
const MathUtil::Rectangle<int>& src_rect,
|
||||
const MathUtil::Rectangle<int>& dst_rect)
|
||||
{
|
||||
Texture2D* src_texture = static_cast<const VKTexture*>(source)->GetRawTexIdentifier();
|
||||
|
||||
// Can't do this within a game render pass.
|
||||
StateTracker::GetInstance()->EndRenderPass();
|
||||
StateTracker::GetInstance()->SetPendingRebind();
|
||||
|
@ -235,27 +192,10 @@ void VKTexture::ScaleTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
|
|||
static_cast<int>(src_texture->GetWidth()),
|
||||
static_cast<int>(src_texture->GetHeight()));
|
||||
draw.EndRenderPass();
|
||||
}
|
||||
|
||||
void VKTexture::CopyRectangleFromTexture(const AbstractTexture* source,
|
||||
const MathUtil::Rectangle<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect)
|
||||
{
|
||||
auto* raw_source_texture = static_cast<const VKTexture*>(source)->GetRawTexIdentifier();
|
||||
CopyRectangleFromTexture(raw_source_texture, srcrect, dstrect);
|
||||
}
|
||||
|
||||
void VKTexture::CopyRectangleFromTexture(Texture2D* source, const MathUtil::Rectangle<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect)
|
||||
{
|
||||
if (srcrect.GetWidth() == dstrect.GetWidth() && srcrect.GetHeight() == dstrect.GetHeight())
|
||||
CopyTextureRectangle(dstrect, source, srcrect);
|
||||
else
|
||||
ScaleTextureRectangle(dstrect, source, srcrect);
|
||||
|
||||
// Ensure both textures remain in the SHADER_READ_ONLY layout so they can be bound.
|
||||
source->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
||||
|
@ -291,7 +231,7 @@ void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8*
|
|||
u32 upload_alignment = static_cast<u32>(g_vulkan_context->GetBufferImageGranularity());
|
||||
u32 block_size = Util::GetBlockSize(m_texture->GetFormat());
|
||||
u32 num_rows = Common::AlignUp(height, block_size) / block_size;
|
||||
size_t source_pitch = CalculateHostTextureLevelPitch(m_config.format, row_length);
|
||||
size_t source_pitch = CalculateStrideForFormat(m_config.format, row_length);
|
||||
size_t upload_size = source_pitch * num_rows;
|
||||
std::unique_ptr<StagingBuffer> temp_buffer;
|
||||
VkBuffer upload_buffer;
|
||||
|
@ -356,4 +296,224 @@ void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8*
|
|||
}
|
||||
}
|
||||
|
||||
VKStagingTexture::VKStagingTexture(StagingTextureType type, const TextureConfig& config,
|
||||
std::unique_ptr<StagingBuffer> buffer)
|
||||
: AbstractStagingTexture(type, config), m_staging_buffer(std::move(buffer))
|
||||
{
|
||||
}
|
||||
|
||||
VKStagingTexture::~VKStagingTexture()
|
||||
{
|
||||
if (m_needs_flush)
|
||||
VKStagingTexture::Flush();
|
||||
}
|
||||
|
||||
std::unique_ptr<VKStagingTexture> VKStagingTexture::Create(StagingTextureType type,
|
||||
const TextureConfig& config)
|
||||
{
|
||||
size_t stride = config.GetStride();
|
||||
size_t buffer_size = stride * static_cast<size_t>(config.height);
|
||||
|
||||
STAGING_BUFFER_TYPE buffer_type;
|
||||
VkImageUsageFlags buffer_usage;
|
||||
if (type == StagingTextureType::Readback)
|
||||
{
|
||||
buffer_type = STAGING_BUFFER_TYPE_READBACK;
|
||||
buffer_usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
else if (type == StagingTextureType::Upload)
|
||||
{
|
||||
buffer_type = STAGING_BUFFER_TYPE_UPLOAD;
|
||||
buffer_usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer_type = STAGING_BUFFER_TYPE_READBACK;
|
||||
buffer_usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
|
||||
VkBuffer buffer;
|
||||
VkDeviceMemory memory;
|
||||
bool coherent;
|
||||
if (!StagingBuffer::AllocateBuffer(buffer_type, buffer_size, buffer_usage, &buffer, &memory,
|
||||
&coherent))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<StagingBuffer> staging_buffer =
|
||||
std::make_unique<StagingBuffer>(buffer_type, buffer, memory, buffer_size, coherent);
|
||||
std::unique_ptr<VKStagingTexture> staging_tex = std::unique_ptr<VKStagingTexture>(
|
||||
new VKStagingTexture(type, config, std::move(staging_buffer)));
|
||||
|
||||
// Use persistent mapping.
|
||||
if (!staging_tex->m_staging_buffer->Map())
|
||||
return nullptr;
|
||||
staging_tex->m_map_pointer = staging_tex->m_staging_buffer->GetMapPointer();
|
||||
staging_tex->m_map_stride = stride;
|
||||
return staging_tex;
|
||||
}
|
||||
|
||||
void VKStagingTexture::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);
|
||||
|
||||
Texture2D* src_tex = static_cast<const VKTexture*>(src)->GetRawTexIdentifier();
|
||||
CopyFromTexture(src_tex, src_rect, src_layer, src_level, dst_rect);
|
||||
}
|
||||
|
||||
void VKStagingTexture::CopyFromTexture(Texture2D* src, const MathUtil::Rectangle<int>& src_rect,
|
||||
u32 src_layer, u32 src_level,
|
||||
const MathUtil::Rectangle<int>& dst_rect)
|
||||
{
|
||||
if (m_needs_flush)
|
||||
{
|
||||
// Drop copy before reusing it.
|
||||
g_command_buffer_mgr->RemoveFencePointCallback(this);
|
||||
m_flush_fence = VK_NULL_HANDLE;
|
||||
m_needs_flush = false;
|
||||
}
|
||||
|
||||
VkImageLayout old_layout = src->GetLayout();
|
||||
src->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
|
||||
// Issue the image->buffer copy, but delay it for now.
|
||||
VkBufferImageCopy image_copy = {};
|
||||
VkImageAspectFlags aspect =
|
||||
Util::IsDepthFormat(src->GetFormat()) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
image_copy.bufferOffset =
|
||||
static_cast<VkDeviceSize>(static_cast<size_t>(dst_rect.top) * m_config.GetStride() +
|
||||
static_cast<size_t>(dst_rect.left) * m_texel_size);
|
||||
image_copy.bufferRowLength = static_cast<u32>(m_config.width);
|
||||
image_copy.bufferImageHeight = 0;
|
||||
image_copy.imageSubresource = {aspect, src_level, src_layer, 1};
|
||||
image_copy.imageOffset = {src_rect.left, src_rect.top, 0};
|
||||
image_copy.imageExtent = {static_cast<u32>(src_rect.GetWidth()),
|
||||
static_cast<u32>(src_rect.GetHeight()), 1u};
|
||||
vkCmdCopyImageToBuffer(g_command_buffer_mgr->GetCurrentCommandBuffer(), src->GetImage(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_staging_buffer->GetBuffer(), 1,
|
||||
&image_copy);
|
||||
|
||||
// Restore old source texture layout.
|
||||
src->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), old_layout);
|
||||
|
||||
m_needs_flush = true;
|
||||
g_command_buffer_mgr->AddFencePointCallback(this,
|
||||
[this](VkCommandBuffer buf, VkFence fence) {
|
||||
_assert_(m_needs_flush);
|
||||
m_flush_fence = fence;
|
||||
},
|
||||
[this](VkFence fence) {
|
||||
m_flush_fence = VK_NULL_HANDLE;
|
||||
m_needs_flush = false;
|
||||
g_command_buffer_mgr->RemoveFencePointCallback(
|
||||
this);
|
||||
});
|
||||
}
|
||||
|
||||
void VKStagingTexture::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 (m_needs_flush)
|
||||
{
|
||||
// Drop copy before reusing it.
|
||||
g_command_buffer_mgr->RemoveFencePointCallback(this);
|
||||
m_flush_fence = VK_NULL_HANDLE;
|
||||
m_needs_flush = false;
|
||||
}
|
||||
|
||||
// Flush caches before copying.
|
||||
m_staging_buffer->FlushCPUCache();
|
||||
|
||||
Texture2D* dst_tex = static_cast<const VKTexture*>(dst)->GetRawTexIdentifier();
|
||||
VkImageLayout old_layout = dst_tex->GetLayout();
|
||||
dst_tex->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
// Issue the image->buffer copy, but delay it for now.
|
||||
VkBufferImageCopy image_copy = {};
|
||||
image_copy.bufferOffset =
|
||||
static_cast<VkDeviceSize>(static_cast<size_t>(src_rect.top) * m_config.GetStride() +
|
||||
static_cast<size_t>(src_rect.left) * m_texel_size);
|
||||
image_copy.bufferRowLength = static_cast<u32>(m_config.width);
|
||||
image_copy.bufferImageHeight = 0;
|
||||
image_copy.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, dst_level, dst_layer, 1};
|
||||
image_copy.imageOffset = {dst_rect.left, dst_rect.top, 0};
|
||||
image_copy.imageExtent = {static_cast<u32>(dst_rect.GetWidth()),
|
||||
static_cast<u32>(dst_rect.GetHeight()), 1u};
|
||||
vkCmdCopyBufferToImage(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
m_staging_buffer->GetBuffer(), dst_tex->GetImage(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
|
||||
|
||||
// Restore old source texture layout.
|
||||
dst_tex->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), old_layout);
|
||||
|
||||
m_needs_flush = true;
|
||||
g_command_buffer_mgr->AddFencePointCallback(this,
|
||||
[this](VkCommandBuffer buf, VkFence fence) {
|
||||
_assert_(m_needs_flush);
|
||||
m_flush_fence = fence;
|
||||
},
|
||||
[this](VkFence fence) {
|
||||
m_flush_fence = VK_NULL_HANDLE;
|
||||
m_needs_flush = false;
|
||||
g_command_buffer_mgr->RemoveFencePointCallback(
|
||||
this);
|
||||
});
|
||||
}
|
||||
|
||||
bool VKStagingTexture::Map()
|
||||
{
|
||||
// Always mapped.
|
||||
return true;
|
||||
}
|
||||
|
||||
void VKStagingTexture::Unmap()
|
||||
{
|
||||
// Always mapped.
|
||||
}
|
||||
|
||||
void VKStagingTexture::Flush()
|
||||
{
|
||||
if (!m_needs_flush)
|
||||
return;
|
||||
|
||||
// Either of the below two calls will cause the callback to fire.
|
||||
g_command_buffer_mgr->RemoveFencePointCallback(this);
|
||||
if (m_flush_fence != VK_NULL_HANDLE)
|
||||
{
|
||||
// WaitForFence should fire the callback.
|
||||
g_command_buffer_mgr->WaitForFence(m_flush_fence);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't have a fence, and are pending. That means the readback is in the current
|
||||
// command buffer, and must execute it to populate the staging texture.
|
||||
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
|
||||
}
|
||||
m_needs_flush = false;
|
||||
|
||||
// For readback textures, invalidate the CPU cache as there is new data there.
|
||||
if (m_type == StagingTextureType::Readback)
|
||||
m_staging_buffer->InvalidateCPUCache();
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
|
|
@ -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 @@ public:
|
|||
~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 @@ private:
|
|||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
std::optional<RawTextureInfo> MapFullImpl() override;
|
||||
std::optional<RawTextureInfo> MapRegionImpl(u32 level, u32 x, u32 y, u32 width,
|
||||
u32 height) override;
|
||||
|
||||
std::unique_ptr<Texture2D> m_texture;
|
||||
std::unique_ptr<StagingTexture2D> m_staging_texture;
|
||||
VkFramebuffer m_framebuffer;
|
||||
};
|
||||
|
||||
class VKStagingTexture final : public AbstractStagingTexture
|
||||
{
|
||||
public:
|
||||
VKStagingTexture() = delete;
|
||||
~VKStagingTexture();
|
||||
|
||||
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;
|
||||
|
||||
// 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,93 +22,51 @@ 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);
|
||||
|
||||
if (!result.has_value())
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
return TextureToPng(reinterpret_cast<const u8*>(readback_texture->GetMappedPointer()),
|
||||
static_cast<int>(readback_texture->GetMappedStride()), filename, level_width,
|
||||
level_height);
|
||||
}
|
||||
|
||||
bool AbstractTexture::IsCompressedFormat(AbstractTextureFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case AbstractTextureFormat::DXT1:
|
||||
case AbstractTextureFormat::DXT3:
|
||||
case AbstractTextureFormat::DXT5:
|
||||
case AbstractTextureFormat::BPTC:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
auto raw_data = result.value();
|
||||
return TextureToPng(raw_data.data, raw_data.stride, filename, raw_data.width, raw_data.height);
|
||||
}
|
||||
|
||||
std::optional<AbstractTexture::RawTextureInfo> AbstractTexture::Map()
|
||||
{
|
||||
if (m_currently_mapped)
|
||||
{
|
||||
Unmap();
|
||||
m_currently_mapped = false;
|
||||
}
|
||||
auto result = MapFullImpl();
|
||||
|
||||
if (!result.has_value())
|
||||
{
|
||||
m_currently_mapped = false;
|
||||
return {};
|
||||
}
|
||||
|
||||
m_currently_mapped = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<AbstractTexture::RawTextureInfo> AbstractTexture::Map(u32 level, u32 x, u32 y,
|
||||
u32 width, u32 height)
|
||||
{
|
||||
_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())
|
||||
{
|
||||
m_currently_mapped = false;
|
||||
return {};
|
||||
}
|
||||
|
||||
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::CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
|
@ -117,8 +77,30 @@ size_t AbstractTexture::CalculateHostTextureLevelPitch(AbstractTextureFormat for
|
|||
case AbstractTextureFormat::BPTC:
|
||||
return static_cast<size_t>(std::max(1u, row_length / 4)) * 16;
|
||||
case AbstractTextureFormat::RGBA8:
|
||||
default:
|
||||
case AbstractTextureFormat::BGRA8:
|
||||
return static_cast<size_t>(row_length) * 4;
|
||||
default:
|
||||
PanicAlert("Unhandled texture format.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t AbstractTexture::GetTexelSizeForFormat(AbstractTextureFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case AbstractTextureFormat::DXT1:
|
||||
return 8;
|
||||
case AbstractTextureFormat::DXT3:
|
||||
case AbstractTextureFormat::DXT5:
|
||||
case AbstractTextureFormat::BPTC:
|
||||
return 16;
|
||||
case AbstractTextureFormat::RGBA8:
|
||||
case AbstractTextureFormat::BGRA8:
|
||||
return 4;
|
||||
default:
|
||||
PanicAlert("Unhandled texture format.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
@ -17,38 +16,27 @@ class AbstractTexture
|
|||
public:
|
||||
explicit AbstractTexture(const TextureConfig& c);
|
||||
virtual ~AbstractTexture();
|
||||
|
||||
virtual void Bind(unsigned int stage) = 0;
|
||||
bool Save(const std::string& filename, unsigned int level);
|
||||
|
||||
struct RawTextureInfo
|
||||
{
|
||||
const u8* data;
|
||||
u32 stride;
|
||||
u32 width;
|
||||
u32 height;
|
||||
};
|
||||
|
||||
std::optional<RawTextureInfo> Map();
|
||||
std::optional<RawTextureInfo> Map(u32 level, u32 x, u32 y, u32 width, u32 height);
|
||||
std::optional<RawTextureInfo> Map(u32 level);
|
||||
virtual void Unmap();
|
||||
|
||||
virtual void CopyRectangleFromTexture(const AbstractTexture* source,
|
||||
const MathUtil::Rectangle<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect) = 0;
|
||||
virtual 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) = 0;
|
||||
virtual void ScaleRectangleFromTexture(const AbstractTexture* source,
|
||||
const MathUtil::Rectangle<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect) = 0;
|
||||
virtual void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
|
||||
size_t buffer_size) = 0;
|
||||
|
||||
static bool IsCompressedHostTextureFormat(AbstractTextureFormat format);
|
||||
static size_t CalculateHostTextureLevelPitch(AbstractTextureFormat format, u32 row_length);
|
||||
bool Save(const std::string& filename, unsigned int level);
|
||||
|
||||
static bool IsCompressedFormat(AbstractTextureFormat format);
|
||||
static size_t CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length);
|
||||
static size_t GetTexelSizeForFormat(AbstractTextureFormat format);
|
||||
|
||||
const TextureConfig& GetConfig() const;
|
||||
|
||||
protected:
|
||||
virtual std::optional<RawTextureInfo> MapFullImpl();
|
||||
virtual std::optional<RawTextureInfo> MapRegionImpl(u32 level, u32 x, u32 y, u32 width,
|
||||
u32 height);
|
||||
bool m_currently_mapped = false;
|
||||
|
||||
const TextureConfig m_config;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
set(SRCS
|
||||
AbstractStagingTexture.cpp
|
||||
AbstractTexture.cpp
|
||||
AsyncRequests.cpp
|
||||
AsyncShaderCompiler.cpp
|
||||
|
|
|
@ -49,7 +49,7 @@ void VideoBackendBase::Video_CleanupShared()
|
|||
{
|
||||
// First stop any framedumping, which might need to dump the last xfb frame. This process
|
||||
// can require additional graphics sub-systems so it needs to be done first
|
||||
g_renderer->ExitFramedumping();
|
||||
g_renderer->ShutdownFrameDumping();
|
||||
|
||||
Video_Cleanup();
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "Core/Movie.h"
|
||||
|
||||
#include "VideoCommon/AVIDump.h"
|
||||
#include "VideoCommon/AbstractStagingTexture.h"
|
||||
#include "VideoCommon/AbstractTexture.h"
|
||||
#include "VideoCommon/BPMemory.h"
|
||||
#include "VideoCommon/CPMemory.h"
|
||||
|
@ -100,15 +101,6 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height)
|
|||
|
||||
Renderer::~Renderer() = default;
|
||||
|
||||
void Renderer::ExitFramedumping()
|
||||
{
|
||||
ShutdownFrameDumping();
|
||||
if (m_frame_dump_thread.joinable())
|
||||
m_frame_dump_thread.join();
|
||||
|
||||
m_dump_texture.reset();
|
||||
}
|
||||
|
||||
void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbStride, u32 fbHeight,
|
||||
float Gamma)
|
||||
{
|
||||
|
@ -635,14 +627,10 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
|
|||
m_aspect_wide = flush_count_anamorphic > 0.75 * flush_total;
|
||||
}
|
||||
|
||||
if (IsFrameDumping() && m_last_xfb_texture)
|
||||
{
|
||||
FinishFrameData();
|
||||
}
|
||||
else
|
||||
{
|
||||
ShutdownFrameDumping();
|
||||
}
|
||||
// Ensure the last frame was written to the dump.
|
||||
// This is required even if frame dumping has stopped, since the frame dump is one frame
|
||||
// behind the renderer.
|
||||
FlushFrameDump();
|
||||
|
||||
bool update_frame_count = false;
|
||||
if (xfbAddr && fbWidth && fbStride && fbHeight)
|
||||
|
@ -668,10 +656,9 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
|
|||
|
||||
m_fps_counter.Update();
|
||||
update_frame_count = true;
|
||||
|
||||
if (IsFrameDumping())
|
||||
{
|
||||
DoDumpFrame();
|
||||
}
|
||||
DumpCurrentFrame();
|
||||
}
|
||||
|
||||
// Update our last xfb values
|
||||
|
@ -701,20 +688,16 @@ bool Renderer::IsFrameDumping()
|
|||
return false;
|
||||
}
|
||||
|
||||
void Renderer::DoDumpFrame()
|
||||
void Renderer::DumpCurrentFrame()
|
||||
{
|
||||
UpdateFrameDumpTexture();
|
||||
// Scale/render to frame dump texture.
|
||||
RenderFrameDump();
|
||||
|
||||
auto result = m_dump_texture->Map();
|
||||
if (result.has_value())
|
||||
{
|
||||
auto raw_data = result.value();
|
||||
DumpFrameData(raw_data.data, raw_data.width, raw_data.height, raw_data.stride,
|
||||
AVIDump::FetchState(m_last_xfb_ticks));
|
||||
}
|
||||
// Queue a readback for the next frame.
|
||||
QueueFrameDumpReadback();
|
||||
}
|
||||
|
||||
void Renderer::UpdateFrameDumpTexture()
|
||||
void Renderer::RenderFrameDump()
|
||||
{
|
||||
int target_width, target_height;
|
||||
if (!g_ActiveConfig.bInternalResolutionFrameDumps && !IsHeadless())
|
||||
|
@ -729,33 +712,99 @@ void Renderer::UpdateFrameDumpTexture()
|
|||
m_last_xfb_texture->GetConfig().width, m_last_xfb_texture->GetConfig().height);
|
||||
}
|
||||
|
||||
if (m_dump_texture == nullptr ||
|
||||
m_dump_texture->GetConfig().width != static_cast<u32>(target_width) ||
|
||||
m_dump_texture->GetConfig().height != static_cast<u32>(target_height))
|
||||
// Ensure framebuffer exists (we lazily allocate it in case frame dumping isn't used).
|
||||
// Or, resize texture if it isn't large enough to accommodate the current frame.
|
||||
if (!m_frame_dump_render_texture ||
|
||||
m_frame_dump_render_texture->GetConfig().width != static_cast<u32>(target_width) ||
|
||||
m_frame_dump_render_texture->GetConfig().height == static_cast<u32>(target_height))
|
||||
{
|
||||
TextureConfig config;
|
||||
config.width = target_width;
|
||||
config.height = target_height;
|
||||
config.rendertarget = true;
|
||||
m_dump_texture = g_texture_cache->CreateTexture(config);
|
||||
// Recreate texture objects. Release before creating so we don't temporarily use twice the RAM.
|
||||
TextureConfig config(target_width, target_height, 1, 1, AbstractTextureFormat::RGBA8, true);
|
||||
m_frame_dump_render_texture.reset();
|
||||
m_frame_dump_render_texture = CreateTexture(config);
|
||||
_assert_(m_frame_dump_render_texture);
|
||||
}
|
||||
m_dump_texture->CopyRectangleFromTexture(m_last_xfb_texture, m_last_xfb_region,
|
||||
EFBRectangle{0, 0, target_width, target_height});
|
||||
|
||||
// Scaling is likely to occur here, but if possible, do a bit-for-bit copy.
|
||||
if (m_last_xfb_region.GetWidth() != target_width ||
|
||||
m_last_xfb_region.GetHeight() != target_height)
|
||||
{
|
||||
m_frame_dump_render_texture->ScaleRectangleFromTexture(
|
||||
m_last_xfb_texture, m_last_xfb_region, EFBRectangle{0, 0, target_width, target_height});
|
||||
}
|
||||
else
|
||||
{
|
||||
m_frame_dump_render_texture->CopyRectangleFromTexture(
|
||||
m_last_xfb_texture, m_last_xfb_region, 0, 0,
|
||||
EFBRectangle{0, 0, target_width, target_height}, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::QueueFrameDumpReadback()
|
||||
{
|
||||
// Index 0 was just sent to AVI dump. Swap with the second texture.
|
||||
if (m_frame_dump_readback_textures[0])
|
||||
std::swap(m_frame_dump_readback_textures[0], m_frame_dump_readback_textures[1]);
|
||||
|
||||
std::unique_ptr<AbstractStagingTexture>& rbtex = m_frame_dump_readback_textures[0];
|
||||
if (!rbtex || rbtex->GetConfig() != m_frame_dump_render_texture->GetConfig())
|
||||
{
|
||||
rbtex = CreateStagingTexture(StagingTextureType::Readback,
|
||||
m_frame_dump_render_texture->GetConfig());
|
||||
}
|
||||
|
||||
m_last_frame_state = AVIDump::FetchState(m_last_xfb_ticks);
|
||||
m_last_frame_exported = true;
|
||||
rbtex->CopyFromTexture(m_frame_dump_render_texture.get(), 0, 0);
|
||||
}
|
||||
|
||||
void Renderer::FlushFrameDump()
|
||||
{
|
||||
if (!m_last_frame_exported)
|
||||
return;
|
||||
|
||||
// Ensure the previously-queued frame was encoded.
|
||||
FinishFrameData();
|
||||
|
||||
// Queue encoding of the last frame dumped.
|
||||
std::unique_ptr<AbstractStagingTexture>& rbtex = m_frame_dump_readback_textures[0];
|
||||
rbtex->Flush();
|
||||
if (rbtex->Map())
|
||||
{
|
||||
DumpFrameData(reinterpret_cast<u8*>(rbtex->GetMappedPointer()), rbtex->GetConfig().width,
|
||||
rbtex->GetConfig().height, static_cast<int>(rbtex->GetMappedStride()),
|
||||
m_last_frame_state);
|
||||
rbtex->Unmap();
|
||||
}
|
||||
|
||||
m_last_frame_exported = false;
|
||||
|
||||
// Shutdown frame dumping if it is no longer active.
|
||||
if (!IsFrameDumping())
|
||||
ShutdownFrameDumping();
|
||||
}
|
||||
|
||||
void Renderer::ShutdownFrameDumping()
|
||||
{
|
||||
// Ensure the last queued readback has been sent to the encoder.
|
||||
FlushFrameDump();
|
||||
|
||||
if (!m_frame_dump_thread_running.IsSet())
|
||||
return;
|
||||
|
||||
// Ensure previous frame has been encoded.
|
||||
FinishFrameData();
|
||||
|
||||
// Wake thread up, and wait for it to exit.
|
||||
m_frame_dump_thread_running.Clear();
|
||||
m_frame_dump_start.Set();
|
||||
if (m_frame_dump_thread.joinable())
|
||||
m_frame_dump_thread.join();
|
||||
}
|
||||
|
||||
void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state)
|
||||
{
|
||||
m_frame_dump_config = FrameDumpConfig{m_last_xfb_texture, data, w, h, stride, state};
|
||||
m_frame_dump_config = FrameDumpConfig{data, w, h, stride, state};
|
||||
|
||||
if (!m_frame_dump_thread_running.IsSet())
|
||||
{
|
||||
|
@ -765,6 +814,7 @@ void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, const AVI
|
|||
m_frame_dump_thread = std::thread(&Renderer::RunFrameDumps, this);
|
||||
}
|
||||
|
||||
// Wake worker thread up.
|
||||
m_frame_dump_start.Set();
|
||||
m_frame_dump_frame_running = true;
|
||||
}
|
||||
|
@ -776,7 +826,6 @@ void Renderer::FinishFrameData()
|
|||
|
||||
m_frame_dump_done.Wait();
|
||||
m_frame_dump_frame_running = false;
|
||||
m_frame_dump_config.texture->Unmap();
|
||||
}
|
||||
|
||||
void Renderer::RunFrameDumps()
|
||||
|
|
|
@ -34,8 +34,11 @@
|
|||
|
||||
class AbstractRawTexture;
|
||||
class AbstractTexture;
|
||||
class AbstractStagingTexture;
|
||||
class PostProcessingShaderImplementation;
|
||||
struct TextureConfig;
|
||||
enum class EFBAccessType;
|
||||
enum class StagingTextureType;
|
||||
|
||||
struct EfbPokeData
|
||||
{
|
||||
|
@ -79,6 +82,10 @@ public:
|
|||
virtual void RestoreState() {}
|
||||
virtual void ResetAPIState() {}
|
||||
virtual void RestoreAPIState() {}
|
||||
virtual std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) = 0;
|
||||
virtual std::unique_ptr<AbstractStagingTexture>
|
||||
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) = 0;
|
||||
|
||||
// Ideal internal resolution - multiple of the native EFB resolution
|
||||
int GetTargetWidth() const { return m_target_width; }
|
||||
int GetTargetHeight() const { return m_target_height; }
|
||||
|
@ -145,7 +152,7 @@ public:
|
|||
virtual void ChangeSurface(void* new_surface_handle) {}
|
||||
bool UseVertexDepthRange() const;
|
||||
|
||||
void ExitFramedumping();
|
||||
void ShutdownFrameDumping();
|
||||
|
||||
protected:
|
||||
std::tuple<int, int> CalculateTargetScale(int x, int y) const;
|
||||
|
@ -185,11 +192,8 @@ protected:
|
|||
u32 m_last_host_config_bits = 0;
|
||||
|
||||
private:
|
||||
void DoDumpFrame();
|
||||
void RunFrameDumps();
|
||||
void ShutdownFrameDumping();
|
||||
std::tuple<int, int> CalculateOutputDimensions(int width, int height);
|
||||
void UpdateFrameDumpTexture();
|
||||
|
||||
PEControl::PixelFormat m_prev_efb_format = PEControl::INVALID_FMT;
|
||||
unsigned int m_efb_scale = 1;
|
||||
|
@ -207,7 +211,6 @@ private:
|
|||
bool m_frame_dump_frame_running = false;
|
||||
struct FrameDumpConfig
|
||||
{
|
||||
AbstractTexture* texture;
|
||||
const u8* data;
|
||||
int width;
|
||||
int height;
|
||||
|
@ -215,13 +218,18 @@ private:
|
|||
AVIDump::Frame state;
|
||||
} m_frame_dump_config;
|
||||
|
||||
// Texture used for screenshot/frame dumping
|
||||
std::unique_ptr<AbstractTexture> m_frame_dump_render_texture;
|
||||
std::array<std::unique_ptr<AbstractStagingTexture>, 2> m_frame_dump_readback_textures;
|
||||
AVIDump::Frame m_last_frame_state;
|
||||
bool m_last_frame_exported = false;
|
||||
|
||||
// Tracking of XFB textures so we don't render duplicate frames.
|
||||
AbstractTexture* m_last_xfb_texture = nullptr;
|
||||
u64 m_last_xfb_id = std::numeric_limits<u64>::max();
|
||||
u64 m_last_xfb_ticks = 0;
|
||||
EFBRectangle m_last_xfb_region;
|
||||
|
||||
std::unique_ptr<AbstractTexture> m_dump_texture;
|
||||
|
||||
// Note: Only used for auto-ir
|
||||
u32 m_last_xfb_width = MAX_XFB_WIDTH;
|
||||
u32 m_last_xfb_height = MAX_XFB_HEIGHT;
|
||||
|
@ -235,7 +243,23 @@ private:
|
|||
void DumpFrameToImage(const FrameDumpConfig& config);
|
||||
|
||||
bool IsFrameDumping();
|
||||
|
||||
// Asynchronously encodes the current staging texture to the frame dump.
|
||||
void DumpCurrentFrame();
|
||||
|
||||
// Fills the frame dump render texture with the current XFB texture.
|
||||
void RenderFrameDump();
|
||||
|
||||
// Queues the current frame for readback, which will be written to AVI next frame.
|
||||
void QueueFrameDumpReadback();
|
||||
|
||||
// Asynchronously encodes the specified pointer of frame data to the frame dump.
|
||||
void DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state);
|
||||
|
||||
// Ensures all rendered frames are queued for encoding.
|
||||
void FlushFrameDump();
|
||||
|
||||
// Ensures all encoded frames have been written to the output file.
|
||||
void FinishFrameData();
|
||||
};
|
||||
|
||||
|
|
|
@ -277,9 +277,9 @@ void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntry* e
|
|||
std::unique_ptr<AbstractTexture> new_texture = AllocateTexture(newconfig);
|
||||
if (new_texture)
|
||||
{
|
||||
new_texture->CopyRectangleFromTexture(entry->texture.get(),
|
||||
entry->texture->GetConfig().GetRect(),
|
||||
new_texture->GetConfig().GetRect());
|
||||
new_texture->ScaleRectangleFromTexture(entry->texture.get(),
|
||||
entry->texture->GetConfig().GetRect(),
|
||||
new_texture->GetConfig().GetRect());
|
||||
entry->texture.swap(new_texture);
|
||||
|
||||
auto config = new_texture->GetConfig();
|
||||
|
@ -406,7 +406,11 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, u8* pale
|
|||
dstrect.top = dst_y;
|
||||
dstrect.right = (dst_x + copy_width);
|
||||
dstrect.bottom = (dst_y + copy_height);
|
||||
entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, dstrect);
|
||||
for (u32 layer = 0; layer < entry->texture->GetConfig().layers; layer++)
|
||||
{
|
||||
entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, layer,
|
||||
0, dstrect, layer, 0);
|
||||
}
|
||||
|
||||
if (isPaletteTexture)
|
||||
{
|
||||
|
@ -1366,33 +1370,16 @@ bool TextureCacheBase::LoadTextureFromOverlappingTextures(TCacheEntry* entry_to_
|
|||
srcrect.right = (src_x + copy_width);
|
||||
srcrect.bottom = (src_y + copy_height);
|
||||
|
||||
if (static_cast<int>(entry->GetWidth()) == srcrect.GetWidth())
|
||||
{
|
||||
srcrect.right -= 1;
|
||||
}
|
||||
|
||||
if (static_cast<int>(entry->GetHeight()) == srcrect.GetHeight())
|
||||
{
|
||||
srcrect.bottom -= 1;
|
||||
}
|
||||
|
||||
dstrect.left = dst_x;
|
||||
dstrect.top = dst_y;
|
||||
dstrect.right = (dst_x + copy_width);
|
||||
dstrect.bottom = (dst_y + copy_height);
|
||||
|
||||
if (static_cast<int>(entry_to_update->GetWidth()) == dstrect.GetWidth())
|
||||
for (u32 layer = 0; layer < entry->texture->GetConfig().layers; layer++)
|
||||
{
|
||||
dstrect.right -= 1;
|
||||
entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, layer,
|
||||
0, dstrect, layer, 0);
|
||||
}
|
||||
|
||||
if (static_cast<int>(entry_to_update->GetHeight()) == dstrect.GetHeight())
|
||||
{
|
||||
dstrect.bottom -= 1;
|
||||
}
|
||||
|
||||
entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, dstrect);
|
||||
|
||||
updated_entry = true;
|
||||
|
||||
if (tex_info.is_palette_texture)
|
||||
|
@ -2090,7 +2077,7 @@ std::unique_ptr<AbstractTexture> TextureCacheBase::AllocateTexture(const Texture
|
|||
}
|
||||
else
|
||||
{
|
||||
entry = CreateTexture(config);
|
||||
entry = g_renderer->CreateTexture(config);
|
||||
if (!entry)
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -273,8 +273,6 @@ public:
|
|||
|
||||
void ScaleTextureCacheEntryTo(TCacheEntry* entry, u32 new_width, u32 new_height);
|
||||
|
||||
virtual std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) = 0;
|
||||
|
||||
protected:
|
||||
TextureCacheBase();
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// Refer to the license.txt file included.
|
||||
|
||||
#include "VideoCommon/TextureConfig.h"
|
||||
#include "VideoCommon/AbstractTexture.h"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
|
@ -12,7 +13,28 @@ bool TextureConfig::operator==(const TextureConfig& o) const
|
|||
std::tie(o.width, o.height, o.levels, o.layers, o.format, o.rendertarget);
|
||||
}
|
||||
|
||||
bool TextureConfig::operator!=(const TextureConfig& o) const
|
||||
{
|
||||
return !operator==(o);
|
||||
}
|
||||
|
||||
MathUtil::Rectangle<int> TextureConfig::GetRect() const
|
||||
{
|
||||
return {0, 0, static_cast<int>(width), static_cast<int>(height)};
|
||||
}
|
||||
|
||||
MathUtil::Rectangle<int> TextureConfig::GetMipRect(u32 level) const
|
||||
{
|
||||
return {0, 0, static_cast<int>(std::max(width >> level, 1u)),
|
||||
static_cast<int>(std::max(height >> level, 1u))};
|
||||
}
|
||||
|
||||
size_t TextureConfig::GetStride() const
|
||||
{
|
||||
return AbstractTexture::CalculateStrideForFormat(format, width);
|
||||
}
|
||||
|
||||
size_t TextureConfig::GetMipStride(u32 level) const
|
||||
{
|
||||
return AbstractTexture::CalculateStrideForFormat(format, std::max(width >> level, 1u));
|
||||
}
|
||||
|
|
|
@ -13,17 +13,37 @@
|
|||
enum class AbstractTextureFormat : u32
|
||||
{
|
||||
RGBA8,
|
||||
BGRA8,
|
||||
DXT1,
|
||||
DXT3,
|
||||
DXT5,
|
||||
BPTC
|
||||
BPTC,
|
||||
Undefined
|
||||
};
|
||||
|
||||
enum class StagingTextureType
|
||||
{
|
||||
Readback, // Optimize for CPU reads, GPU writes, no CPU writes
|
||||
Upload, // Optimize for CPU writes, GPU reads, no CPU reads
|
||||
Mutable // Optimize for CPU reads, GPU writes, allow slow CPU reads
|
||||
};
|
||||
|
||||
struct TextureConfig
|
||||
{
|
||||
constexpr TextureConfig() = default;
|
||||
constexpr TextureConfig(u32 width_, u32 height_, u32 levels_, u32 layers_,
|
||||
AbstractTextureFormat format_, bool rendertarget_)
|
||||
: width(width_), height(height_), levels(levels_), layers(layers_), format(format_),
|
||||
rendertarget(rendertarget_)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const TextureConfig& o) const;
|
||||
bool operator!=(const TextureConfig& o) const;
|
||||
MathUtil::Rectangle<int> GetRect() const;
|
||||
MathUtil::Rectangle<int> GetMipRect(u32 level) const;
|
||||
size_t GetStride() const;
|
||||
size_t GetMipStride(u32 level) const;
|
||||
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AbstractStagingTexture.cpp" />
|
||||
<ClCompile Include="AbstractTexture.cpp" />
|
||||
<ClCompile Include="AsyncRequests.cpp" />
|
||||
<ClCompile Include="AsyncShaderCompiler.cpp" />
|
||||
|
@ -96,6 +97,7 @@
|
|||
<ClCompile Include="XFStructs.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AbstractStagingTexture.h" />
|
||||
<ClInclude Include="AbstractTexture.h" />
|
||||
<ClInclude Include="AsyncRequests.h" />
|
||||
<ClInclude Include="AsyncShaderCompiler.h" />
|
||||
|
|
|
@ -191,6 +191,9 @@
|
|||
<ClCompile Include="UberShaderVertex.cpp">
|
||||
<Filter>Shader Generators</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AbstractStagingTexture.cpp">
|
||||
<Filter>Base</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="CommandProcessor.h" />
|
||||
|
@ -362,6 +365,9 @@
|
|||
<ClInclude Include="UberShaderVertex.h">
|
||||
<Filter>Shader Generators</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AbstractStagingTexture.h">
|
||||
<Filter>Base</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="CMakeLists.txt" />
|
||||
|
|
Loading…
Reference in New Issue